Skip to content

Commit 9d1dada

Browse files
[clangd] Handle an expanded token range that ends in the eof token in TokenBuffer::spelledForExpanded() (llvm#78092)
Such ranges can legitimately arise in the case of invalid code, such as a declaration missing an ending brace. Fixes clangd/clangd#1559
1 parent 8ff0ab0 commit 9d1dada

File tree

3 files changed

+29
-0
lines changed

3 files changed

+29
-0
lines changed

clang-tools-extra/clangd/unittests/DumpASTTests.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ TEST(DumpASTTests, Arcana) {
186186
EXPECT_THAT(Node.children.front().arcana, testing::StartsWith("QualType "));
187187
}
188188

189+
TEST(DumpASTTests, UnbalancedBraces) {
190+
// Test that we don't crash while trying to compute a source range for the
191+
// node whose ending brace is missing, and also that the source range is
192+
// not empty.
193+
Annotations Case("/*error-ok*/ $func[[int main() {]]");
194+
ParsedAST AST = TestTU::withCode(Case.code()).build();
195+
auto Node = dumpAST(DynTypedNode::create(findDecl(AST, "main")),
196+
AST.getTokens(), AST.getASTContext());
197+
ASSERT_EQ(Node.range, Case.range("func"));
198+
}
199+
189200
} // namespace
190201
} // namespace clangd
191202
} // namespace clang

clang/lib/Tooling/Syntax/Tokens.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,12 @@ std::string TokenBuffer::Mapping::str() const {
401401

402402
std::optional<llvm::ArrayRef<syntax::Token>>
403403
TokenBuffer::spelledForExpanded(llvm::ArrayRef<syntax::Token> Expanded) const {
404+
// In cases of invalid code, AST nodes can have source ranges that include
405+
// the `eof` token. As there's no spelling for this token, exclude it from
406+
// the range.
407+
if (!Expanded.empty() && Expanded.back().kind() == tok::eof) {
408+
Expanded = Expanded.drop_back();
409+
}
404410
// Mapping an empty range is ambiguous in case of empty mappings at either end
405411
// of the range, bail out in that case.
406412
if (Expanded.empty())

clang/unittests/Tooling/Syntax/TokensTest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,18 @@ TEST_F(TokenBufferTest, SpelledByExpanded) {
816816
EXPECT_EQ(Buffer.spelledForExpanded(findExpanded("prev good")), std::nullopt);
817817
}
818818

819+
TEST_F(TokenBufferTest, NoCrashForEofToken) {
820+
recordTokens(R"cpp(
821+
int main() {
822+
)cpp");
823+
ASSERT_TRUE(!Buffer.expandedTokens().empty());
824+
ASSERT_EQ(Buffer.expandedTokens().back().kind(), tok::eof);
825+
// Expanded range including `eof` is handled gracefully (`eof` is ignored).
826+
EXPECT_THAT(
827+
Buffer.spelledForExpanded(Buffer.expandedTokens()),
828+
ValueIs(SameRange(Buffer.spelledTokens(SourceMgr->getMainFileID()))));
829+
}
830+
819831
TEST_F(TokenBufferTest, ExpandedTokensForRange) {
820832
recordTokens(R"cpp(
821833
#define SIGN(X) X##_washere

0 commit comments

Comments
 (0)