// Finds the location of the qualifying `const` token in the `FunctionDecl`'s // return type. Returns `None` when the return type is not `const`-qualified or // `const` does not appear in `Def`'s source like when the type is an alias or a // macro. static llvm::Optional<Token> findConstToRemove(const FunctionDecl *Def, const MatchFinder::MatchResult &Result) { if (!Def->getReturnType().isLocalConstQualified()) return None; // Get the begin location for the function name, including any qualifiers // written in the source (for out-of-line declarations). A FunctionDecl's // "location" is the start of its name, so, when the name is unqualified, we // use `getLocation()`. SourceLocation NameBeginLoc = Def->getQualifier() ? Def->getQualifierLoc().getBeginLoc() : Def->getLocation(); // Since either of the locs can be in a macro, use `makeFileCharRange` to be // sure that we have a consistent `CharSourceRange`, located entirely in the // source file. CharSourceRange FileRange = Lexer::makeFileCharRange( CharSourceRange::getCharRange(Def->getBeginLoc(), NameBeginLoc), *Result.SourceManager, Result.Context->getLangOpts()); if (FileRange.isInvalid()) return None; return utils::lexer::getConstQualifyingToken(FileRange, *Result.Context, *Result.SourceManager); }
bool Commit::canRemoveRange(CharSourceRange range, FileOffset &Offs, unsigned &Len) { const SourceManager &SM = SourceMgr; range = Lexer::makeFileCharRange(range, SM, LangOpts); if (range.isInvalid()) return false; if (range.getBegin().isMacroID() || range.getEnd().isMacroID()) return false; if (SM.isInSystemHeader(range.getBegin()) || SM.isInSystemHeader(range.getEnd())) return false; if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange())) return false; std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin()); std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd()); if (beginInfo.first != endInfo.first || beginInfo.second > endInfo.second) return false; Offs = FileOffset(beginInfo.first, beginInfo.second); Len = endInfo.second - beginInfo.second; return true; }
static RawComment toRawComment(ASTContext &Context, CharSourceRange Range) { if (Range.isInvalid()) return RawComment(); auto &SourceMgr = Context.SourceMgr; unsigned BufferID = SourceMgr.findBufferContainingLoc(Range.getStart()); unsigned Offset = SourceMgr.getLocOffsetInBuffer(Range.getStart(), BufferID); unsigned EndOffset = SourceMgr.getLocOffsetInBuffer(Range.getEnd(), BufferID); LangOptions FakeLangOpts; Lexer L(FakeLangOpts, SourceMgr, BufferID, nullptr, LexerMode::Swift, HashbangMode::Disallowed, CommentRetentionMode::ReturnAsTokens, TriviaRetentionMode::WithoutTrivia, Offset, EndOffset); SmallVector<SingleRawComment, 16> Comments; Token Tok; while (true) { L.lex(Tok); if (Tok.is(tok::eof)) break; assert(Tok.is(tok::comment)); addCommentToList(Comments, SingleRawComment(Tok.getRange(), SourceMgr)); } RawComment Result; Result.Comments = Context.AllocateCopy(Comments); return Result; }
void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; if (const auto *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) { CharSourceRange Range = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); if (Range.isInvalid()) return; auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") << FixItHint::CreateInsertion(Range.getEnd(), ")"); if (auto Fix = Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", /*IsAngled=*/true)) Diag << *Fix; return; } SourceLocation AutoPtrLoc; if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { // std::auto_ptr<int> i; // ^ if (auto Loc = TL->getAs<TemplateSpecializationTypeLoc>()) AutoPtrLoc = Loc.getTemplateNameLoc(); } else if (const auto *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { // using std::auto_ptr; // ^ AutoPtrLoc = D->getNameInfo().getBeginLoc(); } else { llvm_unreachable("Bad Callback. No node provided."); } if (AutoPtrLoc.isMacroID()) AutoPtrLoc = SM.getSpellingLoc(AutoPtrLoc); // Ensure that only the 'auto_ptr' token is replaced and not the template // aliases. if (StringRef(SM.getCharacterData(AutoPtrLoc), strlen("auto_ptr")) != "auto_ptr") return; SourceLocation EndLoc = AutoPtrLoc.getLocWithOffset(strlen("auto_ptr") - 1); diag(AutoPtrLoc, "auto_ptr is deprecated, use unique_ptr instead") << FixItHint::CreateReplacement(SourceRange(AutoPtrLoc, EndLoc), "unique_ptr"); }
void ReplaceAutoPtrCheck::check(const MatchFinder::MatchResult &Result) { SourceManager &SM = *Result.SourceManager; if (const auto *E = Result.Nodes.getNodeAs<Expr>(AutoPtrOwnershipTransferId)) { CharSourceRange Range = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(E->getSourceRange()), SM, LangOptions()); if (Range.isInvalid()) return; auto Diag = diag(Range.getBegin(), "use std::move to transfer ownership") << FixItHint::CreateInsertion(Range.getBegin(), "std::move(") << FixItHint::CreateInsertion(Range.getEnd(), ")"); auto Insertion = Inserter->CreateIncludeInsertion(SM.getMainFileID(), "utility", /*IsAngled=*/true); if (Insertion.hasValue()) Diag << Insertion.getValue(); return; } SourceLocation IdentifierLoc; if (const auto *TL = Result.Nodes.getNodeAs<TypeLoc>(AutoPtrTokenId)) { IdentifierLoc = locateFromTypeLoc(TL, SM); } else if (const auto *D = Result.Nodes.getNodeAs<UsingDecl>(AutoPtrTokenId)) { IdentifierLoc = locateFromUsingDecl(D, SM); } else { llvm_unreachable("Bad Callback. No node provided."); } if (IdentifierLoc.isMacroID()) IdentifierLoc = SM.getSpellingLoc(IdentifierLoc); // Ensure that only the 'auto_ptr' token is replaced and not the template // aliases. if (!checkTokenIsAutoPtr(IdentifierLoc, SM, LangOptions())) return; SourceLocation EndLoc = IdentifierLoc.getLocWithOffset(strlen("auto_ptr") - 1); diag(IdentifierLoc, "auto_ptr is deprecated, use unique_ptr instead") << FixItHint::CreateReplacement(SourceRange(IdentifierLoc, EndLoc), "unique_ptr"); }
void UpgradeDurationConversionsCheck::check( const MatchFinder::MatchResult &Result) { const llvm::StringRef Message = "implicit conversion to 'int64_t' is deprecated in this context; use an " "explicit cast instead"; const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>("arg"); SourceLocation Loc = ArgExpr->getBeginLoc(); if (!match(isInTemplateInstantiation(), *ArgExpr, *Result.Context).empty()) { if (MatchedTemplateLocations.count(Loc.getRawEncoding()) == 0) { // For each location matched in a template instantiation, we check if the // location can also be found in `MatchedTemplateLocations`. If it is not // found, that means the expression did not create a match without the // instantiation and depends on template parameters. A manual fix is // probably required so we provide only a warning. diag(Loc, Message); } return; } // We gather source locations from template matches not in template // instantiations for future matches. internal::Matcher<Stmt> IsInsideTemplate = hasAncestor(decl(anyOf(classTemplateDecl(), functionTemplateDecl()))); if (!match(IsInsideTemplate, *ArgExpr, *Result.Context).empty()) MatchedTemplateLocations.insert(Loc.getRawEncoding()); DiagnosticBuilder Diag = diag(Loc, Message); CharSourceRange SourceRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(ArgExpr->getSourceRange()), *Result.SourceManager, Result.Context->getLangOpts()); if (SourceRange.isInvalid()) // An invalid source range likely means we are inside a macro body. A manual // fix is likely needed so we do not create a fix-it hint. return; Diag << FixItHint::CreateInsertion(SourceRange.getBegin(), "static_cast<int64_t>(") << FixItHint::CreateInsertion(SourceRange.getEnd(), ")"); }
bool Rewriter::IncreaseIndentation(CharSourceRange range, SourceLocation parentIndent) { if (range.isInvalid()) return true; if (!isRewritable(range.getBegin())) return true; if (!isRewritable(range.getEnd())) return true; if (!isRewritable(parentIndent)) return true; FileID StartFileID, EndFileID, parentFileID; unsigned StartOff, EndOff, parentOff; StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID); EndOff = getLocationOffsetAndFileID(range.getEnd(), EndFileID); parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID); if (StartFileID != EndFileID || StartFileID != parentFileID) return true; if (StartOff > EndOff) return true; FileID FID = StartFileID; StringRef MB = SourceMgr->getBufferData(FID); unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1; unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1; unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1; const SrcMgr::ContentCache * Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); // Find where the lines start. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; unsigned startLineOffs = Content->SourceLineCache[startLineNo]; // Find the whitespace at the start of each line. StringRef parentSpace, startSpace; { unsigned i = parentLineOffs; while (isWhitespace(MB[i])) ++i; parentSpace = MB.substr(parentLineOffs, i-parentLineOffs); i = startLineOffs; while (isWhitespace(MB[i])) ++i; startSpace = MB.substr(startLineOffs, i-startLineOffs); } if (parentSpace.size() >= startSpace.size()) return true; if (!startSpace.startswith(parentSpace)) return true; StringRef indent = startSpace.substr(parentSpace.size()); // Indent the lines between start/end offsets. RewriteBuffer &RB = getEditBuffer(FID); for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { unsigned offs = Content->SourceLineCache[lineNo]; unsigned i = offs; while (isWhitespace(MB[i])) ++i; StringRef origIndent = MB.substr(offs, i-offs); if (origIndent.startswith(startSpace)) RB.InsertText(offs, indent, /*InsertAfter=*/false); } return false; }
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; }