Skip to content

Commit c1d1a40

Browse files
authored
[clangd] Handle AttributedTypeLoc in SelectionTree (#163926)
This ensures a method name continues to target the method's declaration even if the method's type uses an attribute. Before this change, the AttributedTypeLoc would claim the method name. Fixes clangd/clangd#2488
1 parent cf51836 commit c1d1a40

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

clang-tools-extra/clangd/Selection.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,18 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
958958
claimRange(SourceRange(FTL.getLParenLoc(), FTL.getEndLoc()), Result);
959959
return;
960960
}
961+
if (auto ATL = TL->getAs<AttributedTypeLoc>()) {
962+
// For attributed function types like `int foo() [[attr]]`, the
963+
// AttributedTypeLoc's range includes the function name. We want to
964+
// allow the function name to be associated with the FunctionDecl
965+
// rather than the AttributedTypeLoc, so we only claim the attribute
966+
// range itself.
967+
if (ATL.getModifiedLoc().getAs<FunctionTypeLoc>()) {
968+
// Only claim the attribute's source range, not the whole type.
969+
claimRange(ATL.getLocalSourceRange(), Result);
970+
return;
971+
}
972+
}
961973
}
962974
claimRange(getSourceRange(N), Result);
963975
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,19 @@ TEST(SelectionTest, CommonAncestor) {
311311
{"[[void foo^()]];", "FunctionProtoTypeLoc"},
312312
{"[[^void foo^()]];", "FunctionDecl"},
313313
{"[[void ^foo()]];", "FunctionDecl"},
314+
// Tricky case: with function attributes, the AttributedTypeLoc's range
315+
// includes the function name, but we want the name to be associated with
316+
// the CXXMethodDecl.
317+
{"struct X { [[const int* ^Get() const <:[clang::lifetimebound]:> "
318+
"{return nullptr;}]]; };",
319+
"CXXMethodDecl"},
320+
// When the cursor is on the attribute itself, we should select the
321+
// AttributedTypeLoc. Note: Due to a bug or deliberate quirk in the AST
322+
// modeling of AttributedTypeLoc, its range ends at the attribute name
323+
// token, not including the closing brackets ":>:>".
324+
{"struct X { const [[int* Foo() const <:<:clang::life^timebound]]:>:> "
325+
"{return nullptr;}; };",
326+
"AttributedTypeLoc"},
314327
// Tricky case: two VarDecls share a specifier.
315328
{"[[int ^a]], b;", "VarDecl"},
316329
{"[[int a, ^b]];", "VarDecl"},

0 commit comments

Comments
 (0)