This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Allow embedders some control over when diagnostics are generated.
ClosedPublic

Authored by sammccall on Feb 20 2018, 10:56 AM.

Details

Summary

Through the C++ API, we support for a given snapshot version:

  • Yes: make sure we generate diagnostics for exactly this version
  • Auto: generate eventually-consistent diagnostics for at least this version
  • No: don't generate diagnostics for this version

Eventually auto should be debounced for better UX.

Through LSP, we force diagnostics for initial load (bypassing future debouncing)
and all updates follow the "auto" policy.

This is complicated to implement under the CancellationFlag design, so
rewrote that part to just inspect the queue instead.

It turns out we never pass None to the diagnostics callback, so remove Optional
from the signature. The questionable behavior of not invoking the callback at
all if CppFile::rebuild fails is not changed.

Diff Detail

Repository
rL LLVM

Event Timeline

sammccall created this revision.Feb 20 2018, 10:56 AM
ilya-biryukov added inline comments.Feb 21 2018, 2:14 AM
clangd/ClangdServer.h
282 ↗(On Diff #135104)

Maybe add a parameter name here?
It's mostly a personal preference, I tend to copy-paste parameter lists between declaration/definition site if I change them. Missing parameter names totally break this workflow for me :-)

clangd/TUScheduler.cpp
298 ↗(On Diff #135104)

Instead of skipping requests here we could try removing them from back of the queue in startTask (or rewriting the last request instead of adding a new one).
It feels the code could be simpler, as we will only ever have to remove a single request from the queue. And it could also keep the queue smaller in case of many subsequent Auto requests.
WDYT?

339 ↗(On Diff #135104)

Maybe skip updates directly in this function and make it return void?
Calling a function in a loop that loops through elements itself is a little confusing.

sammccall updated this revision to Diff 135382.Feb 22 2018, 3:07 AM
sammccall marked an inline comment as done.

add param names to decls

Not convinced about the flow control stuff - I think you might be underestimating the complexity of the cancellation-based approach with the extra functionality.
But if you think it would still be more readable the other way, happy to get a third opinion.

clangd/ClangdServer.h
282 ↗(On Diff #135104)

Done.
My preference is to optimise for reading rather than writing the code, and a name that carries no extra semantics is just noise. But I don't care that much.

clangd/TUScheduler.cpp
298 ↗(On Diff #135104)

Having startTask look ahead to find things to cancel was the thing I found most confusing/limiting in the previous code, so I'd rather not go back there :-)
That said, I did try this first, trying to limit the scope of this patch, but it got hard.

The main problems are:

  • you're not just looking ahead one task, or even to a fixed one. After [auto no], no cancels no, auto cancels both, read cancels neither. The states and the state machine are hard to reason about. (unless you just loop over the whole queue, which seems just as complex)
  • the decision of "should task X run" is distributed over time via mutating state, rather than happening at one point via reads
    • when looking at startTask time, you have to reason about the (possibly) concurrently running task. In run(), no task is running and nothing can be enqueued, so there's no concurrency issues.

And it could also keep the queue smaller in case of many subsequent Auto requests.

This is true, but it doesn't seem like a practical concern.

339 ↗(On Diff #135104)

Returning bool constrains the contract of this class to choosing to run one item or not, and the type system forces a specific decision.
Returning void leaves the option of purging one or no or multiple items. The only void function with a clear contract would be "drop all dead requests at the start", which is too complex to get right in one go. (happy to pull the loop out of run into such a function if you think it would help, though.)

ilya-biryukov added inline comments.Feb 22 2018, 3:55 AM
clangd/TUScheduler.cpp
298 ↗(On Diff #135104)

Thanks for clarifying. The first bullet point shouldn't be a big a problem. Yes, the new task can remove multiple items from the back of the queue, but the implementation still looks more natural as it only needs to inspect the last item on the queue on each of the outer loop iterations. (While the current implementation has to do an inner loop through multiple items on the queue in addition to the outer loop).

The second point makes it hard, though. I would probably go with calling pop_front() when removing the request and signalling empty queue separately.

This is true, but it doesn't seem like a practical concern.

It isn't, but I still think it's a nice-to-have.

339 ↗(On Diff #135104)

Another alternative is to return an iterator to the first non-dead request from this function use it to remove the dead requests in the clients of this function.
This would get rid of the outer loop. WDYT?

ilya-biryukov accepted this revision.Feb 22 2018, 5:00 AM

LGTM (just one more possibly useful nit about const)

clangd/TUScheduler.cpp
105 ↗(On Diff #135382)

NIT: this could be made const

298 ↗(On Diff #135104)

As discussed offline, I totally missed the case when the new request comes in and has to cancel requests from the middle of the queue. So the implementation I proposed would probably still have two loops and not be less complex.

So LGTM here, my suggestions won't make things simpler.

This revision is now accepted and ready to land.Feb 22 2018, 5:00 AM
This revision was automatically updated to reflect the committed changes.