This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Using index for GoToDefinition.
ClosedPublic

Authored by hokein on Apr 17 2018, 1:36 AM.

Details

Summary

This patch adds index support for GoToDefinition -- when we don't get the
definition from local AST, we query our index (Static&Dynamic) index to
get it.

Since we currently collect top-level symbol in the index, it doesn't support all
cases (e.g. class members), we will extend the index to include more symbols in
the future.

Note: the newly-added test fails because of a DynamicIndex issue -- the symbol is
overwritten instead of merging.

Diff Detail

Event Timeline

hokein created this revision.Apr 17 2018, 1:36 AM
sammccall added inline comments.Apr 19 2018, 1:26 AM
clangd/XRefs.cpp
250

the approach could some documentation - I find it hard to follow the code.

This function is probably too big by now and should be split up - but without understanding the goal I can't suggest how.

256

this seems suspicious to me - if we can't find an AST based location for the symbol, why would that mean giving up without consulting the index?

264

isn't the condition here something else - that there's at least one declaration that isn't a definition?

267

why are we ignoring the index if it's not a definition (and therefore preferring the AST one?)

289

Here we're returning exactly one candidate per symbol we identified as a candidate. There are other choices - why this one?

clangd/XRefs.h
26

nit: drop the second const here? we don't normally bother to mark by-value parameters as const (particularly not in declarations)

unittests/clangd/XRefsTests.cpp
128

We have a findDefinitionsHelper to avoid all this boilerplate.
Can you extend/overload it it to work with index?

sammccall added inline comments.Apr 19 2018, 1:35 AM
clangd/XRefs.cpp
250

Having read the code in more detail, I'd suggest a structure like:

  • we identify the symbols being searched for
  • for each, we have a definition slot and a non-definition slot
  • initially we populate one of the slots with our preferred declaration from the AST (if location is available)
  • then we query the index and overwrite either/both slots with returned info
  • finally, we return populated slots for all symbols, definition first

(It's not clear how to order the returned symbols if there are multiple. When can this happen?)

hokein updated this revision to Diff 143508.Apr 23 2018, 2:15 AM
hokein marked 7 inline comments as done.

Refine the patch and address all review comments.

I have updated the patch according to offline discussion -- for each symbol, we will return both decl and def locations (if available, def first) as they seems to be most sensible to users. We always prefer location from AST over Index when conflicts.

clangd/XRefs.cpp
289

Yeah, this was one definition location per symbol, made a change according to our discussion.

Thanks for the changes! I still find the logic quite confusing. Happy to chat offline if useful.

clangd/XRefs.cpp
164

this is a nice abstraction - consider hiding the DeclarationAndMacrosFinder inside it!

249–250

So now you're early-out if there are macros.
This means if you have

void log(string s) { cout << s; }
#define LOG log
LOG("hello");

You'll offer only line 2 as an option, and not line 1.
I'm not sure that's bad, but it's non-obvious - I think it's the thing that the comment should call out.
e.g. "If the cursor is on a macro, go to the macro's definition without trying to resolve the code it expands to"
(The current comment just echoes the code)

249–250

I'm not sure this line adds anything unless you are more specific about what "typical" means, maybe just drop it.

(nit: go to, not goto)

256

because it's more up-to-date?

257

why do you not use GetDefinition(D) as the definition, in the case where it's not equal to D?

258

nit: canonical

260

first why?

269

nit: the body of this loop handles building the query and getting the AST locations, but they're interleaved in a confusing way - please separate them, or even make these two separate loops.

In the latter case you could put QueryRequest inside the if(Index) block, which would be easier to follow.

271

why are we skipping the AST location if we can't generate a USR?

297

remove this log message or make it more useful :-)

309

(The double-nested lambda is somewhat hard to read, can this just be a top-level function? That's what's needed to share it, right?)

532–533

can you also use getSymbolAtPosition here?

unittests/clangd/XRefsTests.cpp
43–57

this is too complicated, please find a way to make it simpler.
Take a look in TestFS.h for the filesystem stuff.

44

why allow BaseName to be chosen?

45

why do you need to pass in the FS? The FS shouldn't need to be the same between the index and AST.

53

why do this conditionally?

64

can we reuse FileIndex instead of reimplementing it?

145

combine these into buildIndex? Having multiple ASTs floating around in the test seems confusing...

hokein updated this revision to Diff 143728.Apr 24 2018, 6:29 AM
hokein marked 14 inline comments as done.

Address review comments:

  • clarify the flow of go to definition.
  • simplify the test code.
  • some function move-out stuff.

Thanks for the useful comments! I refined the patch, and it becomes a bit larger (including some moving stuff).

clangd/XRefs.cpp
164

Inlining DeclarationAndMacrosFinder implementation inside this function would hurt the code readability, I'd leave it as it is. Now all client sides are using this function instead of DeclarationAndMacrosFinder.

249–250

This is a non-functional change.

For the above example, only line 2 will be offered. This is expected behavior IMO -- when we go to definition on a macro, users would expect the macro definition.

257

Done. Added a comment.

260

because this is go to definition, so it is sensible to return the definition as the first result, right?

309

Moved it to XRef.h, and also replace the one in findsymbols.

532–533

Yeah, I just missed this.

unittests/clangd/XRefsTests.cpp
44

The same reason (allowed us to use the same FS between the index and AST).

45

I intended to use one FS for index and AST, because we'd like to test the AST case which includes the index header file, but it turns out this is not needed and add complexity of the testcode, removed it.

53

It is unnecessary if no header code is provided. And -include option would affect the test somehow, if we do it unconditionally, it breaks one of the existing test cases (no location was found). I haven't digged into the reason...

cpp
int [[i]];
#define ADDRESSOF(X) &X;
int *j = ADDRESSOF(^i);
64

Shared the implementation from FileIndex.

sammccall accepted this revision.Apr 30 2018, 6:50 AM

A few more nits (and some ideas for further restructuring the merge logic).
Otherwise LG, let's land this!

clangd/XRefs.cpp
249–250

Ah, so these cases can't both happen (we even have an assert elsewhere).

Still, we can treat them as independent, and it seems simpler: you can remove the if statement, the comment for the if statement, and the early return.

255

up-to-update -> up-to-date

260

Again, I don't think "definition" in LSP is being used in its technical c++ sense.
No issue with the behavior here, but I think the comment should be "we think that's what the user wants most often" or something - it's good to know the reasoning in case we want to change the behavior in the future.

268

OK, as discussed offline the bit that remains confusing is why this isn't a map.
The reason is that some symbols don't have USRs and therefore no symbol IDs.

In practice we don't really expect multiple symbol results at all, so what about one of:

  • map<Optional<SymbolID>, CandidateLocations>
  • map<SymbolID, CandidateLocations> and use SymbolID("") as a sentinel

I slightly prefer the latter because it minimizes the invasiveness of handling this rare/unimportant case.

309

This is pretty weird in terms of layering I think :-(
The function is pretty trivial, but depends on a bunch of random stuff.
Can we move it back (and live with the duplication) until we come up with a good home?

clangd/index/FileIndex.h
26

nit: prefer to remove \brief in favor of formatting the comment so the summary gets extracted.

26

This isn't the main interface of the file.
Move to the bottom, and note that this is exposed to assist in unit tests?

This revision is now accepted and ready to land.Apr 30 2018, 6:50 AM
hokein updated this revision to Diff 144561.Apr 30 2018, 8:06 AM
hokein marked 7 inline comments as done.

Address review comments.

Thanks for the review!

clangd/XRefs.cpp
309

Reverted it, made it local in this file and added a FIXME.

This revision was automatically updated to reflect the committed changes.