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.

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
238

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.

272

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?

280

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

291

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

313

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
238

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
313

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
137

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

222–223

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)

222–236

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)

229

because it's more up-to-date?

231

nit: canonical

233

first why?

242

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.

244

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

273

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

282

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

294

(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?)

518

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
137

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.

222–223

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.

233

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

273

Done. Added a comment.

294

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

518

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
222–223

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.

233

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.

244

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

257

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.

294

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
294

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

This revision was automatically updated to reflect the committed changes.