Skip to content

Commit 20f8f46

Browse files
committed
[clangd] Fix selection on multi-dimensional array.
This involves separating out the concepts of "which tokens should we descend into this node for" vs "which tokens should this node claim". Reviewed By: sammccall Differential Revision: https://reviews.llvm.org/D116218
1 parent f2b3e25 commit 20f8f46

File tree

2 files changed

+32
-1
lines changed

2 files changed

+32
-1
lines changed

clang-tools-extra/clangd/Selection.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void recordMetrics(const SelectionTree &S, const LangOptions &Lang) {
5858
SelectionUsedRecovery.record(0, LanguageLabel); // unused.
5959
}
6060

61+
// Return the range covering a node and all its children.
6162
SourceRange getSourceRange(const DynTypedNode &N) {
6263
// MemberExprs to implicitly access anonymous fields should not claim any
6364
// tokens for themselves. Given:
@@ -702,7 +703,7 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
702703
void pop() {
703704
Node &N = *Stack.top();
704705
dlog("{1}pop: {0}", printNodeToString(N.ASTNode, PrintPolicy), indent(-1));
705-
claimRange(getSourceRange(N.ASTNode), N.Selected);
706+
claimTokensFor(N.ASTNode, N.Selected);
706707
if (N.Selected == NoTokens)
707708
N.Selected = SelectionTree::Unselected;
708709
if (N.Selected || !N.Children.empty()) {
@@ -744,6 +745,28 @@ class SelectionVisitor : public RecursiveASTVisitor<SelectionVisitor> {
744745
return SourceRange();
745746
}
746747

748+
// Claim tokens for N, after processing its children.
749+
// By default this claims all unclaimed tokens in getSourceRange().
750+
// We override this if we want to claim fewer tokens (e.g. there are gaps).
751+
void claimTokensFor(const DynTypedNode &N, SelectionTree::Selection &Result) {
752+
if (const auto *TL = N.get<TypeLoc>()) {
753+
// e.g. EltType Foo[OuterSize][InnerSize];
754+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ArrayTypeLoc (Outer)
755+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |-ArrayTypeLoc (Inner)
756+
// ~~~~~~~ | |-RecordType
757+
// ~~~~~~~~~ | `-Expr (InnerSize)
758+
// ~~~~~~~~~ `-Expr (OuterSize)
759+
// Inner ATL must not claim its whole SourceRange, or it clobbers Outer.
760+
if (TL->getAs<ArrayTypeLoc>()) {
761+
claimRange(TL->getLocalSourceRange(), Result);
762+
return;
763+
}
764+
// FIXME: maybe LocalSourceRange is a better default for TypeLocs.
765+
// It doesn't seem to be usable for FunctionTypeLocs.
766+
}
767+
claimRange(getSourceRange(N), Result);
768+
}
769+
747770
// Perform hit-testing of a complete Node against the selection.
748771
// This runs for every node in the AST, and must be fast in common cases.
749772
// This is usually called from pop(), so we can take children into account.

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,14 @@ TEST(SelectionTest, CommonAncestor) {
318318
{"[[st^ruct {int x;}]] y;", "CXXRecordDecl"},
319319
{"[[struct {int x;} ^y]];", "VarDecl"},
320320
{"struct {[[int ^x]];} y;", "FieldDecl"},
321+
322+
// Tricky case: nested ArrayTypeLocs have the same token range.
323+
{"const int x = 1, y = 2; int array[^[[x]]][10][y];", "DeclRefExpr"},
324+
{"const int x = 1, y = 2; int array[x][10][^[[y]]];", "DeclRefExpr"},
325+
{"const int x = 1, y = 2; int array[x][^[[10]]][y];", "IntegerLiteral"},
326+
{"const int x = 1, y = 2; [[i^nt]] array[x][10][y];", "BuiltinTypeLoc"},
327+
{"void func(int x) { int v_array[^[[x]]][10]; }", "DeclRefExpr"},
328+
321329
// FIXME: the AST has no location info for qualifiers.
322330
{"const [[a^uto]] x = 42;", "AutoTypeLoc"},
323331
{"[[co^nst auto x = 42]];", "VarDecl"},

0 commit comments

Comments
 (0)