This fixes issue #163, among other improvements to go-to-definition.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
137 | This part is a super hacky way to keep case #9 in LocateSymbol.Ambiguous passing while not regressing other tests. In that case, the selection tree looks like this: DeclStmt Foo foox = Foo("asdf"); VarDecl Foo foox = Foo("asdf") ExprWithCleanups Foo("asdf") CXXConstructExpr Foo("asdf") MaterializeTemporaryExpr Foo("asdf") CXXFunctionalCastExpr Foo("asdf") .RecordTypeLoc Foo The common ancestor is the RecordTypeLoc, but we'd like to find the constructor referred to by the CXXConstructExpr as a target as well. To achieve that, my current code walks up the parent tree until we encounter a CXXConstructExpr, but aborts the walk if certain node types are encountered to avoid certain other scenarios where we have a CXXConstructExpr ancestor. This is almost certainly wrong, as there are likely many other cases where we have a CXXConstructExpr ancestor but don't want to find the constructor as a target which are not being handled correctly. Is there a better way to identify the syntactic form at issue in this testcase? | |
138 | I would appreciate some tips on how we could handle this in targetDecl(). Please see more specific comments below. | |
140 | This part is intended to handle case #8 in LocateSymbol.Ambiguous, where you have a declaration of the form Foo abcd("asdf"); which invokes a constructor. A naive attempt to add this logic to TargetFinder::add(const Decl*) resulted in the constructor being a target even for references to the variable, not just its declaration. |
Thanks! I'd been meaning to do this before my vacation (and had a draft I'll dig up in case there were other issues).
Generally I think we should be willing to change behavior in some marginal cases, and I do think it's important to care about the layering of seltree vs targetDecl because we'll want to plug them into other things in the future. (E.g. using targetDecl + recursiveASTVisitor for more flexible indexing)
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
137 | This case is gross, but as above I think it would be OK to have the targeting work like: Foo("asdf") FFFCSSSSSSC F -> RecordTypeLoc -> CXXRecordDecl Foo C -> CXXConstructExpr -> CXXConstructorDecl Foo(string) S -> StringLiteral -> nullptr WDYT? | |
138 | My thinking is basically:
The TL;DR is I think I'd be happy to drop this special case and try to make SelectionTree surface CXXConstructExpr more often, even if it changes some behavior. | |
138 | I'd prefer not to privilege any defaults here, I think this is unfortunately something that each caller needs to think about. | |
846 | might be clearer to handle that case first then, and guard each with if (!HI) | |
clang-tools-extra/clangd/unittests/XRefsTests.cpp | ||
2202 | nit: can we add the trailing comma in the init-list to preserve the older, clearer formatting instead? |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
138 | +1 to the idea of landing this and changing behavior. |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
138 | FWIW, I do find being able to get from a variable declaration to the invoked constructor to be very useful. If the class has several constructors, there's no other easy way to determine which one is being invoked (only other thing I can think of is to perform "find references" on each constructor and see which one includes the variable declaration as a reference, which is a lot more tedious), and I think that's an important thing to be able to do (just like for named functions). So I'd definitely like to keep this case working. However, I'd be fine with only having the parens or braces target the constructor. (That's still slightly annoying, as you have to place the cursor more precisely, and it also may not be obvious to users, but at least there's a way to do it.) I'm also fine with changing the behaviour for now, and deferring constructor targeting to a follow-up, as there are other benefits this patch brings. |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
138 | We discussed this offline: finding constructor references is useful, but current approach does not generalize very well, e.g. for implicit constructor calls: struct Foo { Foo(int); }; int func(Foo a, Foo b); int test() { func(1, 2); // no way to find Foo(1) and Foo(2) calls. } So we'd probably want some other mechanism to expose *all* possible references. |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
137 | In your description, the target nodes are written underneath the characters. However, our input here is a cursor position (in between two characters). Should we target the CXXConstructExpr if the cursor is just before the parenthesis, just after, or both? (I am aware that SelectionTree can take a range as well as a position, so from the point of view of that API, "only when the selection is a range covering the parenthesis" is an option as well. However, this would not benefit go-to-definition, for which the request as specified in LSP only contains a position, not a range.) |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
138 |
Any ideas for what such a mechanism might look like? |
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
138 | We were discussing this offline, a few options that came up are: Expose tree view of the AST (that links to the source code) with implicit nodes, allow to navigate from the conversion nodes to the references. |
So, to summarize my understanding of the way forward here:
- Land the refactoring without any constructor targeting (i.e. the current patch, which is ready to review in this state).
- In a follow-up patch: target constructor in cases where this can be accomplished by having SelectionTree expose the CXXConstructorExpr on the parenthesis.
- Longer-term: put in place a mechanism for exposing all implicit names via code lens or somesuch.
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
137 | Ah, never mind, I see now that SelectionTree has existing logic for mapping a cursor position to a one-character selection, which in this case would result in mapping the cursor onto the parenthesis if it's just before the parenthesis. |
While trying to implement #2, I realized that SelectionTree already has this behaviour (exposes CXXConstructorExpr on the parenthesis), we just weren't benefitting from it because locateSymbolAt() was passing in the beginning-of-identifier location to getDeclAtPosition(), so Foo^(arg) and F^oo(arg) would yield the same result.
The updated patch fixes this and updates the tests to reflect the constructor targeting in cases where it now works.
LG, just some style nits, thanks!
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
259 | rather than justifying *not* doing something here, can we make it clearer that macros are the special case? | |
266 | log should have more context, and be an elog instead. | |
269 | I don't really understand this comment. If the intention is just not to look at the AST or index when we have a macro (which seems reasonable to me), then just return Result instead of HaveMacro = true above? | |
895–899 | want -> show references to | |
1132 | Are you sure you don't want type hierarchy to work on aliases to record types? (i.e. specify underlying rather than alias) | |
clang-tools-extra/clangd/unittests/XRefsTests.cpp | ||
527 | I'd suggest dropping these two lines of comment. The fact that it's testing constructors is clear from the range name. The FIXME comment describes the future change. | |
529 | This could just be // FIXME: target constructor instead of class.. |
Address latest comments
clang-tools-extra/clangd/XRefs.cpp | ||
---|---|---|
1132 | Good catch! I added a test case to TypeHierarchyTests for this as well. |
This part is a super hacky way to keep case #9 in LocateSymbol.Ambiguous passing while not regressing other tests.
In that case, the selection tree looks like this:
The common ancestor is the RecordTypeLoc, but we'd like to find the constructor referred to by the CXXConstructExpr as a target as well. To achieve that, my current code walks up the parent tree until we encounter a CXXConstructExpr, but aborts the walk if certain node types are encountered to avoid certain other scenarios where we have a CXXConstructExpr ancestor.
This is almost certainly wrong, as there are likely many other cases where we have a CXXConstructExpr ancestor but don't want to find the constructor as a target which are not being handled correctly.
Is there a better way to identify the syntactic form at issue in this testcase?