SourceLocation RewriteUtils::getVarDeclTypeLocEnd(const VarDecl *VD) { TypeLoc VarTypeLoc = VD->getTypeSourceInfo()->getTypeLoc(); const IdentifierInfo *Id = VD->getType().getBaseTypeIdentifier(); // handle a special case shown as below: // x; // *y[]; // (*z)[]; // void foo(void) {...} // where x implicitly has type of int, whereas y has type of int * if (!Id) { SourceLocation EndLoc = VD->getLocation(); const char *Buf = SrcManager->getCharacterData(EndLoc); int Offset = -1; SourceLocation NewEndLoc = EndLoc.getLocWithOffset(Offset); if (!NewEndLoc.isValid()) return EndLoc; Buf--; while (isspace(*Buf) || (*Buf == '*') || (*Buf == '(')) { Offset--; NewEndLoc = EndLoc.getLocWithOffset(Offset); if (!NewEndLoc.isValid()) return EndLoc.getLocWithOffset(Offset+1); Buf--; } return EndLoc.getLocWithOffset(Offset+1); } TypeLoc NextTL = VarTypeLoc.getNextTypeLoc(); while (!NextTL.isNull()) { VarTypeLoc = NextTL; NextTL = NextTL.getNextTypeLoc(); } SourceRange TypeLocRange = VarTypeLoc.getSourceRange(); SourceLocation EndLoc = getEndLocationFromBegin(TypeLocRange); TransAssert(EndLoc.isValid() && "Invalid EndLoc!"); const Type *Ty = VarTypeLoc.getTypePtr(); // I am not sure why, but for a declaration like below: // unsigned int a; (or long long a;) // TypeLoc.getBeginLoc() returns the position of 'u' // TypeLoc.getEndLoc() also returns the position of 'u' // The size of TypeLoc.getSourceRange() is 8, which is the // length of "unsigned" // Then we are getting trouble, because now EndLoc is right // after 'd', but we need it points to the location after "int". // skipPossibleTypeRange corrects the above deviation // Or am I doing something horrible here? EndLoc = skipPossibleTypeRange(Ty, EndLoc, VD->getLocation()); return EndLoc; }
SourceLocation DeclaratorDecl::getTypeSpecStartLoc() const { if (DeclInfo) { TypeLoc TL = DeclInfo->getTypeLoc(); while (true) { TypeLoc NextTL = TL.getNextTypeLoc(); if (!NextTL) return TL.getSourceRange().getBegin(); TL = NextTL; } } return SourceLocation(); }
void PassByValueCheck::check(const MatchFinder::MatchResult &Result) { const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("Ctor"); const auto *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>("Param"); const auto *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>("Initializer"); SourceManager &SM = *Result.SourceManager; // If the parameter is used or anything other than the copy, do not apply // the changes. if (!paramReferredExactlyOnce(Ctor, ParamDecl)) return; // If the parameter is trivial to copy, don't move it. Moving a trivivally // copyable type will cause a problem with misc-move-const-arg if (ParamDecl->getType().isTriviallyCopyableType(*Result.Context)) return; auto Diag = diag(ParamDecl->getLocStart(), "pass by value and use std::move"); // Iterate over all declarations of the constructor. for (const ParmVarDecl *ParmDecl : collectParamDecls(Ctor, ParamDecl)) { auto ParamTL = ParmDecl->getTypeSourceInfo()->getTypeLoc(); auto RefTL = ParamTL.getAs<ReferenceTypeLoc>(); // Do not replace if it is already a value, skip. if (RefTL.isNull()) continue; TypeLoc ValueTL = RefTL.getPointeeLoc(); auto TypeRange = CharSourceRange::getTokenRange(ParmDecl->getLocStart(), ParamTL.getLocEnd()); std::string ValueStr = Lexer::getSourceText( CharSourceRange::getTokenRange(ValueTL.getSourceRange()), SM, Result.Context->getLangOpts()) .str(); ValueStr += ' '; Diag << FixItHint::CreateReplacement(TypeRange, ValueStr); } // Use std::move in the initialization list. Diag << FixItHint::CreateInsertion(Initializer->getRParenLoc(), ")") << FixItHint::CreateInsertion( Initializer->getLParenLoc().getLocWithOffset(1), "std::move("); if (auto IncludeFixit = Inserter->CreateIncludeInsertion( Result.SourceManager->getFileID(Initializer->getSourceLocation()), "utility", /*IsAngled=*/true)) { Diag << *IncludeFixit; } }
void ConstructorParamReplacer::run(const MatchFinder::MatchResult &Result) { assert(IncludeManager && "Include directives manager not set."); SourceManager &SM = *Result.SourceManager; const CXXConstructorDecl *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>(PassByValueCtorId); const ParmVarDecl *ParamDecl = Result.Nodes.getNodeAs<ParmVarDecl>(PassByValueParamId); const CXXCtorInitializer *Initializer = Result.Nodes.getNodeAs<CXXCtorInitializer>(PassByValueInitializerId); assert(Ctor && ParamDecl && Initializer && "Bad Callback, missing node."); // Check this now to avoid unnecessary work. The param locations are checked // later. if (!Owner.isFileModifiable(SM, Initializer->getSourceLocation())) return; // The parameter will be in an unspecified state after the move, so check if // the parameter is used for anything else other than the copy. If so do not // apply any changes. if (!paramReferredExactlyOnce(Ctor, ParamDecl)) return; llvm::SmallVector<const ParmVarDecl *, 2> AllParamDecls; collectParamDecls(Ctor, ParamDecl, AllParamDecls); // Generate all replacements for the params. llvm::SmallVector<Replacement, 2> ParamReplaces; for (unsigned I = 0, E = AllParamDecls.size(); I != E; ++I) { TypeLoc ParamTL = AllParamDecls[I]->getTypeSourceInfo()->getTypeLoc(); ReferenceTypeLoc RefTL = ParamTL.getAs<ReferenceTypeLoc>(); SourceRange Range(AllParamDecls[I]->getLocStart(), ParamTL.getLocEnd()); CharSourceRange CharRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(Range), SM, LangOptions()); // do not generate a replacement when the parameter is already a value if (RefTL.isNull()) continue; // transform non-value parameters (e.g: const-ref) to values TypeLoc ValueTypeLoc = RefTL.getPointeeLoc(); llvm::SmallString<32> ValueStr = Lexer::getSourceText( CharSourceRange::getTokenRange(ValueTypeLoc.getSourceRange()), SM, LangOptions()); // If it's impossible to change one of the parameter (e.g: comes from an // unmodifiable header) quit the callback now, do not generate any changes. if (CharRange.isInvalid() || ValueStr.empty() || !Owner.isFileModifiable(SM, CharRange.getBegin())) return; // 'const Foo ¶m' -> 'Foo param' // ~~~~~~~~~~~ ~~~^ ValueStr += ' '; ParamReplaces.push_back(Replacement(SM, CharRange, ValueStr)); } // Reject the changes if the the risk level is not acceptable. if (!Owner.isAcceptableRiskLevel(RL_Reasonable)) { RejectedChanges++; return; } // if needed, include <utility> in the file that uses std::move() const FileEntry *STDMoveFile = SM.getFileEntryForID(SM.getFileID(Initializer->getLParenLoc())); const tooling::Replacement &IncludeReplace = IncludeManager->addAngledInclude(STDMoveFile, "utility"); if (IncludeReplace.isApplicable()) { Owner.addReplacementForCurrentTU(IncludeReplace); AcceptedChanges++; } // const-ref params becomes values (const Foo & -> Foo) for (const Replacement *I = ParamReplaces.begin(), *E = ParamReplaces.end(); I != E; ++I) { Owner.addReplacementForCurrentTU(*I); } AcceptedChanges += ParamReplaces.size(); // move the value in the init-list Owner.addReplacementForCurrentTU(Replacement( SM, Initializer->getLParenLoc().getLocWithOffset(1), 0, "std::move(")); Owner.addReplacementForCurrentTU( Replacement(SM, Initializer->getRParenLoc(), 0, ")")); AcceptedChanges += 2; }