diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index 99c16d292afa..eb8ad347d62f 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -331,6 +331,14 @@ public: const LangOptions &LangOpts, SourceLocation *MacroEnd = 0); + /// \brief Accepts a token source range and returns a character range with + /// file locations. + /// Returns a null range if a part of the range resides inside a macro + /// expansion or the range does not reside on the same FileID. + static CharSourceRange makeFileCharRange(SourceRange TokenRange, + const SourceManager &SM, + const LangOptions &LangOpts); + /// \brief Retrieve the name of the immediate macro expansion. /// /// This routine starts from a source location, and finds the name of the macro diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index ddb5eccff5ee..1a469bef488e 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -792,6 +792,35 @@ bool Lexer::isAtEndOfMacroExpansion(SourceLocation loc, return isAtEndOfMacroExpansion(expansionLoc, SM, LangOpts, MacroEnd); } +/// \brief Accepts a token source range and returns a character range with +/// file locations. +/// Returns a null range if a part of the range resides inside a macro +/// expansion or the range does not reside on the same FileID. +CharSourceRange Lexer::makeFileCharRange(SourceRange TokenRange, + const SourceManager &SM, + const LangOptions &LangOpts) { + SourceLocation Begin = TokenRange.getBegin(); + if (Begin.isInvalid()) + return CharSourceRange(); + + if (Begin.isMacroID()) + if (!isAtStartOfMacroExpansion(Begin, SM, LangOpts, &Begin)) + return CharSourceRange(); + + SourceLocation End = getLocForEndOfToken(TokenRange.getEnd(), 0, SM,LangOpts); + if (End.isInvalid()) + return CharSourceRange(); + + // Break down the source locations. + std::pair beginInfo = SM.getDecomposedLoc(Begin); + unsigned EndOffs; + if (!SM.isInFileID(End, beginInfo.first, &EndOffs) || + beginInfo.second > EndOffs) + return CharSourceRange(); + + return CharSourceRange::getCharRange(Begin, End); +} + StringRef Lexer::getImmediateMacroName(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) { diff --git a/clang/unittests/Lex/LexerTest.cpp b/clang/unittests/Lex/LexerTest.cpp index e63f31a705ed..05478eef9f43 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/unittests/Lex/LexerTest.cpp @@ -90,14 +90,30 @@ TEST_F(LexerTest, LexAPI) { SourceLocation lsqrLoc = toks[0].getLocation(); SourceLocation idLoc = toks[1].getLocation(); SourceLocation rsqrLoc = toks[2].getLocation(); - + std::pair + macroPair = SourceMgr.getExpansionRange(lsqrLoc); + SourceRange macroRange = SourceRange(macroPair.first, macroPair.second); + SourceLocation Loc; EXPECT_TRUE(Lexer::isAtStartOfMacroExpansion(lsqrLoc, SourceMgr, LangOpts, &Loc)); - EXPECT_EQ(SourceMgr.getExpansionLoc(lsqrLoc), Loc); + EXPECT_EQ(Loc, macroRange.getBegin()); EXPECT_FALSE(Lexer::isAtStartOfMacroExpansion(idLoc, SourceMgr, LangOpts)); EXPECT_FALSE(Lexer::isAtEndOfMacroExpansion(idLoc, SourceMgr, LangOpts)); EXPECT_TRUE(Lexer::isAtEndOfMacroExpansion(rsqrLoc, SourceMgr, LangOpts, &Loc)); - EXPECT_EQ(SourceMgr.getExpansionRange(rsqrLoc).second, Loc); + EXPECT_EQ(Loc, macroRange.getEnd()); + + CharSourceRange range = Lexer::makeFileCharRange(SourceRange(lsqrLoc, idLoc), + SourceMgr, LangOpts); + EXPECT_TRUE(range.isInvalid()); + range = Lexer::makeFileCharRange(SourceRange(idLoc, rsqrLoc), + SourceMgr, LangOpts); + EXPECT_TRUE(range.isInvalid()); + range = Lexer::makeFileCharRange(SourceRange(lsqrLoc, rsqrLoc), + SourceMgr, LangOpts); + EXPECT_TRUE(!range.isTokenRange()); + EXPECT_EQ(range.getAsRange(), + SourceRange(macroRange.getBegin(), + macroRange.getEnd().getLocWithOffset(1))); } } // anonymous namespace