/// 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; }
static SourceLoc findEndOfLine(SourceManager &SM, SourceLoc loc, unsigned bufferID) { CharSourceRange entireBuffer = SM.getRangeForBuffer(bufferID); CharSourceRange rangeFromLoc{SM, loc, entireBuffer.getEnd()}; StringRef textFromLoc = SM.extractText(rangeFromLoc); size_t newlineOffset = textFromLoc.find_first_of({"\r\n\0", 3}); if (newlineOffset == StringRef::npos) return entireBuffer.getEnd(); return loc.getAdvancedLoc(newlineOffset); }
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::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; }
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; }
// 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; }
bool SourceManager::openVirtualFile(SourceLoc loc, StringRef name, int lineOffset) { CharSourceRange fullRange = getRangeForBuffer(findBufferContainingLoc(loc)); SourceLoc end; auto nextRangeIter = VirtualFiles.upper_bound(loc.Value.getPointer()); if (nextRangeIter != VirtualFiles.end() && fullRange.contains(nextRangeIter->second.Range.getStart())) { const VirtualFile &existingFile = nextRangeIter->second; if (existingFile.Range.getStart() == loc) { assert(existingFile.Name == name); assert(existingFile.LineOffset == lineOffset); return false; } assert(!existingFile.Range.contains(loc) && "must close current open file first"); end = nextRangeIter->second.Range.getStart(); } else { end = fullRange.getEnd(); } CharSourceRange range = CharSourceRange(*this, loc, end); VirtualFiles[end.Value.getPointer()] = { range, name, lineOffset }; CachedVFile = {nullptr, nullptr}; 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; }
void SerializedDiagnosticConsumer::addRangeToRecord(CharSourceRange Range, SourceManager &SM, StringRef Filename, RecordDataImpl &Record) { assert(Range.isValid()); addLocToRecord(Range.getStart(), SM, Filename, Record); addLocToRecord(Range.getEnd(), SM, Filename, Record); }
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); }
SingleRawComment::SingleRawComment(CharSourceRange Range, const SourceManager &SourceMgr) : Range(Range), RawText(SourceMgr.extractText(Range)), Kind(static_cast<unsigned>(getCommentKind(RawText))) { auto StartLineAndColumn = SourceMgr.getLineAndColumn(Range.getStart()); StartLine = StartLineAndColumn.first; StartColumn = StartLineAndColumn.second; EndLine = SourceMgr.getLineNumber(Range.getEnd()); }
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"; }
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; }
// 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; }
/// 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); }
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"); }
/// 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 Parser::diagnoseWhereClauseInGenericParamList(const GenericParamList * GenericParams) { if (GenericParams == nullptr || GenericParams->getWhereLoc().isInvalid()) return; auto WhereRangeInsideBrackets = GenericParams->getWhereClauseSourceRange(); // Move everything immediately following the last generic parameter // as written all the way to the right angle bracket (">") auto LastGenericParam = GenericParams->getParams().back(); auto EndOfLastGenericParam = Lexer::getLocForEndOfToken(SourceMgr, LastGenericParam->getEndLoc()); CharSourceRange RemoveWhereRange { SourceMgr, EndOfLastGenericParam, GenericParams->getRAngleLoc() }; auto WhereCharRange = Lexer::getCharSourceRangeFromSourceRange(SourceMgr, GenericParams->getWhereClauseSourceRange()); SmallString<64> Buffer; llvm::raw_svector_ostream WhereClauseText(Buffer); WhereClauseText << SourceMgr.extractText(Tok.is(tok::kw_where) ? WhereCharRange : RemoveWhereRange); // If, for some reason, there was a where clause in both locations, we're // adding to the list of requirements, so tack on a comma here before // inserting it at the head of the later where clause. if (Tok.is(tok::kw_where)) WhereClauseText << ','; auto Diag = diagnose(WhereRangeInsideBrackets.Start, diag::where_inside_brackets); Diag.fixItRemoveChars(RemoveWhereRange.getStart(), RemoveWhereRange.getEnd()); if (Tok.is(tok::kw_where)) { Diag.fixItReplace(Tok.getLoc(), WhereClauseText.str()); } else { Diag.fixItInsert(Lexer::getLocForEndOfToken(SourceMgr, PreviousLoc), WhereClauseText.str()); } }
void SourceManager::closeVirtualFile(SourceLoc end) { auto *virtualFile = const_cast<VirtualFile *>(getVirtualFile(end)); if (!virtualFile) { #ifndef NDEBUG unsigned bufferID = findBufferContainingLoc(end); CharSourceRange fullRange = getRangeForBuffer(bufferID); assert((fullRange.getByteLength() == 0 || getVirtualFile(end.getAdvancedLoc(-1))) && "no open virtual file for this location"); assert(fullRange.getEnd() == end); #endif return; } CachedVFile = {nullptr, nullptr}; CharSourceRange oldRange = virtualFile->Range; virtualFile->Range = CharSourceRange(*this, virtualFile->Range.getStart(), end); VirtualFiles[end.Value.getPointer()] = std::move(*virtualFile); bool existed = VirtualFiles.erase(oldRange.getEnd().Value.getPointer()); assert(existed); (void)existed; }
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 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(), ")"); }
void XCTMigrator::migrateInclude(llvm::StringRef Filename, CharSourceRange FilenameRange, SourceLocation HashLoc, bool isAngled) { StringRef Parent = "SenTestingKit/"; if (!Filename.startswith(Parent)) return; if (isFromSenTestInclude(HashLoc)) return; edit::Commit commit(Editor); StringRef HeaderName = Filename.substr(Parent.size()); llvm::StringMap<llvm::StringRef>::iterator I = IncludesMap.find(HeaderName); if (I == IncludesMap.end() || I->second.empty()) { commit.remove(CharSourceRange::getCharRange(HashLoc,FilenameRange.getEnd())); } else { SmallString<128> NewInclude; NewInclude.push_back(isAngled ? '<' : '"'); NewInclude += "XCTest/"; NewInclude += I->second; NewInclude.push_back(isAngled ? '>' : '"'); commit.replace(FilenameRange, NewInclude.str()); } Editor.commit(commit); }
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 UseOverride::check(const MatchFinder::MatchResult &Result) { const FunctionDecl *Method = Result.Nodes.getStmtAs<FunctionDecl>("method"); const SourceManager &Sources = *Result.SourceManager; assert(Method != nullptr); if (Method->getInstantiatedFromMemberFunction() != nullptr) Method = Method->getInstantiatedFromMemberFunction(); if (Method->isImplicit() || Method->getLocation().isMacroID() || Method->isOutOfLine()) return; bool HasVirtual = Method->isVirtualAsWritten(); bool HasOverride = Method->getAttr<OverrideAttr>(); bool HasFinal = Method->getAttr<FinalAttr>(); bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal; unsigned KeywordCount = HasVirtual + HasOverride + HasFinal; if (!OnlyVirtualSpecified && KeywordCount == 1) return; // Nothing to do. DiagnosticBuilder Diag = diag( Method->getLocation(), OnlyVirtualSpecified ? "Prefer using 'override' or (rarely) 'final' instead of 'virtual'" : "Annotate this function with 'override' or (rarely) 'final'"); CharSourceRange FileRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(Method->getSourceRange()), Sources, Result.Context->getLangOpts()); if (!FileRange.isValid()) return; // FIXME: Instead of re-lexing and looking for specific macros such as // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each // FunctionDecl. SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result); // Add 'override' on inline declarations that don't already have it. if (!HasFinal && !HasOverride) { SourceLocation InsertLoc; StringRef ReplacementText = "override "; for (Token T : Tokens) { if (T.is(tok::kw___attribute)) { InsertLoc = T.getLocation(); break; } } if (Method->hasAttrs()) { for (const clang::Attr *A : Method->getAttrs()) { if (!A->isImplicit()) { SourceLocation Loc = Sources.getExpansionLoc(A->getRange().getBegin()); if (!InsertLoc.isValid() || Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) InsertLoc = Loc; } } } if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() && Method->getBody() && !Method->isDefaulted()) InsertLoc = Method->getBody()->getLocStart(); if (!InsertLoc.isValid()) { if (Tokens.size() > 2 && GetText(Tokens.back(), Sources) == "0" && GetText(Tokens[Tokens.size() - 2], Sources) == "=") { InsertLoc = Tokens[Tokens.size() - 2].getLocation(); } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") { InsertLoc = Tokens.back().getLocation(); } } if (!InsertLoc.isValid()) { InsertLoc = FileRange.getEnd(); ReplacementText = " override"; } Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText); } if (HasFinal && HasOverride) { SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation(); Diag << FixItHint::CreateRemoval( CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc)); } if (Method->isVirtualAsWritten()) { for (Token Tok : Tokens) { if (Tok.is(tok::kw_virtual)) { Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( Tok.getLocation(), Tok.getLocation())); break; } } } }
void UseOverrideCheck::check(const MatchFinder::MatchResult &Result) { const auto *Method = Result.Nodes.getNodeAs<FunctionDecl>("method"); const SourceManager &Sources = *Result.SourceManager; assert(Method != nullptr); if (Method->getInstantiatedFromMemberFunction() != nullptr) Method = Method->getInstantiatedFromMemberFunction(); if (Method->isImplicit() || Method->getLocation().isMacroID() || Method->isOutOfLine()) return; bool HasVirtual = Method->isVirtualAsWritten(); bool HasOverride = Method->getAttr<OverrideAttr>(); bool HasFinal = Method->getAttr<FinalAttr>(); bool OnlyVirtualSpecified = HasVirtual && !HasOverride && !HasFinal; unsigned KeywordCount = HasVirtual + HasOverride + HasFinal; if (!OnlyVirtualSpecified && KeywordCount == 1) return; // Nothing to do. std::string Message; if (OnlyVirtualSpecified) { Message = "prefer using 'override' or (rarely) 'final' instead of 'virtual'"; } else if (KeywordCount == 0) { Message = "annotate this function with 'override' or (rarely) 'final'"; } else { StringRef Redundant = HasVirtual ? (HasOverride && HasFinal ? "'virtual' and 'override' are" : "'virtual' is") : "'override' is"; StringRef Correct = HasFinal ? "'final'" : "'override'"; Message = (llvm::Twine(Redundant) + " redundant since the function is already declared " + Correct) .str(); } DiagnosticBuilder Diag = diag(Method->getLocation(), Message); CharSourceRange FileRange = Lexer::makeFileCharRange( CharSourceRange::getTokenRange(Method->getSourceRange()), Sources, getLangOpts()); if (!FileRange.isValid()) return; // FIXME: Instead of re-lexing and looking for specific macros such as // 'ABSTRACT', properly store the location of 'virtual' and '= 0' in each // FunctionDecl. SmallVector<Token, 16> Tokens = ParseTokens(FileRange, Result); // Add 'override' on inline declarations that don't already have it. if (!HasFinal && !HasOverride) { SourceLocation InsertLoc; StringRef ReplacementText = "override "; SourceLocation MethodLoc = Method->getLocation(); for (Token T : Tokens) { if (T.is(tok::kw___attribute) && !Sources.isBeforeInTranslationUnit(T.getLocation(), MethodLoc)) { InsertLoc = T.getLocation(); break; } } if (Method->hasAttrs()) { for (const clang::Attr *A : Method->getAttrs()) { if (!A->isImplicit() && !A->isInherited()) { SourceLocation Loc = Sources.getExpansionLoc(A->getRange().getBegin()); if ((!InsertLoc.isValid() || Sources.isBeforeInTranslationUnit(Loc, InsertLoc)) && !Sources.isBeforeInTranslationUnit(Loc, MethodLoc)) InsertLoc = Loc; } } } if (InsertLoc.isInvalid() && Method->doesThisDeclarationHaveABody() && Method->getBody() && !Method->isDefaulted()) { // For methods with inline definition, add the override keyword at the // end of the declaration of the function, but prefer to put it on the // same line as the declaration if the beginning brace for the start of // the body falls on the next line. Token LastNonCommentToken; for (Token T : Tokens) { if (!T.is(tok::comment)) { LastNonCommentToken = T; } } InsertLoc = LastNonCommentToken.getEndLoc(); ReplacementText = " override"; } if (!InsertLoc.isValid()) { // For declarations marked with "= 0" or "= [default|delete]", the end // location will point until after those markings. Therefore, the override // keyword shouldn't be inserted at the end, but before the '='. if (Tokens.size() > 2 && (GetText(Tokens.back(), Sources) == "0" || Tokens.back().is(tok::kw_default) || Tokens.back().is(tok::kw_delete)) && GetText(Tokens[Tokens.size() - 2], Sources) == "=") { InsertLoc = Tokens[Tokens.size() - 2].getLocation(); // Check if we need to insert a space. if ((Tokens[Tokens.size() - 2].getFlags() & Token::LeadingSpace) == 0) ReplacementText = " override "; } else if (GetText(Tokens.back(), Sources) == "ABSTRACT") { InsertLoc = Tokens.back().getLocation(); } } if (!InsertLoc.isValid()) { InsertLoc = FileRange.getEnd(); ReplacementText = " override"; } Diag << FixItHint::CreateInsertion(InsertLoc, ReplacementText); } if (HasFinal && HasOverride) { SourceLocation OverrideLoc = Method->getAttr<OverrideAttr>()->getLocation(); Diag << FixItHint::CreateRemoval( CharSourceRange::getTokenRange(OverrideLoc, OverrideLoc)); } if (HasVirtual) { for (Token Tok : Tokens) { if (Tok.is(tok::kw_virtual)) { Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange( Tok.getLocation(), Tok.getLocation())); break; } } } }