/// getRangeSize - Return the size in bytes of the specified range if they /// are in the same file. If not, this returns -1. int Rewriter::getRangeSize(const CharSourceRange &Range, RewriteOptions opts) const { if (!isRewritable(Range.getBegin()) || !isRewritable(Range.getEnd())) return -1; FileID StartFileID, EndFileID; unsigned StartOff, EndOff; StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID); EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID); if (StartFileID != EndFileID) return -1; // If edits have been made to this buffer, the delta between the range may // have changed. std::map<FileID, RewriteBuffer>::const_iterator I = RewriteBuffers.find(StartFileID); if (I != RewriteBuffers.end()) { const RewriteBuffer &RB = I->second; EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange); StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange); } // Adjust the end offset to the end of the last token, instead of being the // start of the last token if this is a token range. if (Range.isTokenRange()) EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts); return EndOff-StartOff; }
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; }
void MoveConstArgCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); const Expr *Arg = CallMove->getArg(0); SourceManager &SM = Result.Context->getSourceManager(); CharSourceRange MoveRange = CharSourceRange::getCharRange(CallMove->getSourceRange()); CharSourceRange FileMoveRange = Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); if (!FileMoveRange.isValid()) return; bool IsConstArg = Arg->getType().isConstQualified(); bool IsTriviallyCopyable = Arg->getType().isTriviallyCopyableType(*Result.Context); if (IsConstArg || IsTriviallyCopyable) { if (const CXXRecordDecl *R = Arg->getType()->getAsCXXRecordDecl()) { // According to [expr.prim.lambda]p3, "whether the closure type is // trivially copyable" property can be changed by the implementation of // the language, so we shouldn't rely on it when issuing diagnostics. if (R->isLambda()) return; // Don't warn when the type is not copyable. for (const auto *Ctor : R->ctors()) { if (Ctor->isCopyConstructor() && Ctor->isDeleted()) return; } } if (!IsConstArg && IsTriviallyCopyable && !CheckTriviallyCopyableMove) return; bool IsVariable = isa<DeclRefExpr>(Arg); const auto *Var = IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; auto Diag = diag(FileMoveRange.getBegin(), "std::move of the %select{|const }0" "%select{expression|variable %4}1 " "%select{|of the trivially-copyable type %5 }2" "has no effect; remove std::move()" "%select{| or make the variable non-const}3") << IsConstArg << IsVariable << IsTriviallyCopyable << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var << Arg->getType(); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } else if (ReceivingExpr) { auto Diag = diag(FileMoveRange.getBegin(), "passing result of std::move() as a const reference " "argument; no move will actually happen"); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } }
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"); }
bool Commit::replace(CharSourceRange range, StringRef text) { if (text.empty()) return remove(range); FileOffset Offs; unsigned Len; if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) { IsCommitable = false; return false; } addRemove(range.getBegin(), Offs, Len); addInsert(range.getBegin(), Offs, text, false); return true; }
bool Commit::replaceWithInner(CharSourceRange range, CharSourceRange replacementRange) { FileOffset OuterBegin; unsigned OuterLen; if (!canRemoveRange(range, OuterBegin, OuterLen)) { IsCommitable = false; return false; } FileOffset InnerBegin; unsigned InnerLen; if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) { IsCommitable = false; return false; } FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen); FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen); if (OuterBegin.getFID() != InnerBegin.getFID() || InnerBegin < OuterBegin || InnerBegin > OuterEnd || InnerEnd > OuterEnd) { IsCommitable = false; return false; } addRemove(range.getBegin(), OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset()); addRemove(replacementRange.getEnd(), InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset()); return true; }
bool Commit::insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken, bool beforePreviousInsertions) { FileOffset RangeOffs; unsigned RangeLen; if (!canRemoveRange(range, RangeOffs, RangeLen)) { IsCommitable = false; return false; } FileOffset Offs; if ((!afterToken && !canInsert(loc, Offs)) || ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { IsCommitable = false; return false; } if (PPRec && PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) { IsCommitable = false; return false; } addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions); return true; }
// Re-lex the tokens to get precise locations to insert 'override' and remove // 'virtual'. static SmallVector<Token, 16> ParseTokens(CharSourceRange Range, const MatchFinder::MatchResult &Result) { const SourceManager &Sources = *Result.SourceManager; std::pair<FileID, unsigned> LocInfo = Sources.getDecomposedLoc(Range.getBegin()); StringRef File = Sources.getBufferData(LocInfo.first); const char *TokenBegin = File.data() + LocInfo.second; Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), Result.Context->getLangOpts(), File.begin(), TokenBegin, File.end()); SmallVector<Token, 16> Tokens; Token Tok; while (!RawLexer.LexFromRawLexer(Tok)) { if (Tok.is(tok::semi) || Tok.is(tok::l_brace)) break; if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) break; if (Tok.is(tok::raw_identifier)) { IdentifierInfo &Info = Result.Context->Idents.get(StringRef( Sources.getCharacterData(Tok.getLocation()), Tok.getLength())); Tok.setIdentifierInfo(&Info); Tok.setKind(Info.getTokenID()); } Tokens.push_back(Tok); } return Tokens; }
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"); }
static std::pair<unsigned, unsigned> makeStandaloneRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts) { CharSourceRange FileRange = Lexer::makeFileCharRange(Range, SM, LangOpts); unsigned Offset = SM.getFileOffset(FileRange.getBegin()); unsigned EndOffset = SM.getFileOffset(FileRange.getEnd()); return std::make_pair(Offset, EndOffset); }
void Replacement::setFromSourceRange(const SourceManager &Sources, const CharSourceRange &Range, StringRef ReplacementText, const LangOptions &LangOpts) { setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()), getRangeSize(Sources, Range, LangOpts), ReplacementText); }
static void EmitRange(raw_ostream& o, const SourceManager &SM, const LangOptions &LangOpts, CharSourceRange R, const FIDMap &FM, unsigned indent) { Indent(o, indent) << "<array>\n"; EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, R.isTokenRange()); Indent(o, indent) << "</array>\n"; }
void RedundantVoidArgCheck::removeVoidArgumentTokens( const ast_matchers::MatchFinder::MatchResult &Result, SourceRange Range, StringRef GrammarLocation) { CharSourceRange CharRange = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Range), *Result.SourceManager, getLangOpts()); std::string DeclText = Lexer::getSourceText(CharRange, *Result.SourceManager, getLangOpts()) .str(); Lexer PrototypeLexer(CharRange.getBegin(), getLangOpts(), DeclText.data(), DeclText.data(), DeclText.data() + DeclText.size()); enum TokenState { NothingYet, SawLeftParen, SawVoid, }; TokenState State = NothingYet; Token VoidToken; Token ProtoToken; std::string Diagnostic = ("redundant void argument list in " + GrammarLocation).str(); while (!PrototypeLexer.LexFromRawLexer(ProtoToken)) { switch (State) { case NothingYet: if (ProtoToken.is(tok::TokenKind::l_paren)) { State = SawLeftParen; } break; case SawLeftParen: if (ProtoToken.is(tok::TokenKind::raw_identifier) && ProtoToken.getRawIdentifier() == "void") { State = SawVoid; VoidToken = ProtoToken; } else if (ProtoToken.is(tok::TokenKind::l_paren)) { State = SawLeftParen; } else { State = NothingYet; } break; case SawVoid: State = NothingYet; if (ProtoToken.is(tok::TokenKind::r_paren)) { removeVoidToken(VoidToken, Diagnostic); } else if (ProtoToken.is(tok::TokenKind::l_paren)) { State = SawLeftParen; } break; } } if (State == SawVoid && ProtoToken.is(tok::TokenKind::r_paren)) { removeVoidToken(VoidToken, Diagnostic); } }
// FIXME: This should go into the Lexer, but we need to figure out how // to handle ranges for refactoring in general first - there is no obvious // good way how to integrate this into the Lexer yet. static int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) { SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin()); SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); if (Start.first != End.first) return -1; if (Range.isTokenRange()) End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOptions()); return End.second - Start.second; }
bool AffectedRangeManager::affectsCharSourceRange( const CharSourceRange &Range) { for (SmallVectorImpl<CharSourceRange>::const_iterator I = Ranges.begin(), E = Ranges.end(); I != E; ++I) { if (!SourceMgr.isBeforeInTranslationUnit(Range.getEnd(), I->getBegin()) && !SourceMgr.isBeforeInTranslationUnit(I->getEnd(), Range.getBegin())) return true; } return false; }
void SDiagsWriter::AddCharSourceRangeToRecord(CharSourceRange Range, RecordDataImpl &Record, const SourceManager &SM) { AddLocToRecord(Range.getBegin(), Record, &SM); unsigned TokSize = 0; if (Range.isTokenRange()) TokSize = Lexer::MeasureTokenLength(Range.getEnd(), SM, *LangOpts); AddLocToRecord(Range.getEnd(), Record, &SM, TokSize); }
bool Commit::remove(CharSourceRange range) { FileOffset Offs; unsigned Len; if (!canRemoveRange(range, Offs, Len)) { IsCommitable = false; return false; } addRemove(range.getBegin(), Offs, Len); return true; }
/// Check if all the locations in the range have the same macro argument /// expansion, and that that expansion starts with ArgumentLoc. static bool checkRangeForMacroArgExpansion(CharSourceRange Range, const SourceManager &SM, SourceLocation ArgumentLoc) { SourceLocation BegLoc = Range.getBegin(), EndLoc = Range.getEnd(); while (BegLoc != EndLoc) { if (!checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc)) return false; BegLoc.getLocWithOffset(1); } return checkLocForMacroArgExpansion(BegLoc, SM, ArgumentLoc); }
bool Commit::insertWrap(StringRef before, CharSourceRange range, StringRef after) { bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false, /*beforePreviousInsertions=*/true); bool commitableAfter; if (range.isTokenRange()) commitableAfter = insertAfterToken(range.getEnd(), after); else commitableAfter = insert(range.getEnd(), after); return commitableBefore && commitableAfter; }
void MoveConstantArgumentCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); const auto *ReceivingExpr = Result.Nodes.getNodeAs<Expr>("receiving-expr"); const Expr *Arg = CallMove->getArg(0); SourceManager &SM = Result.Context->getSourceManager(); CharSourceRange MoveRange = CharSourceRange::getCharRange(CallMove->getSourceRange()); CharSourceRange FileMoveRange = Lexer::makeFileCharRange(MoveRange, SM, getLangOpts()); if (!FileMoveRange.isValid()) return; bool IsConstArg = Arg->getType().isConstQualified(); bool IsTriviallyCopyable = Arg->getType().isTriviallyCopyableType(*Result.Context); if (IsConstArg || IsTriviallyCopyable) { bool IsVariable = isa<DeclRefExpr>(Arg); const auto *Var = IsVariable ? dyn_cast<DeclRefExpr>(Arg)->getDecl() : nullptr; auto Diag = diag(FileMoveRange.getBegin(), "std::move of the %select{|const }0" "%select{expression|variable %4}1 " "%select{|of the trivially-copyable type %5 }2" "has no effect; remove std::move()" "%select{| or make the variable non-const}3") << IsConstArg << IsVariable << IsTriviallyCopyable << (IsConstArg && IsVariable && !IsTriviallyCopyable) << Var << Arg->getType(); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } else if (ReceivingExpr) { auto Diag = diag(FileMoveRange.getBegin(), "passing result of std::move() as a const reference " "argument; no move will actually happen"); ReplaceCallWithArg(CallMove, Diag, SM, getLangOpts()); } }
/// Find the suitable set of lines to show to include a set of ranges. static llvm::Optional<std::pair<unsigned, unsigned>> findLinesForRange(const CharSourceRange &R, FileID FID, const SourceManager &SM) { if (!R.isValid()) return None; SourceLocation Begin = R.getBegin(); SourceLocation End = R.getEnd(); if (SM.getFileID(Begin) != FID || SM.getFileID(End) != FID) return None; return std::make_pair(SM.getExpansionLineNumber(Begin), SM.getExpansionLineNumber(End)); }
void PreprocessingRecord::InclusionDirective( SourceLocation HashLoc, const clang::Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, const Module *Imported) { InclusionDirective::InclusionKind Kind = InclusionDirective::Include; switch (IncludeTok.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: Kind = InclusionDirective::Include; break; case tok::pp_import: Kind = InclusionDirective::Import; break; case tok::pp_using: Kind = InclusionDirective::Using; break; case tok::pp_include_next: Kind = InclusionDirective::IncludeNext; break; case tok::pp___include_macros: Kind = InclusionDirective::IncludeMacros; break; default: llvm_unreachable("Unknown include directive kind"); } SourceLocation EndLoc; if (!IsAngled) { EndLoc = FilenameRange.getBegin(); } else { EndLoc = FilenameRange.getEnd(); if (FilenameRange.isCharRange()) EndLoc = EndLoc.getLocWithOffset(-1); // the InclusionDirective expects // a token range. } clang::InclusionDirective *ID = new (*this) clang::InclusionDirective(*this, Kind, FileName, !IsAngled, File, SourceRange(HashLoc, EndLoc)); addPreprocessedEntity(ID); }
void RemoveUnusedEnumMember::removeEnumConstantDecl() { SourceLocation StartLoc = (*TheEnumIterator)->getLocStart(); if (StartLoc.isMacroID()) { CharSourceRange Range = SrcManager->getExpansionRange(StartLoc); StartLoc = Range.getBegin(); } SourceLocation EndLoc = (*TheEnumIterator)->getLocEnd(); if (EndLoc.isMacroID()) { CharSourceRange Range = SrcManager->getExpansionRange(EndLoc); EndLoc = Range.getEnd(); } SourceLocation CommaLoc = Lexer::findLocationAfterToken( EndLoc, tok::comma, *SrcManager, Context->getLangOpts(), /*SkipTrailingWhitespaceAndNewLine=*/false); if (CommaLoc.isValid()) EndLoc = CommaLoc; TheRewriter.RemoveText(SourceRange(StartLoc, EndLoc)); }
// Re-lex the tokens to get precise locations to insert 'override' and remove // 'virtual'. static SmallVector<Token, 16> ParseTokens(CharSourceRange Range, const SourceManager &Sources, LangOptions LangOpts) { std::pair<FileID, unsigned> LocInfo = Sources.getDecomposedLoc(Range.getBegin()); StringRef File = Sources.getBufferData(LocInfo.first); const char *TokenBegin = File.data() + LocInfo.second; Lexer RawLexer(Sources.getLocForStartOfFile(LocInfo.first), LangOpts, File.begin(), TokenBegin, File.end()); SmallVector<Token, 16> Tokens; Token Tok; while (!RawLexer.LexFromRawLexer(Tok)) { if (Tok.is(tok::semi) || Tok.is(tok::l_brace)) break; if (Sources.isBeforeInTranslationUnit(Range.getEnd(), Tok.getLocation())) break; Tokens.push_back(Tok); } return Tokens; }
static void printSourceRange(CharSourceRange range, ASTContext &Ctx, raw_ostream &OS) { SourceManager &SM = Ctx.getSourceManager(); const LangOptions &langOpts = Ctx.getLangOpts(); PresumedLoc PL = SM.getPresumedLoc(range.getBegin()); OS << llvm::sys::path::filename(PL.getFilename()); OS << " [" << PL.getLine() << ":" << PL.getColumn(); OS << " - "; SourceLocation end = range.getEnd(); PL = SM.getPresumedLoc(end); unsigned endCol = PL.getColumn() - 1; if (!range.isTokenRange()) endCol += Lexer::MeasureTokenLength(end, SM, langOpts); OS << PL.getLine() << ":" << endCol << "]"; }
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; }
/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. void TextDiagnostic::highlightRange(const CharSourceRange &R, unsigned LineNo, FileID FID, const SourceColumnMap &map, std::string &CaretLine) { if (!R.isValid()) return; SourceLocation Begin = SM.getExpansionLoc(R.getBegin()); SourceLocation End = SM.getExpansionLoc(R.getEnd()); // If the End location and the start location are the same and are a macro // location, then the range was something that came from a macro expansion // or _Pragma. If this is an object-like macro, the best we can do is to // highlight the range. If this is a function-like macro, we'd also like to // highlight the arguments. if (Begin == End && R.getEnd().isMacroID()) End = SM.getExpansionRange(R.getEnd()).second; unsigned StartLineNo = SM.getExpansionLineNumber(Begin); if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) return; // No intersection. unsigned EndLineNo = SM.getExpansionLineNumber(End); if (EndLineNo < LineNo || SM.getFileID(End) != FID) return; // No intersection. // Compute the column number of the start. unsigned StartColNo = 0; if (StartLineNo == LineNo) { StartColNo = SM.getExpansionColumnNumber(Begin); if (StartColNo) --StartColNo; // Zero base the col #. } // Compute the column number of the end. unsigned EndColNo = map.getSourceLine().size(); if (EndLineNo == LineNo) { EndColNo = SM.getExpansionColumnNumber(End); if (EndColNo) { --EndColNo; // Zero base the col #. // Add in the length of the token, so that we cover multi-char tokens if // this is a token range. if (R.isTokenRange()) EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); } else { EndColNo = CaretLine.size(); } } assert(StartColNo <= EndColNo && "Invalid range!"); // Check that a token range does not highlight only whitespace. if (R.isTokenRange()) { // Pick the first non-whitespace column. while (StartColNo < map.getSourceLine().size() && (map.getSourceLine()[StartColNo] == ' ' || map.getSourceLine()[StartColNo] == '\t')) ++StartColNo; // Pick the last non-whitespace column. if (EndColNo > map.getSourceLine().size()) EndColNo = map.getSourceLine().size(); while (EndColNo-1 && (map.getSourceLine()[EndColNo-1] == ' ' || map.getSourceLine()[EndColNo-1] == '\t')) --EndColNo; // If the start/end passed each other, then we are trying to highlight a // range that just exists in whitespace, which must be some sort of other // bug. assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); } assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); // Fill the range with ~'s. StartColNo = map.byteToColumn(StartColNo); EndColNo = map.byteToColumn(EndColNo); assert(StartColNo <= EndColNo && "Invalid range!"); if (CaretLine.size() < EndColNo) CaretLine.resize(EndColNo,' '); std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); }
/// \brief Highlight a SourceRange (with ~'s) for any characters on LineNo. static void highlightRange(const CharSourceRange &R, unsigned LineNo, FileID FID, const SourceColumnMap &map, std::string &CaretLine, const SourceManager &SM, const LangOptions &LangOpts) { if (!R.isValid()) return; SourceLocation Begin = R.getBegin(); SourceLocation End = R.getEnd(); unsigned StartLineNo = SM.getExpansionLineNumber(Begin); if (StartLineNo > LineNo || SM.getFileID(Begin) != FID) return; // No intersection. unsigned EndLineNo = SM.getExpansionLineNumber(End); if (EndLineNo < LineNo || SM.getFileID(End) != FID) return; // No intersection. // Compute the column number of the start. unsigned StartColNo = 0; if (StartLineNo == LineNo) { StartColNo = SM.getExpansionColumnNumber(Begin); if (StartColNo) --StartColNo; // Zero base the col #. } // Compute the column number of the end. unsigned EndColNo = map.getSourceLine().size(); if (EndLineNo == LineNo) { EndColNo = SM.getExpansionColumnNumber(End); if (EndColNo) { --EndColNo; // Zero base the col #. // Add in the length of the token, so that we cover multi-char tokens if // this is a token range. if (R.isTokenRange()) EndColNo += Lexer::MeasureTokenLength(End, SM, LangOpts); } else { EndColNo = CaretLine.size(); } } assert(StartColNo <= EndColNo && "Invalid range!"); // Check that a token range does not highlight only whitespace. if (R.isTokenRange()) { // Pick the first non-whitespace column. while (StartColNo < map.getSourceLine().size() && (map.getSourceLine()[StartColNo] == ' ' || map.getSourceLine()[StartColNo] == '\t')) StartColNo = map.startOfNextColumn(StartColNo); // Pick the last non-whitespace column. if (EndColNo > map.getSourceLine().size()) EndColNo = map.getSourceLine().size(); while (EndColNo-1 && (map.getSourceLine()[EndColNo-1] == ' ' || map.getSourceLine()[EndColNo-1] == '\t')) EndColNo = map.startOfPreviousColumn(EndColNo); // If the start/end passed each other, then we are trying to highlight a // range that just exists in whitespace, which must be some sort of other // bug. assert(StartColNo <= EndColNo && "Trying to highlight whitespace??"); } assert(StartColNo <= map.getSourceLine().size() && "Invalid range!"); assert(EndColNo <= map.getSourceLine().size() && "Invalid range!"); // Fill the range with ~'s. StartColNo = map.byteToContainingColumn(StartColNo); EndColNo = map.byteToContainingColumn(EndColNo); assert(StartColNo <= EndColNo && "Invalid range!"); if (CaretLine.size() < EndColNo) CaretLine.resize(EndColNo,' '); std::fill(CaretLine.begin()+StartColNo,CaretLine.begin()+EndColNo,'~'); }