diff --git a/clang/include/clang/Basic/SourceLocation.h b/clang/include/clang/Basic/SourceLocation.h --- a/clang/include/clang/Basic/SourceLocation.h +++ b/clang/include/clang/Basic/SourceLocation.h @@ -188,9 +188,20 @@ return !(LHS == RHS); } +// Ordering is meaningful only if LHS and RHS have the same FileID! +// Otherwise use SourceManager::isBeforeInTranslationUnit(). inline bool operator<(const SourceLocation &LHS, const SourceLocation &RHS) { return LHS.getRawEncoding() < RHS.getRawEncoding(); } +inline bool operator>(const SourceLocation &LHS, const SourceLocation &RHS) { + return LHS.getRawEncoding() > RHS.getRawEncoding(); +} +inline bool operator<=(const SourceLocation &LHS, const SourceLocation &RHS) { + return LHS.getRawEncoding() <= RHS.getRawEncoding(); +} +inline bool operator>=(const SourceLocation &LHS, const SourceLocation &RHS) { + return LHS.getRawEncoding() >= RHS.getRawEncoding(); +} /// A trivial tuple used to represent a source range. class SourceRange { 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 @@ -309,6 +309,17 @@ const SourceManager *SourceMgr; }; +/// The spelled tokens that overlap or touch a spelling location Loc. +/// This always returns 0-2 tokens. +llvm::ArrayRef +spelledTokensTouching(SourceLocation Loc, const syntax::TokenBuffer &Tokens); + +/// The identifier token that overlaps or touches a spelling location Loc. +/// If there is none, returns nullptr. +const syntax::Token * +spelledIdentifierTouching(SourceLocation Loc, + const syntax::TokenBuffer &Tokens); + /// Lex the text buffer, corresponding to \p FID, in raw mode and record the /// resulting spelled tokens. Does minimal post-processing on raw identifiers, /// setting the appropriate token kind (instead of the raw_identifier reported 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 @@ -248,6 +248,31 @@ return E; } +llvm::ArrayRef +syntax::spelledTokensTouching(SourceLocation Loc, + const syntax::TokenBuffer &Tokens) { + assert(Loc.isFileID()); + llvm::ArrayRef All = + Tokens.spelledTokens(Tokens.sourceManager().getFileID(Loc)); + // Comparing SourceLocations is well-defined within a FileID. + auto *Right = llvm::partition_point( + All, [&](const syntax::Token &Tok) { return Tok.location() < Loc; }); + bool AcceptRight = Right != All.end() && Right->location() <= Loc; + bool AcceptLeft = Right != All.begin() && (Right - 1)->endLocation() >= Loc; + return llvm::makeArrayRef(Right - (AcceptLeft ? 1 : 0), + Right + (AcceptRight ? 1 : 0)); +} + +const syntax::Token * +syntax::spelledIdentifierTouching(SourceLocation Loc, + const syntax::TokenBuffer &Tokens) { + for (const syntax::Token &Tok : spelledTokensTouching(Loc, Tokens)) { + if (Tok.kind() == tok::identifier) + return &Tok; + } + return nullptr; +} + std::vector TokenBuffer::macroExpansions(FileID FID) const { auto FileIt = Files.find(FID); 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 @@ -793,4 +793,45 @@ ActualMacroRanges.push_back(Expansion->range(SM)); EXPECT_EQ(ExpectedMacroRanges, ActualMacroRanges); } + +TEST_F(TokenBufferTest, Touching) { + llvm::Annotations Code("^i^nt^ ^a^b^=^1;^"); + recordTokens(Code.code()); + + auto Touching = [&](int Index) { + SourceLocation Loc = SourceMgr->getComposedLoc(SourceMgr->getMainFileID(), + Code.points()[Index]); + return spelledTokensTouching(Loc, Buffer); + }; + auto Identifier = [&](int Index) { + SourceLocation Loc = SourceMgr->getComposedLoc(SourceMgr->getMainFileID(), + Code.points()[Index]); + const syntax::Token *Tok = spelledIdentifierTouching(Loc, Buffer); + return Tok ? Tok->text(*SourceMgr) : ""; + }; + + EXPECT_THAT(Touching(0), SameRange(findSpelled("int"))); + EXPECT_EQ(Identifier(0), ""); + EXPECT_THAT(Touching(1), SameRange(findSpelled("int"))); + EXPECT_EQ(Identifier(1), ""); + EXPECT_THAT(Touching(2), SameRange(findSpelled("int"))); + EXPECT_EQ(Identifier(2), ""); + + EXPECT_THAT(Touching(3), SameRange(findSpelled("ab"))); + EXPECT_EQ(Identifier(3), "ab"); + EXPECT_THAT(Touching(4), SameRange(findSpelled("ab"))); + EXPECT_EQ(Identifier(4), "ab"); + + EXPECT_THAT(Touching(5), SameRange(findSpelled("ab ="))); + EXPECT_EQ(Identifier(5), "ab"); + + EXPECT_THAT(Touching(6), SameRange(findSpelled("= 1"))); + EXPECT_EQ(Identifier(6), ""); + + EXPECT_THAT(Touching(7), SameRange(findSpelled(";"))); + EXPECT_EQ(Identifier(7), ""); + + ASSERT_EQ(Code.points().size(), 8u); +} + } // namespace