[Tooling/Syntax] Helpers to find spelled tokens touching a location.

Summary: Useful when positions are used to target nodes, with before/after ambiguity.

Reviewers: ilya-biryukov, kbobyrev

Subscribers: cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D71356
This commit is contained in:
Sam McCall 2019-12-11 15:40:23 +01:00
parent 471d9f3e69
commit 3f8da5d091
4 changed files with 88 additions and 0 deletions

View File

@ -188,9 +188,20 @@ inline bool operator!=(const SourceLocation &LHS, const SourceLocation &RHS) {
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 {

View File

@ -309,6 +309,17 @@ private:
const SourceManager *SourceMgr;
};
/// The spelled tokens that overlap or touch a spelling location Loc.
/// This always returns 0-2 tokens.
llvm::ArrayRef<syntax::Token>
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

View File

@ -248,6 +248,31 @@ TokenBuffer::expansionStartingAt(const syntax::Token *Spelled) const {
return E;
}
llvm::ArrayRef<syntax::Token>
syntax::spelledTokensTouching(SourceLocation Loc,
const syntax::TokenBuffer &Tokens) {
assert(Loc.isFileID());
llvm::ArrayRef<syntax::Token> 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<const syntax::Token *>
TokenBuffer::macroExpansions(FileID FID) const {
auto FileIt = Files.find(FID);

View File

@ -793,4 +793,45 @@ TEST_F(TokenBufferTest, macroExpansions) {
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