| //===-- 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; |
| } |