Skip to content

Add a RangeSpecificDiagnosticConsumer #14844

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

jrose-apple
Copy link
Contributor

@jrose-apple jrose-apple commented Feb 27, 2018

If a top-level diagnostic is in a particular source range, the RangeSpecificDiagnosticConsumer will funnel it and any attached notes to a particular "sub-consumer" designated for that range (intended to be used with whole files). If it's not in a range designated for any sub-consumer, the diagnostic is passed to all registered sub-consumers.

This is intended to be used for batch mode, so that diagnostics that are definitely associated with a particular file can be emitted in that file's .dia output, while diagnostics that may be associated with other files (such as those that come from Clang) will still get presented to the user.

@jrose-apple
Copy link
Contributor Author

@swift-ci Please smoke test

Copy link
Contributor

@davidungar davidungar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very clever and elegant. When I read the code I had to stare at it for a bit to see what was going on, as my comments show. I'd be tempted to break it down into small pieces (small functions) to more directly manifest the structure of the computation.

StringRef FormatString, ArrayRef<DiagnosticArgument> FormatArgs,
const DiagnosticInfo &Info) {
if (Kind != DiagnosticKind::Note) {
// When emitting a new top-level diagnostic, figure out if it belongs to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My style would be to split out the function that finds a containing buffer (or the CurrentGroupConsumer) from the function that handles the diagnostic. The latter would call the former to get the buffer (consumer) (if any). It would read a tad more clearly that way to my eyes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I get further into this code, I would like handleDiagnostic to be something like this:
[There would be a static function in this class called shouldResetCurrentGroupConsumer that would take a diagnostic as a parameter and return true iff not a note.]
This function would only say:
if (shouldResetCurrentGroupConsumer(Kind)) {
CurrentGroupConsumer = findConsumerFor(Loc);
}
consumeDiagnostic(CurrentGroupConsumer, SM, Loc, Kind, FormatString, FormatArgs, Info)

The idea is to more clearly manifest the two-step nature of the process.

Another option would be to have a function that returned a vector consisting of either all the SubConsumers or just the one appropriate SubConsumer for that diagnostic. That would eliminate the if-else with the duplicated call to handleDiagnostic. Or have a forAllRelevantConsumers function that took a closure and either replicated the diagnostic handling or not.

I guess I'm hoping to break this function up a bit to make it easier for eyes like mine to see the structure of the computation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad you like the idea. Tx.


FileSpecificDiagnosticConsumer::FileSpecificDiagnosticConsumer(
MutableArrayRef<ConsumerPair> consumers) {
for (ConsumerPair &pair : consumers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My style would be to extract out the body of this constructor into a function called something like buildSortedConsumerMap. That function would in turn call populateMap then sortMap. Even though the code is clear as is, I like to make it even easier to read quickly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this one pulls its weight. I added comments, though.

SmallVector<std::unique_ptr<DiagnosticConsumer>, 4> SubConsumers;
SmallVector<std::pair<CharSourceRange, unsigned>, 4> LocationToConsumerMap;
static const unsigned NO_SPECIFIC_CONSUMER = -1;
unsigned CurrentGroupConsumer = NO_SPECIFIC_CONSUMER;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work as a local variable inside of handleDiagnostic? I don't see other uses of it, and AFAICT it is set each time that routine is called.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah--now I see it is set for non-notes and reused for notes. A better name would be GroupConsumerForPreviousNonNote. No, that's not quite right. At least a Doxygen comment to explain it would help.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad you like the idea. Tx.

topConsumer.finishProcessing();
}

TEST(FileSpecificDiagnosticConsumer, CrossFileNotes) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find these test routines a bit daunting. Would it make sense to add comments and/or break them up a bit? I'm thinking of a comment or subroutine name like: "Ensure that a Note ends up in the same consumer as the most recent non-note." Or some such, so a reader can gather the intention without delving into the code.

Another approach: list the ways you can imagine it failing, and test each one. That's probably what this is, but it's hard to tell at a glance.

At a glance, there might be a factoring that could make these test routines a lot more concise. Just a thought...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I admit to testing multiple things in each function, since the setup is so clumsy. But ownership and having all the expectations up front makes it hard to make the setup not clumsy. Maybe making the diagnostic emission less noisy would help, so you could compare the expected per-file output with what's going to be emitted?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it turns out I can come up with reasonable names for all these tests.

If a top-level diagnostic is in a particular source range, the
RangeSpecificDiagnosticConsumer will funnel it and any attached notes
to a particular "sub-consumer" designated for that range (intended to
be used with whole files). If it's not in a range designated for any
sub-consumer, the diagnostic is passed to all registered
sub-consumers.

This is intended to be used for batch mode, so that diagnostics that
are definitely associated with a particular file can be emitted in
that file's .dia output, while diagnostics that may be associated with
other files (such as those that come from Clang) will still get
presented to the user.
@jrose-apple jrose-apple force-pushed the FileSpecificDiagnosticConsumer branch from a546ae3 to 54867c0 Compare February 28, 2018 19:40
@jrose-apple
Copy link
Contributor Author

@swift-ci Please smoke test

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - a546ae3a58fe98414e5d2a5c4a13f13d4b4da709

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - a546ae3a58fe98414e5d2a5c4a13f13d4b4da709

Copy link
Contributor

@graydon graydon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

@jrose-apple jrose-apple changed the title Add a FileSpecificDiagnosticConsumer Add a RangeSpecificDiagnosticConsumer Feb 28, 2018
@jrose-apple
Copy link
Contributor Author

@davidungar: Still waiting for another round of review, or you could jump to #14900 and see how it's used (and the unfortunate but unavoidable switch to basing on file names).

@jrose-apple
Copy link
Contributor Author

(We moved to just doing it all at once in #14900. Closing.)

@jrose-apple jrose-apple closed this Mar 2, 2018
@jrose-apple jrose-apple deleted the FileSpecificDiagnosticConsumer branch March 2, 2018 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants