Skip to content

Report Which Plugin a Diagnostic came from#784

Open
InsertCreativityHere wants to merge 6 commits into
icerpc:mainfrom
InsertCreativityHere:store-plugin-diagnostic-origin
Open

Report Which Plugin a Diagnostic came from#784
InsertCreativityHere wants to merge 6 commits into
icerpc:mainfrom
InsertCreativityHere:store-plugin-diagnostic-origin

Conversation

@InsertCreativityHere
Copy link
Copy Markdown
Member

This PR adds a field to our Diagnostic types tracking which plugin (if any) a diagnostic came from.
In the case of multiple plugins reporting the same error, we still only emit a single error, but it's now more clear that it was reported by multiple plugins.

The main advantage to doing this is that errors about the plugin itself (like providing a bad option) will clearly identify which plugin had the problem.


Here is sample output from the console:
image
And for JSON output, we now include a new "reported_by":[...] field.
The Task will need to be updated to properly handle this field in a follow-up PR.

/// An annotated version of a [`Diagnostic`], whose [`DiagnosticLevel`] has been computed (taking into account any
/// 'allow' attributes or command-line flags), and that has pre-extracted text snippets to display alongside messages.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq)]
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We implement PartialEq by hand now, so that we exclude the reported_by from the equality check.

});

// If the diagnostic was reported by a plugin, we just use the filename of the plugin (not its entire path).
let reported_by = diagnostic.plugin.as_deref().map_or("slicec", |plugin_path| {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I went back and forth on this, but I decided that in the case where the diagnostic is issued by slicec, it's worthwhile to still explicitly say that this came from slicec, and not a plugin.

It also makes the implementation cleaner and keep the error format 100% consistent, instead of being a little different for plugin diagnostics vs those about bad syntax for example.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Note that AnnotatedDiagnostic is the "finished" form of a diagnostic.
These are always de-duplicated, and are what we're actually going to print to the user.

The normal Diagnostic type is what gets reported throughout the code, and before we exit, we convert the Diagnostics to AnnotatedDiagnostics so that we can print them.


Therefore Diagnostic can have at most one reporter (since only one thing reported it).
But AnnotatedDiagnostic can have multiple (since duplicate diagnostics will of been merged together).

Comment on lines +56 to 59
if let Some(original) = annotated_diagnostics.iter_mut().find(|d| **d == converted) {
original.reported_by.extend(converted.reported_by);
} else {
annotated_diagnostics.push(converted);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Previously, we completely threw away duplicate diagnostics.
Now if we encounter a duplicate, we update the original's reported_by before throwing away the duplicate now.

/// present in the returned [`Vec`].
pub fn get_annotated_diagnostics(&self, options: &SliceOptions) -> Vec<AnnotatedDiagnostic> {
let mut annotated_diagnostics = Vec::new();
let mut annotated_diagnostics = Vec::<AnnotatedDiagnostic>::new();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I have no idea why, but the compiler insists on putting a type-hint here now.
It seems unnecessary to me, but who am I to argue with the compiler...

writeln!(self.output, "{prefix}: {}", console::style(&diagnostic.message).bold())?;

// Emit what the diagnostic was reported by.
writeln!(self.output, " Reported by: [{}]", diagnostic.reported_by.join(", "))?;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

See the diagnostic_output_test or my PR description for an example of what the output looks like.

Comment thread slicec/src/main.rs
diagnostics.extend(generator_diagnostics.into_inner());
// Store generator's diagnostics for later emission, after setting the plugin that reported them.
for mut generator_diagnostic in generator_diagnostics.into_inner() {
generator_diagnostic.plugin = Some(generator.path.clone());
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Where we actually set plugin.
This is after we've finished (or tried) to run the plugin and are storing its diagnostics.


// Construct and push the converted 'slicec' `Diagnostic`.
let converted_diagnostic = slicec::diagnostics::Diagnostic { kind, span, scope, notes };
let converted_diagnostic = slicec::diagnostics::Diagnostic { kind, span, scope, notes, plugin: None };
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We construct the diagnostic in one-shot here, so had to update it to include the new field.
We manually set it to it's default value (None).

Rust doesn't have the idea of default field values like other languages.

@@ -156,9 +180,13 @@ error [E008]: invalid enum 'E': enums must contain at least one enumerator
// Assert
assert_eq!(converted_diagnostics.len(), 4);
assert_eq!(converted_diagnostics[0].message, "This is a test");
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Check that the reported_bys are correct after merging.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds diagnostic provenance so emitted diagnostics indicate whether they came from slicec or one or more plugins, including JSON output support.

Changes:

  • Adds plugin/reporting metadata to diagnostics and annotated diagnostics.
  • Emits Reported by in human output and reported_by in JSON output.
  • Updates diagnostic output tests and duplicate diagnostic merging behavior.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
slicec/src/diagnostics/diagnostic.rs Adds optional plugin source tracking to core diagnostics.
slicec/src/diagnostics/annotated_diagnostic.rs Converts plugin paths into displayed reporter names.
slicec/src/compilation_state.rs Merges duplicate annotated diagnostics while aggregating reporters.
slicec/src/diagnostic_emitter.rs Emits reporter information in human and JSON diagnostic formats.
slicec/src/main.rs Tags plugin-generated diagnostics with the generator path.
slicec/src/slice_file_converter.rs Initializes converted plugin response diagnostics with no direct plugin source.
slicec/tests/diagnostic_output_tests.rs Updates expected output and duplicate merging tests for reporter metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread slicec/src/compilation_state.rs
Comment thread slicec/src/diagnostics/annotated_diagnostic.rs
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

/// Any additional information that should be reported alongside this diagnostic's main message.
pub notes: Vec<Note>,

/// The plugin that reported this diagnostic, or `None` if this diagnostic was reported by 'slicec' itself.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We use the term plugin and not generator in slicec?

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.

3 participants