ctrl-clicking on #include statements now opens the file being pointed by that statement.
Details
Diff Detail
- Repository
- rCTE Clang Tools Extra
- Build Status
Buildable 13363 Build 13363: arc lint + arc unit
Event Timeline
clangd/ClangdUnit.cpp | ||
---|---|---|
148 | Please use our descriptive Path typedef. | |
unittests/clangd/ClangdTests.cpp | ||
764 | Some includes may come outside of preamble, I would expect current implementation to fail for this case: #include <vector> // <-- works here // preamble ends here int main() { #include "my_include.h" // <-- does not work here } |
clangd/ClangdUnit.cpp | ||
---|---|---|
136 | If I were to use InclusionDirective , how would that callback be called automatically? As far as I know, it wouldn't be called automatically for every file that gets indexed the same way AfterExecute would be. |
clangd/ClangdUnit.cpp | ||
---|---|---|
136 | It will be called for each #include directive that Preprocessor encountered while building the preamble. Does that answer your question? |
- Now overriding InclusionDirective as a callback to get proper includes information.
- Fixed tests.
- Fixed adding incorrect test file.
This revision requires modifications to clang which can be reviewed on revision D39375.
I'm not sure it's better to use the InclusionDirective callback for this. We need to get the includes in two places: in the preamble and non-preamble. In the preamble we use the callback, have to store some temporary stuff because we don't have a SourceManager in InclusionDirective, then in finish we use the SourceManager to convert everything. In the non-preamble, we cannot use the callback so we use the SourceManager to go through the includes. Therefore, we have to maintain two different ways of getting the inclusion map. Without using the InclusionDirective callback, we use the SourceManager in both cases and can use the same code to iterate through inclusions, just on two different SourceManagers at different moments. We also don't need to make another patch to modify the PreambleCallbacks. I also double this is much slower but I think this simplifies the code nicely.
What do you think?
I think we should never iterate through SourceManager, as it's much easier to get wrong than using the callbacks. I agree that all that fiddling with callbacks is unfortunate, but it's well worth the fact that it'd be much easier to tell that the implementation is correct by simply looking at the implementation and not knowing how SourceManager works. PPCallbacks is a much more direct API that was designed to handle our purposes.
Note that we don't need to use SourceManager to find non-preamble includes, we should implement proper PPCallbacks and use them when building the AST.
clangd/ClangdServer.cpp | ||
---|---|---|
368 ↗ | (On Diff #120485) | This map and two vectors come up quite often in your commit. Something like class IncludeReferenceMap { llvm::Optional<PathRef> findIncludeTargetAtLoc(Location Loc); private: // maps, vectors, whatever we need go here .... }; We'd build it using the callbacks, then store it in PreambleData (for PCH include) and ParsedAST (for non-PCH includes). And later query both maps to get the result. |
clangd/ClangdUnit.cpp | ||
30 | We probably don't need it this include | |
34 | What's the purpose of this class? | |
145 | Please also use PPCallbacks interface instead of the SourceManager (should be possible to hook it up somewhere in ParsedAST::Build). |
clangd/ClangdUnit.cpp | ||
---|---|---|
34 | We need to be able to use a wrapper class to be able to make a unique_ptr to be sent to PrecompiledPreamble::Build in order to add the list of preprocessor Callbacks. |
Using PPCallbacks interface to find non-preamble includes
Created inner class to store vectors in to find locations
Refactored methods to remove some unnecessary parameters
Refactored Unit tests
Merge with most recent master branch + clang
clangd/ClangdUnit.cpp | ||
---|---|---|
34 | Could we implement an instance of PPCallbacks that contains CppFilePreambleCallbacks and forwards to that specific method instead? The reason is that we're not really delegating other methods in this calls(nor should we, the implementation would be too compilcated). |
Creating unique_ptr for wrapper class now uses overriden method createPPCallbacks() from PrecompiledPreamble class Moved wrapper class to inline inside createPPCallbacks() Wrapper class now uses CppFilePreambleCallbacks as field
clangd/ClangdServer.cpp | ||
---|---|---|
454 ↗ | (On Diff #126429) | IncludeReferenceMap & here? See other comment in takeIRM |
clangd/ClangdUnit.cpp | ||
30 | remove | |
109 | remove | |
116 | These need to be swapped to be in the same order that they are declared below. | |
118 | I don't think we need this if we pass the map by reference (and store it as a reference, see other comment) | |
125 | Remove (see previous comment) | |
128 | remove | |
144 | I tried to simply the methods introduced here: void AfterExecute(CompilerInstance &CI) override { SourceMgr = &CI.getSourceManager(); for (auto InclusionLoc : TempPreambleIncludeLocations) addIncludeLocation(InclusionLoc); } void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName, bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File, StringRef SearchPath, StringRef RelativePath, const Module *Imported) override { auto SR = FilenameRange.getAsRange(); if (SR.isInvalid() || !File || File->tryGetRealPathName().empty()) return; if (SourceMgr) { addIncludeLocation({SR, File->tryGetRealPathName()}); } else { // When we are processing the inclusion directives inside the preamble, // we don't have access to a SourceManager, so we cannot convert // SourceRange to Range. This is done instead in AfterExecute when a // SourceManager becomes available. TempPreambleIncludeLocations.push_back({SR, File->tryGetRealPathName()}); } } void addIncludeLocation(std::pair<SourceRange, std::string> InclusionLoc) { // Only inclusion directives in the main file make sense. The user cannot // select directives not in the main file. if (SourceMgr->getMainFileID() == SourceMgr->getFileID(InclusionLoc.first.getBegin())) IRM.insert({getRange(InclusionLoc.first), InclusionLoc.second}); } Range getRange(SourceRange SR) { Position Begin; Begin.line = SourceMgr->getSpellingLineNumber(SR.getBegin()); Begin.character = SourceMgr->getSpellingColumnNumber(SR.getBegin()); Position End; End.line = SourceMgr->getSpellingLineNumber(SR.getEnd()); End.character = SourceMgr->getSpellingColumnNumber(SR.getEnd()); return {Begin, End}; } | |
148 | IncludeReferenceMap &IRM; That way, we can use the same map and pass it multiple times to different "collectors". | |
166 | I think we need a "is main file" check here. In case this is used on a AST with no preamble. | |
267 | I don't think this change was brought back intentionally? | |
277–282 | IncludeReferenceMap &IRM | |
308 | unnecessary std::move | |
317 | std::move(IRM) | |
326–327 | I think after rebase, all this code in DeclarationLocationsFinder will need to be moved around, probably inside clangd::findDefinitions? | |
328 | for (auto &IncludeLoc : IncludeLocationMap) { Range R = IncludeLoc.first; if (isSameLine(R.start.line)) addLocation(URI::fromFile(IncludeLoc.second), R); } | |
335 | Probably if an include location was found, we don't want to continue and it should early return. | |
555 | I think you can put the construction of SerializedDeclsCollector back to where it was. | |
633 | You can pass just IRM, so there is no need for the getIRM. We'll have eventually to transfer ownership to ParsedAST so maybe std::move(IRM) here? | |
clangd/ClangdUnit.h | ||
62 | With the map being the only field left (see other comments), there is no need for a class I think. Maybe make a handy type alias for this? using IncludeReferenceMap = std::unordered_map<Range, Path, RangeHash>; | |
63 | findIncludeTargetAtLoc is never used, remove | |
67 | No need to keep this. | |
68 | No need to keep this. | |
94 | We need to keep this copy of IRM alive for subsequent calls to "open definition" for the same ParsedAST. So it is correct to not do a std::move here. However, I think it should be changed to: IncludeReferenceMap &getIRM() and also the caller should assign to a reference. | |
121 | this wrongly came back? | |
unittests/clangd/ClangdTests.cpp | ||
624–625 | why is this check removed? | |
753 | Need to test includes outside preamble. | |
780 | I get an "unchecked assertion" here before the Expected is not checked. I was able to replace it with: auto ExpectedLocations = Server.findDefinitions(FooCpp, P); ASSERT_TRUE(!!ExpectedLocations); std::vector<Location> Locations = ExpectedLocations->Value; | |
786 | I get a test failure here. ../tools/clang/tools/extra/unittests/clangd/ClangdTests.cpp(785): Error: Expected: check Which is: "clangd-test/foo." To be equal to: FooH Which is: { '/' (47, 0x2F), 'c' (99, 0x63), 'l' (108, 0x6C), 'a' (97, 0x61), 'n' (110, 0x6E), 'g' (103, 0x67), 'd' (100, 0x64), '-' (45, 0x2D), 't' (116, 0x74), 'e' (101, 0x65), 's' (115, 0x73), 't' (116, 0x74), '/' (47, 0x2F), 'f' (102, 0x66), 'o' (111, 0x6F), 'o' (111, 0x6F), '.' (46, 0x2E), 'h' (104, 0x68) } Not sure what's wrong. There is an additional front slash and missing ".h" at the end? | |
795 | Similar unchecked problem here, this works (although a bit yucky): ExpectedLocations = Server.findDefinitions(FooCpp, P3); ASSERT_TRUE(!!ExpectedLocations); Locations = ExpectedLocations->Value; EXPECT_TRUE(!Locations.empty()); // Test invalid include Position P2 = Position{2, 11}; ExpectedLocations = Server.findDefinitions(FooCpp, P2); ASSERT_TRUE(!!ExpectedLocations); Locations = ExpectedLocations->Value; EXPECT_TRUE(Locations.empty()); |
inner class IncludeReferenceMap replaced by one map
fillRangeVector() and findPreambleIncludes() code moved into wrapper class
Open definition now returns an empty Range struct (line, character = 0)
Fixed unit tests
Minor code improvements
clangd/ClangdServer.cpp | ||
---|---|---|
446 ↗ | (On Diff #127161) | We don't need this Preamble variable, right? |
clangd/ClangdUnit.cpp | ||
109 | We don't need to derive this class from PPCallbacks, DelegatingPPCallbacks can call any method on it anyway as it knows the exact type. Please remove this inheritance. | |
115 | Instead of passing SourceManager in constructor, maybe add a BeforeExecute(CompilerInstance &CI) method to PreambleCallbacks and set SourceManager when its called. | |
150 | Adding BeforeExecute would allow to get rid of this vector. | |
201 | NIT: replace with return llvm::make_unique<DelegatingPPCallbacks>(*this); and remove the local variable. | |
277–282 | Let's store IncludeReferenceMap in ParsedAST instead of having it as out parameter. | |
305 | Why do we use CppFilePreambleCallbacks here? Factor the code that handles the references into a separate class and use it here: class IncludeRefsCollector : public PPCallbacks { public: IncludeRefsCollector(IncludeReferenceMap &Refs) : Refs(Refs) {} // implementation of PPCallbacks ... private; IncludeReferenceMap &Refs; }; /// ..... class CppFilePreambleCaallbacks { public: // .... std::unique_ptr<PPCallbacks> createPPCallbacks() { return llvm::make_unique<IncludeRefsCollector>(this->IRM); } }; // .... void ParsedAST::Build() { // .... // No need to create CppFilePreambleCallbacks here. Clang->getPreprocessor().addPPCallbacks(llvm::make_unique<IncludeRefsCollector>(IRM)); //.... } | |
327 | Use IncludeReferenceMap typedef here. | |
327 | Do we change semantics of getting the file paths here? Maybe leave the code as is? | |
327 | NIT: empty linke at the start of the function | |
329 |
| |
330 | Let's check the the point is in range, not that it's not the same line. So that we don't trigger goto in strange places, like comments: #include <vector> // this include is very important. <--- we should not skip to include when editor caret is over a comment | |
330 | If we store IncludeLocationMap in ParsedAST we don't need to change interface of this function. | |
332 | This method does not seem particularly useful. One can construct location like this: Location{Uri, R}, the whole method can be written as DeclarationLocations.push_back(Location{Uri, R}). | |
clangd/ClangdUnit.h | ||
62 | We store a map, but only iterate through it in order. Let's use vector<pair<Range, Path>> instead. |
CppFilePreambleCallbacks no longer extends PPCallbacks CppFilePreambleCallbacks no longer needs SourceManager parameter Removed temporary vector TempPreambleIncludeLocations IncludeReferenceMap in ParsedAST no longer passed by reference Code handling includes outside of preambles is now separated in IncludeRefsCollector class Removed addLocation Changed isSameLine function name and now checks if searched location is in range instead of verifying the line Removed changes to findDefinitions interface
Thanks for addressing the comments quickly.
I took another look and added a few more comments.
This moves in the right direction, though, this is really close to landing.
clangd/ClangdServer.cpp | ||
---|---|---|
25 ↗ | (On Diff #127386) | Is this include redundant now? Can we remove it? |
430 ↗ | (On Diff #127386) | Capturing this seems redundant. Remove it? |
clangd/ClangdUnit.cpp | ||
85 | clang-format please | |
85 | Do we need both checks? Doesn't SourceMgr.isInMainFile handles all the cases? | |
115 | Let's create a new empty map inside this class and have a takeIncludeReferences method (similar to TopLevelDeclIDs and takeTopLevelDeclIDs) | |
150 | We should have SourceMgr at all the proper places now, let's store IncludeReferenceMap directly | |
278 | What reference does this IncludeReferenceMap contain? Includes from the preamble? | |
326–327 | This class handles processing AST and preprocessor, it does not need to get IncludeLocationMap in constructor or store it at all. | |
clangd/ClangdUnit.h | ||
94 | Make it all const? const IncludeReferenceMap &getIRM() const { return IRM; } | |
278 | This function moved without need and lost a comment. |
clangd/ClangdUnit.cpp | ||
---|---|---|
85 | This check was here to prevent an issue that appeared previously. I don't think this check is needed anymore. | |
150 | The idea was to pass a single map around to fill with every reference instead of having separate maps being merged together. | |
278 | This should contain every reference available for one file. |
Removed some useless inclusions Removed superfluous check when inserting data in map Moved addition to DeclarationLocations in finish() outside of DeclMacrosFinder Merged with revision 321087 (moved findDefinitions and findDocumentHighlights
Another round of review
clangd/ClangdUnit.cpp | ||
---|---|---|
115 | This comment is not addressed yet, but marked as done. | |
141 | Maybe add assert(SourceMgr && "SourceMgr must be set at this point") here? | |
150 | You copy the map for preamble and then append to it in CppFilePreambleCallbacks? That also LG, we should not have many references there anyway. | |
278 | Thanks for clarifying, LG. | |
282 | Don't we already store the map we need in PreambleData? Why do we need an extra IRM parameter? | |
clangd/ClangdUnit.h | ||
278 | Now we have duplicated definitions here. Bad merge? | |
clangd/CodeComplete.cpp | ||
328 ↗ | (On Diff #127617) | Accidental change? |
807 ↗ | (On Diff #127617) | Accidental change? |
clangd/XRefs.cpp | ||
41 | We don't need locations anymore. Remove them. | |
177 | Probably a good place for a comment. Also, maybe rename local var to something easier to understand like IncludeDefinitions /// Process targets for paths inside #include directive. std::vector<Location> IncludeTargets; | |
178 | No need to special case empty maps, remove the if altogether. | |
183 | Replace with DeclMacrosFinder->getSearchedLocation() SourceLocationBeg, it makes the code easier to read. | |
185 | why do we need to convert to unsigned? To slience compiler warnings? | |
188 | this should be R.start.charater <= CharNumber && CharNumber <= R.end.character |
clangd/XRefs.cpp | ||
---|---|---|
183 | Sorry, the comment got messed up. Here's the correct version: Replace DeclMacrosFinder->getSearchedLocation() with SourceLocationBeg, it makes the code easier to read. |
@ilya-biryukov Hi! I'll be updating William's patches that were in progress. I just have a few comments/question before I send a new update. (I also don't know if I can update this diff or I have to create a new diff on Phabricator... I guess we'll see!!).
clangd/ClangdUnit.cpp | ||
---|---|---|
115 | As mentioned below, the idea was to have a single map being appended to, without having to merge two separate maps. However, I can change the code so that two separate maps are built and merged if you prefer. But I'm not so clear if that's what you'd prefer:
It's not meant to have any copy. The idea was to create a single IncludeReferenceMap in CppFile::deferRebuild, populate it with both preamble and non-preamble include references and std::move it around for later use (stored in ParsedAST). | |
282 | Since the map will be now stored in ParsedAST and the instance doesn't exist yet, we need to keep the IRM parameter. I noticed that it wasn't being std::move'd though. | |
clangd/XRefs.cpp | ||
185 | Yes, "line" from the protocol is signed, whereas getSpellingColumn/lineNumber returns unsigned. I'll extract another var for the line number and cast both to int instead to have less casts and make the condition smaller. |
@malaperle, hi! Both new diff and updating this works, so feel free the one that suits you best. I tend to look over the whole change on each new round of reviews anyway.
clangd/ClangdUnit.cpp | ||
---|---|---|
115 | We can't have a single map because AST is rebuilt more often than the Preamble, so we have two options:
I think both are fine, since the copy of the map will be cheap anyway, as we only store a list of includes inside the main file. | |
282 | That looks fine. Also see the other comment on why we need to copy the map from the Preamble, and not std::move it. | |
clangd/XRefs.cpp | ||
185 | Can we maybe convert to clangd::Position using the helper methods first and do the comparison of two clangd::Positions? |
clangd/ClangdUnit.cpp | ||
---|---|---|
115 |
Doh! Indeed. OK so I added the map in PreambleData, this one will say every reparse unless the preamble changes. When the AST is built/rebuilt, the map is copied (or empty) and includes from the AST are appended to it then stored in ParsedAST (option #1?). | |
clangd/XRefs.cpp | ||
185 | offsetToPosition (if that's what you mean) uses a StringRef of the code, which is not handy in this case. I'll add another one "sourceLocToPosition" to convert a SourceLocation to a Position. WDYT? It can be used in a few other places too. |
Store map in PremableData and copy it on reparse.
Convert SourceLoc to Position when comparing with include Positions.
clangd/ClangdUnit.h | ||
---|---|---|
51 | We use unordered_map as a vector<pair<>> here. (i.e. we never look up values by key, because we don't only the range, merely a point in the range) | |
clangd/XRefs.cpp | ||
185 | Looks good, thanks! | |
unittests/clangd/ClangdTests.cpp | ||
780 | We moved findDefinition tests to XRefsTests.cpp and added a useful Annotation helper to make writing these tests simpler. |
clangd/ClangdUnit.cpp | ||
---|---|---|
97 | NIT: replace FilenameRange.getAsRange() with SR | |
119 | NIT: remove ctor, use initializer on the member instead: private: SourceManager *SourceMgr = nullptr; | |
149 | Maybe swap IncLocations and SourceMgr? Grouping TopLevelDecls, TopLevelDeclIDs and IncLocations together seems reasonable, as all of them are essentially output parameters. | |
286 | NIT: move IncLocations closer to their first use, i.e. create the variable right before addPPCallbacks() | |
clangd/ClangdUnit.h | ||
51 | LG | |
95 | NIT: move to .cpp file to be consisten with other decls in this file. | |
clangd/XRefs.cpp | ||
181 | NIT: SourceMgr does not depend on the loop variable, declare it out of the loop. | |
184 | NIT: define operator <= for Position and do this instead: R.start <= Pos && Pos < R.end (note that LSP ranges are half-open, i.e. the end is excluded). | |
188 | Let's follow the pattern of decls and macros here. I.e. not return from the function, merely collect all the results. | |
unittests/clangd/XRefsTests.cpp | ||
53 ↗ | (On Diff #133978) | NIT: remove this class, use IgnoreDiagnostics from Compiler.h instead. |
245 ↗ | (On Diff #133978) | Could we also add tests for corner cases: cursor before opening quote, cursor after the closing quote, cursor in the middle of #include token? (we shouldn't navigate anywhere in the middle of the #include token) |
unittests/clangd/XRefsTests.cpp | ||
---|---|---|
53 ↗ | (On Diff #133978) | It looks like IgnoreDiagnostics from Compiler.h implements DiagnosticConsumer from Compiler.h, and not DiagnosticsConsumer from ClangdServer.h so that wouldn't work. |
unittests/clangd/XRefsTests.cpp | ||
---|---|---|
245 ↗ | (On Diff #133978) |
I assume we don't want to navigate anywhere for these positions? I don't have an opinion.
It did in CDT and I thought it worked nicely as it made it easier to click on it. You can hold ctrl on the whole line and it underlined it (well except trailing comments). But clients don't handle the spaces nicely (underline between #include and file name) so I thought I'd work on the client first before making the server do it. Anyhow, for now it shouldn't navigate indeed. |
unittests/clangd/XRefsTests.cpp | ||
---|---|---|
53 ↗ | (On Diff #133978) | You're right, I got confused, sorry. Disregard my comment. |
245 ↗ | (On Diff #133978) |
I'd say we should navigate when the cursor is right before the opening quote. For the closing quote I don't have any opinions either :-) |
Last drop of NITs. After they're fixed, this change is ready to land!
unittests/clangd/XRefsTests.cpp | ||
---|---|---|
282 ↗ | (On Diff #134296) | There's a typo, should be HeaderAnnotations |
302 ↗ | (On Diff #134296) | NIT: one assertion using EXPECT_THAT will read better and produce more helpful messages in case of failures: // Create FooHUri to avoid typing URIForFile everywhere. auto FooHUri = URIForFile{FooH.str()}; EXPECT_THAT(Locations, ElementsAre(Location{FooHUri, HeaderAnnoations.range()})); |
328 ↗ | (On Diff #134296) | NIT: Use EXPECT_THAT here too: |
LGTM modulo the ASSERT_TRUE nit.
unittests/clangd/XRefsTests.cpp | ||
---|---|---|
295 ↗ | (On Diff #134432) | Maybe replace with ASSERT_TRUE? |
We use unordered_map as a vector<pair<>> here. (i.e. we never look up values by key, because we don't only the range, merely a point in the range)
Replace map with vector<pair<>> and remove RangeHash that we don't need anymore.