llvm-project/clang-tools-extra/clang-modernize/UseAuto/UseAutoActions.cpp

148 lines
5.8 KiB
C++

//===-- UseAuto/UseAutoActions.cpp - Matcher callback impl ----------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief This file contains the implementation of callbacks for the UseAuto
/// transform.
///
//===----------------------------------------------------------------------===//
#include "UseAutoActions.h"
#include "UseAutoMatchers.h"
#include "clang/AST/ASTContext.h"
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace clang;
void IteratorReplacer::run(const MatchFinder::MatchResult &Result) {
const DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
const Expr *ExprInit = V->getInit();
// Skip expressions with cleanups from the initializer expression.
if (const ExprWithCleanups *E = dyn_cast<ExprWithCleanups>(ExprInit))
ExprInit = E->getSubExpr();
const CXXConstructExpr *Construct = cast<CXXConstructExpr>(ExprInit);
assert(Construct->getNumArgs() == 1u &&
"Expected constructor with single argument");
// Drill down to the as-written initializer.
const Expr *E = Construct->arg_begin()->IgnoreParenImpCasts();
if (E != E->IgnoreConversionOperator())
// We hit a conversion operator. Early-out now as they imply an implicit
// conversion from a different type. Could also mean an explicit
// conversion from the same type but that's pretty rare.
return;
if (const CXXConstructExpr *NestedConstruct = dyn_cast<CXXConstructExpr>(E))
// If we ran into an implicit conversion constructor, can't convert.
//
// FIXME: The following only checks if the constructor can be used
// implicitly, not if it actually was. Cases where the converting
// constructor was used explicitly won't get converted.
if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
return;
if (!Result.Context->hasSameType(V->getType(), E->getType()))
return;
}
// Get the type location using the first declartion.
const VarDecl *V = cast<VarDecl>(*D->decl_begin());
TypeLoc TL = V->getTypeSourceInfo()->getTypeLoc();
// WARNING: TypeLoc::getSourceRange() will include the identifier for things
// like function pointers. Not a concern since this action only works with
// iterators but something to keep in mind in the future.
CharSourceRange Range(TL.getSourceRange(), true);
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto"));
++AcceptedChanges;
}
void NewReplacer::run(const MatchFinder::MatchResult &Result) {
const DeclStmt *D = Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId);
assert(D && "Bad Callback. No node provided");
SourceManager &SM = *Result.SourceManager;
if (!Owner.isFileModifiable(SM, D->getLocStart()))
return;
const VarDecl *FirstDecl = cast<VarDecl>(*D->decl_begin());
// Ensure that there is at least one VarDecl within de DeclStmt.
assert(FirstDecl && "No VarDecl provided");
const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
std::vector<SourceLocation> StarLocations;
for (clang::DeclStmt::const_decl_iterator DI = D->decl_begin(),
DE = D->decl_end();
DI != DE; ++DI) {
const VarDecl *V = cast<VarDecl>(*DI);
// Ensure that every DeclStmt child is a VarDecl.
assert(V && "No VarDecl provided");
const CXXNewExpr *NewExpr =
cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
// Ensure that every VarDecl has a CXXNewExpr initializer.
assert(NewExpr && "No CXXNewExpr provided");
// If VarDecl and Initializer have mismatching unqualified types.
if (!Result.Context->hasSameUnqualifiedType(V->getType(),
NewExpr->getType()))
return;
// Remove explicitly written '*' from declarations where there's more than
// one declaration in the declaration list.
if (DI == D->decl_begin())
continue;
// All subsequent delcarations should match the same non-decorated type.
if (FirstDeclType != V->getType().getCanonicalType())
return;
PointerTypeLoc Q =
V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
while (!Q.isNull()) {
StarLocations.push_back(Q.getStarLoc());
Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
}
}
// Remove '*' from declarations using the saved star locations.
for (std::vector<SourceLocation>::iterator I = StarLocations.begin(),
E = StarLocations.end();
I != E; ++I) {
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, *I, 1, ""));
}
// FIXME: There is, however, one case we can address: when the VarDecl
// pointee is the same as the initializer, just more CV-qualified. However,
// TypeLoc information is not reliable where CV qualifiers are concerned so
// we can't do anything about this case for now.
CharSourceRange Range(
FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange(), true);
// Space after 'auto' to handle cases where the '*' in the pointer type
// is next to the identifier. This avoids changing 'int *p' into 'autop'.
Owner.addReplacementForCurrentTU(tooling::Replacement(SM, Range, "auto "));
++AcceptedChanges;
}