-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Diagnostics][NFC] Optionally add notes as explicit children of an error/warning #26830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I definitely like this idea, and it helps enforce the idea that notes should always be attached to a parent. I am worried about the implementation, though. Ownership needs to be clear at all stages. Would it be possible to reuse more of the existing transaction machinery for this?
ae19b41
to
4d1eb0c
Compare
@jrose-apple Thanks for the feedback, apologies for some of the rookie c++ mistakes! I've fixed the smaller issues you pointed out.
|
4d1eb0c
to
c076133
Compare
8c81fd7
to
d74a340
Compare
@jrose-apple I've updated this PR to use a transaction-based API to group diagnostics, which simplifies argument ownership quite a bit. Do you mind taking another look? The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like how this turned out, though it's too bad we end up copying things around a little more instead of getting to rely on strings just being alive by virtue of being on the stack when we call diagnose
. I think there's still an ownership problem here, though—see the comments.
lib/Sema/CSApply.cpp
Outdated
{ | ||
CompoundDiagnosticTransaction transaction(tc.Diags); | ||
tc.diagnose(loc, swift::diag::optional_ambiguous_case_ref, | ||
baseTyName, baseTyUnwrappedName, memberName.str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The separate transaction is a little clunky. What you think about having something like diagnoseWithNotes
on DiagnosticEngine that returns an InFlightDiagnosticWithNotes, which inherits from InFlightDiagnostic but also carries a CompoundDiagnosticTransaction?
auto errorDiag = tc.diagnoseWithNotes(…);
tc.diagnose(…);
tc.diagnose(…);
instead of
CompoundDiagnosticTransaction transaction(tc.Diags);
tc.diagnose(…);
tc.diagnose(…);
tc.diagnose(…);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, this is a really great idea! It strikes a nice balance between the two approaches I've tried so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It turns out this doesn't work out too well because the transaction needs to outlive the parent InFlightDiagnostic(WithNotes). Otherwise we end up keeping the parent alive while diagnosing the notes which breaks a lot of important assumptions.
The best alternative I can come up with at the moment is a buildCompoundDiagnostic
helper which accepts a lambda with an implicit transaction (see my second commit). This doesn't reduce the code involved, but it's easier to read the code and understand what's going on.
tc.buildCompoundDiagnostic([&]() {
tc.diagnose(...);
tc.diagnose(...);
tc.diagnose(...);
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. I like the explicit stack scoping, but it still feels like the parent diagnostic should be in the compound action. What about this (on DiagnosticEngine)?
template<typename ...ArgTypes>
void diagnoseWithNotes(
SourceLoc Loc, Diag<ArgTypes...> ID,
typename detail::PassArgument<ArgTypes>::type... Args,
llvm::function_ref<void(void)> builder) {
CompoundDiagnosticTransaction transaction(*this);
diagnose(Loc, ID, Args...);
builder();
}
The one thing I don't like is that you can't add highlights or fix-its this way, but it would be possible to have another overload that took an InFlightDiagnostic as well. Though you'd have to call flush()
explicitly there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, types are only deduced for variadic parameter packs when they come at the end of an argument list though, which makes this kind of unwieldy. On the other hand, moving builder to the front of the list would be pretty unintuitive.
I don't think the inability to add fix-its is that big an issue. If an error has notes, it very rarely has fix-its of its own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up updating this to just accept an InFlightDiagnostic, manually specifying template parameters became too annoying.
That allows us to write:
tc.diagnoseWithNotes(
tc.diagnose(...),
[&]() {
tc.diagnose(...);
tc.diagnose(...);
});
It's not perfect, but I think it's still an improvement
…ror/warning Add a new type of diagnostic transaction, CompoundDiagnosticTransaction. The first diagnostic emitted inside the transaction will become the parent of the subsequent notes. DiagnosticConsumers may opt in to consuming these child notes alongside the parent diagnostic, or they can continue to consider them seperately. Moved PrintingDiagnosticConsumer and a couple of diagnostics to the new system as a proof of concept.
8368b74
to
8d6b60d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works for me! @xedin, what do you think of these use sites?
…need to be used manually in simple cases
8d6b60d
to
6e1e768
Compare
@swift-ci Please smoke test |
@xedin bump, mind taking a quick look at this when you have a chance? |
@xedin bump |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I missed this one. Nice improvement! I think time will be a good judge whether we'll have to change something here when we start using this in more places.
Let's do a retest just to be sure. @swift-ci Please smoke test |
I'm pretty sure the linux CI didn't actually hit an unexpected compile error, it looks like the real failure was in sourcekit-lsp integration testing. It's hard to say for sure whether it's related to this change, I may try to spin up an Ubuntu install later and investigate if it continues to fail. |
Let's try again. @swift-ci Please smoke test Linux |
@swift-ci Please smoke test Linux |
*grumble* @swift-ci Please smoke test Linux |
Thanks, @owenv! |
🎉🎉🎉 Thanks for keeping an eye on the CI! |
@owenv Adoption of the new setup in https://github.com/apple/swift/blob/master/lib/Sema/ConstraintSystem.cpp#L2521 would be a real challenge, so I'd suggest we try to do that next. |
Add a new builder method, addChildNotes, to InFlightDiagnostic, which accepts a lambda expression. Any notes diagnosed inside will be added as children of the parent error or warning. DiagnosticConsumers may opt in to consuming these child notes alongside the parent
diagnostic, or they can continue to consider them seperately.
Moved PrintingDiagnosticConsumer and a couple of diagnostics to the new
system as a proof of concept.
This is intended to unblock some future improvements to diagnostic presentation and formatting. The most controversial bits of this are probably the DiagnosticInfo refactor and the use of
addChildNotes
blocks to associate notes with a parent diagnostic.This change is opt-in for DiagnosticConsumers. Child notes are both attached to their parent, and emitted standalone. Consumers which have been updated with knowledge of child notes can ignore the standalone emissions. Also, this change makes many of the
handleDiagnostic
arguments redundant now that everything is stored in DiagnosticInfo. Since there are many consumers that would need to be migrated, I thought it would be best not to clutter this PR.