diff --git a/clang/include/clang/Tooling/Syntax/Tokens.h b/clang/include/clang/Tooling/Syntax/Tokens.h --- a/clang/include/clang/Tooling/Syntax/Tokens.h +++ b/clang/include/clang/Tooling/Syntax/Tokens.h @@ -245,6 +245,10 @@ /// "DECL", "(", "a", ")", ";"} llvm::ArrayRef spelledTokens(FileID FID) const; + /// Returns the spelled Token starting at Loc, if there are no such tokens + /// returns nullptr. + const syntax::Token *spelledTokenAt(SourceLocation Loc) const; + /// Get all tokens that expand a macro in \p FID. For the following input /// #define FOO B /// #define FOO2(X) int X diff --git a/clang/lib/Tooling/Syntax/Tokens.cpp b/clang/lib/Tooling/Syntax/Tokens.cpp --- a/clang/lib/Tooling/Syntax/Tokens.cpp +++ b/clang/lib/Tooling/Syntax/Tokens.cpp @@ -183,6 +183,16 @@ return It->second.SpelledTokens; } +const syntax::Token *TokenBuffer::spelledTokenAt(SourceLocation Loc) const { + assert(Loc.isFileID()); + const auto *Tok = llvm::partition_point( + spelledTokens(SourceMgr->getFileID(Loc)), + [&](const syntax::Token &Tok) { return Tok.location() < Loc; }); + if (!Tok || Tok->location() != Loc) + return nullptr; + return Tok; +} + std::string TokenBuffer::Mapping::str() const { return std::string( llvm::formatv("spelled tokens: [{0},{1}), expanded tokens: [{2},{3})", diff --git a/clang/unittests/Tooling/Syntax/TokensTest.cpp b/clang/unittests/Tooling/Syntax/TokensTest.cpp --- a/clang/unittests/Tooling/Syntax/TokensTest.cpp +++ b/clang/unittests/Tooling/Syntax/TokensTest.cpp @@ -59,6 +59,7 @@ using ::testing::Field; using ::testing::Matcher; using ::testing::Not; +using ::testing::Pointee; using ::testing::StartsWith; namespace { @@ -363,6 +364,12 @@ AllOf(Kind(tok::equal), RangeIs(Code.range("r3"))), AllOf(Kind(tok::string_literal), RangeIs(Code.range("r4"))), AllOf(Kind(tok::semi), RangeIs(Code.range("r5"))))); + + auto StartLoc = SourceMgr->getLocForStartOfFile(SourceMgr->getMainFileID()); + for (auto &R : Code.ranges()) { + EXPECT_THAT(Buffer.spelledTokenAt(StartLoc.getLocWithOffset(R.Begin)), + Pointee(RangeIs(R))); + } } TEST_F(TokenCollectorTest, MacroDirectives) {