diff --git a/clang/include/clang/Tooling/Transformer/SourceCode.h b/clang/include/clang/Tooling/Transformer/SourceCode.h --- a/clang/include/clang/Tooling/Transformer/SourceCode.h +++ b/clang/include/clang/Tooling/Transformer/SourceCode.h @@ -112,6 +112,27 @@ return getRangeForEdit(EditRange, Context.getSourceManager(), Context.getLangOpts(), IncludeMacroExpansion); } + +/// Attempts to resolve the given range to one that starts and ends in a +/// particular file. +/// +/// If \c IncludeMacroExpansion is true, a limited set of cases involving source +/// locations in macro expansions is supported. For example, if we're looking to +/// get the range of the int literal 3, and we have the following definition: +/// #define DO_NOTHING(x) x +/// foo(DO_NOTHING(3)) +/// the returned range will hold the source text `DO_NOTHING(3)`. +std::optional getFileRange(const CharSourceRange &EditRange, + const SourceManager &SM, + const LangOptions &LangOpts, + bool IncludeMacroExpansion); +inline std::optional +getFileRange(const CharSourceRange &EditRange, const ASTContext &Context, + bool IncludeMacroExpansion) { + return getFileRange(EditRange, Context.getSourceManager(), + Context.getLangOpts(), IncludeMacroExpansion); +} + } // namespace tooling } // namespace clang #endif // LLVM_CLANG_TOOLING_TRANSFORMER_SOURCECODE_H diff --git a/clang/lib/Tooling/Transformer/SourceCode.cpp b/clang/lib/Tooling/Transformer/SourceCode.cpp --- a/clang/lib/Tooling/Transformer/SourceCode.cpp +++ b/clang/lib/Tooling/Transformer/SourceCode.cpp @@ -50,8 +50,9 @@ return CharSourceRange::getTokenRange(Range.getBegin(), Tok.getLocation()); } -llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range, - const SourceManager &SM) { +static llvm::Error validateRange(const CharSourceRange &Range, + const SourceManager &SM, + bool AllowSystemHeaders) { if (Range.isInvalid()) return llvm::make_error(errc::invalid_argument, "Invalid range"); @@ -60,10 +61,12 @@ return llvm::make_error( errc::invalid_argument, "Range starts or ends in a macro expansion"); - if (SM.isInSystemHeader(Range.getBegin()) || - SM.isInSystemHeader(Range.getEnd())) - return llvm::make_error(errc::invalid_argument, - "Range is in system header"); + if (!AllowSystemHeaders) { + if (SM.isInSystemHeader(Range.getBegin()) || + SM.isInSystemHeader(Range.getEnd())) + return llvm::make_error(errc::invalid_argument, + "Range is in system header"); + } std::pair BeginInfo = SM.getDecomposedLoc(Range.getBegin()); std::pair EndInfo = SM.getDecomposedLoc(Range.getEnd()); @@ -72,13 +75,18 @@ errc::invalid_argument, "Range begins and ends in different files"); if (BeginInfo.second > EndInfo.second) - return llvm::make_error( - errc::invalid_argument, "Range's begin is past its end"); + return llvm::make_error(errc::invalid_argument, + "Range's begin is past its end"); return llvm::Error::success(); } -static bool SpelledInMacroDefinition(SourceLocation Loc, +llvm::Error clang::tooling::validateEditRange(const CharSourceRange &Range, + const SourceManager &SM) { + return validateRange(Range, SM, /*AllowSystemHeaders=*/false); +} + +static bool spelledInMacroDefinition(SourceLocation Loc, const SourceManager &SM) { while (Loc.isMacroID()) { const auto &Expansion = SM.getSLocEntry(SM.getFileID(Loc)).getExpansion(); @@ -93,16 +101,17 @@ return false; } -std::optional clang::tooling::getRangeForEdit( - const CharSourceRange &EditRange, const SourceManager &SM, - const LangOptions &LangOpts, bool IncludeMacroExpansion) { +static CharSourceRange getRange(const CharSourceRange &EditRange, + const SourceManager &SM, + const LangOptions &LangOpts, + bool IncludeMacroExpansion) { CharSourceRange Range; if (IncludeMacroExpansion) { Range = Lexer::makeFileCharRange(EditRange, SM, LangOpts); } else { - if (SpelledInMacroDefinition(EditRange.getBegin(), SM) || - SpelledInMacroDefinition(EditRange.getEnd(), SM)) - return std::nullopt; + if (spelledInMacroDefinition(EditRange.getBegin(), SM) || + spelledInMacroDefinition(EditRange.getEnd(), SM)) + return {}; auto B = SM.getSpellingLoc(EditRange.getBegin()); auto E = SM.getSpellingLoc(EditRange.getEnd()); @@ -110,13 +119,32 @@ E = Lexer::getLocForEndOfToken(E, 0, SM, LangOpts); Range = CharSourceRange::getCharRange(B, E); } + return Range; +} +std::optional clang::tooling::getRangeForEdit( + const CharSourceRange &EditRange, const SourceManager &SM, + const LangOptions &LangOpts, bool IncludeMacroExpansion) { + CharSourceRange Range = + getRange(EditRange, SM, LangOpts, IncludeMacroExpansion); bool IsInvalid = llvm::errorToBool(validateEditRange(Range, SM)); if (IsInvalid) return std::nullopt; return Range; } +std::optional clang::tooling::getFileRange( + const CharSourceRange &EditRange, const SourceManager &SM, + const LangOptions &LangOpts, bool IncludeMacroExpansion) { + CharSourceRange Range = + getRange(EditRange, SM, LangOpts, IncludeMacroExpansion); + bool IsInvalid = + llvm::errorToBool(validateRange(Range, SM, /*AllowSystemHeaders=*/true)); + if (IsInvalid) + return std::nullopt; + return Range; +} + static bool startsWithNewline(const SourceManager &SM, const Token &Tok) { return isVerticalWhitespace(SM.getCharacterData(Tok.getLocation())[0]); }