From 60f757575d1a5094830305c4ae29f009883fc123 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Mon, 28 Jul 2025 01:02:28 -0600 Subject: [PATCH] chore: Don't allow Title in element --- examples/highlight_source.rs | 2 +- src/renderer/mod.rs | 92 +++++++++++---------- src/snippet.rs | 15 ++-- tests/color/multiline_removal_suggestion.rs | 4 +- tests/formatter.rs | 20 ++--- tests/rustc_tests.rs | 15 ++-- 6 files changed, 76 insertions(+), 72 deletions(-) diff --git a/examples/highlight_source.rs b/examples/highlight_source.rs index 22ab0d6..5cedfa5 100644 --- a/examples/highlight_source.rs +++ b/examples/highlight_source.rs @@ -22,7 +22,7 @@ fn main() {} ), ) .element( - Level::NOTE.title("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."), + Level::NOTE.message("The runtime heap is not yet available at compile-time, so no runtime heap allocations can be created."), )]; diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index cf72d02..8620d79 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -285,32 +285,45 @@ impl Renderer { source_map_annotated_lines.push_back((source_map, annotated_lines)); } } - let mut message_iter = group.elements.iter().enumerate().peekable(); + let mut message_iter = group.elements.iter().peekable(); let mut last_was_suggestion = false; - let mut first_was_title = false; - while let Some((i, section)) = message_iter.next() { - let peek = message_iter.peek().map(|(_, s)| s).copied(); + if let Some(title) = &group.title { + let peek = message_iter.peek().copied(); + let title_style = if g == 0 { + TitleStyle::MainHeader + } else { + TitleStyle::Header + }; + let buffer_msg_line_offset = buffer.num_lines(); + self.render_title( + &mut buffer, + title, + max_line_num_len, + title_style, + matches!(peek, Some(Element::Message(_))), + buffer_msg_line_offset, + ); + let buffer_msg_line_offset = buffer.num_lines(); + + if matches!(peek, Some(Element::Message(_))) { + self.draw_col_separator_no_space( + &mut buffer, + buffer_msg_line_offset, + max_line_num_len + 1, + ); + } + if peek.is_none() && g == 0 && group_len > 1 { + self.draw_col_separator_end( + &mut buffer, + buffer_msg_line_offset, + max_line_num_len + 1, + ); + } + } + let mut seen_primary = false; + while let Some(section) = message_iter.next() { + let peek = message_iter.peek().copied(); match §ion { - Element::Title(title) => { - if i == 0 { - first_was_title = true; - } - let title_style = match (i == 0, g == 0) { - (true, true) => TitleStyle::MainHeader, - (true, false) => TitleStyle::Header, - (false, _) => TitleStyle::Secondary, - }; - let buffer_msg_line_offset = buffer.num_lines(); - self.render_title( - &mut buffer, - title, - max_line_num_len, - title_style, - matches!(peek, Some(Element::Title(_) | Element::Message(_))), - buffer_msg_line_offset, - ); - last_was_suggestion = false; - } Element::Message(title) => { let title_style = TitleStyle::Secondary; let buffer_msg_line_offset = buffer.num_lines(); @@ -321,8 +334,7 @@ impl Renderer { title_style, matches!( peek, - Some(Element::Title(_) | Element::Message(_)) - | Some(Element::Padding(_)) + Some(Element::Message(_)) | Some(Element::Padding(_)) ), buffer_msg_line_offset, ); @@ -332,8 +344,9 @@ impl Renderer { if let Some((source_map, annotated_lines)) = source_map_annotated_lines.pop_front() { - let is_primary = primary_path == cause.path.as_ref() - && i == first_was_title as usize; + let is_primary = + primary_path == cause.path.as_ref() && !seen_primary; + seen_primary |= is_primary; self.render_snippet_annotations( &mut buffer, max_line_num_len, @@ -348,7 +361,7 @@ impl Renderer { if g == 0 { let current_line = buffer.num_lines(); match peek { - Some(Element::Message(_) | Element::Title(_)) => { + Some(Element::Message(_)) => { self.draw_col_separator_no_space( &mut buffer, current_line, @@ -389,6 +402,8 @@ impl Renderer { Element::Origin(origin) => { let buffer_msg_line_offset = buffer.num_lines(); + let is_primary = primary_path == Some(&origin.path) && !seen_primary; + seen_primary |= is_primary; self.render_origin( &mut buffer, max_line_num_len, @@ -414,10 +429,7 @@ impl Renderer { } } } - if g == 0 - && (matches!(section, Element::Origin(_)) - || (matches!(section, Element::Title(_)) && i == 0)) - { + if g == 0 && matches!(section, Element::Origin(_)) { let current_line = buffer.num_lines(); if peek.is_none() && group_len > 1 { self.draw_col_separator_end( @@ -425,7 +437,7 @@ impl Renderer { current_line, max_line_num_len + 1, ); - } else if matches!(peek, Some(Element::Message(_) | Element::Title(_))) { + } else if matches!(peek, Some(Element::Message(_))) { self.draw_col_separator_no_space( &mut buffer, current_line, @@ -452,11 +464,8 @@ impl Renderer { let mut labels = None; let group = groups.first().expect("Expected at least one group"); - let Some(Element::Title(title)) = group.elements.first() else { - panic!( - "Expected first element to be a Title, got: {:?}", - group.elements.first() - ); + let Some(title) = &group.title else { + panic!("Expected a Title"); }; if let Some(Element::Cause(cause)) = group @@ -2952,10 +2961,7 @@ fn max_line_number(groups: &[Group<'_>]) -> usize { v.elements .iter() .map(|s| match s { - Element::Title(_) - | Element::Message(_) - | Element::Origin(_) - | Element::Padding(_) => 0, + Element::Message(_) | Element::Origin(_) | Element::Padding(_) => 0, Element::Cause(cause) => { if cause.fold { let end = cause diff --git a/src/snippet.rs b/src/snippet.rs index 3ec5d02..d38fe01 100644 --- a/src/snippet.rs +++ b/src/snippet.rs @@ -33,6 +33,7 @@ pub(crate) struct Id<'a> { #[derive(Clone, Debug)] pub struct Group<'a> { pub(crate) primary_level: Level<'a>, + pub(crate) title: Option>, pub(crate) elements: Vec>, } @@ -40,7 +41,9 @@ impl<'a> Group<'a> { /// Create group with a title, deriving the primary [`Level`] for [`Annotation`]s from it pub fn with_title(title: Title<'a>) -> Self { let level = title.level.clone(); - Self::with_level(level).element(title) + let mut x = Self::with_level(level); + x.title = Some(title); + x } /// Create a title-less group with a primary [`Level`] for [`Annotation`]s @@ -55,6 +58,7 @@ impl<'a> Group<'a> { pub fn with_level(level: Level<'a>) -> Self { Self { primary_level: level, + title: None, elements: vec![], } } @@ -70,7 +74,7 @@ impl<'a> Group<'a> { } pub fn is_empty(&self) -> bool { - self.elements.is_empty() + self.elements.is_empty() && self.title.is_none() } } @@ -78,7 +82,6 @@ impl<'a> Group<'a> { #[derive(Clone, Debug)] #[non_exhaustive] pub enum Element<'a> { - Title(Title<'a>), Message(Message<'a>), Cause(Snippet<'a, Annotation<'a>>), Suggestion(Snippet<'a, Patch<'a>>), @@ -86,12 +89,6 @@ pub enum Element<'a> { Padding(Padding), } -impl<'a> From> for Element<'a> { - fn from(value: Title<'a>) -> Self { - Element::Title(value) - } -} - impl<'a> From> for Element<'a> { fn from(value: Message<'a>) -> Self { Element::Message(value) diff --git a/tests/color/multiline_removal_suggestion.rs b/tests/color/multiline_removal_suggestion.rs index 2442947..c5b7ecb 100644 --- a/tests/color/multiline_removal_suggestion.rs +++ b/tests/color/multiline_removal_suggestion.rs @@ -81,10 +81,10 @@ fn main() {} ) .element( Level::HELP - .title("the trait `Iterator` is not implemented for `(bool, HashSet)`"), + .message("the trait `Iterator` is not implemented for `(bool, HashSet)`"), ) .element( - Level::NOTE.title("required for `(bool, HashSet)` to implement `IntoIterator`"), + Level::NOTE.message("required for `(bool, HashSet)` to implement `IntoIterator`"), ), Group::with_title(Level::NOTE.title("required by a bound in `flatten`")) .element( diff --git a/tests/formatter.rs b/tests/formatter.rs index efb9793..c0e9752 100644 --- a/tests/formatter.rs +++ b/tests/formatter.rs @@ -1666,7 +1666,7 @@ fn main() { .annotation(AnnotationKind::Primary.span(89..90)) ).element( Level::NOTE - .title("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`") + .message("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`") , )]; @@ -1752,9 +1752,9 @@ fn main() { .annotation(AnnotationKind::Primary.span(89..90)) ).element( Level::NOTE - .title("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`") + .message("required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>`") ).element( - Level::NOTE.title("a second note"), + Level::NOTE.message("a second note"), )]; let expected = str![[r#" @@ -1902,13 +1902,13 @@ fn main() { ) ).element( Level::NOTE - .title("expected struct `Atype, i32>`\n found enum `Result, _>`") + .message("expected struct `Atype, i32>`\n found enum `Result, _>`") ).element( Level::NOTE - .title("the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'") + .message("the full name for the type has been written to '$TEST_BUILD_DIR/$FILE.long-type-hash.txt'") ).element( Level::NOTE - .title("consider using `--verbose` to print the full type name to the console") + .message("consider using `--verbose` to print the full type name to the console") , )]; @@ -1986,7 +1986,7 @@ fn main() { ), ).element( Level::NOTE - .title("expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`\n found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`") + .message("expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>`\n found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}`") , ),Group::with_title( Level::NOTE.title("function defined here"), @@ -2077,7 +2077,7 @@ fn unicode_cut_handling2() { .fold(false) .annotation(AnnotationKind::Primary.span(499..500).label("expected item")) ).element( - Level::NOTE.title("for a full list of items that can appear in modules, see ") + Level::NOTE.message("for a full list of items that can appear in modules, see ") )]; @@ -2114,7 +2114,7 @@ fn unicode_cut_handling3() { .fold(false) .annotation(AnnotationKind::Primary.span(251..254).label("expected item")) ).element( - Level::NOTE.title("for a full list of items that can appear in modules, see ") + Level::NOTE.message("for a full list of items that can appear in modules, see ") )]; @@ -2151,7 +2151,7 @@ fn unicode_cut_handling4() { .fold(false) .annotation(AnnotationKind::Primary.span(334..335).label("expected item")) ).element( - Level::NOTE.title("for a full list of items that can appear in modules, see ") + Level::NOTE.message("for a full list of items that can appear in modules, see ") )]; diff --git a/tests/rustc_tests.rs b/tests/rustc_tests.rs index 330e302..5de1643 100644 --- a/tests/rustc_tests.rs +++ b/tests/rustc_tests.rs @@ -1365,11 +1365,11 @@ outer_macro!(FirstStruct, FirstAttrStruct); ) .element( Level::HELP - .title("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`") + .message("remove the `#[macro_export]` or move this `macro_rules!` outside the of the current function `main`") ) .element( Level::NOTE - .title("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute") + .message("a `macro_rules!` definition is non-local if it is nested inside an item and has a `#[macro_export]` attribute") ), Group::with_title(Level::NOTE.title("the lint level is defined here")) .element( @@ -1965,7 +1965,7 @@ fn main() { .annotation(AnnotationKind::Primary.span(267..380)), ) .element( - Level::NOTE.title("`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated")), + Level::NOTE.message("`Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated")), Group::with_title(Level::HELP.title("you might have meant to use `Iterator::for_each`")) .element( Snippet::source(source) @@ -2555,7 +2555,8 @@ fn mismatched_types1() { ), ) .element( - Level::NOTE.title("expected reference `&[u8]`\n found reference `&'static str`"), + Level::NOTE + .message("expected reference `&[u8]`\n found reference `&'static str`"), ), ]; @@ -2607,7 +2608,7 @@ fn mismatched_types2() { ) .element( Level::NOTE - .title("expected reference `&str`\n found reference `&'static [u8; 0]`"), + .message("expected reference `&str`\n found reference `&'static [u8; 0]`"), ), ]; @@ -2734,7 +2735,7 @@ pub struct Foo; //~^ ERROR .annotation(AnnotationKind::Primary.span(111..126)), ) .element( - Level::NOTE.title("bare URLs are not automatically turned into clickable links"), + Level::NOTE.message("bare URLs are not automatically turned into clickable links"), ), Group::with_title(Level::NOTE.title("the lint level is defined here")).element( Snippet::source(source_0) @@ -3427,7 +3428,7 @@ fn main() { .label("associated type `Pr` not found for this struct"), ), ) - .element(Level::NOTE.title("the associated type was found for\n"))]; + .element(Level::NOTE.message("the associated type was found for\n"))]; let expected = str![[r#" error[E0220]: associated type `Pr` not found for `S` in the current scope