clangd will use findReferences to provide LSP's reference feature.
Details
Diff Detail
- Repository
- rCTE Clang Tools Extra
- Build Status
Buildable 22086 Build 22086: arc lint + arc unit
Event Timeline
This patch is incomplete, and it is a prototype based on our discussion last week. You can patch it locally, and play around with VSCode.
Mostly just reviewed the references() implementation.
For the infrastructure I have some high level questions:
- why are we combining SymbolSlab and occurrences?
- what's the motivation for the separate preamble/main-file indexes
- why put more functionality into SymbolCollector?
Let's discuss offline a bit if that works?
clangd/TUScheduler.h | ||
---|---|---|
54 ↗ | (On Diff #161441) | (we're replacing a std::function, why not just a struct with two std::functions? saves boilerplate in a bunch of places...) |
clangd/XRefs.cpp | ||
715 | I'm not sure unpacking the reference options into individual bools is going to scale well. I'd suggest passing the whole struct instead. | |
724 | "local" is ambiguous here (this function cares both about file-local references and function-local symbols). | |
730 | (This saves us hitting the index to look up references for one symbol, not sure if it's worth it at all). I wouldn't particularly trust the helpers in clang::index::. Indeed the implementation looks wrong here (e.g. it would treat lambda captures as global, I think?) I'd prefer D->getParentFunctionOrMethod() here. | |
737 | I'm not sure this is the best interpretation of LSP includeDeclaration. The LSP spec is very vague here, and we can usually assume it's written with typescript in mind :-) where the declaration/definition distinction doesn't really exist. Alternatively we could punt on this and just ignore the flag for now, and add it in a followup. | |
743 | Reusing symbolcollector here seems odd. It forces us to go through SymbolID rather than just using the Decl*. This is certainly reliable for global symbols, but I've got no idea how robust USRs are for local symbols. It also ends up building the Symbol objects, which we then throw away. How much of SymbolCollector are we really reusing, vs a custom IndexDataConsumer? How is this different from document-highlights? Maybe we can reuse the consumer. | |
748 | This comment is surprising as it stands. Maybe // Look in the AST for references from the current file. (and hoist the commentto the top of the code that deals with AST, currently line 686) | |
762 | This is also a bit confusing - the symbols are non-local (in the function-scope sense) and the references are non-local (in the file sense). // Consult the index for references in other files. // We only need to consider symbols visible to other files. | |
771 | in the usual case, SeenURIs is the current file (if there were any local usages), or empty (if there weren't). This means if there are refs from the current file in the index but not locally (they were deleted), you'll return them when it would be better not to. Why not just obtain the URI for the main file directly and compare that, instead of extracting it from the local references found? | |
clangd/index/Index.h | ||
368 ↗ | (On Diff #161441) | why are we moving occurrences into the symbol slab? |
Ah ok, that explains some of the unrelated changes.
I do have some questions there that would be good to discuss. Meanwhile, @hokein can you rebase this patch against head?
Thanks for the comments.
Yes, I'd love to, but since this patch is quite large, I need split it into smaller patches. I will update this patch (focus on the references implementation), it depends on the following patches
- address review comments
- rescope the patch only focus on findReferences implementation.
clangd/XRefs.cpp | ||
---|---|---|
715 | Removed this parameter. | |
730 | I think it is worth the effort here. At least for function-local symbols, traversing AST is sufficient to get complete xref results. | |
737 | Ignore this flag, and always return all kinds. | |
743 | I refined the highlight IndexDataConsumer, and now the common code is shared. |
Looking forward to using this!
Unless you object, I'd like to address the remaining comments (and get a review), so you can make the most of your leave!
clangd/XRefs.cpp | ||
---|---|---|
334 | why is this a map keyed by D? the keys are never used, the result is always flattened into a vector. | |
362 | why is this a subclass rather than just using OccurrenceCollector and putting the finish() code in findReferences()? | |
379 | out of curiosity: how do we actually get duplicates? | |
389 | as above, why does this need to be a subclass | |
719 | (comment just echoes code) | |
722 | visiable -> visible | |
725 | we can check isExternallyVisible, I think? | |
unittests/clangd/XRefsTests.cpp | ||
1191 | can we use a simple real implementation TU.index() instead of mocking here? |
Thanks, feel free to pick it up. The comments make sense to me. Just let you know I have one more xref patch: https://reviews.llvm.org/D50896.
clangd/XRefs.cpp | ||
---|---|---|
334 | Using map can group results by the Decl, but it doesn't matter here. | |
379 | Some AST nodes will be visited more than once, so we might have duplicated locations. | |
725 | I'm not sure whether isExternallyVisible suits our cases. For example, if a header defines a static const int MAX, and the header is widely included in the project, we want to query it in the index, but the linkage of the symbol MAX is internal. So I'd prefer to be conservative here (only for function-local symbols). | |
unittests/clangd/XRefsTests.cpp | ||
1191 | Yes, we can. But the test here is only to verify whether the index has been queried. The results from index are not important. |
Address comments and tighten up highlights/refs reuse in XRefs.cpp a bit.
Still to do:
- test interaction with index, including actual data
- maybe add the ClangdServer/LSP binding and lit test, if it's just boilerplate
Oops, and I rebased on head (Occurrences -> Refs) of course.
@ilya-biryukov @ioeric, any interest in taking over review here?
clangd/XRefs.cpp | ||
---|---|---|
329 | In fact SmallSet is missing this constructor :-( |
nit: we can initialize TargetDecls like Targets(TargetDecls.begin(), TargetDecls.end()).