The new implementation attaches notes to diagnostic message and shows
the original diagnostics in the message of the note.
Details
Diff Detail
- Repository
- rCTE Clang Tools Extra
- Build Status
Buildable 15832 Build 15832: arc lint + arc unit
Event Timeline
This revision is still missing tests, but I wanted to get feedback for the overall design and the new diagnostic messages. So sending out for review now.
clangd/Diagnostics.h | ||
---|---|---|
29 | This could probably use a better name |
Behavior looks good. I think we can do a bit better with (most) fixits - your call on whether it makes sense to do here.
As discussed i'd simplify the diagnostic containers until we know there's a strong need.
clangd/ClangdLSPServer.cpp | ||
---|---|---|
329 | nit: while here, can we add a colon after FixIt (or if appropriate in practice, just drop the prefix altogether?). My recollection is this is a bit hard to parse. | |
clangd/Diagnostics.cpp | ||
86 | inside | |
126 | mind pasting a small example as a function comment? | |
148 | or just mainMessage (this is another verbs-for-pure-functions case where the style recommendation seems to hurt readability to me, but up to you) | |
193 | PreBuild could be FillBasicFields or something? found this name confusing. | |
201 | (nit: if the goal with the callback function is to avoid allocations, shouldn't we reuse the DiagWithFixIts?) | |
323 | do you need else log here? (and just log the main diag) | |
clangd/Diagnostics.h | ||
29 | I found it a bit confusing that this represents both "top-level" diagnostics and notes. It obscures the nature of the hierarchy a bit: there *are* a fixed number of levels with different "kinds", but the kinds are similar enough to share a type. I'd actually consider using inheritance here to model the relationships more clearly. (Yes, gross, I know) struct DiagBase { Message, File, Range, InMainFile } struct Diag : DiagBase { FixIts, Notes, Severity } struct Note : DiagBase {} // or leave this one out until needed (I think we came to the conclusion that different severity of notes isn't interesting and potentially just confusing) | |
36 | As discussed offline - I think fixits should probably be a different struct hanging off the main diagnostic, with a name. Following clang's example seems... less than clear here. They could also be modeled as notes with an optional TextEdit - this seems sligthly less clear but more faithful to clang internals*. I think this probably affects how we should expose them through LSP - they should be named code actions attached to the original diagnostic. | |
36 | Nit: can we call these "Fix"es, which is a noun (at least in common usage?) Clang calls them FixItHint, but I think Fix is no less clear (and shorter) | |
59 | This is cool :) but really looks like premature optimization. (rawDiags() has potential uses but having callers explicitly traverse is going to be clearer as your comment suggests) | |
114 | Can we try removing this struct in favor of two callback params? It seems like a small source of confusion, considering how important the type used to be. | |
124 | I don't think this overload pays for itself - making callers write the outer loop would be a little clearer, I think. (I'd consider having that fn return a smallvector instead of taking a callback, but don't feel strongly) | |
129 | brief doc. | |
129 | Maybe just StoreDiags? | |
131 | If there's no strong reason otherwise, DiagList take() is slightly easier/clearer for tests. compare DiagList Diags; StoreDiagsConsumer Cons(Diags); ... EXPECT_THAT(Diags, ...) vs StoreDiagsConsumer Diags; ... EXPECT_THAT(Diags.take(), ...) | |
clangd/SourceCode.cpp | ||
53 | isn't this sourceLocToPosition? | |
test/clangd/diagnostics.test | ||
23 | I think ideally this wouldn't show up at all, "change return type to int" would be the name of the code action returned for the first diag. Note the range is the same, so we're not giving the user an extra place to apply the fix. |
This is not final, there are still unadressed comments.
- Significantly simplified the implementation by removing the DiagList, etc.
- Addressed some of the rest of the comments.
clangd/ClangdLSPServer.cpp | ||
---|---|---|
329 | I added a colon for now, but happy to look into removing the prefix. The use-case that's slightly confusing is: use of undeclared identifier 'foo'. did you mean 'bar'? Since the actual fix-it is at the end, it's might be a bit hard to understand. | |
clangd/Diagnostics.cpp | ||
201 | Can we actually avoid allocating some of the strings there? | |
clangd/Diagnostics.h | ||
59 | Using a straight-forward representation for now. | |
124 | Still using a callback, but happy to return a SmallVector instead. It seems the pattern for using SmallVector in llvm is passing it in as an output parameter (so that the users might decide themselves what's the count of items on the stack), which is slightly ugly. |
Addressed review comments.
The only thing that's left on my todo-list is a test for toLSPDiags().
Everything else should be ready.
Not sure if you're waiting on comments from me: the changes look good.
As discussed I still think Fix should be a separate thing with a name because that's how editors treat it, and this is the right layer to decide how to do that mapping.
clangd/ClangdLSPServer.cpp | ||
---|---|---|
329 | Yeah. I'd still be inclined to drop the prefix:
| |
329 | nit: fixit --> fix in prefix and in lambda | |
clangd/Diagnostics.h | ||
40 | I don't understand what "persistent" means in this context. But with composition, this one seems rike it should be "Diag" and the type of Main could be DiagDetails or so? |
Sorry, last comments were concurrent with the latest snapshot.
Except where noted, the old comments still apply I think.
clangd/ClangdLSPServer.cpp | ||
---|---|---|
324 | inline this into the for loop? | |
clangd/ClangdLSPServer.h | ||
77 | getFixes? | |
clangd/Diagnostics.h | ||
36 | This is done, and looks good to me. | |
40 | (this happened already, yay!) | |
48 | nit: severity of notes can... | |
52 | Seems a bit confusing to use "diagnostic" to mean something importantly different than "diag", and to define top-level diagnostic in terms of notes. Maybe just "A top-level diagnostic that may have Notes and Fixes"? | |
55 | I think these deserve some comments:
| |
58 | I'm not sure what "conventional" means here? | |
unittests/clangd/ClangdUnitTests.cpp | ||
24 | can you make these operator<< and move to the main code? This seems a fine general-purpose debug representation (could be tweaked, but also later). That avoids the ODR issue, and makes casual logging easier. | |
43 | Ah, and now that you've written this... ;-) For the specific case of "field X matches matcher M" you can use ::testing::Field (you'd keep WithNote, but make it call Field) Messages are good as long as you pass the optional field_name param. | |
81 | this "same range and message as the diagnostic itself" is an important property of the code being tested, and it's hidden here in a comment of a helper function. |
- Addressed review comments
clangd/Diagnostics.h | ||
---|---|---|
36 | We now provide a separate Fix class, but we still don't return it to LSP, keeping the behaviour as is (i.e. mapping LSP ranges to code actions on the server side). The fixes are not reported as separate diagnostics, they are only shown as code actions when users hovers over the diagnostic. | |
58 | Removed conventional from the comment. Other parts should make sense. | |
unittests/clangd/ClangdUnitTests.cpp | ||
43 | Was a useful exercise anyway. Couldn't find the optional 'field_name' param, though, am I missing something? | |
81 | I agree that, but spelling this in the code is so much noise. Renaming to DiagWithEqualFix makes total sense, did that. |
clangd/ClangdLSPServer.cpp | ||
---|---|---|
329 | I still find a prefix useful in some cases, e.g. the unresolved identifier foo. did you mean bar?. |
Ship it! (but let's drop the extra operator== if possible?)
clangd/ClangdLSPServer.cpp | ||
---|---|---|
329 | that's... exactly the case I was replying to :) | |
clangd/Protocol.h | ||
166 ↗ | (On Diff #137987) | The TextEdit/Diagnostic == operators seem odd - what do we need these for? |
unittests/clangd/ClangdUnitTests.cpp | ||
43 | You're not, sorry - it was added recently to google's copy and hasn't been pushed to upstream gmock yet. So for now we have slightly bad error messages (or have the full MatcherInterface implementation) |
getFixes?