diff --git a/clang-tools-extra/clangd/Selection.cpp b/clang-tools-extra/clangd/Selection.cpp --- a/clang-tools-extra/clangd/Selection.cpp +++ b/clang-tools-extra/clangd/Selection.cpp @@ -324,7 +324,11 @@ // Comparing SourceLocations against bounds is cheaper than getFileID(). SourceLocation Limit = SM.getComposedLoc(FID, SM.getFileIDSize(FID)); auto Batch = ExpandedTokens.take_while([&](const syntax::Token &T) { - return T.location() >= Start && T.location() < Limit; + // [Start, Limit) rather than [Start, Limit] seems correct. + // However PP error-correction can produce one-past-the-end locations. + // (The selections we produce may be dubious, but we won't hang!) + // See https://github.com/llvm/llvm-project/issues/58482 + return T.location() >= Start && T.location() <= Limit; }); assert(!Batch.empty()); ExpandedTokens = ExpandedTokens.drop_front(Batch.size()); diff --git a/clang-tools-extra/clangd/unittests/SelectionTests.cpp b/clang-tools-extra/clangd/unittests/SelectionTests.cpp --- a/clang-tools-extra/clangd/unittests/SelectionTests.cpp +++ b/clang-tools-extra/clangd/unittests/SelectionTests.cpp @@ -697,6 +697,24 @@ EXPECT_EQ("WhileStmt", T.commonAncestor()->Parent->kind()); } +TEST(SelectionTest, MacroInitListCorrection) { + // This macro use is broken: the , is unparenthesized and so forms two args. + // clang can try to repair it, inserting extra parentheses. + // However these parens have dubious locations, we used to infloop here. + // https://github.com/llvm/llvm-project/issues/58482 + const char *Case = R"cpp( + struct SS{int x,y;}; + #define ID(X) X + auto s = ID(S^S{1,2}); // error-ok + )cpp"; + auto AST = TestTU::withCode(Annotations(Case).code()).build(); + auto T = makeSelectionTree(Case, AST); + // Ideally we would select RecordLoc, but the token sequence emitted by the + // preprocessor is confusing (there's an l_paren with the same loc as SS). + // Let's just be happy with selecting something and not crashing. + EXPECT_NE(nullptr, T.commonAncestor()); +} + TEST(SelectionTest, IncludedFile) { const char *Case = R"cpp( void test() {