Add default strategy for selecting primary label. Change attribute syntax to #[label(primary)].

This commit is contained in:
Will Crichton 2023-09-19 16:23:14 -07:00
parent 4c970fb4cf
commit 1905af07ef
6 changed files with 76 additions and 51 deletions

View File

@ -25,6 +25,7 @@ struct Label {
struct LabelAttr { struct LabelAttr {
label: Option<Display>, label: Option<Display>,
primary: bool,
} }
impl Parse for LabelAttr { impl Parse for LabelAttr {
@ -41,10 +42,22 @@ impl Parse for LabelAttr {
} }
}); });
let la = input.lookahead1(); let la = input.lookahead1();
let label = if la.peek(syn::token::Paren) { let (primary, label) = if la.peek(syn::token::Paren) {
// #[label("{}", x)] // #[label(primary?, "{}", x)]
let content; let content;
parenthesized!(content in input); parenthesized!(content in input);
let primary = if content.peek(syn::Ident) {
let ident: syn::Ident = content.parse()?;
if ident != "primary" {
return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The argument must be a literal string or the keyword `primary`."));
}
let _ = content.parse::<Token![,]>();
true
} else {
false
};
if content.peek(syn::LitStr) { if content.peek(syn::LitStr) {
let fmt = content.parse()?; let fmt = content.parse()?;
let args = if content.is_empty() { let args = if content.is_empty() {
@ -57,22 +70,27 @@ impl Parse for LabelAttr {
args, args,
has_bonus_display: false, has_bonus_display: false,
}; };
Some(display) (primary, Some(display))
} else if !primary {
return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The argument must be a literal string or the keyword `primary`."));
} else { } else {
return Err(syn::Error::new(input.span(), "Invalid argument to label() attribute. The first argument must be a literal string.")); (primary, None)
} }
} else if la.peek(Token![=]) { } else if la.peek(Token![=]) {
// #[label = "blabla"] // #[label = "blabla"]
input.parse::<Token![=]>()?; input.parse::<Token![=]>()?;
Some(Display { (
fmt: input.parse()?, false,
args: TokenStream::new(), Some(Display {
has_bonus_display: false, fmt: input.parse()?,
}) args: TokenStream::new(),
has_bonus_display: false,
}),
)
} else { } else {
None (false, None)
}; };
Ok(LabelAttr { label }) Ok(LabelAttr { label, primary })
} }
} }
@ -91,16 +109,7 @@ impl Labels {
let mut labels = Vec::new(); let mut labels = Vec::new();
for (i, field) in fields.iter().enumerate() { for (i, field) in fields.iter().enumerate() {
for attr in &field.attrs { for attr in &field.attrs {
let is_label = attr.path().is_ident("label"); if attr.path().is_ident("label") {
let is_primary_label = attr.path().is_ident("primary_label");
if is_label || is_primary_label {
if is_primary_label && labels.iter().any(|l: &Label| l.primary) {
return Err(syn::Error::new(
field.span(),
"Cannot have more than one primary label.",
));
}
let span = if let Some(ident) = field.ident.clone() { let span = if let Some(ident) = field.ident.clone() {
syn::Member::Named(ident) syn::Member::Named(ident)
} else { } else {
@ -110,13 +119,21 @@ impl Labels {
}) })
}; };
use quote::ToTokens; use quote::ToTokens;
let LabelAttr { label } = let LabelAttr { label, primary } =
syn::parse2::<LabelAttr>(attr.meta.to_token_stream())?; syn::parse2::<LabelAttr>(attr.meta.to_token_stream())?;
if primary && labels.iter().any(|l: &Label| l.primary) {
return Err(syn::Error::new(
field.span(),
"Cannot have more than one primary label.",
));
}
labels.push(Label { labels.push(Label {
label, label,
span, span,
ty: field.ty.clone(), ty: field.ty.clone(),
primary: is_primary_label, primary,
}); });
} }
} }

View File

@ -391,7 +391,10 @@ impl GraphicalReportHandler {
) -> fmt::Result { ) -> fmt::Result {
let (contents, lines) = self.get_lines(source, context.inner())?; let (contents, lines) = self.get_lines(source, context.inner())?;
let primary_label = labels.iter().find(|label| label.primary()); let primary_label = labels
.iter()
.find(|label| label.primary())
.or_else(|| labels.first());
// sorting is your friend // sorting is your friend
let labels = labels let labels = labels

View File

@ -252,7 +252,7 @@ impl MietteDiagnostic {
/// ``` /// ```
pub fn and_labels(mut self, labels: impl IntoIterator<Item = LabeledSpan>) -> Self { pub fn and_labels(mut self, labels: impl IntoIterator<Item = LabeledSpan>) -> Self {
let mut all_labels = self.labels.unwrap_or_default(); let mut all_labels = self.labels.unwrap_or_default();
all_labels.extend(labels.into_iter()); all_labels.extend(labels);
self.labels = Some(all_labels); self.labels = Some(all_labels);
self self
} }

View File

@ -86,7 +86,7 @@ fn single_line_highlight_span_full_line() {
println!("Error: {}", out); println!("Error: {}", out);
let expected = r#" × oops! let expected = r#" × oops!
[issue:1:1] [issue:2:1]
1 source 1 source
2 text 2 text
· ·
@ -120,7 +120,7 @@ fn single_line_with_wide_char() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:7]
1 source 1 source
2 👼🏼text 2 👼🏼text
· ·
@ -159,7 +159,7 @@ fn single_line_with_two_tabs() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -198,7 +198,7 @@ fn single_line_with_tab_in_middle() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:8]
1 source 1 source
2 text = text 2 text = text
· ·
@ -235,7 +235,7 @@ fn single_line_highlight() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -270,7 +270,7 @@ fn external_source() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -343,7 +343,7 @@ fn single_line_highlight_offset_end_of_line() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:1:7]
1 source 1 source
· ·
· this bit here · this bit here
@ -379,7 +379,7 @@ fn single_line_highlight_include_end_of_line() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -416,7 +416,7 @@ fn single_line_highlight_include_end_of_line_crlf() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -453,7 +453,7 @@ fn single_line_highlight_with_empty_span() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -490,7 +490,7 @@ fn single_line_highlight_no_label() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -526,7 +526,7 @@ fn single_line_highlight_at_line_start() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:1]
1 source 1 source
2 text 2 text
· ·
@ -569,7 +569,7 @@ fn multiple_same_line_highlights() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text text text text text 2 text text text text text
· ·
@ -616,7 +616,7 @@ fn multiple_same_line_highlights_with_tabs_in_middle() -> Result<(), MietteError
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text text text text text 2 text text text text text
· ·
@ -655,7 +655,7 @@ fn multiline_highlight_adjacent() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
3 here 3 here
@ -969,7 +969,7 @@ fn related() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -1031,7 +1031,7 @@ fn related_source_code_propagation() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -1136,7 +1136,7 @@ fn related_severity() -> Result<(), MietteError> {
let expected = r#"oops::my::bad let expected = r#"oops::my::bad
× oops! × oops!
[bad_file.rs:1:1] [bad_file.rs:2:3]
1 source 1 source
2 text 2 text
· ·
@ -1201,7 +1201,7 @@ fn zero_length_eol_span() {
println!("Error: {}", out); println!("Error: {}", out);
let expected = r#" × oops! let expected = r#" × oops!
[issue:1:1] [issue:2:1]
1 this is the first line 1 this is the first line
2 this is the second line 2 this is the second line
· ·
@ -1220,22 +1220,27 @@ fn primary_label() {
struct MyBad { struct MyBad {
#[source_code] #[source_code]
src: NamedSource, src: NamedSource,
#[primary_label("The root cause")] #[label]
bad_bit: SourceSpan, first_label: SourceSpan,
#[label(primary, "nope")]
second_label: SourceSpan,
} }
let err = MyBad { let err = MyBad {
src: NamedSource::new("issue", "this is the first line\nthis is the second line"), src: NamedSource::new("issue", "this is the first line\nthis is the second line"),
bad_bit: (24, 4).into(), first_label: (2, 4).into(),
second_label: (24, 4).into(),
}; };
let out = fmt_report(err.into()); let out = fmt_report(err.into());
println!("Error: {}", out); println!("Error: {}", out);
// line 2 should be the primary, not line 1
let expected = r#" × oops! let expected = r#" × oops!
[issue:2:2] [issue:2:2]
1 this is the first line 1 this is the first line
·
2 this is the second line 2 this is the second line
· ·
· The root cause · nope
"# "#
.to_string(); .to_string();

View File

@ -91,7 +91,7 @@ fn test_diagnostic_source_pass_extra_info() {
println!("Error: {}", out); println!("Error: {}", out);
let expected = r#" × TestError let expected = r#" × TestError
× A complex error happened × A complex error happened
[1:1] [1:2]
1 Hello 1 Hello
· ·
· here · here