From 981dc02eaf876a25b95581411e841ba664dc9e97 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 4 Nov 2024 15:41:32 +1100 Subject: Revert "Avoid nested replacement ranges" from #129346. It caused a test regression in the `cfg_eval.rs` crate. (The bugfix in #129346 was in a different commit; this commit was just a code simplification.) --- compiler/rustc_parse/src/parser/attr_wrapper.rs | 28 ++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 8bd615e6d79..c85d0bd05cb 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -136,8 +136,9 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { node_replacements.array_windows() { assert!( - node_range.0.end <= next_node_range.0.start, - "Node ranges should be disjoint: ({:?}, {:?}) ({:?}, {:?})", + node_range.0.end <= next_node_range.0.start + || node_range.0.end >= next_node_range.0.end, + "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})", node_range, tokens, next_node_range, @@ -145,8 +146,20 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { ); } - // Process the replace ranges. - for (node_range, target) in node_replacements.into_iter() { + // Process the replace ranges, starting from the highest start + // position and working our way back. If have tokens like: + // + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // Then we will generate replace ranges for both + // the `#[cfg(FALSE)] field: bool` and the entire + // `#[cfg(FALSE)] struct Foo { #[cfg(FALSE)] field: bool }` + // + // By starting processing from the replace range with the greatest + // start position, we ensure that any (outer) replace range which + // encloses another (inner) replace range will fully overwrite the + // inner range's replacement. + for (node_range, target) in node_replacements.into_iter().rev() { assert!( !node_range.0.is_empty(), "Cannot replace an empty node range: {:?}", @@ -383,9 +396,10 @@ impl<'a> Parser<'a> { // from `ParserRange` form to `NodeRange` form. We will perform the actual // replacement only when we convert the `LazyAttrTokenStream` to an // `AttrTokenStream`. - self.capture_state - .parser_replacements - .drain(parser_replacements_start..parser_replacements_end) + self.capture_state.parser_replacements + [parser_replacements_start..parser_replacements_end] + .iter() + .cloned() .chain(inner_attr_parser_replacements) .map(|(parser_range, data)| { (NodeRange::new(parser_range, collect_pos.start_pos), data) -- cgit 1.4.1-3-g733a5 From 1d78004575bea3b958b199d50a8491ae3fd65679 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Mon, 17 Jun 2024 15:14:07 +0000 Subject: Add Unicode block-drawing compiler output support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add nightly-only theming support to rustc output using Unicode box drawing characters instead of ASCII-art to draw the terminal UI: After: ``` error: foo ╭▸ test.rs:3:3 │ 3 │ X0 Y0 Z0 │ ┌───╿──│──┘ │ ┌│───│──┘ │ ┏││━━━┙ │ ┃││ 4 │ ┃││ X1 Y1 Z1 5 │ ┃││ X2 Y2 Z2 │ ┃│└────╿──│──┘ `Z` label │ ┃└─────│──┤ │ ┗━━━━━━┥ `Y` is a good letter too │ `X` is a good letter ╰╴ note: bar ╭▸ test.rs:4:3 │ 4 │ ┏ X1 Y1 Z1 5 │ ┃ X2 Y2 Z2 6 │ ┃ X3 Y3 Z3 │ ┗━━━━━━━━━━┛ ├ note: bar ╰ note: baz note: qux ╭▸ test.rs:4:3 │ 4 │ X1 Y1 Z1 ╰╴ ━━━━━━━━ ``` Before: ``` error: foo --> test.rs:3:3 | 3 | X0 Y0 Z0 | ___^__-__- | |___|__| | ||___| | ||| 4 | ||| X1 Y1 Z1 5 | ||| X2 Y2 Z2 | |||____^__-__- `Z` label | ||_____|__| | |______| `Y` is a good letter too | `X` is a good letter | note: bar --> test.rs:4:3 | 4 | / X1 Y1 Z1 5 | | X2 Y2 Z2 6 | | X3 Y3 Z3 | |__________^ = note: bar = note: baz note: qux --> test.rs:4:3 | 4 | X1 Y1 Z1 | ^^^^^^^^ ``` --- compiler/rustc_errors/src/emitter.rs | 703 ++++++++-- compiler/rustc_errors/src/json.rs | 7 +- compiler/rustc_parse/src/parser/tests.rs | 1401 ++++++++++++++++++-- compiler/rustc_session/src/config.rs | 9 + compiler/rustc_session/src/session.rs | 14 +- src/librustdoc/core.rs | 9 +- src/librustdoc/doctest.rs | 5 + .../tests/ui/empty_line_after/doc_comments.stderr | 20 +- .../ui/empty_line_after/outer_attribute.stderr | 18 +- .../clippy/tests/ui/four_forward_slashes.stderr | 10 +- .../ui/four_forward_slashes_first_line.stderr | 2 +- .../ui/too_long_first_doc_paragraph-fix.stderr | 2 +- .../tests/ui/too_long_first_doc_paragraph.stderr | 4 +- tests/rustdoc-ui/invalid-syntax.stderr | 2 +- .../huge_multispan_highlight.ascii.svg | 116 ++ tests/ui/codemap_tests/huge_multispan_highlight.rs | 5 +- .../ui/codemap_tests/huge_multispan_highlight.svg | 116 -- .../huge_multispan_highlight.unicode.svg | 116 ++ tests/ui/diagnostic-width/E0271.ascii.stderr | 22 + tests/ui/diagnostic-width/E0271.rs | 6 +- tests/ui/diagnostic-width/E0271.stderr | 22 - tests/ui/diagnostic-width/E0271.unicode.stderr | 22 + tests/ui/diagnostic-width/flag-human.ascii.stderr | 11 + tests/ui/diagnostic-width/flag-human.rs | 6 +- tests/ui/diagnostic-width/flag-human.stderr | 11 - .../ui/diagnostic-width/flag-human.unicode.stderr | 11 + tests/ui/diagnostic-width/long-E0308.ascii.stderr | 82 ++ tests/ui/diagnostic-width/long-E0308.rs | 4 +- tests/ui/diagnostic-width/long-E0308.stderr | 82 -- .../ui/diagnostic-width/long-E0308.unicode.stderr | 82 ++ ...on-1-width-unicode-multiline-label.ascii.stderr | 18 + .../non-1-width-unicode-multiline-label.rs | 4 +- .../non-1-width-unicode-multiline-label.stderr | 18 - ...-1-width-unicode-multiline-label.unicode.stderr | 18 + .../non-whitespace-trimming-2.ascii.stderr | 11 + .../diagnostic-width/non-whitespace-trimming-2.rs | 4 +- .../non-whitespace-trimming-2.stderr | 11 - .../non-whitespace-trimming-2.unicode.stderr | 11 + tests/ui/error-emitter/highlighting.svg | 2 +- tests/ui/error-emitter/highlighting.windows.svg | 2 +- tests/ui/error-emitter/unicode-output.rs | 21 + tests/ui/error-emitter/unicode-output.svg | 72 + tests/ui/parser/bad-char-literals.stderr | 2 +- 43 files changed, 2579 insertions(+), 535 deletions(-) create mode 100644 tests/ui/codemap_tests/huge_multispan_highlight.ascii.svg delete mode 100644 tests/ui/codemap_tests/huge_multispan_highlight.svg create mode 100644 tests/ui/codemap_tests/huge_multispan_highlight.unicode.svg create mode 100644 tests/ui/diagnostic-width/E0271.ascii.stderr delete mode 100644 tests/ui/diagnostic-width/E0271.stderr create mode 100644 tests/ui/diagnostic-width/E0271.unicode.stderr create mode 100644 tests/ui/diagnostic-width/flag-human.ascii.stderr delete mode 100644 tests/ui/diagnostic-width/flag-human.stderr create mode 100644 tests/ui/diagnostic-width/flag-human.unicode.stderr create mode 100644 tests/ui/diagnostic-width/long-E0308.ascii.stderr delete mode 100644 tests/ui/diagnostic-width/long-E0308.stderr create mode 100644 tests/ui/diagnostic-width/long-E0308.unicode.stderr create mode 100644 tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr delete mode 100644 tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.stderr create mode 100644 tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr create mode 100644 tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr delete mode 100644 tests/ui/diagnostic-width/non-whitespace-trimming-2.stderr create mode 100644 tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr create mode 100644 tests/ui/error-emitter/unicode-output.rs create mode 100644 tests/ui/error-emitter/unicode-output.svg (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 0ccc71ae06c..120f5ba7d48 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -44,6 +44,7 @@ const DEFAULT_COLUMN_WIDTH: usize = 140; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HumanReadableErrorType { Default, + Unicode, AnnotateSnippet, Short, } @@ -595,6 +596,12 @@ impl ColorConfig { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum OutputTheme { + Ascii, + Unicode, +} + /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` #[derive(Setters)] pub struct HumanEmitter { @@ -613,6 +620,7 @@ pub struct HumanEmitter { macro_backtrace: bool, track_diagnostics: bool, terminal_url: TerminalUrl, + theme: OutputTheme, } #[derive(Debug)] @@ -637,6 +645,7 @@ impl HumanEmitter { macro_backtrace: false, track_diagnostics: false, terminal_url: TerminalUrl::No, + theme: OutputTheme::Ascii, } } @@ -680,17 +689,19 @@ impl HumanEmitter { }) .collect(); buffer.puts(line_offset, code_offset, &code, Style::Quotation); + let placeholder = self.margin(); if margin.was_cut_left() { // We have stripped some code/whitespace from the beginning, make it clear. - buffer.puts(line_offset, code_offset, "...", Style::LineNumber); + buffer.puts(line_offset, code_offset, placeholder, Style::LineNumber); } if margin.was_cut_right(line_len) { + let padding: usize = placeholder.chars().map(|ch| char_width(ch)).sum(); // We have stripped some code after the rightmost span end, make it clear we did so. - buffer.puts(line_offset, code_offset + taken - 3, "...", Style::LineNumber); + buffer.puts(line_offset, code_offset + taken - padding, placeholder, Style::LineNumber); } buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); - draw_col_separator_no_space(buffer, line_offset, width_offset - 2); + self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); } #[instrument(level = "trace", skip(self), ret)] @@ -702,6 +713,7 @@ impl HumanEmitter { width_offset: usize, code_offset: usize, margin: Margin, + close_window: bool, ) -> Vec<(usize, Style)> { // Draw: // @@ -767,13 +779,10 @@ impl HumanEmitter { for ann in &line.annotations { if let AnnotationType::MultilineStart(depth) = ann.annotation_type { if source_string.chars().take(ann.start_col.display).all(|c| c.is_whitespace()) { - let style = if ann.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; - annotations.push((depth, style)); - buffer_ops.push((line_offset, width_offset + depth - 1, '/', style)); + let uline = self.underline(ann.is_primary); + let chr = uline.multiline_whole_line; + annotations.push((depth, uline.style)); + buffer_ops.push((line_offset, width_offset + depth - 1, chr, uline.style)); } else { short_start = false; break; @@ -970,7 +979,7 @@ impl HumanEmitter { // 3 │ X0 Y0 Z0 // │ ┏━━━━━┛ │ │ < We are writing these lines // │ ┃┌───────┘ │ < by reverting the "depth" of - // │ ┃│┌─────────┘ < their multilne spans. + // │ ┃│┌─────────┘ < their multiline spans. // 4 │ ┃││ X1 Y1 Z1 // 5 │ ┃││ X2 Y2 Z2 // │ ┃│└────╿──│──┘ `Z` label @@ -997,7 +1006,10 @@ impl HumanEmitter { // 4 | } // | for pos in 0..=line_len { - draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2); + self.draw_col_separator_no_space(buffer, line_offset + pos + 1, width_offset - 2); + } + if close_window { + self.draw_col_separator_end(buffer, line_offset + line_len + 1, width_offset - 2); } // Write the horizontal lines for multiline annotations @@ -1013,21 +1025,17 @@ impl HumanEmitter { // 4 | } // | _ for &(pos, annotation) in &annotations_position { - let style = if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; + let underline = self.underline(annotation.is_primary); let pos = pos + 1; match annotation.annotation_type { AnnotationType::MultilineStart(depth) | AnnotationType::MultilineEnd(depth) => { - draw_range( + self.draw_range( buffer, - '_', + underline.multiline_horizontal, line_offset + pos, width_offset + depth, (code_offset + annotation.start_col.display).saturating_sub(left), - style, + underline.style, ); } _ if self.teach => { @@ -1035,7 +1043,7 @@ impl HumanEmitter { line_offset, (code_offset + annotation.start_col.display).saturating_sub(left), (code_offset + annotation.end_col.display).saturating_sub(left), - style, + underline.style, annotation.is_primary, ); } @@ -1055,33 +1063,78 @@ impl HumanEmitter { // 4 | | } // | |_ for &(pos, annotation) in &annotations_position { - let style = if annotation.is_primary { - Style::UnderlinePrimary - } else { - Style::UnderlineSecondary - }; + let underline = self.underline(annotation.is_primary); let pos = pos + 1; if pos > 1 && (annotation.has_label() || annotation.takes_space()) { for p in line_offset + 1..=line_offset + pos { + if let AnnotationType::MultilineLine(_) = annotation.annotation_type { + buffer.putc( + p, + (code_offset + annotation.start_col.display).saturating_sub(left), + underline.multiline_vertical, + underline.style, + ); + } else { + buffer.putc( + p, + (code_offset + annotation.start_col.display).saturating_sub(left), + underline.vertical_text_line, + underline.style, + ); + } + } + if let AnnotationType::MultilineStart(_) = annotation.annotation_type { buffer.putc( - p, + line_offset + pos, (code_offset + annotation.start_col.display).saturating_sub(left), - '|', - style, + underline.bottom_right, + underline.style, + ); + } + if let AnnotationType::MultilineEnd(_) = annotation.annotation_type + && annotation.has_label() + { + buffer.putc( + line_offset + pos, + (code_offset + annotation.start_col.display).saturating_sub(left), + underline.multiline_bottom_right_with_text, + underline.style, ); } } match annotation.annotation_type { AnnotationType::MultilineStart(depth) => { + buffer.putc( + line_offset + pos, + width_offset + depth - 1, + underline.top_left, + underline.style, + ); for p in line_offset + pos + 1..line_offset + line_len + 2 { - buffer.putc(p, width_offset + depth - 1, '|', style); + buffer.putc( + p, + width_offset + depth - 1, + underline.multiline_vertical, + underline.style, + ); } } AnnotationType::MultilineEnd(depth) => { - for p in line_offset..=line_offset + pos { - buffer.putc(p, width_offset + depth - 1, '|', style); + for p in line_offset..line_offset + pos { + buffer.putc( + p, + width_offset + depth - 1, + underline.multiline_vertical, + underline.style, + ); } + buffer.putc( + line_offset + pos, + width_offset + depth - 1, + underline.bottom_left, + underline.style, + ); } _ => (), } @@ -1102,7 +1155,11 @@ impl HumanEmitter { let style = if annotation.is_primary { Style::LabelPrimary } else { Style::LabelSecondary }; let (pos, col) = if pos == 0 { - (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + if annotation.end_col.display == 0 { + (pos + 1, (annotation.end_col.display + 2).saturating_sub(left)) + } else { + (pos + 1, (annotation.end_col.display + 1).saturating_sub(left)) + } } else { (pos + 2, annotation.start_col.display.saturating_sub(left)) }; @@ -1135,18 +1192,60 @@ impl HumanEmitter { // 3 | // 4 | } // | _^ test - for &(_, annotation) in &annotations_position { - let (underline, style) = if annotation.is_primary { - ('^', Style::UnderlinePrimary) - } else { - ('-', Style::UnderlineSecondary) - }; + for &(pos, annotation) in &annotations_position { + let uline = self.underline(annotation.is_primary); for p in annotation.start_col.display..annotation.end_col.display { + // The default span label underline. buffer.putc( line_offset + 1, (code_offset + p).saturating_sub(left), - underline, - style, + uline.underline, + uline.style, + ); + } + + if pos == 0 + && matches!( + annotation.annotation_type, + AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) + ) + { + // The beginning of a multiline span with its leftward moving line on the same line. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + match annotation.annotation_type { + AnnotationType::MultilineStart(_) => uline.top_right_flat, + AnnotationType::MultilineEnd(_) => uline.multiline_end_same_line, + _ => panic!("unexpected annotation type: {annotation:?}"), + }, + uline.style, + ); + } else if pos != 0 + && matches!( + annotation.annotation_type, + AnnotationType::MultilineStart(_) | AnnotationType::MultilineEnd(_) + ) + { + // The beginning of a multiline span with its leftward moving line on another line, + // so we start going down first. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + match annotation.annotation_type { + AnnotationType::MultilineStart(_) => uline.multiline_start_down, + AnnotationType::MultilineEnd(_) => uline.multiline_end_up, + _ => panic!("unexpected annotation type: {annotation:?}"), + }, + uline.style, + ); + } else if pos != 0 && annotation.has_label() { + // The beginning of a span label with an actual label, we'll point down. + buffer.putc( + line_offset + 1, + (code_offset + annotation.start_col.display).saturating_sub(left), + uline.label_start, + uline.style, ); } } @@ -1217,7 +1316,7 @@ impl HumanEmitter { padding: usize, label: &str, override_style: Option + + + + + error[E0308]: `match` arms have incompatible types + + --> $DIR/huge_multispan_highlight.rs:99:18 + + | + + LL | let _ = match true { + + | ---------- `match` arms have incompatible types + + LL | true => ( + + | _________________- + + LL | | // last line shown in multispan header + + ... | + + LL | | + + LL | | ), + + | |_________- this is found to be of type `()` + + LL | false => " + + | __________________^ + + ... | + + LL | | + + LL | | ", + + | |_________^ expected `()`, found `&str` + + + + error[E0308]: `match` arms have incompatible types + + --> $DIR/huge_multispan_highlight.rs:216:18 + + | + + LL | let _ = match true { + + | ---------- `match` arms have incompatible types + + LL | true => ( + + | _________________- + + LL | | + + LL | | 1 // last line shown in multispan header + + ... | + + LL | | + + LL | | ), + + | |_________- this is found to be of type `{integer}` + + LL | false => " + + | __________________^ + + LL | | + + LL | | + + LL | | 1 last line shown in multispan + + ... | + + LL | | + + LL | | ", + + | |_________^ expected integer, found `&str` + + + + error: aborting due to 2 previous errors + + + + For more information about this error, try `rustc --explain E0308`. + + + + + + diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.rs b/tests/ui/codemap_tests/huge_multispan_highlight.rs index 28c595cca64..7d7b7570823 100644 --- a/tests/ui/codemap_tests/huge_multispan_highlight.rs +++ b/tests/ui/codemap_tests/huge_multispan_highlight.rs @@ -1,4 +1,7 @@ -//@ compile-flags: --error-format=human --color=always +//@ revisions: ascii unicode +//@ compile-flags: --color=always +//@[ascii] compile-flags: --error-format=human +//@[unicode] compile-flags: -Zunstable-options=yes --error-format=human-unicode //@ ignore-windows fn main() { let _ = match true { diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.svg b/tests/ui/codemap_tests/huge_multispan_highlight.svg deleted file mode 100644 index 12058176dc0..00000000000 --- a/tests/ui/codemap_tests/huge_multispan_highlight.svg +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - error[E0308]: `match` arms have incompatible types - - --> $DIR/huge_multispan_highlight.rs:96:18 - - | - - LL | let _ = match true { - - | ---------- `match` arms have incompatible types - - LL | true => ( - - | _________________- - - LL | | // last line shown in multispan header - - ... | - - LL | | - - LL | | ), - - | |_________- this is found to be of type `()` - - LL | false => " - - | __________________^ - - ... | - - LL | | - - LL | | ", - - | |_________^ expected `()`, found `&str` - - - - error[E0308]: `match` arms have incompatible types - - --> $DIR/huge_multispan_highlight.rs:213:18 - - | - - LL | let _ = match true { - - | ---------- `match` arms have incompatible types - - LL | true => ( - - | _________________- - - LL | | - - LL | | 1 // last line shown in multispan header - - ... | - - LL | | - - LL | | ), - - | |_________- this is found to be of type `{integer}` - - LL | false => " - - | __________________^ - - LL | | - - LL | | - - LL | | 1 last line shown in multispan - - ... | - - LL | | - - LL | | ", - - | |_________^ expected integer, found `&str` - - - - error: aborting due to 2 previous errors - - - - For more information about this error, try `rustc --explain E0308`. - - - - - - diff --git a/tests/ui/codemap_tests/huge_multispan_highlight.unicode.svg b/tests/ui/codemap_tests/huge_multispan_highlight.unicode.svg new file mode 100644 index 00000000000..4e1a8d14a28 --- /dev/null +++ b/tests/ui/codemap_tests/huge_multispan_highlight.unicode.svg @@ -0,0 +1,116 @@ + + + + + + + error[E0308]: `match` arms have incompatible types + + ╭▸ $DIR/huge_multispan_highlight.rs:99:18 + + + + LL let _ = match true { + + ────────── `match` arms have incompatible types + + LL true => ( + + ┌─────────────────┘ + + LL // last line shown in multispan header + + + + LL + + LL ), + + └─────────┘ this is found to be of type `()` + + LL false => " + + ┏━━━━━━━━━━━━━━━━━━┛ + + + + LL + + LL ", + + ╰╴┗━━━━━━━━━┛ expected `()`, found `&str` + + + + error[E0308]: `match` arms have incompatible types + + ╭▸ $DIR/huge_multispan_highlight.rs:216:18 + + + + LL let _ = match true { + + ────────── `match` arms have incompatible types + + LL true => ( + + ┌─────────────────┘ + + LL + + LL 1 // last line shown in multispan header + + + + LL + + LL ), + + └─────────┘ this is found to be of type `{integer}` + + LL false => " + + ┏━━━━━━━━━━━━━━━━━━┛ + + LL + + LL + + LL 1 last line shown in multispan + + + + LL + + LL ", + + ╰╴┗━━━━━━━━━┛ expected integer, found `&str` + + + + error: aborting due to 2 previous errors + + + + For more information about this error, try `rustc --explain E0308`. + + + + + + diff --git a/tests/ui/diagnostic-width/E0271.ascii.stderr b/tests/ui/diagnostic-width/E0271.ascii.stderr new file mode 100644 index 00000000000..e276299e9e8 --- /dev/null +++ b/tests/ui/diagnostic-width/E0271.ascii.stderr @@ -0,0 +1,22 @@ +error[E0271]: type mismatch resolving `>, ...>>, ...>>, ...> as Future>::Error == Foo` + --> $DIR/E0271.rs:20:5 + | +LL | / Box::new( +LL | | Ok::<_, ()>( +LL | | Err::<(), _>( +LL | | Ok::<_, ()>( +... | +LL | | ) +LL | | ) + | |_____^ type mismatch resolving `, ...>>, ...> as Future>::Error == Foo` + | +note: expected this to be `Foo` + --> $DIR/E0271.rs:10:18 + | +LL | type Error = E; + | ^ + = note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/diagnostic-width/E0271.rs b/tests/ui/diagnostic-width/E0271.rs index d8cb24898ac..ce41ad2952b 100644 --- a/tests/ui/diagnostic-width/E0271.rs +++ b/tests/ui/diagnostic-width/E0271.rs @@ -1,4 +1,6 @@ -//@ compile-flags: --diagnostic-width=40 +//@ revisions: ascii unicode +//@[ascii] compile-flags: --diagnostic-width=40 +//@[unicode] compile-flags: -Zunstable-options=yes --error-format=human-unicode --diagnostic-width=40 //@ normalize-stderr-test: "long-type-\d+" -> "long-type-hash" trait Future { type Error; @@ -15,7 +17,7 @@ impl Future for Option { struct Foo; fn foo() -> Box> { - Box::new( //~ ERROR E0271 + Box::new( //[ascii]~ ERROR E0271 Ok::<_, ()>( Err::<(), _>( Ok::<_, ()>( diff --git a/tests/ui/diagnostic-width/E0271.stderr b/tests/ui/diagnostic-width/E0271.stderr deleted file mode 100644 index 31ec3fe366f..00000000000 --- a/tests/ui/diagnostic-width/E0271.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0271]: type mismatch resolving `>, ...>>, ...>>, ...> as Future>::Error == Foo` - --> $DIR/E0271.rs:18:5 - | -LL | / Box::new( -LL | | Ok::<_, ()>( -LL | | Err::<(), _>( -LL | | Ok::<_, ()>( -... | -LL | | ) -LL | | ) - | |_____^ type mismatch resolving `, ...>>, ...> as Future>::Error == Foo` - | -note: expected this to be `Foo` - --> $DIR/E0271.rs:8:18 - | -LL | type Error = E; - | ^ - = note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/diagnostic-width/E0271.unicode.stderr b/tests/ui/diagnostic-width/E0271.unicode.stderr new file mode 100644 index 00000000000..4a96ca36cd7 --- /dev/null +++ b/tests/ui/diagnostic-width/E0271.unicode.stderr @@ -0,0 +1,22 @@ +error[E0271]: type mismatch resolving `>, ...>>, ...>>, ...> as Future>::Error == Foo` + ╭▸ $DIR/E0271.rs:20:5 + │ +LL │ ┏ Box::new( +LL │ ┃ Ok::<_, ()>( +LL │ ┃ Err::<(), _>( +LL │ ┃ Ok::<_, ()>( + ‡ ┃ +LL │ ┃ ) +LL │ ┃ ) + │ ┗━━━━━┛ type mismatch resolving `, ...>>, ...> as Future>::Error == Foo` + ╰╴ +note: expected this to be `Foo` + ╭▸ $DIR/E0271.rs:10:18 + │ +LL │ type Error = E; + │ ━ + ╰ note: required for the cast from `Box>, ()>>, ()>>, ()>>` to `Box<(dyn Future + 'static)>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/diagnostic-width/flag-human.ascii.stderr b/tests/ui/diagnostic-width/flag-human.ascii.stderr new file mode 100644 index 00000000000..4593304e087 --- /dev/null +++ b/tests/ui/diagnostic-width/flag-human.ascii.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/flag-human.rs:9:17 + | +LL | ..._: () = 42; + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/flag-human.rs b/tests/ui/diagnostic-width/flag-human.rs index a46122ed783..1af41659141 100644 --- a/tests/ui/diagnostic-width/flag-human.rs +++ b/tests/ui/diagnostic-width/flag-human.rs @@ -1,9 +1,11 @@ -//@ compile-flags: --diagnostic-width=20 +//@ revisions: ascii unicode +//@[ascii] compile-flags: --diagnostic-width=20 +//@[unicode] compile-flags: -Zunstable-options=yes --error-format=human-unicode --diagnostic-width=20 // This test checks that `-Z output-width` effects the human error output by restricting it to an // arbitrarily low value so that the effect is visible. fn main() { let _: () = 42; - //~^ ERROR mismatched types + //[ascii]~^ ERROR mismatched types } diff --git a/tests/ui/diagnostic-width/flag-human.stderr b/tests/ui/diagnostic-width/flag-human.stderr deleted file mode 100644 index eaa96841080..00000000000 --- a/tests/ui/diagnostic-width/flag-human.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/flag-human.rs:7:17 - | -LL | ..._: () = 42; - | -- ^^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/flag-human.unicode.stderr b/tests/ui/diagnostic-width/flag-human.unicode.stderr new file mode 100644 index 00000000000..50176564786 --- /dev/null +++ b/tests/ui/diagnostic-width/flag-human.unicode.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + ╭▸ $DIR/flag-human.rs:9:17 + │ +LL │ …t _: () = 42; + │ ┬─ ━━ expected `()`, found integer + │ │ + ╰╴ expected due to this + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-E0308.ascii.stderr b/tests/ui/diagnostic-width/long-E0308.ascii.stderr new file mode 100644 index 00000000000..d45d6bf329b --- /dev/null +++ b/tests/ui/diagnostic-width/long-E0308.ascii.stderr @@ -0,0 +1,82 @@ +error[E0308]: mismatched types + --> $DIR/long-E0308.rs:46:9 + | +LL | let x: Atype< + | _____________- +LL | | Btype< +LL | | Ctype< +LL | | Atype< +... | +LL | | i32 +LL | | > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... + | | _____-___^ + | ||_____| + | | expected due to this +LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... +LL | | Ok("") +LL | | )))))))))))))))))))))))))))))) +LL | | )))))))))))))))))))))))))))))); + | |__________________________________^ expected `Atype, ...>`, found `Result, ...>` + | + = note: expected struct `Atype, ...>` + found enum `Result, ...>` + = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + --> $DIR/long-E0308.rs:59:26 + | +LL | ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... + | __________________________^ +LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... +LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) +LL | | )))))))))))))))))))))))))))))) +LL | | )))))))))))))))))))))))); + | |____________________________^ expected `Option>`, found `Result, ...>` + | + = note: expected enum `Option>` + found enum `Result, ...>` + = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + --> $DIR/long-E0308.rs:90:9 + | +LL | let x: Atype< + | ____________- +LL | | Btype< +LL | | Ctype< +LL | | Atype< +... | +LL | | i32 +LL | | > = (); + | | - ^^ expected `Atype, ...>`, found `()` + | |_____| + | expected due to this + | + = note: expected struct `Atype, ...>` + found unit type `()` + = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + --> $DIR/long-E0308.rs:93:17 + | +LL | let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... + | ____________--___^ + | | | + | | expected due to this +LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... +LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) +LL | | )))))))))))))))))))))))))))))) +LL | | )))))))))))))))))))))))); + | |____________________________^ expected `()`, found `Result, ...>` + | + = note: expected unit type `()` + found enum `Result, ...>` + = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.ascii/long-E0308.long-type-hash.txt' + = note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-E0308.rs b/tests/ui/diagnostic-width/long-E0308.rs index 150164ba21b..73f81f5872a 100644 --- a/tests/ui/diagnostic-width/long-E0308.rs +++ b/tests/ui/diagnostic-width/long-E0308.rs @@ -1,4 +1,6 @@ -//@ compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes +//@ revisions: ascii unicode +//@[ascii] compile-flags: --diagnostic-width=60 -Zwrite-long-types-to-disk=yes +//@[unicode] compile-flags: -Zunstable-options=yes --json=diagnostic-unicode --diagnostic-width=60 -Zwrite-long-types-to-disk=yes //@ normalize-stderr-test: "long-type-\d+" -> "long-type-hash" mod a { diff --git a/tests/ui/diagnostic-width/long-E0308.stderr b/tests/ui/diagnostic-width/long-E0308.stderr deleted file mode 100644 index eb37da037e9..00000000000 --- a/tests/ui/diagnostic-width/long-E0308.stderr +++ /dev/null @@ -1,82 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/long-E0308.rs:44:9 - | -LL | let x: Atype< - | _____________- -LL | | Btype< -LL | | Ctype< -LL | | Atype< -... | -LL | | i32 -LL | | > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... - | | _____-___^ - | ||_____| - | | expected due to this -LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... -LL | | Ok("") -LL | | )))))))))))))))))))))))))))))) -LL | | )))))))))))))))))))))))))))))); - | |__________________________________^ expected `Atype, ...>`, found `Result, ...>` - | - = note: expected struct `Atype, ...>` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt' - = note: consider using `--verbose` to print the full type name to the console - -error[E0308]: mismatched types - --> $DIR/long-E0308.rs:57:26 - | -LL | ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... - | __________________________^ -LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... -LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) -LL | | )))))))))))))))))))))))))))))) -LL | | )))))))))))))))))))))))); - | |____________________________^ expected `Option>`, found `Result, ...>` - | - = note: expected enum `Option>` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt' - = note: consider using `--verbose` to print the full type name to the console - -error[E0308]: mismatched types - --> $DIR/long-E0308.rs:88:9 - | -LL | let x: Atype< - | ____________- -LL | | Btype< -LL | | Ctype< -LL | | Atype< -... | -LL | | i32 -LL | | > = (); - | | - ^^ expected `Atype, ...>`, found `()` - | |_____| - | expected due to this - | - = note: expected struct `Atype, ...>` - found unit type `()` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt' - = note: consider using `--verbose` to print the full type name to the console - -error[E0308]: mismatched types - --> $DIR/long-E0308.rs:91:17 - | -LL | let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O... - | ____________--___^ - | | | - | | expected due to this -LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... -LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) -LL | | )))))))))))))))))))))))))))))) -LL | | )))))))))))))))))))))))); - | |____________________________^ expected `()`, found `Result, ...>` - | - = note: expected unit type `()` - found enum `Result, ...>` - = note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308/long-E0308.long-type-hash.txt' - = note: consider using `--verbose` to print the full type name to the console - -error: aborting due to 4 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/long-E0308.unicode.stderr b/tests/ui/diagnostic-width/long-E0308.unicode.stderr new file mode 100644 index 00000000000..3e8d881d7a6 --- /dev/null +++ b/tests/ui/diagnostic-width/long-E0308.unicode.stderr @@ -0,0 +1,82 @@ +error[E0308]: mismatched types + ╭▸ $DIR/long-E0308.rs:46:9 + │ +LL │ let x: Atype< + │ ┌─────────────┘ +LL │ │ Btype< +LL │ │ Ctype< +LL │ │ Atype< + ‡ │ +LL │ │ i32 +LL │ │ > = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O… + │ │┏━━━━━│━━━┛ + │ └┃─────┤ + │ ┃ expected due to this +LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O… +LL │ ┃ Ok("") +LL │ ┃ )))))))))))))))))))))))))))))) +LL │ ┃ )))))))))))))))))))))))))))))); + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype, ...>`, found `Result, ...>` + │ + ├ note: expected struct `Atype, ...>` + │ found enum `Result, ...>` + ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ╰ note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + ╭▸ $DIR/long-E0308.rs:59:26 + │ +LL │ ))))))))))))))))) == Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… + │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━┛ +LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… +LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) +LL │ ┃ )))))))))))))))))))))))))))))) +LL │ ┃ )))))))))))))))))))))))); + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Option>`, found `Result, ...>` + │ + ├ note: expected enum `Option>` + │ found enum `Result, ...>` + ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ╰ note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + ╭▸ $DIR/long-E0308.rs:90:9 + │ +LL │ let x: Atype< + │ ┌────────────┘ +LL │ │ Btype< +LL │ │ Ctype< +LL │ │ Atype< + ‡ │ +LL │ │ i32 +LL │ │ > = (); + │ │ │ ━━ expected `Atype, ...>`, found `()` + │ └─────┤ + │ expected due to this + │ + ├ note: expected struct `Atype, ...>` + │ found unit type `()` + ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ╰ note: consider using `--verbose` to print the full type name to the console + +error[E0308]: mismatched types + ╭▸ $DIR/long-E0308.rs:93:17 + │ +LL │ let _: () = Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(… + │ ┏━━━━━━━━━━━━┬─━━━┛ + │ ┃ │ + │ ┃ expected due to this +LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… +LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) +LL │ ┃ )))))))))))))))))))))))))))))) +LL │ ┃ )))))))))))))))))))))))); + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `()`, found `Result, ...>` + │ + ├ note: expected unit type `()` + │ found enum `Result, ...>` + ├ note: the full type name has been written to '$TEST_BUILD_DIR/diagnostic-width/long-E0308.unicode/long-E0308.long-type-hash.txt' + ╰ note: consider using `--verbose` to print the full type name to the console + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr new file mode 100644 index 00000000000..4d8afb6f3ad --- /dev/null +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.ascii.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `&str` to `&str` + --> $DIR/non-1-width-unicode-multiline-label.rs:7:260 + | +LL | ...ཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇...࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + | -------------- ^ -------------- &str + | | | + | | `+` cannot be used to concatenate two `&str` strings + | &str + | + = note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + | +LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs index 1989ea88635..61c4b31e03a 100644 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.rs @@ -1,7 +1,9 @@ +//@ revisions: ascii unicode +//@[unicode] compile-flags: -Zunstable-options=yes --error-format=human-unicode // ignore-tidy-linelength fn main() { let unicode_is_fun = "؁‱ஹ௸௵꧄.ဪ꧅⸻𒈙𒐫﷽𒌄𒈟𒍼𒁎𒀱𒌧𒅃 𒈓𒍙𒊎𒄡𒅌𒁏𒀰𒐪𒐩𒈙𒐫𪚥"; let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - //~^ ERROR cannot add `&str` to `&str` + //[ascii]~^ ERROR cannot add `&str` to `&str` } diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.stderr deleted file mode 100644 index 8f200e15c64..00000000000 --- a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0369]: cannot add `&str` to `&str` - --> $DIR/non-1-width-unicode-multiline-label.rs:5:260 - | -LL | ...ཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇...࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; - | -------------- ^ -------------- &str - | | | - | | `+` cannot be used to concatenate two `&str` strings - | &str - | - = note: string concatenation requires an owned `String` on the left -help: create an owned `String` from a string reference - | -LL | let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; - | +++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr new file mode 100644 index 00000000000..ed8ce770bb7 --- /dev/null +++ b/tests/ui/diagnostic-width/non-1-width-unicode-multiline-label.unicode.stderr @@ -0,0 +1,18 @@ +error[E0369]: cannot add `&str` to `&str` + ╭▸ $DIR/non-1-width-unicode-multiline-label.rs:7:260 + │ +LL │ …ཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉…࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun + " really fun!"; + │ ┬───────────── ┯ ────────────── &str + │ │ │ + │ │ `+` cannot be used to concatenate two `&str` strings + │ &str + │ + ╰ note: string concatenation requires an owned `String` on the left +help: create an owned `String` from a string reference + ╭╴ +LL │ let _ = "ༀ༁༂༃༄༅༆༇༈༉༊་༌།༎༏༐༑༒༓༔༕༖༗༘༙༚༛༜༝༞༟༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿ཀཁགགྷངཅཆཇ཈ཉཊཋཌཌྷཎཏཐདདྷནཔཕབབྷམཙཚཛཛྷཝཞཟའཡརལཤཥསཧཨཀྵཪཫཬ཭཮཯཰ཱཱཱིིུུྲྀཷླྀཹེཻོཽཾཿ྄ཱྀྀྂྃ྅྆྇ྈྉྊྋྌྍྎྏྐྑྒྒྷྔྕྖྗ྘ྙྚྛྜྜྷྞྟྠྡྡྷྣྤྥྦྦྷྨྩྪྫྫྷྭྮྯྰྱྲླྴྵྶྷྸྐྵྺྻྼ྽྾྿࿀࿁࿂࿃࿄࿅࿆࿇࿈࿉࿊࿋࿌࿍࿎࿏࿐࿑࿒࿓࿔࿕࿖࿗࿘࿙࿚"; let _a = unicode_is_fun.to_owned() + " really fun!"; + ╰╴ +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr new file mode 100644 index 00000000000..70bd149545c --- /dev/null +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.ascii.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + --> $DIR/non-whitespace-trimming-2.rs:6:311 + | +LL | ...13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let ... + | -- ^^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs index abd9e189a75..283506bd6c9 100644 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.rs @@ -1,6 +1,8 @@ +//@ revisions: ascii unicode +//@[unicode] compile-flags: -Zunstable-options=yes --error-format=human-unicode // ignore-tidy-linelength fn main() { let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let _: usize = 4; let _: usize = 5; let _: usize = 6; let _: usize = 7; let _: usize = 8; let _: usize = 9; let _: usize = 10; let _: usize = 11; let _: usize = 12; let _: usize = 13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let _: usize = 4; let _: usize = 5; let _: usize = 6; let _: usize = 7; let _: usize = 8; let _: usize = 9; let _: usize = 10; let _: usize = 11; let _: usize = 12; let _: usize = 13; let _: usize = 14; let _: usize = 15; -//~^ ERROR mismatched types +//[ascii]~^ ERROR mismatched types } diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.stderr deleted file mode 100644 index a7d5c0bfb94..00000000000 --- a/tests/ui/diagnostic-width/non-whitespace-trimming-2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/non-whitespace-trimming-2.rs:4:311 - | -LL | ...13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let ... - | -- ^^ expected `()`, found integer - | | - | expected due to this - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr new file mode 100644 index 00000000000..600d196de16 --- /dev/null +++ b/tests/ui/diagnostic-width/non-whitespace-trimming-2.unicode.stderr @@ -0,0 +1,11 @@ +error[E0308]: mismatched types + ╭▸ $DIR/non-whitespace-trimming-2.rs:6:311 + │ +LL │ …= 13; let _: usize = 14; let _: usize = 15; let _: () = 42; let _: usize = 0; let _: usize = 1; let _: usize = 2; let _: usize = 3; let _:… + │ ┬─ ━━ expected `()`, found integer + │ │ + ╰╴ expected due to this + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/error-emitter/highlighting.svg b/tests/ui/error-emitter/highlighting.svg index be92c00c19b..a4019c78f48 100644 --- a/tests/ui/error-emitter/highlighting.svg +++ b/tests/ui/error-emitter/highlighting.svg @@ -49,7 +49,7 @@ LL | fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<( - | ____^^^^^_- + | ____^^^^^_- LL | | dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static diff --git a/tests/ui/error-emitter/highlighting.windows.svg b/tests/ui/error-emitter/highlighting.windows.svg index 152245da9dd..c2378113b86 100644 --- a/tests/ui/error-emitter/highlighting.windows.svg +++ b/tests/ui/error-emitter/highlighting.windows.svg @@ -50,7 +50,7 @@ LL | fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<( - | ____^^^^^_- + | ____^^^^^_- LL | | dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static diff --git a/tests/ui/error-emitter/unicode-output.rs b/tests/ui/error-emitter/unicode-output.rs new file mode 100644 index 00000000000..ba6db37b66c --- /dev/null +++ b/tests/ui/error-emitter/unicode-output.rs @@ -0,0 +1,21 @@ +//@ compile-flags: -Zunstable-options=yes --error-format=human-unicode --color=always +//@ edition:2018 +//@ only-linux + +use core::pin::Pin; +use core::future::Future; +use core::any::Any; + +fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin, String>> + Send + 'static +)>>) {} + +fn wrapped_fn<'a>(_: Box<(dyn Any + Send)>) -> Pin, String>> + Send + 'static +)>> { + Box::pin(async { Err("nope".into()) }) +} + +fn main() { + query(wrapped_fn); +} diff --git a/tests/ui/error-emitter/unicode-output.svg b/tests/ui/error-emitter/unicode-output.svg new file mode 100644 index 00000000000..f98fd8b7403 --- /dev/null +++ b/tests/ui/error-emitter/unicode-output.svg @@ -0,0 +1,72 @@ + + + + + + + error[E0308]: mismatched types + + ╭▸ $DIR/unicode-output.rs:20:11 + + + + LL query(wrapped_fn); + + ┬──── ━━━━━━━━━━ one type is more general than the other + + + + arguments to this function are incorrect + + + + note: expected fn pointer `for<'a> fn(Box<(dyn Any + Send + 'a)>) -> Pin<_>` + + found fn item `fn(Box<(dyn Any + Send + 'static)>) -> Pin<_> {wrapped_fn}` + + note: function defined here + + ╭▸ $DIR/unicode-output.rs:9:4 + + + + LL fn query(_: fn(Box<(dyn Any + Send + '_)>) -> Pin<Box<( + + ┌────━━━━━─┘ + + LL dyn Future<Output = Result<Box<(dyn Any + 'static)>, String>> + Send + 'static + + LL )>>) {} + + ╰╴└───┘ + + + + error: aborting due to 1 previous error + + + + For more information about this error, try `rustc --explain E0308`. + + + + + + diff --git a/tests/ui/parser/bad-char-literals.stderr b/tests/ui/parser/bad-char-literals.stderr index 1fb324a1b7e..5a81ede0336 100644 --- a/tests/ui/parser/bad-char-literals.stderr +++ b/tests/ui/parser/bad-char-literals.stderr @@ -15,7 +15,7 @@ error: character constant must be escaped: `\n` LL | ' | ______^ LL | | '; - | |_ + | |_^ | help: escape the character | -- cgit 1.4.1-3-g733a5 From 99d02fb40fd339255ed08596ebeb41e9b8a09d45 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 7 Nov 2024 16:09:39 +1100 Subject: Optimize `check_keyword_case`. `to_lowercase` allocates, but `eq_ignore_ascii_case` doesn't. This path is hot enough that this makes a small but noticeable difference in benchmarking. --- compiler/rustc_parse/src/parser/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 50a8b6542df..042ee96bbe8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -641,9 +641,10 @@ impl<'a> Parser<'a> { return true; } + // Do an ASCII case-insensitive match, because all keywords are ASCII. if case == Case::Insensitive && let Some((ident, IdentIsRaw::No)) = self.token.ident() - && ident.as_str().to_lowercase() == kw.as_str().to_lowercase() + && ident.as_str().eq_ignore_ascii_case(kw.as_str()) { true } else { -- cgit 1.4.1-3-g733a5 From 1136bbf0668d84d2b4b09b772e33fd8532994f83 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Wed, 13 Nov 2024 13:17:01 +0000 Subject: Trim extra space when suggesting removing bad `let` --- compiler/rustc_parse/src/parser/pat.rs | 4 +++- tests/ui/parser/unnecessary-let.stderr | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7f114013320..3546e5b0f04 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -683,7 +683,9 @@ impl<'a> Parser<'a> { }) { self.bump(); - self.dcx().emit_err(RemoveLet { span: lo }); + // Trim extra space after the `let` + let span = lo.with_hi(self.token.span.lo()); + self.dcx().emit_err(RemoveLet { span }); lo = self.token.span; } diff --git a/tests/ui/parser/unnecessary-let.stderr b/tests/ui/parser/unnecessary-let.stderr index c6ac0d562f8..05ac1faafd4 100644 --- a/tests/ui/parser/unnecessary-let.stderr +++ b/tests/ui/parser/unnecessary-let.stderr @@ -2,12 +2,12 @@ error: expected pattern, found `let` --> $DIR/unnecessary-let.rs:2:9 | LL | for let x of [1, 2, 3] {} - | ^^^ + | ^^^^ | help: remove the unnecessary `let` keyword | LL - for let x of [1, 2, 3] {} -LL + for x of [1, 2, 3] {} +LL + for x of [1, 2, 3] {} | error: missing `in` in `for` loop @@ -25,12 +25,12 @@ error: expected pattern, found `let` --> $DIR/unnecessary-let.rs:7:9 | LL | let 1 => {} - | ^^^ + | ^^^^ | help: remove the unnecessary `let` keyword | LL - let 1 => {} -LL + 1 => {} +LL + 1 => {} | error: aborting due to 3 previous errors -- cgit 1.4.1-3-g733a5 From dd557c988f63ae000d56ced1ce52c86c507a47e3 Mon Sep 17 00:00:00 2001 From: Tyrone Wu Date: Fri, 15 Nov 2024 02:27:51 +0000 Subject: Trim whitespace in RemoveLet primary span Separate `RemoveLet` span into primary span for `let` and removal suggestion span for `let `, so that primary span does not include whitespace. Fixes: #133031 Signed-off-by: Tyrone Wu --- compiler/rustc_parse/src/errors.rs | 3 ++- compiler/rustc_parse/src/parser/pat.rs | 2 +- tests/ui/parser/unnecessary-let.fixed | 13 +++++++++++++ tests/ui/parser/unnecessary-let.rs | 4 +++- tests/ui/parser/unnecessary-let.stderr | 24 ++++++++++++------------ 5 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 tests/ui/parser/unnecessary-let.fixed (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 7ec4ad6dc35..37eb463cba6 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -650,8 +650,9 @@ pub(crate) struct LeftArrowOperator { #[diag(parse_remove_let)] pub(crate) struct RemoveLet { #[primary_span] - #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")] pub span: Span, + #[suggestion(applicability = "machine-applicable", code = "", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 3546e5b0f04..c4326427f67 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -685,7 +685,7 @@ impl<'a> Parser<'a> { self.bump(); // Trim extra space after the `let` let span = lo.with_hi(self.token.span.lo()); - self.dcx().emit_err(RemoveLet { span }); + self.dcx().emit_err(RemoveLet { span: lo, suggestion: span }); lo = self.token.span; } diff --git a/tests/ui/parser/unnecessary-let.fixed b/tests/ui/parser/unnecessary-let.fixed new file mode 100644 index 00000000000..bdee07e76aa --- /dev/null +++ b/tests/ui/parser/unnecessary-let.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix + +fn main() { + for _x in [1, 2, 3] {} + //~^ ERROR expected pattern, found `let` + //~| ERROR missing `in` in `for` loop + + match 1 { + 1 => {} + //~^ ERROR expected pattern, found `let` + _ => {} + } +} diff --git a/tests/ui/parser/unnecessary-let.rs b/tests/ui/parser/unnecessary-let.rs index 6279109621d..a889d9f7789 100644 --- a/tests/ui/parser/unnecessary-let.rs +++ b/tests/ui/parser/unnecessary-let.rs @@ -1,5 +1,7 @@ +//@ run-rustfix + fn main() { - for let x of [1, 2, 3] {} + for let _x of [1, 2, 3] {} //~^ ERROR expected pattern, found `let` //~| ERROR missing `in` in `for` loop diff --git a/tests/ui/parser/unnecessary-let.stderr b/tests/ui/parser/unnecessary-let.stderr index 05ac1faafd4..0b28123747a 100644 --- a/tests/ui/parser/unnecessary-let.stderr +++ b/tests/ui/parser/unnecessary-let.stderr @@ -1,31 +1,31 @@ error: expected pattern, found `let` - --> $DIR/unnecessary-let.rs:2:9 + --> $DIR/unnecessary-let.rs:4:9 | -LL | for let x of [1, 2, 3] {} - | ^^^^ +LL | for let _x of [1, 2, 3] {} + | ^^^ | help: remove the unnecessary `let` keyword | -LL - for let x of [1, 2, 3] {} -LL + for x of [1, 2, 3] {} +LL - for let _x of [1, 2, 3] {} +LL + for _x of [1, 2, 3] {} | error: missing `in` in `for` loop - --> $DIR/unnecessary-let.rs:2:15 + --> $DIR/unnecessary-let.rs:4:16 | -LL | for let x of [1, 2, 3] {} - | ^^ +LL | for let _x of [1, 2, 3] {} + | ^^ | help: try using `in` here instead | -LL | for let x in [1, 2, 3] {} - | ~~ +LL | for let _x in [1, 2, 3] {} + | ~~ error: expected pattern, found `let` - --> $DIR/unnecessary-let.rs:7:9 + --> $DIR/unnecessary-let.rs:9:9 | LL | let 1 => {} - | ^^^^ + | ^^^ | help: remove the unnecessary `let` keyword | -- cgit 1.4.1-3-g733a5 From 04fe8391775683e58d861f28678bf80940c91f44 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Thu, 14 Nov 2024 20:29:24 +0000 Subject: Increase accuracy of `if` condition misparse suggestion Look at the expression that was parsed when trying to recover from a bad `if` condition to determine what was likely intended by the user beyond "maybe this was meant to be an `else` body". ``` error: expected `{`, found `map` --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:4:30 | LL | for _ in [1, 2, 3].iter()map(|x| x) {} | ^^^ expected `{` | help: you might have meant to write a method call | LL | for _ in [1, 2, 3].iter().map(|x| x) {} | + ``` --- compiler/rustc_parse/src/parser/stmt.rs | 98 ++++++++++++++++++-- tests/ui/issues/issue-39848.stderr | 6 +- tests/ui/parser/else-no-if.stderr | 5 - ...ng-dot-on-if-condition-expression-fixable.fixed | 39 ++++++++ ...ssing-dot-on-if-condition-expression-fixable.rs | 39 ++++++++ ...g-dot-on-if-condition-expression-fixable.stderr | 57 ++++++++++++ .../missing-dot-on-if-condition-expression.rs | 57 ++++++++++++ .../missing-dot-on-if-condition-expression.stderr | 101 +++++++++++++++++++++ 8 files changed, 385 insertions(+), 17 deletions(-) create mode 100644 tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed create mode 100644 tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs create mode 100644 tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr create mode 100644 tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs create mode 100644 tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index b7cdae3e3e1..e8877b8c541 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -475,6 +475,7 @@ impl<'a> Parser<'a> { } fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> { + let prev = self.prev_token.span; let sp = self.token.span; let mut e = self.dcx().struct_span_err(sp, msg); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon; @@ -514,15 +515,94 @@ impl<'a> Parser<'a> { } else { stmt.span }; - e.multipart_suggestion( - "try placing this code inside a block", - vec![ - (stmt_span.shrink_to_lo(), "{ ".to_string()), - (stmt_span.shrink_to_hi(), " }".to_string()), - ], - // Speculative; has been misleading in the past (#46836). - Applicability::MaybeIncorrect, - ); + match (&self.token.kind, &stmt.kind) { + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Call(..) = expr.kind => + { + // for _ in x y() {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a method call", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Field(..) = expr.kind => + { + // for _ in x y.z {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Struct(expr) = &expr.kind + && let None = expr.qself + && expr.path.segments.len() == 1 => + { + // This is specific to "mistyped `if` condition followed by empty body" + // + // for _ in x y {} + e.span_suggestion_verbose( + prev.between(sp), + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Lit(lit) = expr.kind + && let None = lit.suffix + && let token::LitKind::Integer | token::LitKind::Float = lit.kind => + { + // for _ in x 0 {} + // for _ in x 0.0 {} + e.span_suggestion_verbose( + prev.between(sp), + format!("you might have meant to write a field access"), + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Loop(..) + | ExprKind::If(..) + | ExprKind::While(..) + | ExprKind::Match(..) + | ExprKind::ForLoop { .. } + | ExprKind::TryBlock(..) + | ExprKind::Ret(..) + | ExprKind::Closure(..) + | ExprKind::Struct(..) + | ExprKind::Try(..) = expr.kind => + { + // These are more likely to have been meant as a block body. + e.multipart_suggestion( + "try placing this code inside a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), _) => {} + (_, _) => { + e.multipart_suggestion( + "try placing this code inside a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + } } Err(e) => { self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); diff --git a/tests/ui/issues/issue-39848.stderr b/tests/ui/issues/issue-39848.stderr index a6c6c61f170..1ffed2d4a1d 100644 --- a/tests/ui/issues/issue-39848.stderr +++ b/tests/ui/issues/issue-39848.stderr @@ -16,10 +16,10 @@ LL | if $tgt.has_$field() {} LL | get_opt!(bar, foo); | ------------------ in this macro invocation = note: this error originates in the macro `get_opt` (in Nightly builds, run with -Z macro-backtrace for more info) -help: try placing this code inside a block +help: you might have meant to write a method call | -LL | if $tgt.has_{ $field() } {} - | + + +LL | if $tgt.has_.$field() {} + | + error: aborting due to 1 previous error diff --git a/tests/ui/parser/else-no-if.stderr b/tests/ui/parser/else-no-if.stderr index 2e3e8f6b50e..c86791ec896 100644 --- a/tests/ui/parser/else-no-if.stderr +++ b/tests/ui/parser/else-no-if.stderr @@ -75,11 +75,6 @@ error: expected `{`, found `falsy` | LL | } else falsy! {} { | ^^^^^ expected `{` - | -help: try placing this code inside a block - | -LL | } else { falsy! {} } { - | + + error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:54:12 diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed new file mode 100644 index 00000000000..ea9dc4cf6cc --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.fixed @@ -0,0 +1,39 @@ +//@ run-rustfix +#![allow(dead_code)] +fn main() { + for _ in [1, 2, 3].iter().map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x.0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x.0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x.unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x.a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs new file mode 100644 index 00000000000..1833f458a8a --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.rs @@ -0,0 +1,39 @@ +//@ run-rustfix +#![allow(dead_code)] +fn main() { + for _ in [1, 2, 3].iter()map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x 0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x 0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr new file mode 100644 index 00000000000..87f76efffa6 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression-fixable.stderr @@ -0,0 +1,57 @@ +error: expected `{`, found `map` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:4:30 + | +LL | for _ in [1, 2, 3].iter()map(|x| x) {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in [1, 2, 3].iter().map(|x| x) {} + | + + +error: expected `{`, found `0` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:10:16 + | +LL | for _ in x 0 {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0 {} + | + + +error: expected `{`, found `0.0` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:16:16 + | +LL | for _ in x 0.0 {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0.0 {} + | + + +error: expected `{`, found `unwrap` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:22:16 + | +LL | for _ in x unwrap() {} + | ^^^^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in x.unwrap() {} + | + + +error: expected `{`, found `a` + --> $DIR/missing-dot-on-if-condition-expression-fixable.rs:28:16 + | +LL | for _ in x a.b {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.a.b {} + | + + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs new file mode 100644 index 00000000000..c4a9ed66a83 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.rs @@ -0,0 +1,57 @@ +fn main() { + for _ in [1, 2, 3].iter()map(|x| x) {} + //~^ ERROR expected `{`, found `map` + //~| HELP you might have meant to write a method call +} +fn foo1() { + for _ in 1.3f64 cos() {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a method call +} +fn foo2() { + for _ in 1.3 cos {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a field access +} +fn foo3() { + for _ in 1 cos() {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a method call +} +fn foo4() { + for _ in 1 cos {} + //~^ ERROR expected `{`, found `cos` + //~| HELP you might have meant to write a field access +} +fn foo5() { + let x = (vec![1, 2, 3],); + for _ in x 0 {} + //~^ ERROR expected `{`, found `0` + //~| HELP you might have meant to write a field access +} +fn foo6() { + let x = ((vec![1, 2, 3],),); + for _ in x 0.0 {} + //~^ ERROR expected `{`, found `0.0` + //~| HELP you might have meant to write a field access +} +fn foo7() { + let x = Some(vec![1, 2, 3]); + for _ in x unwrap() {} + //~^ ERROR expected `{`, found `unwrap` + //~| HELP you might have meant to write a method call +} +fn foo8() { + let x = S { a: A { b: vec![1, 2, 3] } }; + for _ in x a.b {} + //~^ ERROR expected `{`, found `a` + //~| HELP you might have meant to write a field access +} + +struct S { + a: A, +} + +struct A { + b: Vec, +} diff --git a/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr new file mode 100644 index 00000000000..bfb72b95682 --- /dev/null +++ b/tests/ui/parser/recover/missing-dot-on-if-condition-expression.stderr @@ -0,0 +1,101 @@ +error: expected `{`, found `map` + --> $DIR/missing-dot-on-if-condition-expression.rs:2:30 + | +LL | for _ in [1, 2, 3].iter()map(|x| x) {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in [1, 2, 3].iter().map(|x| x) {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:7:21 + | +LL | for _ in 1.3f64 cos() {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in 1.3f64.cos() {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:12:18 + | +LL | for _ in 1.3 cos {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in 1.3.cos {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:17:16 + | +LL | for _ in 1 cos() {} + | ^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in 1.cos() {} + | + + +error: expected `{`, found `cos` + --> $DIR/missing-dot-on-if-condition-expression.rs:22:16 + | +LL | for _ in 1 cos {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in 1.cos {} + | + + +error: expected `{`, found `0` + --> $DIR/missing-dot-on-if-condition-expression.rs:28:16 + | +LL | for _ in x 0 {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0 {} + | + + +error: expected `{`, found `0.0` + --> $DIR/missing-dot-on-if-condition-expression.rs:34:16 + | +LL | for _ in x 0.0 {} + | ^^^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.0.0 {} + | + + +error: expected `{`, found `unwrap` + --> $DIR/missing-dot-on-if-condition-expression.rs:40:16 + | +LL | for _ in x unwrap() {} + | ^^^^^^ expected `{` + | +help: you might have meant to write a method call + | +LL | for _ in x.unwrap() {} + | + + +error: expected `{`, found `a` + --> $DIR/missing-dot-on-if-condition-expression.rs:46:16 + | +LL | for _ in x a.b {} + | ^ expected `{` + | +help: you might have meant to write a field access + | +LL | for _ in x.a.b {} + | + + +error: aborting due to 9 previous errors + -- cgit 1.4.1-3-g733a5 From 629a69f3e2728251774f825ff54cb642b38249df Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Thu, 14 Nov 2024 20:39:25 +0000 Subject: Better account for `else if` macro conditions mising an `if` If a macro statement has been parsed after `else`, suggest a missing `if`: ``` error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:47:12 | LL | } else falsy! {} { | ---- ^^^^^ | | | expected an `if` or a block after this `else` | help: add an `if` if this is the condition of a chained `else if` statement | LL | } else if falsy! {} { | ++ ``` --- compiler/rustc_parse/src/parser/expr.rs | 11 ++++++++++- tests/ui/parser/else-no-if.stderr | 9 ++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0ac6133e828..0012db471ef 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2683,6 +2683,13 @@ impl<'a> Parser<'a> { // ^^ // } // + // We account for macro calls that were meant as conditions as well. + // + // if ... { + // } else if macro! { foo bar } { + // ^^ + // } + // // If $cond is "statement-like" such as ExprKind::While then we // want to suggest wrapping in braces. // @@ -2693,7 +2700,9 @@ impl<'a> Parser<'a> { // } // ^ if self.check(&TokenKind::OpenDelim(Delimiter::Brace)) - && classify::expr_requires_semi_to_be_stmt(&cond) => + && (classify::expr_requires_semi_to_be_stmt(&cond) + || matches!(cond.kind, ExprKind::MacCall(..))) + => { self.dcx().emit_err(errors::ExpectedElseBlock { first_tok_span, diff --git a/tests/ui/parser/else-no-if.stderr b/tests/ui/parser/else-no-if.stderr index c86791ec896..9954505e7c8 100644 --- a/tests/ui/parser/else-no-if.stderr +++ b/tests/ui/parser/else-no-if.stderr @@ -74,7 +74,14 @@ error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:47:12 | LL | } else falsy! {} { - | ^^^^^ expected `{` + | ---- ^^^^^ + | | + | expected an `if` or a block after this `else` + | +help: add an `if` if this is the condition of a chained `else if` statement + | +LL | } else if falsy! {} { + | ++ error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:54:12 -- cgit 1.4.1-3-g733a5 From c09c73b99622905e8df074b81377c9ca82467dce Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 16 Nov 2024 00:22:54 +0000 Subject: Reword suggestion message --- compiler/rustc_parse/src/parser/stmt.rs | 4 ++-- tests/ui/let-else/let-else-if.stderr | 2 +- tests/ui/lint/issue-104392.stderr | 2 +- tests/ui/missing/missing-block-hint.stderr | 2 +- tests/ui/parser/bad-if-statements.stderr | 4 ++-- tests/ui/parser/block-no-opening-brace.stderr | 6 +++--- tests/ui/parser/closure-return-syntax.stderr | 2 +- tests/ui/parser/else-no-if.stderr | 8 ++++---- tests/ui/parser/label-after-block-like.stderr | 14 +++++++------- tests/ui/unsafe/unsafe-block-without-braces.stderr | 2 +- 10 files changed, 23 insertions(+), 23 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index e8877b8c541..4ab056a4e13 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -581,7 +581,7 @@ impl<'a> Parser<'a> { { // These are more likely to have been meant as a block body. e.multipart_suggestion( - "try placing this code inside a block", + "you might have meant to write this as part of a block", vec![ (stmt_span.shrink_to_lo(), "{ ".to_string()), (stmt_span.shrink_to_hi(), " }".to_string()), @@ -593,7 +593,7 @@ impl<'a> Parser<'a> { (token::OpenDelim(Delimiter::Brace), _) => {} (_, _) => { e.multipart_suggestion( - "try placing this code inside a block", + "you might have meant to write this as part of a block", vec![ (stmt_span.shrink_to_lo(), "{ ".to_string()), (stmt_span.shrink_to_hi(), " }".to_string()), diff --git a/tests/ui/let-else/let-else-if.stderr b/tests/ui/let-else/let-else-if.stderr index 7e2215c8c05..ad36b423150 100644 --- a/tests/ui/let-else/let-else-if.stderr +++ b/tests/ui/let-else/let-else-if.stderr @@ -4,7 +4,7 @@ error: conditional `else if` is not supported for `let...else` LL | let Some(_) = Some(()) else if true { | ^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL ~ let Some(_) = Some(()) else { if true { LL | diff --git a/tests/ui/lint/issue-104392.stderr b/tests/ui/lint/issue-104392.stderr index 8e466439ae6..4d8d8c56d41 100644 --- a/tests/ui/lint/issue-104392.stderr +++ b/tests/ui/lint/issue-104392.stderr @@ -6,7 +6,7 @@ LL | { unsafe 92 } | | | while parsing this `unsafe` expression | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { unsafe { 92 } } | + + diff --git a/tests/ui/missing/missing-block-hint.stderr b/tests/ui/missing/missing-block-hint.stderr index 18719289abd..7a08d70d0ce 100644 --- a/tests/ui/missing/missing-block-hint.stderr +++ b/tests/ui/missing/missing-block-hint.stderr @@ -25,7 +25,7 @@ note: the `if` expression is missing a block after this condition | LL | if (foo) | ^^^^^ -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { bar; } | + + diff --git a/tests/ui/parser/bad-if-statements.stderr b/tests/ui/parser/bad-if-statements.stderr index ee839db6455..320b1176993 100644 --- a/tests/ui/parser/bad-if-statements.stderr +++ b/tests/ui/parser/bad-if-statements.stderr @@ -29,7 +29,7 @@ note: the `if` expression is missing a block after this condition | LL | if true x | ^^^^ -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | if true { x } | + + @@ -65,7 +65,7 @@ note: the `if` expression is missing a block after this condition | LL | if true x else {} | ^^^^ -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | if true { x } else {} | + + diff --git a/tests/ui/parser/block-no-opening-brace.stderr b/tests/ui/parser/block-no-opening-brace.stderr index 83360944ed5..b65de4eac3f 100644 --- a/tests/ui/parser/block-no-opening-brace.stderr +++ b/tests/ui/parser/block-no-opening-brace.stderr @@ -6,7 +6,7 @@ LL | loop LL | let x = 0; | ^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { let x = 0; } | + + @@ -21,7 +21,7 @@ LL | while true LL | let x = 0; | ^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { let x = 0; } | + + @@ -32,7 +32,7 @@ error: expected `{`, found keyword `let` LL | let x = 0; | ^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { let x = 0; } | + + diff --git a/tests/ui/parser/closure-return-syntax.stderr b/tests/ui/parser/closure-return-syntax.stderr index eb8428854af..aacc31ed871 100644 --- a/tests/ui/parser/closure-return-syntax.stderr +++ b/tests/ui/parser/closure-return-syntax.stderr @@ -4,7 +4,7 @@ error: expected `{`, found `22` LL | let x = || -> i32 22; | ^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | let x = || -> i32 { 22 }; | + + diff --git a/tests/ui/parser/else-no-if.stderr b/tests/ui/parser/else-no-if.stderr index 9954505e7c8..eec64b0f4bc 100644 --- a/tests/ui/parser/else-no-if.stderr +++ b/tests/ui/parser/else-no-if.stderr @@ -30,7 +30,7 @@ error: expected `{`, found `falsy` LL | } else falsy(); | ^^^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | } else { falsy() }; | + + @@ -41,7 +41,7 @@ error: expected `{`, found keyword `loop` LL | } else loop{} | ^^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | } else { loop{} } | + + @@ -65,7 +65,7 @@ error: expected `{`, found `falsy` LL | } else falsy!(); | ^^^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | } else { falsy!() }; | + + @@ -89,7 +89,7 @@ error: expected `{`, found `falsy` LL | } else falsy! {}; | ^^^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | } else { falsy! {} }; | + + diff --git a/tests/ui/parser/label-after-block-like.stderr b/tests/ui/parser/label-after-block-like.stderr index be8c679d8ce..4dea225e3f3 100644 --- a/tests/ui/parser/label-after-block-like.stderr +++ b/tests/ui/parser/label-after-block-like.stderr @@ -23,7 +23,7 @@ note: the `if` expression is missing a block after this condition | LL | if let () = () 'a {} | ^^^^^^^^^^^ -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | if let () = () { 'a {} } | + + @@ -53,7 +53,7 @@ note: the `if` expression is missing a block after this condition | LL | if true 'a {} | ^^^^ -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | if true { 'a {} } | + + @@ -80,7 +80,7 @@ LL | loop 'a {} | | | while parsing this `loop` expression | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | loop { 'a {} } | + + @@ -108,7 +108,7 @@ LL | while true 'a {} | | this `while` condition successfully parsed | while parsing the body of this `while` expression | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | while true { 'a {} } | + + @@ -136,7 +136,7 @@ LL | while let () = () 'a {} | | this `while` condition successfully parsed | while parsing the body of this `while` expression | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | while let () = () { 'a {} } | + + @@ -161,7 +161,7 @@ error: expected `{`, found `'a` LL | for _ in 0..0 'a {} | ^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | for _ in 0..0 { 'a {} } | + + @@ -188,7 +188,7 @@ LL | unsafe 'a {} | | | while parsing this `unsafe` expression | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | unsafe { 'a {} } | + + diff --git a/tests/ui/unsafe/unsafe-block-without-braces.stderr b/tests/ui/unsafe/unsafe-block-without-braces.stderr index d29e49d73a6..3d8234c15e7 100644 --- a/tests/ui/unsafe/unsafe-block-without-braces.stderr +++ b/tests/ui/unsafe/unsafe-block-without-braces.stderr @@ -6,7 +6,7 @@ LL | unsafe //{ LL | std::mem::transmute::(1.0); | ^^^ expected `{` | -help: try placing this code inside a block +help: you might have meant to write this as part of a block | LL | { std::mem::transmute::(1.0); } | + + -- cgit 1.4.1-3-g733a5 From 6913194b8ea47ebe8e65b627e83f029d0968d8f8 Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 16 Nov 2024 20:02:27 +0000 Subject: review comment: move logic to new method --- compiler/rustc_parse/src/parser/stmt.rs | 191 +++++++++++++++++--------------- 1 file changed, 103 insertions(+), 88 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 4ab056a4e13..190cd9ed061 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -515,94 +515,12 @@ impl<'a> Parser<'a> { } else { stmt.span }; - match (&self.token.kind, &stmt.kind) { - (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) - if let ExprKind::Call(..) = expr.kind => - { - // for _ in x y() {} - e.span_suggestion_verbose( - prev.between(sp), - "you might have meant to write a method call", - ".".to_string(), - Applicability::MaybeIncorrect, - ); - } - (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) - if let ExprKind::Field(..) = expr.kind => - { - // for _ in x y.z {} - e.span_suggestion_verbose( - prev.between(sp), - "you might have meant to write a field access", - ".".to_string(), - Applicability::MaybeIncorrect, - ); - } - (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr)) - if let ExprKind::Struct(expr) = &expr.kind - && let None = expr.qself - && expr.path.segments.len() == 1 => - { - // This is specific to "mistyped `if` condition followed by empty body" - // - // for _ in x y {} - e.span_suggestion_verbose( - prev.between(sp), - "you might have meant to write a field access", - ".".to_string(), - Applicability::MaybeIncorrect, - ); - } - (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) - if let ExprKind::Lit(lit) = expr.kind - && let None = lit.suffix - && let token::LitKind::Integer | token::LitKind::Float = lit.kind => - { - // for _ in x 0 {} - // for _ in x 0.0 {} - e.span_suggestion_verbose( - prev.between(sp), - format!("you might have meant to write a field access"), - ".".to_string(), - Applicability::MaybeIncorrect, - ); - } - (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) - if let ExprKind::Loop(..) - | ExprKind::If(..) - | ExprKind::While(..) - | ExprKind::Match(..) - | ExprKind::ForLoop { .. } - | ExprKind::TryBlock(..) - | ExprKind::Ret(..) - | ExprKind::Closure(..) - | ExprKind::Struct(..) - | ExprKind::Try(..) = expr.kind => - { - // These are more likely to have been meant as a block body. - e.multipart_suggestion( - "you might have meant to write this as part of a block", - vec![ - (stmt_span.shrink_to_lo(), "{ ".to_string()), - (stmt_span.shrink_to_hi(), " }".to_string()), - ], - // Speculative; has been misleading in the past (#46836). - Applicability::MaybeIncorrect, - ); - } - (token::OpenDelim(Delimiter::Brace), _) => {} - (_, _) => { - e.multipart_suggestion( - "you might have meant to write this as part of a block", - vec![ - (stmt_span.shrink_to_lo(), "{ ".to_string()), - (stmt_span.shrink_to_hi(), " }".to_string()), - ], - // Speculative; has been misleading in the past (#46836). - Applicability::MaybeIncorrect, - ); - } - } + self.suggest_fixes_misparsed_for_loop_head( + &mut e, + prev.between(sp), + stmt_span, + &stmt.kind, + ); } Err(e) => { self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); @@ -614,6 +532,103 @@ impl<'a> Parser<'a> { e } + fn suggest_fixes_misparsed_for_loop_head( + &self, + e: &mut Diag<'_>, + between: Span, + stmt_span: Span, + stmt_kind: &StmtKind, + ) { + match (&self.token.kind, &stmt_kind) { + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Call(..) = expr.kind => + { + // for _ in x y() {} + e.span_suggestion_verbose( + between, + "you might have meant to write a method call", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Field(..) = expr.kind => + { + // for _ in x y.z {} + e.span_suggestion_verbose( + between, + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::CloseDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Struct(expr) = &expr.kind + && let None = expr.qself + && expr.path.segments.len() == 1 => + { + // This is specific to "mistyped `if` condition followed by empty body" + // + // for _ in x y {} + e.span_suggestion_verbose( + between, + "you might have meant to write a field access", + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Lit(lit) = expr.kind + && let None = lit.suffix + && let token::LitKind::Integer | token::LitKind::Float = lit.kind => + { + // for _ in x 0 {} + // for _ in x 0.0 {} + e.span_suggestion_verbose( + between, + format!("you might have meant to write a field access"), + ".".to_string(), + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), StmtKind::Expr(expr)) + if let ExprKind::Loop(..) + | ExprKind::If(..) + | ExprKind::While(..) + | ExprKind::Match(..) + | ExprKind::ForLoop { .. } + | ExprKind::TryBlock(..) + | ExprKind::Ret(..) + | ExprKind::Closure(..) + | ExprKind::Struct(..) + | ExprKind::Try(..) = expr.kind => + { + // These are more likely to have been meant as a block body. + e.multipart_suggestion( + "you might have meant to write this as part of a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + (token::OpenDelim(Delimiter::Brace), _) => {} + (_, _) => { + e.multipart_suggestion( + "you might have meant to write this as part of a block", + vec![ + (stmt_span.shrink_to_lo(), "{ ".to_string()), + (stmt_span.shrink_to_hi(), " }".to_string()), + ], + // Speculative; has been misleading in the past (#46836). + Applicability::MaybeIncorrect, + ); + } + } + } + fn error_block_no_opening_brace(&mut self) -> PResult<'a, T> { let tok = super::token_descr(&self.token); let msg = format!("expected `{{`, found {tok}"); -- cgit 1.4.1-3-g733a5 From e5f1555000158b796a1e245b7c75b5a969506e53 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 17 Nov 2024 12:25:19 -0800 Subject: Inline ExprPrecedence::order into Expr::precedence --- compiler/rustc_ast/src/ast.rs | 112 +++++++++++--------- compiler/rustc_ast/src/util/parser.rs | 115 --------------------- compiler/rustc_ast_pretty/src/pprust/state/expr.rs | 4 +- .../rustc_ast_pretty/src/pprust/state/fixup.rs | 2 +- compiler/rustc_hir/src/hir.rs | 81 +++++++++------ compiler/rustc_hir_pretty/src/lib.rs | 4 +- compiler/rustc_hir_typeck/src/callee.rs | 2 +- compiler/rustc_hir_typeck/src/cast.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 10 +- compiler/rustc_parse/src/parser/pat.rs | 17 +-- src/tools/clippy/clippy_lints/src/dereference.rs | 10 +- .../clippy_lints/src/loops/single_element_loop.rs | 2 +- .../clippy_lints/src/matches/manual_utils.rs | 2 +- src/tools/clippy/clippy_lints/src/neg_multiply.rs | 2 +- .../clippy/clippy_lints/src/redundant_slicing.rs | 2 +- .../transmutes_expressible_as_ptr_casts.rs | 4 +- 16 files changed, 146 insertions(+), 225 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5f71fb97d76..013d08f1de0 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -39,7 +39,9 @@ pub use crate::format::*; use crate::ptr::P; use crate::token::{self, CommentKind, Delimiter}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; -pub use crate::util::parser::ExprPrecedence; +use crate::util::parser::{ + AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_RANGE, PREC_UNAMBIGUOUS, +}; /// A "Label" is an identifier of some point in sources, /// e.g. in the following code: @@ -1319,53 +1321,71 @@ impl Expr { Some(P(Ty { kind, id: self.id, span: self.span, tokens: None })) } - pub fn precedence(&self) -> ExprPrecedence { + pub fn precedence(&self) -> i8 { match self.kind { - ExprKind::Array(_) => ExprPrecedence::Array, - ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, - ExprKind::Call(..) => ExprPrecedence::Call, - ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, - ExprKind::Tup(_) => ExprPrecedence::Tup, - ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), - ExprKind::Unary(..) => ExprPrecedence::Unary, - ExprKind::Lit(_) | ExprKind::IncludedBytes(..) => ExprPrecedence::Lit, - ExprKind::Cast(..) => ExprPrecedence::Cast, - ExprKind::Let(..) => ExprPrecedence::Let, - ExprKind::If(..) => ExprPrecedence::If, - ExprKind::While(..) => ExprPrecedence::While, - ExprKind::ForLoop { .. } => ExprPrecedence::ForLoop, - ExprKind::Loop(..) => ExprPrecedence::Loop, - ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, - ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, - ExprKind::Closure(..) => ExprPrecedence::Closure, - ExprKind::Block(..) => ExprPrecedence::Block, - ExprKind::TryBlock(..) => ExprPrecedence::TryBlock, - ExprKind::Gen(..) => ExprPrecedence::Gen, - ExprKind::Await(..) => ExprPrecedence::Await, - ExprKind::Assign(..) => ExprPrecedence::Assign, - ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, - ExprKind::Field(..) => ExprPrecedence::Field, - ExprKind::Index(..) => ExprPrecedence::Index, - ExprKind::Range(..) => ExprPrecedence::Range, - ExprKind::Underscore => ExprPrecedence::Path, - ExprKind::Path(..) => ExprPrecedence::Path, - ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, - ExprKind::Break(..) => ExprPrecedence::Break, - ExprKind::Continue(..) => ExprPrecedence::Continue, - ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::Struct(..) => ExprPrecedence::Struct, - ExprKind::Repeat(..) => ExprPrecedence::Repeat, - ExprKind::Paren(..) => ExprPrecedence::Paren, - ExprKind::Try(..) => ExprPrecedence::Try, - ExprKind::Yield(..) => ExprPrecedence::Yield, - ExprKind::Yeet(..) => ExprPrecedence::Yeet, - ExprKind::Become(..) => ExprPrecedence::Become, - ExprKind::InlineAsm(..) - | ExprKind::Type(..) - | ExprKind::OffsetOf(..) + ExprKind::Closure(..) => PREC_CLOSURE, + + ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Yield(..) + | ExprKind::Yeet(..) + | ExprKind::Become(..) => PREC_JUMP, + + // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to + // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence + // ensures that `pprust` will add parentheses in the right places to get the desired + // parse. + ExprKind::Range(..) => PREC_RANGE, + + // Binop-like expr kinds, handled by `AssocOp`. + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, + ExprKind::Cast(..) => AssocOp::As.precedence() as i8, + + ExprKind::Assign(..) | + ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, + + // Unary, prefix + ExprKind::AddrOf(..) + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + | ExprKind::Let(..) + | ExprKind::Unary(..) => PREC_PREFIX, + + // Never need parens + ExprKind::Array(_) + | ExprKind::Await(..) + | ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::ConstBlock(_) + | ExprKind::Field(..) + | ExprKind::ForLoop { .. } | ExprKind::FormatArgs(..) - | ExprKind::MacCall(..) => ExprPrecedence::Mac, - ExprKind::Err(_) | ExprKind::Dummy => ExprPrecedence::Err, + | ExprKind::Gen(..) + | ExprKind::If(..) + | ExprKind::IncludedBytes(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::Lit(_) + | ExprKind::Loop(..) + | ExprKind::MacCall(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Paren(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Try(..) + | ExprKind::TryBlock(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::Underscore + | ExprKind::While(..) + | ExprKind::Err(_) + | ExprKind::Dummy => PREC_UNAMBIGUOUS, } } diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index d8dad4550c0..ed9265d5159 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -237,121 +237,6 @@ pub const PREC_PREFIX: i8 = 50; pub const PREC_UNAMBIGUOUS: i8 = 60; pub const PREC_FORCE_PAREN: i8 = 100; -#[derive(Debug, Clone, Copy)] -pub enum ExprPrecedence { - Closure, - Break, - Continue, - Ret, - Yield, - Yeet, - Become, - - Range, - - Binary(BinOpKind), - - Cast, - - Assign, - AssignOp, - - AddrOf, - Let, - Unary, - - Call, - MethodCall, - Field, - Index, - Try, - Mac, - - Array, - Repeat, - Tup, - Lit, - Path, - Paren, - If, - While, - ForLoop, - Loop, - Match, - PostfixMatch, - ConstBlock, - Block, - TryBlock, - Struct, - Gen, - Await, - Err, -} - -impl ExprPrecedence { - pub fn order(self) -> i8 { - match self { - ExprPrecedence::Closure => PREC_CLOSURE, - - ExprPrecedence::Break - | ExprPrecedence::Continue - | ExprPrecedence::Ret - | ExprPrecedence::Yield - | ExprPrecedence::Yeet - | ExprPrecedence::Become => PREC_JUMP, - - // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to - // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence - // ensures that `pprust` will add parentheses in the right places to get the desired - // parse. - ExprPrecedence::Range => PREC_RANGE, - - // Binop-like expr kinds, handled by `AssocOp`. - ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, - ExprPrecedence::Cast => AssocOp::As.precedence() as i8, - - ExprPrecedence::Assign | - ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, - - // Unary, prefix - ExprPrecedence::AddrOf - // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. - // However, this is not exactly right. When `let _ = a` is the LHS of a binop we - // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` - // but we need to print `(let _ = a) < b` as-is with parens. - | ExprPrecedence::Let - | ExprPrecedence::Unary => PREC_PREFIX, - - // Never need parens - ExprPrecedence::Array - | ExprPrecedence::Await - | ExprPrecedence::Block - | ExprPrecedence::Call - | ExprPrecedence::ConstBlock - | ExprPrecedence::Field - | ExprPrecedence::ForLoop - | ExprPrecedence::Gen - | ExprPrecedence::If - | ExprPrecedence::Index - | ExprPrecedence::Lit - | ExprPrecedence::Loop - | ExprPrecedence::Mac - | ExprPrecedence::Match - | ExprPrecedence::MethodCall - | ExprPrecedence::Paren - | ExprPrecedence::Path - | ExprPrecedence::PostfixMatch - | ExprPrecedence::Repeat - | ExprPrecedence::Struct - | ExprPrecedence::Try - | ExprPrecedence::TryBlock - | ExprPrecedence::Tup - | ExprPrecedence::While - | ExprPrecedence::Err => PREC_UNAMBIGUOUS, - } - } -} - /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. pub fn prec_let_scrutinee_needs_par() -> usize { AssocOp::LAnd.precedence() diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 893bfaf8f71..04ec135c428 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -59,7 +59,7 @@ impl<'a> State<'a> { } fn print_expr_maybe_paren(&mut self, expr: &ast::Expr, prec: i8, fixup: FixupContext) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec, fixup); + self.print_expr_cond_paren(expr, expr.precedence() < prec, fixup); } /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in @@ -615,7 +615,7 @@ impl<'a> State<'a> { expr, // Parenthesize if required by precedence, or in the // case of `break 'inner: loop { break 'inner 1 } + 1` - expr.precedence().order() < parser::PREC_JUMP + expr.precedence() < parser::PREC_JUMP || (opt_label.is_none() && classify::leading_labeled_expr(expr)), fixup.subsequent_subexpression(), ); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs index 50fd12a4e8b..6f5382ce61d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/fixup.rs @@ -191,6 +191,6 @@ impl FixupContext { /// "let chain". pub(crate) fn needs_par_as_let_scrutinee(self, expr: &Expr) -> bool { self.parenthesize_exterior_struct_lit && parser::contains_exterior_struct_lit(expr) - || parser::needs_par_as_let_scrutinee(expr.precedence().order()) + || parser::needs_par_as_let_scrutinee(expr.precedence()) } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 554097bf115..2ece9a10e3a 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1,7 +1,7 @@ use std::fmt; use rustc_abi::ExternAbi; -use rustc_ast::util::parser::ExprPrecedence; +use rustc_ast::util::parser::{AssocOp, PREC_CLOSURE, PREC_JUMP, PREC_PREFIX, PREC_UNAMBIGUOUS}; use rustc_ast::{ self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitKind, TraitObjectSyntax, UintTy, @@ -1719,41 +1719,54 @@ pub struct Expr<'hir> { } impl Expr<'_> { - pub fn precedence(&self) -> ExprPrecedence { + pub fn precedence(&self) -> i8 { match self.kind { - ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock, - ExprKind::Array(_) => ExprPrecedence::Array, - ExprKind::Call(..) => ExprPrecedence::Call, - ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, - ExprKind::Tup(_) => ExprPrecedence::Tup, - ExprKind::Binary(op, ..) => ExprPrecedence::Binary(op.node), - ExprKind::Unary(..) => ExprPrecedence::Unary, - ExprKind::Lit(_) => ExprPrecedence::Lit, - ExprKind::Cast(..) => ExprPrecedence::Cast, + ExprKind::Closure { .. } => PREC_CLOSURE, + + ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::Yield(..) + | ExprKind::Become(..) => PREC_JUMP, + + // Binop-like expr kinds, handled by `AssocOp`. + ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence() as i8, + ExprKind::Cast(..) => AssocOp::As.precedence() as i8, + + ExprKind::Assign(..) | + ExprKind::AssignOp(..) => AssocOp::Assign.precedence() as i8, + + // Unary, prefix + ExprKind::AddrOf(..) + // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. + // However, this is not exactly right. When `let _ = a` is the LHS of a binop we + // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` + // but we need to print `(let _ = a) < b` as-is with parens. + | ExprKind::Let(..) + | ExprKind::Unary(..) => PREC_PREFIX, + + // Never need parens + ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Call(..) + | ExprKind::ConstBlock(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Index(..) + | ExprKind::InlineAsm(..) + | ExprKind::Lit(_) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::MethodCall(..) + | ExprKind::OffsetOf(..) + | ExprKind::Path(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::Err(_) => PREC_UNAMBIGUOUS, + ExprKind::DropTemps(ref expr, ..) => expr.precedence(), - ExprKind::If(..) => ExprPrecedence::If, - ExprKind::Let(..) => ExprPrecedence::Let, - ExprKind::Loop(..) => ExprPrecedence::Loop, - ExprKind::Match(..) => ExprPrecedence::Match, - ExprKind::Closure { .. } => ExprPrecedence::Closure, - ExprKind::Block(..) => ExprPrecedence::Block, - ExprKind::Assign(..) => ExprPrecedence::Assign, - ExprKind::AssignOp(..) => ExprPrecedence::AssignOp, - ExprKind::Field(..) => ExprPrecedence::Field, - ExprKind::Index(..) => ExprPrecedence::Index, - ExprKind::Path(..) => ExprPrecedence::Path, - ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, - ExprKind::Break(..) => ExprPrecedence::Break, - ExprKind::Continue(..) => ExprPrecedence::Continue, - ExprKind::Ret(..) => ExprPrecedence::Ret, - ExprKind::Become(..) => ExprPrecedence::Become, - ExprKind::Struct(..) => ExprPrecedence::Struct, - ExprKind::Repeat(..) => ExprPrecedence::Repeat, - ExprKind::Yield(..) => ExprPrecedence::Yield, - ExprKind::Type(..) | ExprKind::InlineAsm(..) | ExprKind::OffsetOf(..) => { - ExprPrecedence::Mac - } - ExprKind::Err(_) => ExprPrecedence::Err, } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0a3aa8fec90..f4c20e34eda 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1015,7 +1015,7 @@ impl<'a> State<'a> { } fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) { - self.print_expr_cond_paren(expr, expr.precedence().order() < prec) + self.print_expr_cond_paren(expr, expr.precedence() < prec) } /// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in @@ -1049,7 +1049,7 @@ impl<'a> State<'a> { } self.space(); self.word_space("="); - let npals = || parser::needs_par_as_let_scrutinee(init.precedence().order()); + let npals = || parser::needs_par_as_let_scrutinee(init.precedence()); self.print_expr_cond_paren(init, Self::cond_needs_par(init) || npals()) } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e6e0f62b54d..a92482e6a0e 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -606,7 +606,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if let Ok(rest_snippet) = rest_snippet { - let sugg = if callee_expr.precedence().order() >= PREC_UNAMBIGUOUS { + let sugg = if callee_expr.precedence() >= PREC_UNAMBIGUOUS { vec![ (up_to_rcvr_span, "".to_string()), (rest_span, format!(".{}({rest_snippet}", segment.ident)), diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 483a8d1d9a9..0c3f21d540d 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -1107,7 +1107,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_prec = self.expr.precedence().order(); + let expr_prec = self.expr.precedence(); let needs_parens = expr_prec < rustc_ast::util::parser::PREC_UNAMBIGUOUS; let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 919e83724d7..f7355d11811 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2,7 +2,7 @@ use core::cmp::min; use core::iter; use hir::def_id::LocalDefId; -use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS}; +use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; use rustc_hir as hir; @@ -398,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // so we remove the user's `clone` call. { vec![(receiver_method.ident.span, conversion_method.name.to_string())] - } else if expr.precedence().order() < ExprPrecedence::MethodCall.order() { + } else if expr.precedence() < PREC_UNAMBIGUOUS { vec![ (expr.span.shrink_to_lo(), "(".to_string()), (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), @@ -1376,7 +1376,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let span = expr.span.find_oldest_ancestor_in_same_ctxt(); - let mut sugg = if expr.precedence().order() >= PREC_UNAMBIGUOUS { + let mut sugg = if expr.precedence() >= PREC_UNAMBIGUOUS { vec![(span.shrink_to_hi(), ".into()".to_owned())] } else { vec![ @@ -2985,7 +2985,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", ); - let close_paren = if expr.precedence().order() < PREC_UNAMBIGUOUS { + let close_paren = if expr.precedence() < PREC_UNAMBIGUOUS { sugg.push((expr.span.shrink_to_lo(), "(".to_string())); ")" } else { @@ -3010,7 +3010,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = src.trim_end_matches(&checked_ty.to_string()).len(); expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) }, - if expr.precedence().order() < PREC_UNAMBIGUOUS { + if expr.precedence() < PREC_UNAMBIGUOUS { // Readd `)` format!("{expected_ty})") } else { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 3546e5b0f04..007ea1c1f8f 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,11 +1,12 @@ use rustc_ast::mut_visit::{self, MutVisitor}; use rustc_ast::ptr::P; use rustc_ast::token::{self, BinOpToken, Delimiter, IdentIsRaw, Token}; +use rustc_ast::util::parser::AssocOp; use rustc_ast::visit::{self, Visitor}; use rustc_ast::{ - self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, ExprPrecedence, - LocalKind, MacCall, Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, - RangeSyntax, Stmt, StmtKind, + self as ast, Arm, AttrVec, BinOpKind, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, + Mutability, Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, + StmtKind, }; use rustc_ast_pretty::pprust; use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey}; @@ -458,7 +459,7 @@ impl<'a> Parser<'a> { .create_err(UnexpectedExpressionInPattern { span, is_bound, - expr_precedence: expr.precedence().order(), + expr_precedence: expr.precedence(), }) .stash(span, StashKey::ExprInPat) .unwrap(), @@ -545,7 +546,8 @@ impl<'a> Parser<'a> { let expr = match &err.args["expr_precedence"] { DiagArgValue::Number(expr_precedence) => { if *expr_precedence - <= ExprPrecedence::Binary(BinOpKind::Eq).order() as i32 + <= AssocOp::from_ast_binop(BinOpKind::Eq).precedence() + as i32 { format!("({expr})") } else { @@ -568,8 +570,9 @@ impl<'a> Parser<'a> { } Some(guard) => { // Are parentheses required around the old guard? - let wrap_guard = guard.precedence().order() - <= ExprPrecedence::Binary(BinOpKind::And).order(); + let wrap_guard = guard.precedence() + <= AssocOp::from_ast_binop(BinOpKind::And).precedence() + as i8; err.subdiagnostic( UnexpectedExpressionInPatternSugg::UpdateGuard { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index b167d7f2208..834606094ae 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -959,7 +959,7 @@ fn report<'tcx>( // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary. /* - expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX { Cow::Owned(format!("({expr_str})")) } else { expr_str @@ -999,7 +999,7 @@ fn report<'tcx>( Node::Expr(e) => match e.kind { ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false), ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))), - _ => (e.precedence().order(), false), + _ => (e.precedence(), false), }, _ => (0, false), }; @@ -1012,7 +1012,7 @@ fn report<'tcx>( ); let sugg = if !snip_is_macro - && (calls_field || expr.precedence().order() < precedence) + && (calls_field || expr.precedence() < precedence) && !has_enclosing_paren(&snip) && !is_in_tuple { @@ -1067,7 +1067,7 @@ fn report<'tcx>( let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app); let sugg = - if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { + if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) { format!("{prefix}({snip})") } else { format!("{prefix}{snip}") @@ -1154,7 +1154,7 @@ impl<'tcx> Dereferencing<'tcx> { }, Some(parent) if !parent.span.from_expansion() => { // Double reference might be needed at this point. - if parent.precedence().order() == PREC_UNAMBIGUOUS { + if parent.precedence() == PREC_UNAMBIGUOUS { // Parentheses would be needed here, don't lint. *outer_pat = None; } else { diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 70f76ced09a..35dc8e9aa4e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -84,7 +84,7 @@ pub(super) fn check<'tcx>( if !prefix.is_empty() && ( // Precedence of internal expression is less than or equal to precedence of `&expr`. - arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) + arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression) ) { arg_snip = format!("({arg_snip})").into(); diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index d38560998a5..9c6df4d8ac0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -117,7 +117,7 @@ where // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); - let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_UNAMBIGUOUS { + let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS { format!("({scrutinee_str})") } else { scrutinee_str.into() diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs index f84d9fadb85..a0ba2aaf552 100644 --- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs +++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs @@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) { { let mut applicability = Applicability::MachineApplicable; let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability); - let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) { + let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) { format!("-({snip})") } else { format!("-{snip}") diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index dc66fb28fa8..159404e130d 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr)); let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed)); let parent_expr = get_parent_expr(cx, expr); - let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence().order() > PREC_PREFIX); + let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX); if expr_ty == indexed_ty { if expr_ref_count > indexed_ref_count { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index fca332dba40..cad15b1e982 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,7 +1,7 @@ use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; -use rustc_ast::ExprPrecedence; +use rustc_ast::util::parser::AssocOp; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_hir_typeck::cast::check_cast; @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( }; if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id) - && parent.precedence().order() > ExprPrecedence::Cast.order() + && parent.precedence() > AssocOp::As.precedence() as i8 { sugg = format!("({sugg})"); } -- cgit 1.4.1-3-g733a5 From 7765f23ea14d16fb0182b475a0ec96891058ae24 Mon Sep 17 00:00:00 2001 From: Kornel Date: Sun, 17 Nov 2024 21:43:55 +0000 Subject: Diagnostics for let mut in item context --- compiler/rustc_parse/src/parser/item.rs | 33 ++++++++++++++++------ .../ui/parser/suggest-const-for-global-var.stderr | 7 ++++- .../ui/parser/suggest-static-for-global-var-mut.rs | 5 ++++ .../suggest-static-for-global-var-mut.stderr | 11 ++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 tests/ui/parser/suggest-static-for-global-var-mut.rs create mode 100644 tests/ui/parser/suggest-static-for-global-var-mut.stderr (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6b4e2d0f4e2..fddbf5896ad 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -77,18 +77,35 @@ impl<'a> Parser<'a> { if !self.eat(term) { let token_str = super::token_descr(&self.token); if !self.maybe_consume_incorrect_semicolon(items.last().map(|x| &**x)) { + let is_let = self.token.is_keyword(kw::Let); + let is_let_mut = is_let && self.look_ahead(1, |t| t.is_keyword(kw::Mut)); + let let_has_ident = is_let && !is_let_mut && self.is_kw_followed_by_ident(kw::Let); + let msg = format!("expected item, found {token_str}"); let mut err = self.dcx().struct_span_err(self.token.span, msg); - let span = self.token.span; - if self.is_kw_followed_by_ident(kw::Let) { - err.span_label( - span, - "consider using `const` or `static` instead of `let` for global variables", - ); + + let label = if is_let { + "`let` cannot be used for global variables" } else { - err.span_label(span, "expected item") - .note("for a full list of items that can appear in modules, see "); + "expected item" }; + err.span_label(self.token.span, label); + + if is_let { + if is_let_mut { + err.help("consider using `static` and a `Mutex` instead of `let mut`"); + } else if let_has_ident { + err.span_suggestion_short( + self.token.span, + "consider using `static` or `const` instead of `let`", + "static", + Applicability::MaybeIncorrect, + ); + } else { + err.help("consider using `static` or `const` instead of `let`"); + } + } + err.note("for a full list of items that can appear in modules, see "); return Err(err); } } diff --git a/tests/ui/parser/suggest-const-for-global-var.stderr b/tests/ui/parser/suggest-const-for-global-var.stderr index 235e621d882..6ac7fe8f092 100644 --- a/tests/ui/parser/suggest-const-for-global-var.stderr +++ b/tests/ui/parser/suggest-const-for-global-var.stderr @@ -2,7 +2,12 @@ error: expected item, found keyword `let` --> $DIR/suggest-const-for-global-var.rs:1:1 | LL | let X: i32 = 12; - | ^^^ consider using `const` or `static` instead of `let` for global variables + | ^^^ + | | + | `let` cannot be used for global variables + | help: consider using `static` or `const` instead of `let` + | + = note: for a full list of items that can appear in modules, see error: aborting due to 1 previous error diff --git a/tests/ui/parser/suggest-static-for-global-var-mut.rs b/tests/ui/parser/suggest-static-for-global-var-mut.rs new file mode 100644 index 00000000000..c63b09bb7a7 --- /dev/null +++ b/tests/ui/parser/suggest-static-for-global-var-mut.rs @@ -0,0 +1,5 @@ +let mut _data = vec![1,2,3]; +//~^ ERROR expected item, found keyword `let` + +fn main() { +} diff --git a/tests/ui/parser/suggest-static-for-global-var-mut.stderr b/tests/ui/parser/suggest-static-for-global-var-mut.stderr new file mode 100644 index 00000000000..4b00d1a24f3 --- /dev/null +++ b/tests/ui/parser/suggest-static-for-global-var-mut.stderr @@ -0,0 +1,11 @@ +error: expected item, found keyword `let` + --> $DIR/suggest-static-for-global-var-mut.rs:1:1 + | +LL | let mut _data = vec![1,2,3]; + | ^^^ `let` cannot be used for global variables + | + = help: consider using `static` and a `Mutex` instead of `let mut` + = note: for a full list of items that can appear in modules, see + +error: aborting due to 1 previous error + -- cgit 1.4.1-3-g733a5 From afe238f66fc9348b8c4a1daacefb93c0ec36296a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 17 Apr 2024 09:59:27 +1000 Subject: Introduce `InvisibleOrigin` on invisible delimiters. It's not used meaningfully yet, but will be needed to get rid of interpolated tokens. --- compiler/rustc_ast/src/attr/mod.rs | 4 +- compiler/rustc_ast/src/token.rs | 114 ++++++++++++++++++++++-- compiler/rustc_ast/src/tokenstream.rs | 6 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 5 +- compiler/rustc_expand/src/mbe/macro_rules.rs | 2 +- compiler/rustc_expand/src/mbe/quoted.rs | 11 +-- compiler/rustc_expand/src/proc_macro_server.rs | 4 +- compiler/rustc_parse/src/parser/attr_wrapper.rs | 4 +- compiler/rustc_parse/src/parser/mod.rs | 12 +-- src/tools/rustfmt/src/macros.rs | 2 +- 10 files changed, 133 insertions(+), 31 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 54e826585d2..888b13efa31 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -457,7 +457,7 @@ impl MetaItemKind { tokens: &mut impl Iterator, ) -> Option { match tokens.next() { - Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => { + Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees()) } Some(TokenTree::Token(token, _)) => { @@ -605,7 +605,7 @@ impl MetaItemInner { tokens.next(); return Some(MetaItemInner::Lit(lit)); } - Some(TokenTree::Delimited(.., Delimiter::Invisible, inner_tokens)) => { + Some(TokenTree::Delimited(.., Delimiter::Invisible(_), inner_tokens)) => { tokens.next(); return MetaItemInner::from_tokens(&mut inner_tokens.trees().peekable()); } diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 3b9edef0615..592b56cc08f 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -42,11 +42,86 @@ pub enum BinOpToken { Shr, } +// This type must not implement `Hash` due to the unusual `PartialEq` impl below. +#[derive(Copy, Clone, Debug, Encodable, Decodable, HashStable_Generic)] +pub enum InvisibleOrigin { + // From the expansion of a metavariable in a declarative macro. + MetaVar(MetaVarKind), + + // Converted from `proc_macro::Delimiter` in + // `proc_macro::Delimiter::to_internal`, i.e. returned by a proc macro. + ProcMacro, + + // Converted from `TokenKind::Interpolated` in + // `TokenStream::flatten_token`. Treated similarly to `ProcMacro`. + FlattenToken, +} + +impl PartialEq for InvisibleOrigin { + #[inline] + fn eq(&self, _other: &InvisibleOrigin) -> bool { + // When we had AST-based nonterminals we couldn't compare them, and the + // old `Nonterminal` type had an `eq` that always returned false, + // resulting in this restriction: + // https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment + // This `eq` emulates that behaviour. We could consider lifting this + // restriction now but there are still cases involving invisible + // delimiters that make it harder than it first appears. + false + } +} + +/// Annoyingly similar to `NonterminalKind`, but the slight differences are important. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] +pub enum MetaVarKind { + Item, + Block, + Stmt, + Pat(NtPatKind), + Expr { + kind: NtExprKind, + // This field is needed for `Token::can_begin_literal_maybe_minus`. + can_begin_literal_maybe_minus: bool, + // This field is needed for `Token::can_begin_string_literal`. + can_begin_string_literal: bool, + }, + Ty, + Ident, + Lifetime, + Literal, + Meta, + Path, + Vis, + TT, +} + +impl fmt::Display for MetaVarKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let sym = match self { + MetaVarKind::Item => sym::item, + MetaVarKind::Block => sym::block, + MetaVarKind::Stmt => sym::stmt, + MetaVarKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat, + MetaVarKind::Pat(PatParam { inferred: false }) => sym::pat_param, + MetaVarKind::Expr { kind: Expr2021 { inferred: true } | Expr, .. } => sym::expr, + MetaVarKind::Expr { kind: Expr2021 { inferred: false }, .. } => sym::expr_2021, + MetaVarKind::Ty => sym::ty, + MetaVarKind::Ident => sym::ident, + MetaVarKind::Lifetime => sym::lifetime, + MetaVarKind::Literal => sym::literal, + MetaVarKind::Meta => sym::meta, + MetaVarKind::Path => sym::path, + MetaVarKind::Vis => sym::vis, + MetaVarKind::TT => sym::tt, + }; + write!(f, "{sym}") + } +} + /// Describes how a sequence of token trees is delimited. /// Cannot use `proc_macro::Delimiter` directly because this /// structure should implement some additional traits. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[derive(Encodable, Decodable, Hash, HashStable_Generic)] +#[derive(Copy, Clone, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum Delimiter { /// `( ... )` Parenthesis, @@ -59,7 +134,34 @@ pub enum Delimiter { /// "macro variable" `$var`. It is important to preserve operator priorities in cases like /// `$var * 3` where `$var` is `1 + 2`. /// Invisible delimiters might not survive roundtrip of a token stream through a string. - Invisible, + Invisible(InvisibleOrigin), +} + +impl Delimiter { + // Should the parser skip these delimiters? Only happens for certain kinds + // of invisible delimiters. Ideally this function will eventually disappear + // and no invisible delimiters will be skipped. + #[inline] + pub fn skip(&self) -> bool { + match self { + Delimiter::Parenthesis | Delimiter::Bracket | Delimiter::Brace => false, + Delimiter::Invisible(InvisibleOrigin::MetaVar(_)) => false, + Delimiter::Invisible(InvisibleOrigin::FlattenToken | InvisibleOrigin::ProcMacro) => { + true + } + } + } + + // This exists because `InvisibleOrigin`s should be compared. It is only used for assertions. + pub fn eq_ignoring_invisible_origin(&self, other: &Delimiter) -> bool { + match (self, other) { + (Delimiter::Parenthesis, Delimiter::Parenthesis) => true, + (Delimiter::Brace, Delimiter::Brace) => true, + (Delimiter::Bracket, Delimiter::Bracket) => true, + (Delimiter::Invisible(_), Delimiter::Invisible(_)) => true, + _ => false, + } + } } // Note that the suffix is *not* considered when deciding the `LitKind` in this @@ -896,7 +998,7 @@ impl PartialEq for Token { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NtPatKind { // Matches or-patterns. Was written using `pat` in edition 2021 or later. PatWithOr, @@ -906,7 +1008,7 @@ pub enum NtPatKind { PatParam { inferred: bool }, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NtExprKind { // Matches expressions using the post-edition 2024. Was written using // `expr` in edition 2024 or later. @@ -933,7 +1035,7 @@ pub enum Nonterminal { NtVis(P), } -#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] pub enum NonterminalKind { Item, Block, diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index 0b4bfc0b36a..c6b6addc946 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -24,7 +24,7 @@ use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use crate::ast::{AttrStyle, StmtKind}; use crate::ast_traits::{HasAttrs, HasTokens}; -use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind}; +use crate::token::{self, Delimiter, InvisibleOrigin, Nonterminal, Token, TokenKind}; use crate::{AttrVec, Attribute}; /// Part of a `TokenStream`. @@ -484,13 +484,13 @@ impl TokenStream { token::NtLifetime(ident, is_raw) => TokenTree::Delimited( DelimSpan::from_single(token.span), DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible, + Delimiter::Invisible(InvisibleOrigin::FlattenToken), TokenStream::token_alone(token::Lifetime(ident.name, is_raw), ident.span), ), token::Interpolated(ref nt) => TokenTree::Delimited( DelimSpan::from_single(token.span), DelimSpacing::new(Spacing::JointHidden, spacing), - Delimiter::Invisible, + Delimiter::Invisible(InvisibleOrigin::FlattenToken), TokenStream::from_nonterminal_ast(&nt).flattened(), ), _ => TokenTree::Token(token.clone(), spacing), diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index de9f5187be7..d7c531f3760 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -942,9 +942,8 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere token::CloseDelim(Delimiter::Bracket) => "]".into(), token::OpenDelim(Delimiter::Brace) => "{".into(), token::CloseDelim(Delimiter::Brace) => "}".into(), - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => { - "".into() - } + token::OpenDelim(Delimiter::Invisible(_)) + | token::CloseDelim(Delimiter::Invisible(_)) => "".into(), token::Pound => "#".into(), token::Dollar => "$".into(), token::Question => "?".into(), diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index fcc90c3ce0d..a373c753cc1 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -693,7 +693,7 @@ fn has_compile_error_macro(rhs: &mbe::TokenTree) -> bool { && let mbe::TokenTree::Token(bang) = bang && let TokenKind::Not = bang.kind && let mbe::TokenTree::Delimited(.., del) = args - && del.delim != Delimiter::Invisible + && !del.delim.skip() { true } else { diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index 1345f06d5ac..36094707fac 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -165,11 +165,12 @@ fn parse_tree<'a>( // during parsing. let mut next = outer_trees.next(); let mut trees: Box>; - if let Some(tokenstream::TokenTree::Delimited(.., Delimiter::Invisible, tts)) = next { - trees = Box::new(tts.trees()); - next = trees.next(); - } else { - trees = Box::new(outer_trees); + match next { + Some(tokenstream::TokenTree::Delimited(.., delim, tts)) if delim.skip() => { + trees = Box::new(tts.trees()); + next = trees.next(); + } + _ => trees = Box::new(outer_trees), } match next { diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 0dc35618ff8..263df235b3e 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -38,7 +38,7 @@ impl FromInternal for Delimiter { token::Delimiter::Parenthesis => Delimiter::Parenthesis, token::Delimiter::Brace => Delimiter::Brace, token::Delimiter::Bracket => Delimiter::Bracket, - token::Delimiter::Invisible => Delimiter::None, + token::Delimiter::Invisible(_) => Delimiter::None, } } } @@ -49,7 +49,7 @@ impl ToInternal for Delimiter { Delimiter::Parenthesis => token::Delimiter::Parenthesis, Delimiter::Brace => token::Delimiter::Brace, Delimiter::Bracket => token::Delimiter::Bracket, - Delimiter::None => token::Delimiter::Invisible, + Delimiter::None => token::Delimiter::Invisible(token::InvisibleOrigin::ProcMacro), } } } diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index c85d0bd05cb..434f71beac2 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -510,8 +510,8 @@ fn make_attr_token_stream( FlatToken::Token((Token { kind: TokenKind::CloseDelim(delim), span }, spacing)) => { let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); - assert_eq!( - open_delim, delim, + assert!( + open_delim.eq_ignoring_invisible_origin(&delim), "Mismatched open/close delims: open={open_delim:?} close={span:?}" ); let dspan = DelimSpan::from_pair(open_sp, span); diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 042ee96bbe8..226f29f9900 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -317,7 +317,7 @@ impl TokenCursor { spacing, delim, )); - if delim != Delimiter::Invisible { + if !delim.skip() { return (Token::new(token::OpenDelim(delim), sp.open), spacing.open); } // No open delimiter to return; continue on to the next iteration. @@ -326,7 +326,7 @@ impl TokenCursor { } else if let Some((tree_cursor, span, spacing, delim)) = self.stack.pop() { // We have exhausted this token stream. Move back to its parent token stream. self.tree_cursor = tree_cursor; - if delim != Delimiter::Invisible { + if !delim.skip() { return (Token::new(token::CloseDelim(delim), span.close), spacing.close); } // No close delimiter to return; continue on to the next iteration. @@ -1163,7 +1163,7 @@ impl<'a> Parser<'a> { } debug_assert!(!matches!( next.0.kind, - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) + token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip() )); self.inlined_bump_with(next) } @@ -1187,7 +1187,7 @@ impl<'a> Parser<'a> { match tree { TokenTree::Token(token, _) => return looker(token), &TokenTree::Delimited(dspan, _, delim, _) => { - if delim != Delimiter::Invisible { + if !delim.skip() { return looker(&Token::new(token::OpenDelim(delim), dspan.open)); } } @@ -1197,7 +1197,7 @@ impl<'a> Parser<'a> { // The tree cursor lookahead went (one) past the end of the // current token tree. Try to return a close delimiter. if let Some(&(_, span, _, delim)) = self.token_cursor.stack.last() - && delim != Delimiter::Invisible + && !delim.skip() { // We are not in the outermost token stream, so we have // delimiters. Also, those delimiters are not skipped. @@ -1216,7 +1216,7 @@ impl<'a> Parser<'a> { token = cursor.next().0; if matches!( token.kind, - token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) + token::OpenDelim(delim) | token::CloseDelim(delim) if delim.skip() ) { continue; } diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs index 5a35e115d8f..4083d9398f6 100644 --- a/src/tools/rustfmt/src/macros.rs +++ b/src/tools/rustfmt/src/macros.rs @@ -620,7 +620,7 @@ fn delim_token_to_str( ("{ ", " }") } } - Delimiter::Invisible => unreachable!(), + Delimiter::Invisible(_) => unreachable!(), }; if use_multiple_lines { let indent_str = shape.indent.to_string_with_newline(context.config); -- cgit 1.4.1-3-g733a5 From cfafa9380b446b74708986a344172ec48e295740 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 18 Apr 2024 08:58:06 +1000 Subject: Add metavariables to `TokenDescription`. Pasted metavariables are wrapped in invisible delimiters, which pretty-print as empty strings, and changing that can break some proc macros. But error messages saying "expected identifer, found ``" are bad. So this commit adds support for metavariables in `TokenDescription` so they print as "metavariable" in error messages, instead of "``". It's not used meaningfully yet, but will be needed to get rid of interpolated tokens. --- compiler/rustc_parse/messages.ftl | 7 ++++++ compiler/rustc_parse/src/errors.rs | 28 +++++++++++++++++++++-- compiler/rustc_parse/src/parser/mod.rs | 41 +++++++++++++++++++++------------- 3 files changed, 59 insertions(+), 17 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ef259703f0c..cafd4b6dca2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -216,6 +216,9 @@ parse_expected_identifier_found_doc_comment = expected identifier, found doc com parse_expected_identifier_found_doc_comment_str = expected identifier, found doc comment `{$token}` parse_expected_identifier_found_keyword = expected identifier, found keyword parse_expected_identifier_found_keyword_str = expected identifier, found keyword `{$token}` +parse_expected_identifier_found_metavar = expected identifier, found metavariable +# This one deliberately doesn't print a token. +parse_expected_identifier_found_metavar_str = expected identifier, found metavariable parse_expected_identifier_found_reserved_identifier = expected identifier, found reserved identifier parse_expected_identifier_found_reserved_identifier_str = expected identifier, found reserved identifier `{$token}` parse_expected_identifier_found_reserved_keyword = expected identifier, found reserved keyword @@ -227,6 +230,8 @@ parse_expected_mut_or_const_in_raw_pointer_type = expected `mut` or `const` keyw parse_expected_semi_found_doc_comment_str = expected `;`, found doc comment `{$token}` parse_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}` +# This one deliberately doesn't print a token. +parse_expected_semi_found_metavar_str = expected `;`, found metavariable parse_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}` parse_expected_semi_found_reserved_keyword_str = expected `;`, found reserved keyword `{$token}` parse_expected_semi_found_str = expected `;`, found `{$token}` @@ -864,6 +869,8 @@ parse_unexpected_token_after_not_logical = use `!` to perform logical negation parse_unexpected_token_after_struct_name = expected `where`, `{"{"}`, `(`, or `;` after struct name parse_unexpected_token_after_struct_name_found_doc_comment = expected `where`, `{"{"}`, `(`, or `;` after struct name, found doc comment `{$token}` parse_unexpected_token_after_struct_name_found_keyword = expected `where`, `{"{"}`, `(`, or `;` after struct name, found keyword `{$token}` +# This one deliberately doesn't print a token. +parse_unexpected_token_after_struct_name_found_metavar = expected `where`, `{"{"}`, `(`, or `;` after struct name, found metavar parse_unexpected_token_after_struct_name_found_other = expected `where`, `{"{"}`, `(`, or `;` after struct name, found `{$token}` parse_unexpected_token_after_struct_name_found_reserved_identifier = expected `where`, `{"{"}`, `(`, or `;` after struct name, found reserved identifier `{$token}` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 37eb463cba6..9bdb99dc000 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1086,6 +1086,8 @@ pub(crate) enum ExpectedIdentifierFound { ReservedKeyword(#[primary_span] Span), #[label(parse_expected_identifier_found_doc_comment)] DocComment(#[primary_span] Span), + #[label(parse_expected_identifier_found_metavar)] + MetaVar(#[primary_span] Span), #[label(parse_expected_identifier)] Other(#[primary_span] Span), } @@ -1099,6 +1101,7 @@ impl ExpectedIdentifierFound { Some(TokenDescription::Keyword) => ExpectedIdentifierFound::Keyword, Some(TokenDescription::ReservedKeyword) => ExpectedIdentifierFound::ReservedKeyword, Some(TokenDescription::DocComment) => ExpectedIdentifierFound::DocComment, + Some(TokenDescription::MetaVar(_)) => ExpectedIdentifierFound::MetaVar, None => ExpectedIdentifierFound::Other, })(span) } @@ -1117,6 +1120,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); + let mut add_token = true; let mut diag = Diag::new(dcx, level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { fluent::parse_expected_identifier_found_reserved_identifier_str @@ -1128,10 +1132,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier { Some(TokenDescription::DocComment) => { fluent::parse_expected_identifier_found_doc_comment_str } + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_identifier_found_metavar_str + } None => fluent::parse_expected_identifier_found_str, }); diag.span(self.span); - diag.arg("token", self.token); + if add_token { + diag.arg("token", self.token); + } if let Some(sugg) = self.suggest_raw { sugg.add_to_diag(&mut diag); @@ -1171,6 +1181,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { let token_descr = TokenDescription::from_token(&self.token); + let mut add_token = true; let mut diag = Diag::new(dcx, level, match token_descr { Some(TokenDescription::ReservedIdentifier) => { fluent::parse_expected_semi_found_reserved_identifier_str @@ -1180,10 +1191,16 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi { fluent::parse_expected_semi_found_reserved_keyword_str } Some(TokenDescription::DocComment) => fluent::parse_expected_semi_found_doc_comment_str, + Some(TokenDescription::MetaVar(_)) => { + add_token = false; + fluent::parse_expected_semi_found_metavar_str + } None => fluent::parse_expected_semi_found_str, }); diag.span(self.span); - diag.arg("token", self.token); + if add_token { + diag.arg("token", self.token); + } if let Some(unexpected_token_label) = self.unexpected_token_label { diag.span_label(unexpected_token_label, fluent::parse_label_unexpected_token); @@ -1925,6 +1942,12 @@ pub(crate) enum UnexpectedTokenAfterStructName { span: Span, token: Token, }, + #[diag(parse_unexpected_token_after_struct_name_found_metavar)] + MetaVar { + #[primary_span] + #[label(parse_unexpected_token_after_struct_name)] + span: Span, + }, #[diag(parse_unexpected_token_after_struct_name_found_other)] Other { #[primary_span] @@ -1941,6 +1964,7 @@ impl UnexpectedTokenAfterStructName { Some(TokenDescription::Keyword) => Self::Keyword { span, token }, Some(TokenDescription::ReservedKeyword) => Self::ReservedKeyword { span, token }, Some(TokenDescription::DocComment) => Self::DocComment { span, token }, + Some(TokenDescription::MetaVar(_)) => Self::MetaVar { span }, None => Self::Other { span, token }, } } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 226f29f9900..0ed8d152d2d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -21,7 +21,9 @@ pub(crate) use item::FnParseMode; pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma}; use path::PathStyle; use rustc_ast::ptr::P; -use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind}; +use rustc_ast::token::{ + self, Delimiter, IdentIsRaw, InvisibleOrigin, MetaVarKind, Nonterminal, Token, TokenKind, +}; use rustc_ast::tokenstream::{ AttrsTarget, DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree, TokenTreeCursor, }; @@ -410,6 +412,12 @@ pub(super) enum TokenDescription { Keyword, ReservedKeyword, DocComment, + + // Expanded metavariables are wrapped in invisible delimiters which aren't + // pretty-printed. In error messages we must handle these specially + // otherwise we get confusing things in messages like "expected `(`, found + // ``". It's better to say e.g. "expected `(`, found type metavariable". + MetaVar(MetaVarKind), } impl TokenDescription { @@ -419,26 +427,29 @@ impl TokenDescription { _ if token.is_used_keyword() => Some(TokenDescription::Keyword), _ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword), token::DocComment(..) => Some(TokenDescription::DocComment), + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => { + Some(TokenDescription::MetaVar(kind)) + } _ => None, } } } pub fn token_descr(token: &Token) -> String { - let name = pprust::token_to_string(token).to_string(); - - let kind = match (TokenDescription::from_token(token), &token.kind) { - (Some(TokenDescription::ReservedIdentifier), _) => Some("reserved identifier"), - (Some(TokenDescription::Keyword), _) => Some("keyword"), - (Some(TokenDescription::ReservedKeyword), _) => Some("reserved keyword"), - (Some(TokenDescription::DocComment), _) => Some("doc comment"), - (None, TokenKind::NtIdent(..)) => Some("identifier"), - (None, TokenKind::NtLifetime(..)) => Some("lifetime"), - (None, TokenKind::Interpolated(node)) => Some(node.descr()), - (None, _) => None, - }; - - if let Some(kind) = kind { format!("{kind} `{name}`") } else { format!("`{name}`") } + let s = pprust::token_to_string(token).to_string(); + + match (TokenDescription::from_token(token), &token.kind) { + (Some(TokenDescription::ReservedIdentifier), _) => format!("reserved identifier `{s}`"), + (Some(TokenDescription::Keyword), _) => format!("keyword `{s}`"), + (Some(TokenDescription::ReservedKeyword), _) => format!("reserved keyword `{s}`"), + (Some(TokenDescription::DocComment), _) => format!("doc comment `{s}`"), + // Deliberately doesn't print `s`, which is empty. + (Some(TokenDescription::MetaVar(kind)), _) => format!("`{kind}` metavariable"), + (None, TokenKind::NtIdent(..)) => format!("identifier `{s}`"), + (None, TokenKind::NtLifetime(..)) => format!("lifetime `{s}`"), + (None, TokenKind::Interpolated(node)) => format!("{} `{s}`", node.descr()), + (None, _) => format!("`{s}`"), + } } impl<'a> Parser<'a> { -- cgit 1.4.1-3-g733a5 From cee88f7a3f1c5bc284d0c6a87fb1d5535ebb3af8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 17 Apr 2024 09:37:00 +1000 Subject: Prepare for invisible delimiters. Current places where `Interpolated` is used are going to change to instead use invisible delimiters. This prepares for that. - It adds invisible delimiter cases to the `can_begin_*`/`may_be_*` methods and the `failed_to_match_macro` that are equivalent to the existing `Interpolated` cases. - It adds panics/asserts in some places where invisible delimiters should never occur. - In `Parser::parse_struct_fields` it excludes an ident + invisible delimiter from special consideration in an error message, because that's quite different to an ident + paren/brace/bracket. --- compiler/rustc_ast/src/token.rs | 38 ++++++++++++++++++-- compiler/rustc_expand/src/mbe/diagnostics.rs | 6 ++-- compiler/rustc_parse/src/lexer/tokentrees.rs | 16 ++++++--- compiler/rustc_parse/src/parser/expr.rs | 12 +++++-- compiler/rustc_parse/src/parser/nonterminal.rs | 50 +++++++++++++++++++++++--- 5 files changed, 108 insertions(+), 14 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index 592b56cc08f..678f43e3511 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -598,10 +598,11 @@ impl Token { /// **NB**: Take care when modifying this function, since it will change /// the stable set of tokens that are allowed to match an expr nonterminal. pub fn can_begin_expr(&self) -> bool { + use Delimiter::*; match self.uninterpolate().kind { Ident(name, is_raw) => ident_can_begin_expr(name, self.span, is_raw), // value name or keyword - OpenDelim(..) | // tuple, array or block + OpenDelim(Parenthesis | Brace | Bracket) | // tuple, array or block Literal(..) | // literal Not | // operator not BinOp(Minus) | // unary minus @@ -612,7 +613,7 @@ impl Token { // DotDotDot is no longer supported, but we need some way to display the error DotDot | DotDotDot | DotDotEq | // range notation Lt | BinOp(Shl) | // associated path - PathSep | // global path + PathSep | // global path Lifetime(..) | // labeled loop Pound => true, // expression attributes Interpolated(ref nt) => @@ -622,6 +623,12 @@ impl Token { NtLiteral(..) | NtPath(..) ), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Block | + MetaVarKind::Expr { .. } | + MetaVarKind::Literal | + MetaVarKind::Path + ))) => true, _ => false, } } @@ -655,6 +662,14 @@ impl Token { | NtPath(..) | NtTy(..) ), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Expr { .. } | + MetaVarKind::Literal | + MetaVarKind::Meta | + MetaVarKind::Pat(_) | + MetaVarKind::Path | + MetaVarKind::Ty + ))) => true, _ => false, } } @@ -675,6 +690,10 @@ impl Token { Lt | BinOp(Shl) | // associated path PathSep => true, // global path Interpolated(ref nt) => matches!(&**nt, NtTy(..) | NtPath(..)), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Ty | + MetaVarKind::Path + ))) => true, // For anonymous structs or unions, which only appear in specific positions // (type of struct fields or union fields), we don't consider them as regular types _ => false, @@ -687,6 +706,9 @@ impl Token { OpenDelim(Delimiter::Brace) | Literal(..) | BinOp(Minus) => true, Ident(name, IdentIsRaw::No) if name.is_bool_lit() => true, Interpolated(ref nt) => matches!(&**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)), + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar( + MetaVarKind::Expr { .. } | MetaVarKind::Block | MetaVarKind::Literal, + ))) => true, _ => false, } } @@ -743,6 +765,13 @@ impl Token { }, _ => false, }, + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + MetaVarKind::Literal => true, + MetaVarKind::Expr { can_begin_literal_maybe_minus, .. } => { + can_begin_literal_maybe_minus + } + _ => false, + }, _ => false, } } @@ -758,6 +787,11 @@ impl Token { }, _ => false, }, + OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(mv_kind))) => match mv_kind { + MetaVarKind::Literal => true, + MetaVarKind::Expr { can_begin_string_literal, .. } => can_begin_string_literal, + _ => false, + }, _ => false, } } diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index ffcce1e52f6..77b8d228922 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use rustc_ast::token::{self, Token, TokenKind}; +use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::TokenStream; use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage}; use rustc_macros::Subdiagnostic; @@ -68,7 +68,9 @@ pub(super) fn failed_to_match_macro( if let MatcherLoc::Token { token: expected_token } = &remaining_matcher && (matches!(expected_token.kind, TokenKind::Interpolated(_)) - || matches!(token.kind, TokenKind::Interpolated(_))) + || matches!(token.kind, TokenKind::Interpolated(_)) + || matches!(expected_token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_))) + || matches!(token.kind, TokenKind::OpenDelim(Delimiter::Invisible(_)))) { err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens"); err.note("see for more information"); diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs index d35c9c7bae7..7b21ffacc84 100644 --- a/compiler/rustc_parse/src/lexer/tokentrees.rs +++ b/compiler/rustc_parse/src/lexer/tokentrees.rs @@ -43,11 +43,19 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> { let mut buf = Vec::new(); loop { match self.token.kind { - token::OpenDelim(delim) => buf.push(match self.lex_token_tree_open_delim(delim) { - Ok(val) => val, - Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), - }), + token::OpenDelim(delim) => { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); + buf.push(match self.lex_token_tree_open_delim(delim) { + Ok(val) => val, + Err(errs) => return (open_spacing, TokenStream::new(buf), Err(errs)), + }) + } token::CloseDelim(delim) => { + // Invisible delimiters cannot occur here because `TokenTreesReader` parses + // code directly from strings, with no macro expansion involved. + debug_assert!(!matches!(delim, Delimiter::Invisible(_))); return ( open_spacing, TokenStream::new(buf), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 0012db471ef..b110d891c84 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3591,11 +3591,19 @@ impl<'a> Parser<'a> { && !self.token.is_reserved_ident() && self.look_ahead(1, |t| { AssocOp::from_token(t).is_some() - || matches!(t.kind, token::OpenDelim(_)) + || matches!( + t.kind, + token::OpenDelim( + Delimiter::Parenthesis + | Delimiter::Bracket + | Delimiter::Brace + ) + ) || *t == token::Dot }) { - // Looks like they tried to write a shorthand, complex expression. + // Looks like they tried to write a shorthand, complex expression, + // E.g.: `n + m`, `f(a)`, `a[i]`, `S { x: 3 }`, or `x.y`. e.span_suggestion_verbose( self.token.span.shrink_to_lo(), "try naming a field", diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 43c3de90d9d..8fb6f85d0dd 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -3,7 +3,9 @@ use rustc_ast::ptr::P; use rustc_ast::token::Nonterminal::*; use rustc_ast::token::NtExprKind::*; use rustc_ast::token::NtPatKind::*; -use rustc_ast::token::{self, Delimiter, NonterminalKind, Token}; +use rustc_ast::token::{ + self, Delimiter, InvisibleOrigin, MetaVarKind, Nonterminal, NonterminalKind, Token, +}; use rustc_ast_pretty::pprust; use rustc_data_structures::sync::Lrc; use rustc_errors::PResult; @@ -22,7 +24,28 @@ impl<'a> Parser<'a> { #[inline] pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool { /// Checks whether the non-terminal may contain a single (non-keyword) identifier. - fn may_be_ident(nt: &token::Nonterminal) -> bool { + fn may_be_ident(kind: MetaVarKind) -> bool { + match kind { + MetaVarKind::Stmt + | MetaVarKind::Pat(_) + | MetaVarKind::Expr { .. } + | MetaVarKind::Ty + | MetaVarKind::Literal // `true`, `false` + | MetaVarKind::Meta + | MetaVarKind::Path => true, + + MetaVarKind::Item + | MetaVarKind::Block + | MetaVarKind::Vis => false, + + MetaVarKind::Ident + | MetaVarKind::Lifetime + | MetaVarKind::TT => unreachable!(), + } + } + + /// Old variant of `may_be_ident`. Being phased out. + fn nt_may_be_ident(nt: &Nonterminal) -> bool { match nt { NtStmt(_) | NtPat(_) @@ -69,7 +92,8 @@ impl<'a> Parser<'a> { | token::Ident(..) | token::NtIdent(..) | token::NtLifetime(..) - | token::Interpolated(_) => true, + | token::Interpolated(_) + | token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(_))) => true, _ => token.can_begin_type(), }, NonterminalKind::Block => match &token.kind { @@ -79,11 +103,29 @@ impl<'a> Parser<'a> { NtBlock(_) | NtStmt(_) | NtExpr(_) | NtLiteral(_) => true, NtItem(_) | NtPat(_) | NtTy(_) | NtMeta(_) | NtPath(_) | NtVis(_) => false, }, + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(k))) => match k { + MetaVarKind::Block + | MetaVarKind::Stmt + | MetaVarKind::Expr { .. } + | MetaVarKind::Literal => true, + MetaVarKind::Item + | MetaVarKind::Pat(_) + | MetaVarKind::Ty + | MetaVarKind::Meta + | MetaVarKind::Path + | MetaVarKind::Vis => false, + MetaVarKind::Lifetime | MetaVarKind::Ident | MetaVarKind::TT => { + unreachable!() + } + }, _ => false, }, NonterminalKind::Path | NonterminalKind::Meta => match &token.kind { token::PathSep | token::Ident(..) | token::NtIdent(..) => true, - token::Interpolated(nt) => may_be_ident(nt), + token::Interpolated(nt) => nt_may_be_ident(nt), + token::OpenDelim(Delimiter::Invisible(InvisibleOrigin::MetaVar(kind))) => { + may_be_ident(*kind) + } _ => false, }, NonterminalKind::Pat(pat_kind) => token.can_begin_pattern(pat_kind), -- cgit 1.4.1-3-g733a5 From 03159d4bff6de12f26daee0f6c6890e71407a389 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 20 Sep 2024 13:02:14 +1000 Subject: Remove `ErrorGuaranteed` retval from `error_unexpected_after_dot`. It was added in #130349, but it's not used meaningfully, and causes difficulties for Nonterminal removal in #124141. --- compiler/rustc_parse/src/parser/expr.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index b110d891c84..aa5e9586daf 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -49,7 +49,7 @@ pub(super) enum DestructuredFloat { /// 1.2 | 1.2e3 MiddleDot(Symbol, Span, Span, Symbol, Span), /// Invalid - Error(ErrorGuaranteed), + Error, } impl<'a> Parser<'a> { @@ -1005,7 +1005,7 @@ impl<'a> Parser<'a> { self.mk_expr_tuple_field_access(lo, ident1_span, base, sym1, None); self.mk_expr_tuple_field_access(lo, ident2_span, base1, sym2, suffix) } - DestructuredFloat::Error(_) => base, + DestructuredFloat::Error => base, }) } _ => { @@ -1015,7 +1015,7 @@ impl<'a> Parser<'a> { } } - fn error_unexpected_after_dot(&self) -> ErrorGuaranteed { + fn error_unexpected_after_dot(&self) { let actual = pprust::token_to_string(&self.token); let span = self.token.span; let sm = self.psess.source_map(); @@ -1025,7 +1025,7 @@ impl<'a> Parser<'a> { } _ => (span, actual), }; - self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual }) + self.dcx().emit_err(errors::UnexpectedTokenAfterDot { span, actual }); } /// We need an identifier or integer, but the next token is a float. @@ -1111,8 +1111,8 @@ impl<'a> Parser<'a> { // 1.2e+3 | 1.2e-3 [IdentLike(_), Punct('.'), IdentLike(_), Punct('+' | '-'), IdentLike(_)] => { // See the FIXME about `TokenCursor` above. - let guar = self.error_unexpected_after_dot(); - DestructuredFloat::Error(guar) + self.error_unexpected_after_dot(); + DestructuredFloat::Error } _ => panic!("unexpected components in a float token: {components:?}"), } @@ -1178,7 +1178,7 @@ impl<'a> Parser<'a> { fields.insert(start_idx, Ident::new(symbol2, span2)); fields.insert(start_idx, Ident::new(symbol1, span1)); } - DestructuredFloat::Error(_) => { + DestructuredFloat::Error => { trailing_dot = None; fields.insert(start_idx, Ident::new(symbol, self.prev_token.span)); } -- cgit 1.4.1-3-g733a5 From 9022bb2d6f49c328ef463d95caa4959fac63c478 Mon Sep 17 00:00:00 2001 From: Luca Versari Date: Sun, 27 Oct 2024 01:35:33 +0200 Subject: Implement the unsafe-fields RFC. Co-Authored-By: Jacob Pratt --- compiler/rustc_ast/src/ast.rs | 1 + compiler/rustc_ast/src/mut_visit.rs | 3 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/item.rs | 1 + compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_expand/src/placeholders.rs | 3 +- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_hir_analysis/messages.ftl | 7 ++ compiler/rustc_hir_analysis/src/check/check.rs | 104 ++++++++++++++------ compiler/rustc_hir_analysis/src/collect.rs | 1 + compiler/rustc_hir_analysis/src/errors.rs | 23 +++++ compiler/rustc_metadata/src/rmeta/decoder.rs | 6 ++ compiler/rustc_metadata/src/rmeta/encoder.rs | 4 + compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_metadata/src/rmeta/table.rs | 7 ++ compiler/rustc_middle/src/ty/mod.rs | 17 +++- compiler/rustc_middle/src/ty/parameterized.rs | 1 + compiler/rustc_mir_build/messages.ftl | 30 ++++++ compiler/rustc_mir_build/src/check_unsafety.rs | 107 +++++++++++++++----- compiler/rustc_mir_build/src/errors.rs | 67 +++++++++++++ compiler/rustc_parse/src/parser/item.rs | 22 ++++- compiler/rustc_span/src/symbol.rs | 1 + src/tools/rustfmt/src/items.rs | 6 +- src/tools/rustfmt/tests/source/unsafe-field.rs | 16 +++ src/tools/rustfmt/tests/target/unsafe-field.rs | 11 +++ tests/ui/auxiliary/unsafe-fields-crate-dep.rs | 7 ++ .../ui/feature-gates/feature-gate-unsafe_fields.rs | 25 +++++ .../feature-gate-unsafe_fields.with_gate.stderr | 11 +++ .../feature-gate-unsafe_fields.without_gate.stderr | 33 +++++++ tests/ui/stats/input-stats.stderr | 30 +++--- tests/ui/thir-print/thir-tree-match.stdout | 6 +- tests/ui/unsafe-fields-crate.rs | 62 ++++++++++++ tests/ui/unsafe-fields-crate.stderr | 47 +++++++++ tests/ui/unsafe-fields-parse.rs | 10 ++ tests/ui/unsafe-fields-parse.stderr | 18 ++++ tests/ui/unsafe-fields.rs | 109 +++++++++++++++++++++ tests/ui/unsafe-fields.stderr | 75 ++++++++++++++ 38 files changed, 793 insertions(+), 85 deletions(-) create mode 100644 src/tools/rustfmt/tests/source/unsafe-field.rs create mode 100644 src/tools/rustfmt/tests/target/unsafe-field.rs create mode 100644 tests/ui/auxiliary/unsafe-fields-crate-dep.rs create mode 100644 tests/ui/feature-gates/feature-gate-unsafe_fields.rs create mode 100644 tests/ui/feature-gates/feature-gate-unsafe_fields.with_gate.stderr create mode 100644 tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr create mode 100644 tests/ui/unsafe-fields-crate.rs create mode 100644 tests/ui/unsafe-fields-crate.stderr create mode 100644 tests/ui/unsafe-fields-parse.rs create mode 100644 tests/ui/unsafe-fields-parse.stderr create mode 100644 tests/ui/unsafe-fields.rs create mode 100644 tests/ui/unsafe-fields.stderr (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 5f71fb97d76..792de77e9d4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3063,6 +3063,7 @@ pub struct FieldDef { pub id: NodeId, pub span: Span, pub vis: Visibility, + pub safety: Safety, pub ident: Option, pub ty: P, diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 61f2f91635d..198e1bca774 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1115,10 +1115,11 @@ fn walk_poly_trait_ref(vis: &mut T, p: &mut PolyTraitRef) { } pub fn walk_field_def(visitor: &mut T, fd: &mut FieldDef) { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = fd; + let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _, safety } = fd; visitor.visit_id(id); visit_attrs(visitor, attrs); visitor.visit_vis(vis); + visit_safety(visitor, safety); visit_opt(ident, |ident| visitor.visit_ident(ident)); visitor.visit_ty(ty); visitor.visit_span(span); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 0302c9fa7f8..20ac9fa02bb 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -961,7 +961,7 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>( } pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) -> V::Result { - let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _ } = field; + let FieldDef { attrs, id: _, span: _, vis, ident, ty, is_placeholder: _, safety: _ } = field; walk_list!(visitor, visit_attribute, attrs); try_visit!(visitor.visit_vis(vis)); visit_opt!(visitor, visit_ident, ident); diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index d7245607501..48b42fa2e97 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -724,6 +724,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }, vis_span: self.lower_span(f.vis.span), ty, + safety: self.lower_safety(f.safety, hir::Safety::Safe), } } diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index be6f2c152a4..8a392e4407b 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -557,6 +557,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(global_registration, "global registration is experimental"); gate_all!(return_type_notation, "return type notation is experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); + gate_all!(unsafe_fields, "`unsafe` fields are experimental"); if !visitor.features.never_patterns() { if let Some(spans) = spans.get(&sym::never_patterns) { diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index bae16a18bcb..f044d964f13 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -2,7 +2,7 @@ use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::token::Delimiter; use rustc_ast::visit::AssocCtxt; -use rustc_ast::{self as ast}; +use rustc_ast::{self as ast, Safety}; use rustc_data_structures::fx::FxHashMap; use rustc_span::DUMMY_SP; use rustc_span::symbol::Ident; @@ -173,6 +173,7 @@ pub(crate) fn placeholder( ty: ty(), vis, is_placeholder: true, + safety: Safety::Default, }]), AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant { attrs: Default::default(), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index e3dc73c1401..0de06bd7d1a 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -627,6 +627,8 @@ declare_features! ( /// Allows creation of instances of a struct by moving fields that have /// not changed from prior instances of the same struct (RFC #2528) (unstable, type_changing_struct_update, "1.58.0", Some(86555)), + /// Allows declaring fields `unsafe`. + (incomplete, unsafe_fields, "CURRENT_RUSTC_VERSION", Some(132922)), /// Allows const generic parameters to be defined with types that /// are not `Sized`, e.g. `fn foo() {`. (incomplete, unsized_const_params, "1.82.0", Some(95174)), diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 554097bf115..16e53a27128 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3177,6 +3177,7 @@ pub struct FieldDef<'hir> { pub hir_id: HirId, pub def_id: LocalDefId, pub ty: &'hir Ty<'hir>, + pub safety: Safety, } impl FieldDef<'_> { diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 64a30e633cf..f5ccf8c9dff 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -253,6 +253,13 @@ hir_analysis_invalid_union_field = hir_analysis_invalid_union_field_sugg = wrap the field type in `ManuallyDrop<...>` +hir_analysis_invalid_unsafe_field = + field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe + .note = unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` + +hir_analysis_invalid_unsafe_field_sugg = + wrap the field type in `ManuallyDrop<...>` + hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl .label = const parameter declared here diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index cf3f3003bf5..c66572a9377 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::MultiSpan; use rustc_errors::codes::*; use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::{Node, intravisit}; +use rustc_hir::{Node, Safety, intravisit}; use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_lint_defs::builtin::{ @@ -70,6 +70,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_transparent(tcx, def); check_packed(tcx, span, def); + check_unsafe_fields(tcx, def_id); } fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { @@ -81,38 +82,45 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { check_packed(tcx, span, def); } +fn allowed_union_or_unsafe_field<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + typing_env: ty::TypingEnv<'tcx>, + span: Span, +) -> bool { + // We don't just accept all !needs_drop fields, due to semver concerns. + let allowed = match ty.kind() { + ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) + ty::Tuple(tys) => { + // allow tuples of allowed types + tys.iter().all(|ty| allowed_union_or_unsafe_field(tcx, ty, typing_env, span)) + } + ty::Array(elem, _len) => { + // Like `Copy`, we do *not* special-case length 0. + allowed_union_or_unsafe_field(tcx, *elem, typing_env, span) + } + _ => { + // Fallback case: allow `ManuallyDrop` and things that are `Copy`, + // also no need to report an error if the type is unresolved. + ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) + || ty.is_copy_modulo_regions(tcx, typing_env) + || ty.references_error() + } + }; + if allowed && ty.needs_drop(tcx, typing_env) { + // This should never happen. But we can get here e.g. in case of name resolution errors. + tcx.dcx() + .span_delayed_bug(span, "we should never accept maybe-dropping union or unsafe fields"); + } + allowed +} + /// Check that the fields of the `union` do not need dropping. fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { let item_type = tcx.type_of(item_def_id).instantiate_identity(); if let ty::Adt(def, args) = item_type.kind() { assert!(def.is_union()); - fn allowed_union_field<'tcx>( - ty: Ty<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> bool { - // We don't just accept all !needs_drop fields, due to semver concerns. - match ty.kind() { - ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check) - ty::Tuple(tys) => { - // allow tuples of allowed types - tys.iter().all(|ty| allowed_union_field(ty, tcx, typing_env)) - } - ty::Array(elem, _len) => { - // Like `Copy`, we do *not* special-case length 0. - allowed_union_field(*elem, tcx, typing_env) - } - _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`, - // also no need to report an error if the type is unresolved. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) - || ty.is_copy_modulo_regions(tcx, typing_env) - || ty.references_error() - } - } - } - let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); for field in &def.non_enum_variant().fields { let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) @@ -121,7 +129,7 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b continue; }; - if !allowed_union_field(field_ty, tcx, typing_env) { + if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) { // We are currently checking the type this field came from, so it must be local. Some(Node::Field(field)) => (field.span, field.ty.span), @@ -136,10 +144,6 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b note: (), }); return false; - } else if field_ty.needs_drop(tcx, typing_env) { - // This should never happen. But we can get here e.g. in case of name resolution errors. - tcx.dcx() - .span_delayed_bug(span, "we should never accept maybe-dropping union fields"); } } } else { @@ -148,6 +152,41 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b true } +/// Check that the unsafe fields do not need dropping. +fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) { + let span = tcx.def_span(item_def_id); + let item_type = tcx.type_of(item_def_id).instantiate_identity(); + let ty::Adt(def, args) = item_type.kind() else { + span_bug!(span, "structs/enums must be ty::Adt, but got {:?}", item_type.kind()); + }; + let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); + for field in def.all_fields() { + if field.safety != Safety::Unsafe { + continue; + } + let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args)) + else { + tcx.dcx().span_delayed_bug(span, "could not normalize field type"); + continue; + }; + + if !allowed_union_or_unsafe_field(tcx, field_ty, typing_env, span) { + let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else { + unreachable!("field has to correspond to hir field") + }; + let ty_span = field.ty.span; + tcx.dcx().emit_err(errors::InvalidUnsafeField { + field_span: field.span, + sugg: errors::InvalidUnsafeFieldSuggestion { + lo: ty_span.shrink_to_lo(), + hi: ty_span.shrink_to_hi(), + }, + note: (), + }); + } + } +} + /// Check that a `static` is inhabited. fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { // Make sure statics are inhabited. @@ -1464,6 +1503,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { detect_discriminant_duplicate(tcx, def); check_transparent(tcx, def); + check_unsafe_fields(tcx, def_id); } /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 73b73afb0a5..93be3e06e5d 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -1040,6 +1040,7 @@ fn lower_variant( did: f.def_id.to_def_id(), name: f.ident.name, vis: tcx.visibility(f.def_id), + safety: f.safety, }) .collect(); let recovered = match def { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 07d3273b09c..f5ca3c49475 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -734,6 +734,17 @@ pub(crate) struct InvalidUnionField { pub note: (), } +#[derive(Diagnostic)] +#[diag(hir_analysis_invalid_unsafe_field, code = E0740)] +pub(crate) struct InvalidUnsafeField { + #[primary_span] + pub field_span: Span, + #[subdiagnostic] + pub sugg: InvalidUnsafeFieldSuggestion, + #[note] + pub note: (), +} + #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_on_non_rpitit)] pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> { @@ -755,6 +766,18 @@ pub(crate) struct InvalidUnionFieldSuggestion { pub hi: Span, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + hir_analysis_invalid_unsafe_field_sugg, + applicability = "machine-applicable" +)] +pub(crate) struct InvalidUnsafeFieldSuggestion { + #[suggestion_part(code = "std::mem::ManuallyDrop<")] + pub lo: Span, + #[suggestion_part(code = ">")] + pub hi: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_equality_bound)] pub(crate) struct ReturnTypeNotationEqualityBound { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 87357b74c41..56beff5aa64 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -15,6 +15,7 @@ use rustc_data_structures::sync::{Lock, Lrc, OnceLock}; use rustc_data_structures::unhash::UnhashMap; use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind}; use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro}; +use rustc_hir::Safety; use rustc_hir::def::Res; use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE}; use rustc_hir::definitions::{DefPath, DefPathData}; @@ -1101,6 +1102,7 @@ impl<'a> CrateMetadataRef<'a> { did, name: self.item_name(did.index), vis: self.get_visibility(did.index), + safety: self.get_safety(did.index), }) .collect(), adt_kind, @@ -1162,6 +1164,10 @@ impl<'a> CrateMetadataRef<'a> { .map_id(|index| self.local_def_id(index)) } + fn get_safety(self, id: DefIndex) -> Safety { + self.root.tables.safety.get(self, id).unwrap_or_else(|| self.missing("safety", id)) + } + fn get_trait_item_def_id(self, id: DefIndex) -> Option { self.root.tables.trait_item_def_id.get(self, id).map(|d| d.decode_from_cdata(self)) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 8378e7c6e9b..dd149af5205 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1599,6 +1599,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { f.did.index })); + for field in &variant.fields { + self.tables.safety.set_some(field.did.index, field.safety); + } + if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor { let fn_sig = tcx.fn_sig(ctor_def_id); // FIXME only encode signature for ctor_def_id diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4a8f8521b4f..a486ad42482 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -411,6 +411,7 @@ define_tables! { associated_item_or_field_def_ids: Table>, def_kind: Table, visibility: Table>>, + safety: Table, def_span: Table>, def_ident_span: Table>, lookup_stability: Table>, diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 63abd2be9a5..9438350ca09 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -198,6 +198,13 @@ fixed_size_enum! { } } +fixed_size_enum! { + hir::Safety { + ( Unsafe ) + ( Safe ) + } +} + fixed_size_enum! { ty::Asyncness { ( Yes ) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2bc055453a4..a6c875ec618 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -34,9 +34,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::steal::Steal; use rustc_data_structures::tagged_ptr::CopyTaggedPtr; use rustc_errors::{Diag, ErrorGuaranteed, StashKey}; -use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; +use rustc_hir::{LangItem, Safety}; use rustc_index::IndexVec; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, @@ -1365,6 +1365,11 @@ impl VariantDef { pub fn tail(&self) -> &FieldDef { self.tail_opt().expect("expected unsized ADT to have a tail field") } + + /// Returns whether this variant has unsafe fields. + pub fn has_unsafe_fields(&self) -> bool { + self.fields.iter().any(|x| x.safety == Safety::Unsafe) + } } impl PartialEq for VariantDef { @@ -1447,6 +1452,7 @@ pub struct FieldDef { pub did: DefId, pub name: Symbol, pub vis: Visibility, + pub safety: hir::Safety, } impl PartialEq for FieldDef { @@ -1459,15 +1465,16 @@ impl PartialEq for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did: lhs_did, name: _, vis: _ } = &self; + let Self { did: lhs_did, name: _, vis: _, safety: _ } = &self; - let Self { did: rhs_did, name: _, vis: _ } = other; + let Self { did: rhs_did, name: _, vis: _, safety: _ } = other; let res = lhs_did == rhs_did; // Double check that implicit assumption detailed above. if cfg!(debug_assertions) && res { - let deep = self.name == other.name && self.vis == other.vis; + let deep = + self.name == other.name && self.vis == other.vis && self.safety == other.safety; assert!(deep, "FieldDef for the same def-id has differing data"); } @@ -1487,7 +1494,7 @@ impl Hash for FieldDef { // of `FieldDef` changes, a compile-error will be produced, reminding // us to revisit this assumption. - let Self { did, name: _, vis: _ } = &self; + let Self { did, name: _, vis: _, safety: _ } = &self; did.hash(s) } diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs index 7c280bc8b49..f7322217aa3 100644 --- a/compiler/rustc_middle/src/ty/parameterized.rs +++ b/compiler/rustc_middle/src/ty/parameterized.rs @@ -86,6 +86,7 @@ trivially_parameterized_over_tcx! { rustc_attr::Stability, rustc_hir::Constness, rustc_hir::Defaultness, + rustc_hir::Safety, rustc_hir::CoroutineKind, rustc_hir::IsAsync, rustc_hir::LangItem, diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 55149570dbc..02ed3b09053 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -125,6 +125,16 @@ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_initializing_type_with_unsafe_field_requires_unsafe = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + +mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + mir_build_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -340,6 +350,16 @@ mir_build_unreachable_pattern = unreachable pattern .unreachable_covered_by_many = multiple earlier patterns match some of the same values .suggestion = remove the match arm +mir_build_unsafe_field_requires_unsafe = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + +mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + mir_build_unsafe_fn_safe_body = an unsafe function restricts its caller, but its body is safe by default mir_build_unsafe_not_inherited = items do not inherit unsafety from separate enclosing items @@ -388,6 +408,11 @@ mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe = .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior .label = initializing type with `rustc_layout_scalar_valid_range` attr +mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe = + initializing type with an unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = initialization of struct with unsafe field + mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe = use of inline assembly is unsafe and requires unsafe block .note = inline assembly is entirely unchecked and can cause undefined behavior @@ -408,6 +433,11 @@ mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe = .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior .label = access to union field +mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe = + use of unsafe field is unsafe and requires unsafe block + .note = unsafe fields may carry library invariants + .label = use of unsafe field + mir_build_unsized_pattern = cannot use unsized non-slice type `{$non_sm_ty}` in constant patterns mir_build_unused_unsafe = unnecessary `unsafe` block diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index b497e54d480..f37b3f977fa 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -4,7 +4,7 @@ use std::ops::Bound; use rustc_errors::DiagArgValue; use rustc_hir::def::DefKind; -use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability}; +use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Safety}; use rustc_middle::middle::codegen_fn_attrs::TargetFeature; use rustc_middle::mir::BorrowKind; use rustc_middle::span_bug; @@ -339,8 +339,13 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }; match &pat.kind { - PatKind::Leaf { .. } => { + PatKind::Leaf { subpatterns, .. } => { if let ty::Adt(adt_def, ..) = pat.ty.kind() { + for pat in subpatterns { + if adt_def.non_enum_variant().fields[pat.field].safety == Safety::Unsafe { + self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); + } + } if adt_def.is_union() { let old_in_union_destructure = std::mem::replace(&mut self.in_union_destructure, true); @@ -359,6 +364,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { visit::walk_pat(self, pat); } } + PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => { + for pat in subpatterns { + let field = &pat.field; + if adt_def.variant(*variant_index).fields[*field].safety == Safety::Unsafe { + self.requires_unsafe(pat.pattern.span, UseOfUnsafeField); + } + } + visit::walk_pat(self, pat); + } PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => { if self.inside_adt { let ty::Ref(_, ty, _) = ty.kind() else { @@ -579,15 +593,20 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } ExprKind::Adt(box AdtExpr { adt_def, - variant_index: _, + variant_index, args: _, user_ty: _, fields: _, base: _, - }) => match self.tcx.layout_scalar_valid_range(adt_def.did()) { - (Bound::Unbounded, Bound::Unbounded) => {} - _ => self.requires_unsafe(expr.span, InitializingTypeWith), - }, + }) => { + if adt_def.variant(variant_index).has_unsafe_fields() { + self.requires_unsafe(expr.span, InitializingTypeWithUnsafeField) + } + match self.tcx.layout_scalar_valid_range(adt_def.did()) { + (Bound::Unbounded, Bound::Unbounded) => {} + _ => self.requires_unsafe(expr.span, InitializingTypeWith), + } + } ExprKind::Closure(box ClosureExpr { closure_id, args: _, @@ -601,23 +620,24 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { let def_id = did.expect_local(); self.visit_inner_body(def_id); } - ExprKind::Field { lhs, .. } => { + ExprKind::Field { lhs, variant_index, name } => { let lhs = &self.thir[lhs]; - if let ty::Adt(adt_def, _) = lhs.ty.kind() - && adt_def.is_union() - { - if let Some(assigned_ty) = self.assignment_info { - if assigned_ty.needs_drop(self.tcx, self.typing_env) { - // This would be unsafe, but should be outright impossible since we - // reject such unions. - assert!( - self.tcx.dcx().has_errors().is_some(), - "union fields that need dropping should be impossible: \ - {assigned_ty}" - ); + if let ty::Adt(adt_def, _) = lhs.ty.kind() { + if adt_def.variant(variant_index).fields[name].safety == Safety::Unsafe { + self.requires_unsafe(expr.span, UseOfUnsafeField); + } else if adt_def.is_union() { + if let Some(assigned_ty) = self.assignment_info { + if assigned_ty.needs_drop(self.tcx, self.typing_env) { + // This would be unsafe, but should be outright impossible since we + // reject such unions. + assert!( + self.tcx.dcx().has_errors().is_some(), + "union fields that need dropping should be impossible: {assigned_ty}" + ); + } + } else { + self.requires_unsafe(expr.span, AccessToUnionField); } - } else { - self.requires_unsafe(expr.span, AccessToUnionField); } } } @@ -689,8 +709,10 @@ enum UnsafeOpKind { CallToUnsafeFunction(Option), UseOfInlineAssembly, InitializingTypeWith, + InitializingTypeWithUnsafeField, UseOfMutableStatic, UseOfExternStatic, + UseOfUnsafeField, DerefOfRawPointer, AccessToUnionField, MutationOfLayoutConstrainedField, @@ -770,6 +792,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + InitializingTypeWithUnsafeField => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), UseOfMutableStatic => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -788,6 +819,15 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }, ), + UseOfUnsafeField => tcx.emit_node_span_lint( + UNSAFE_OP_IN_UNSAFE_FN, + hir_id, + span, + UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }, + ), DerefOfRawPointer => tcx.emit_node_span_lint( UNSAFE_OP_IN_UNSAFE_FN, hir_id, @@ -927,6 +967,20 @@ impl UnsafeOpKind { unsafe_not_inherited_note, }); } + InitializingTypeWithUnsafeField if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err( + InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }, + ); + } + InitializingTypeWithUnsafeField => { + dcx.emit_err(InitializingTypeWithUnsafeFieldRequiresUnsafe { + span, + unsafe_not_inherited_note, + }); + } UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, @@ -945,6 +999,15 @@ impl UnsafeOpKind { UseOfExternStatic => { dcx.emit_err(UseOfExternStaticRequiresUnsafe { span, unsafe_not_inherited_note }); } + UseOfUnsafeField if unsafe_op_in_unsafe_fn_allowed => { + dcx.emit_err(UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + span, + unsafe_not_inherited_note, + }); + } + UseOfUnsafeField => { + dcx.emit_err(UseOfUnsafeFieldRequiresUnsafe { span, unsafe_not_inherited_note }); + } DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => { dcx.emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 676f7c98b8f..4a7727ec518 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -86,6 +86,16 @@ pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UnsafeOpInUnsafeFnInitializingTypeWithUnsafeFieldRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -106,6 +116,16 @@ pub(crate) struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(LintDiagnostic)] +#[diag(mir_build_unsafe_op_in_unsafe_fn_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UnsafeOpInUnsafeFnUseOfUnsafeFieldRequiresUnsafe { + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(LintDiagnostic)] #[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] @@ -250,6 +270,17 @@ pub(crate) struct InitializingTypeWithRequiresUnsafe { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag(mir_build_initializing_type_with_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag( mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, @@ -264,6 +295,20 @@ pub(crate) struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag( + mir_build_initializing_type_with_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, + code = E0133 +)] +#[note] +pub(crate) struct InitializingTypeWithUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag(mir_build_mutable_static_requires_unsafe, code = E0133)] #[note] @@ -308,6 +353,28 @@ pub(crate) struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { pub(crate) unsafe_not_inherited_note: Option, } +#[derive(Diagnostic)] +#[diag(mir_build_unsafe_field_requires_unsafe, code = E0133)] +#[note] +pub(crate) struct UseOfUnsafeFieldRequiresUnsafe { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + +#[derive(Diagnostic)] +#[diag(mir_build_unsafe_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = E0133)] +#[note] +pub(crate) struct UseOfUnsafeFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { + #[primary_span] + #[label] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) unsafe_not_inherited_note: Option, +} + #[derive(Diagnostic)] #[diag(mir_build_deref_raw_pointer_requires_unsafe, code = E0133)] #[note] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index fddbf5896ad..26e81b7676b 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1791,6 +1791,17 @@ impl<'a> Parser<'a> { Ok((fields, recovered)) } + fn parse_unsafe_field(&mut self) -> Safety { + // not using parse_safety as that also accepts `safe`. + if self.eat_keyword(kw::Unsafe) { + let span = self.prev_token.span; + self.psess.gated_spans.gate(sym::unsafe_fields, span); + Safety::Unsafe(span) + } else { + Safety::Default + } + } + pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec> { // This is the case where we find `struct Foo(T) where T: Copy;` // Unit like structs are handled in parse_item_struct function @@ -1814,6 +1825,8 @@ impl<'a> Parser<'a> { return Err(err); } }; + // Unsafe fields are not supported in tuple structs, as doing so would result in a + // parsing ambiguity for `struct X(unsafe fn())`. let ty = match p.parse_ty() { Ok(ty) => ty, Err(err) => { @@ -1828,6 +1841,7 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, + safety: Safety::Default, ident: None, id: DUMMY_NODE_ID, ty, @@ -1850,7 +1864,8 @@ impl<'a> Parser<'a> { self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; - this.parse_single_struct_field(adt_ty, lo, vis, attrs) + let safety = this.parse_unsafe_field(); + this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs) .map(|field| (field, Trailing::No, UsePreAttrPos::No)) }) } @@ -1861,10 +1876,11 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let mut seen_comma: bool = false; - let a_var = self.parse_name_and_ty(adt_ty, lo, vis, attrs)?; + let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?; if self.token == token::Comma { seen_comma = true; } @@ -1992,6 +2008,7 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; @@ -2017,6 +2034,7 @@ impl<'a> Parser<'a> { span: lo.to(self.prev_token.span), ident: Some(name), vis, + safety, id: DUMMY_NODE_ID, ty, attrs, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a2d9859645f..1f001e88120 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2086,6 +2086,7 @@ symbols! { unsafe_cell, unsafe_cell_raw_get, unsafe_extern_blocks, + unsafe_fields, unsafe_no_drop_flag, unsafe_pin_internals, unsize, diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 68c481bda6e..18932587f1f 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1916,15 +1916,15 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> RewriteResult { let vis = format_visibility(context, &field.vis); + let safety = format_safety(field.safety); let type_annotation_spacing = type_annotation_spacing(context.config); Ok(match field.ident { Some(name) => format!( - "{}{}{}:", - vis, + "{vis}{safety}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => vis.to_string(), + None => format!("{vis}{safety}"), }) } diff --git a/src/tools/rustfmt/tests/source/unsafe-field.rs b/src/tools/rustfmt/tests/source/unsafe-field.rs new file mode 100644 index 00000000000..35cf40a22d3 --- /dev/null +++ b/src/tools/rustfmt/tests/source/unsafe-field.rs @@ -0,0 +1,16 @@ +struct Foo { + unsafe + field: (), +} + +enum Bar { + Variant { + unsafe + field: (), + }, +} + +union Baz { + unsafe + field: (), +} diff --git a/src/tools/rustfmt/tests/target/unsafe-field.rs b/src/tools/rustfmt/tests/target/unsafe-field.rs new file mode 100644 index 00000000000..fc7fd3750c6 --- /dev/null +++ b/src/tools/rustfmt/tests/target/unsafe-field.rs @@ -0,0 +1,11 @@ +struct Foo { + unsafe field: (), +} + +enum Bar { + Variant { unsafe field: () }, +} + +union Baz { + unsafe field: (), +} diff --git a/tests/ui/auxiliary/unsafe-fields-crate-dep.rs b/tests/ui/auxiliary/unsafe-fields-crate-dep.rs new file mode 100644 index 00000000000..95b928402a9 --- /dev/null +++ b/tests/ui/auxiliary/unsafe-fields-crate-dep.rs @@ -0,0 +1,7 @@ +#![allow(incomplete_features)] +#![feature(unsafe_fields)] + +pub struct WithUnsafeField { + pub unsafe unsafe_field: u32, + pub safe_field: u32, +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.rs b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs new file mode 100644 index 00000000000..8f9b411df46 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.rs @@ -0,0 +1,25 @@ +//@ compile-flags: --crate-type=lib +//@ revisions: with_gate without_gate +//@ [with_gate] check-pass + +#![cfg_attr(with_gate, feature(unsafe_fields))] //[with_gate]~ WARNING + +#[cfg(any())] +struct Foo { + unsafe field: (), //[without_gate]~ ERROR +} + +// This should not parse as an unsafe field definition. +struct FooTuple(unsafe fn()); + +#[cfg(any())] +enum Bar { + Variant { unsafe field: () }, //[without_gate]~ ERROR + // This should not parse as an unsafe field definition. + VariantTuple(unsafe fn()), +} + +#[cfg(any())] +union Baz { + unsafe field: (), //[without_gate]~ ERROR +} diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.with_gate.stderr b/tests/ui/feature-gates/feature-gate-unsafe_fields.with_gate.stderr new file mode 100644 index 00000000000..a7deeb057d8 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.with_gate.stderr @@ -0,0 +1,11 @@ +warning: the feature `unsafe_fields` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/feature-gate-unsafe_fields.rs:5:32 + | +LL | #![cfg_attr(with_gate, feature(unsafe_fields))] + | ^^^^^^^^^^^^^ + | + = note: see issue #132922 for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr b/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr new file mode 100644 index 00000000000..ad0f5fd59ac --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unsafe_fields.without_gate.stderr @@ -0,0 +1,33 @@ +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:9:5 + | +LL | unsafe field: (), + | ^^^^^^ + | + = note: see issue #132922 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:17:15 + | +LL | Variant { unsafe field: () }, + | ^^^^^^ + | + = note: see issue #132922 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `unsafe` fields are experimental + --> $DIR/feature-gate-unsafe_fields.rs:24:5 + | +LL | unsafe field: (), + | ^^^^^^ + | + = note: see issue #132922 for more information + = help: add `#![feature(unsafe_fields)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index 2cc2c8019fa..f2dd9043759 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -15,17 +15,17 @@ ast-stats-1 ForeignItem 88 ( 1.3%) 1 88 ast-stats-1 - Fn 88 ( 1.3%) 1 ast-stats-1 Arm 96 ( 1.4%) 2 48 ast-stats-1 FnDecl 120 ( 1.8%) 5 24 -ast-stats-1 FieldDef 160 ( 2.4%) 2 80 ast-stats-1 Param 160 ( 2.4%) 4 40 ast-stats-1 Stmt 160 ( 2.4%) 5 32 ast-stats-1 - Let 32 ( 0.5%) 1 ast-stats-1 - MacCall 32 ( 0.5%) 1 ast-stats-1 - Expr 96 ( 1.4%) 3 +ast-stats-1 FieldDef 176 ( 2.6%) 2 88 ast-stats-1 Block 192 ( 2.9%) 6 32 ast-stats-1 Variant 208 ( 3.1%) 2 104 ast-stats-1 AssocItem 352 ( 5.3%) 4 88 -ast-stats-1 - Type 176 ( 2.7%) 2 -ast-stats-1 - Fn 176 ( 2.7%) 2 +ast-stats-1 - Type 176 ( 2.6%) 2 +ast-stats-1 - Fn 176 ( 2.6%) 2 ast-stats-1 GenericBound 352 ( 5.3%) 4 88 ast-stats-1 - Trait 352 ( 5.3%) 4 ast-stats-1 GenericParam 480 ( 7.2%) 5 96 @@ -38,7 +38,7 @@ ast-stats-1 - Path 72 ( 1.1%) 1 ast-stats-1 - Match 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Lit 144 ( 2.2%) 2 -ast-stats-1 - Block 216 ( 3.3%) 3 +ast-stats-1 - Block 216 ( 3.2%) 3 ast-stats-1 PathSegment 744 (11.2%) 31 24 ast-stats-1 Ty 896 (13.5%) 14 64 ast-stats-1 - Ref 64 ( 1.0%) 1 @@ -53,7 +53,7 @@ ast-stats-1 - Enum 136 ( 2.0%) 1 ast-stats-1 - Fn 272 ( 4.1%) 2 ast-stats-1 - Use 408 ( 6.1%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_640 116 +ast-stats-1 Total 6_656 116 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -73,14 +73,14 @@ ast-stats-2 InlineAsm 120 ( 1.6%) 1 120 ast-stats-2 Attribute 128 ( 1.8%) 4 32 ast-stats-2 - DocComment 32 ( 0.4%) 1 ast-stats-2 - Normal 96 ( 1.3%) 3 -ast-stats-2 FieldDef 160 ( 2.2%) 2 80 ast-stats-2 Param 160 ( 2.2%) 4 40 ast-stats-2 Stmt 160 ( 2.2%) 5 32 ast-stats-2 - Let 32 ( 0.4%) 1 ast-stats-2 - Semi 32 ( 0.4%) 1 ast-stats-2 - Expr 96 ( 1.3%) 3 +ast-stats-2 FieldDef 176 ( 2.4%) 2 88 ast-stats-2 Block 192 ( 2.6%) 6 32 -ast-stats-2 Variant 208 ( 2.9%) 2 104 +ast-stats-2 Variant 208 ( 2.8%) 2 104 ast-stats-2 AssocItem 352 ( 4.8%) 4 88 ast-stats-2 - Type 176 ( 2.4%) 2 ast-stats-2 - Fn 176 ( 2.4%) 2 @@ -98,7 +98,7 @@ ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Block 216 ( 3.0%) 3 -ast-stats-2 PathSegment 864 (11.9%) 36 24 +ast-stats-2 PathSegment 864 (11.8%) 36 24 ast-stats-2 Ty 896 (12.3%) 14 64 ast-stats-2 - Ref 64 ( 0.9%) 1 ast-stats-2 - Ptr 64 ( 0.9%) 1 @@ -111,9 +111,9 @@ ast-stats-2 - Impl 136 ( 1.9%) 1 ast-stats-2 - ExternCrate 136 ( 1.9%) 1 ast-stats-2 - ForeignMod 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.7%) 2 -ast-stats-2 - Use 544 ( 7.5%) 4 +ast-stats-2 - Use 544 ( 7.4%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_288 127 +ast-stats-2 Total 7_304 127 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size @@ -132,11 +132,11 @@ hir-stats Body 72 ( 0.8%) 3 24 hir-stats ImplItemRef 72 ( 0.8%) 2 36 hir-stats InlineAsm 72 ( 0.8%) 1 72 hir-stats Arm 80 ( 0.9%) 2 40 -hir-stats FieldDef 96 ( 1.1%) 2 48 hir-stats Stmt 96 ( 1.1%) 3 32 hir-stats - Let 32 ( 0.4%) 1 hir-stats - Semi 32 ( 0.4%) 1 hir-stats - Expr 32 ( 0.4%) 1 +hir-stats FieldDef 112 ( 1.2%) 2 56 hir-stats FnDecl 120 ( 1.3%) 3 40 hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats GenericArgs 144 ( 1.6%) 3 48 @@ -162,17 +162,17 @@ hir-stats - Match 64 ( 0.7%) 1 hir-stats - Struct 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Lit 128 ( 1.4%) 2 -hir-stats - Block 384 ( 4.3%) 6 +hir-stats - Block 384 ( 4.2%) 6 hir-stats Item 968 (10.7%) 11 88 hir-stats - Enum 88 ( 1.0%) 1 hir-stats - Trait 88 ( 1.0%) 1 hir-stats - Impl 88 ( 1.0%) 1 hir-stats - ExternCrate 88 ( 1.0%) 1 hir-stats - ForeignMod 88 ( 1.0%) 1 -hir-stats - Fn 176 ( 2.0%) 2 +hir-stats - Fn 176 ( 1.9%) 2 hir-stats - Use 352 ( 3.9%) 4 hir-stats Path 1_240 (13.7%) 31 40 -hir-stats PathSegment 1_920 (21.3%) 40 48 +hir-stats PathSegment 1_920 (21.2%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 9_024 180 +hir-stats Total 9_040 180 hir-stats diff --git a/tests/ui/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index a9d6985928a..d56f15fb221 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -92,7 +92,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] @@ -154,7 +154,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] @@ -206,7 +206,7 @@ body: adt_def: AdtDef { did: DefId(0:10 ~ thir_tree_match[fcf8]::Foo) - variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])) }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] + variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[fcf8]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[fcf8]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[fcf8]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[fcf8])), safety: Safe }], tainted: None, flags: }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[fcf8]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[fcf8]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], tainted: None, flags: }] flags: IS_ENUM repr: ReprOptions { int: None, align: None, pack: None, flags: , field_shuffle_seed: 3477539199540094892 } args: [] diff --git a/tests/ui/unsafe-fields-crate.rs b/tests/ui/unsafe-fields-crate.rs new file mode 100644 index 00000000000..cfb9ad6b544 --- /dev/null +++ b/tests/ui/unsafe-fields-crate.rs @@ -0,0 +1,62 @@ +//@ compile-flags: --crate-type=lib +//@ aux-build: unsafe-fields-crate-dep.rs + +extern crate unsafe_fields_crate_dep; + +use unsafe_fields_crate_dep::WithUnsafeField; + +fn new_without_unsafe() -> WithUnsafeField { + WithUnsafeField { + //~^ ERROR + unsafe_field: 0, + safe_field: 0, + } +} + +fn operate_on_safe_field(s: &mut WithUnsafeField) { + s.safe_field = 2; + &s.safe_field; + s.safe_field; +} + +fn set_unsafe_field(s: &mut WithUnsafeField) { + unsafe { + s.unsafe_field = 2; + } +} + +fn read_unsafe_field(s: &WithUnsafeField) -> u32 { + unsafe { s.unsafe_field } +} + +fn ref_unsafe_field(s: &WithUnsafeField) -> &u32 { + unsafe { &s.unsafe_field } +} + +fn destructure(s: &WithUnsafeField) { + unsafe { + let WithUnsafeField { safe_field, unsafe_field } = s; + } +} + +fn set_unsafe_field_without_unsafe(s: &mut WithUnsafeField) { + s.unsafe_field = 2; + //~^ ERROR +} + +fn read_unsafe_field_without_unsafe(s: &WithUnsafeField) -> u32 { + s.unsafe_field + //~^ ERROR +} + +fn ref_unsafe_field_without_unsafe(s: &WithUnsafeField) -> &u32 { + &s.unsafe_field + //~^ ERROR +} + +fn destructure_without_unsafe(s: &WithUnsafeField) { + let WithUnsafeField { safe_field, unsafe_field } = s; + //~^ ERROR + + let WithUnsafeField { safe_field, .. } = s; +} diff --git a/tests/ui/unsafe-fields-crate.stderr b/tests/ui/unsafe-fields-crate.stderr new file mode 100644 index 00000000000..778c26e0a43 --- /dev/null +++ b/tests/ui/unsafe-fields-crate.stderr @@ -0,0 +1,47 @@ +error[E0133]: initializing type with an unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields-crate.rs:9:5 + | +LL | / WithUnsafeField { +LL | | +LL | | unsafe_field: 0, +LL | | safe_field: 0, +LL | | } + | |_____^ initialization of struct with unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields-crate.rs:43:5 + | +LL | s.unsafe_field = 2; + | ^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields-crate.rs:48:5 + | +LL | s.unsafe_field + | ^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields-crate.rs:53:6 + | +LL | &s.unsafe_field + | ^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields-crate.rs:58:39 + | +LL | let WithUnsafeField { safe_field, unsafe_field } = s; + | ^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0133`. diff --git a/tests/ui/unsafe-fields-parse.rs b/tests/ui/unsafe-fields-parse.rs new file mode 100644 index 00000000000..67277731293 --- /dev/null +++ b/tests/ui/unsafe-fields-parse.rs @@ -0,0 +1,10 @@ +//@ compile-flags: --crate-type=lib +#![allow(incomplete_features)] +#![feature(unsafe_fields)] + +// Parse errors even *with* unsafe_fields, which would make the compiler early-exit otherwise. +enum A { + TupleLike(unsafe u32), //~ ERROR +} + +struct B(unsafe u32); //~ ERROR diff --git a/tests/ui/unsafe-fields-parse.stderr b/tests/ui/unsafe-fields-parse.stderr new file mode 100644 index 00000000000..5a45ab03ffe --- /dev/null +++ b/tests/ui/unsafe-fields-parse.stderr @@ -0,0 +1,18 @@ +error: expected type, found keyword `unsafe` + --> $DIR/unsafe-fields-parse.rs:7:15 + | +LL | enum A { + | - while parsing this enum +LL | TupleLike(unsafe u32), + | ^^^^^^ expected type + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: expected type, found keyword `unsafe` + --> $DIR/unsafe-fields-parse.rs:10:10 + | +LL | struct B(unsafe u32); + | ^^^^^^ expected type + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unsafe-fields.rs b/tests/ui/unsafe-fields.rs new file mode 100644 index 00000000000..637471582d7 --- /dev/null +++ b/tests/ui/unsafe-fields.rs @@ -0,0 +1,109 @@ +//@ compile-flags: --crate-type=lib +#![allow(incomplete_features)] +#![feature(unsafe_fields)] + +struct WithUnsafeField { + unsafe unsafe_field: u32, + safe_field: u32, +} + +enum A { + WithUnsafeField { unsafe unsafe_field: u32, safe_field: u32 }, +} + +fn f(a: A) { + let A::WithUnsafeField { unsafe_field, safe_field } = a; + //~^ ERROR +} + +struct WithInvalidUnsafeField { + unsafe unsafe_noncopy_field: Vec, //~ ERROR +} + +struct WithManuallyDropUnsafeField { + unsafe unsafe_noncopy_field: std::mem::ManuallyDrop>, +} + +union WithUnsafeFieldUnion { + unsafe unsafe_field: u32, + safe_field: u32, +} + +impl WithUnsafeField { + fn new() -> WithUnsafeField { + unsafe { + WithUnsafeField { + unsafe_field: 0, + safe_field: 0, + } + } + } + + fn new_without_unsafe() -> WithUnsafeField { + WithUnsafeField { //~ ERROR + unsafe_field: 0, + safe_field: 0, + } + } + + fn operate_on_safe_field(&mut self) { + self.safe_field = 2; + &self.safe_field; + self.safe_field; + } + + fn set_unsafe_field(&mut self) { + unsafe { + self.unsafe_field = 2; + } + } + + fn read_unsafe_field(&self) -> u32 { + unsafe { + self.unsafe_field + } + } + + fn ref_unsafe_field(&self) -> &u32 { + unsafe { + &self.unsafe_field + } + } + + fn destructure(&self) { + unsafe { + let Self { safe_field, unsafe_field } = self; + } + } + + fn set_unsafe_field_without_unsafe(&mut self) { + self.unsafe_field = 2; + //~^ ERROR + } + + fn read_unsafe_field_without_unsafe(&self) -> u32 { + self.unsafe_field + //~^ ERROR + } + + fn ref_unsafe_field_without_unsafe(&self) -> &u32 { + &self.unsafe_field + //~^ ERROR + } + + fn destructure_without_unsafe(&self) { + let Self { safe_field, unsafe_field } = self; + //~^ ERROR + + let WithUnsafeField { safe_field, .. } = self; + } + + fn offset_of(&self) -> usize { + std::mem::offset_of!(WithUnsafeField, unsafe_field) + } + + fn raw_const(&self) -> *const u32 { + &raw const self.unsafe_field + //~^ ERROR + } +} diff --git a/tests/ui/unsafe-fields.stderr b/tests/ui/unsafe-fields.stderr new file mode 100644 index 00000000000..a1c5d2b44cd --- /dev/null +++ b/tests/ui/unsafe-fields.stderr @@ -0,0 +1,75 @@ +error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe + --> $DIR/unsafe-fields.rs:20:5 + | +LL | unsafe unsafe_noncopy_field: Vec, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>` +help: wrap the field type in `ManuallyDrop<...>` + | +LL | unsafe unsafe_noncopy_field: std::mem::ManuallyDrop>, + | +++++++++++++++++++++++ + + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:15:30 + | +LL | let A::WithUnsafeField { unsafe_field, safe_field } = a; + | ^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: initializing type with an unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:43:9 + | +LL | / WithUnsafeField { +LL | | unsafe_field: 0, +LL | | safe_field: 0, +LL | | } + | |_________^ initialization of struct with unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:80:9 + | +LL | self.unsafe_field = 2; + | ^^^^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:85:9 + | +LL | self.unsafe_field + | ^^^^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:90:10 + | +LL | &self.unsafe_field + | ^^^^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:95:32 + | +LL | let Self { safe_field, unsafe_field } = self; + | ^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error[E0133]: use of unsafe field is unsafe and requires unsafe block + --> $DIR/unsafe-fields.rs:106:20 + | +LL | &raw const self.unsafe_field + | ^^^^^^^^^^^^^^^^^ use of unsafe field + | + = note: unsafe fields may carry library invariants + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0133, E0740. +For more information about an error, try `rustc --explain E0133`. -- cgit 1.4.1-3-g733a5 From 161221da9e8b4ba07ed6662191fbf7f4560156ba Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 25 Nov 2024 16:38:35 +0800 Subject: Refactor `where` predicates, and reserve for attributes support --- compiler/rustc_ast/src/ast.rs | 23 +++--- compiler/rustc_ast/src/mut_visit.rs | 32 +++++---- compiler/rustc_ast/src/visit.rs | 20 ++++-- compiler/rustc_ast_lowering/src/index.rs | 13 ++-- compiler/rustc_ast_lowering/src/item.rs | 46 ++++++------ compiler/rustc_ast_passes/src/ast_validation.rs | 32 +++++---- compiler/rustc_ast_passes/src/feature_gate.rs | 4 +- compiler/rustc_ast_pretty/src/pprust/state/item.rs | 11 +-- compiler/rustc_borrowck/src/diagnostics/mod.rs | 4 +- .../src/diagnostics/region_errors.rs | 14 ++-- .../src/deriving/coerce_pointee.rs | 25 ++++--- .../src/deriving/generic/mod.rs | 28 ++------ compiler/rustc_hir/src/hir.rs | 68 +++++++++--------- compiler/rustc_hir/src/intravisit.rs | 14 ++-- .../src/check/compare_impl_item.rs | 4 +- compiler/rustc_hir_analysis/src/check/wfcheck.rs | 8 +-- .../src/collect/predicates_of.rs | 21 +++--- .../src/collect/resolve_bound_vars.rs | 15 ++-- .../src/hir_ty_lowering/bounds.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 81 +++++++++++----------- compiler/rustc_hir_typeck/src/expr.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 6 +- compiler/rustc_lint/src/builtin.rs | 30 ++++---- compiler/rustc_lint/src/lints.rs | 2 +- compiler/rustc_lint/src/unused.rs | 6 +- compiler/rustc_middle/src/hir/map/mod.rs | 4 +- compiler/rustc_middle/src/ty/diagnostics.rs | 4 +- compiler/rustc_parse/src/parser/generics.rs | 62 ++++++++--------- compiler/rustc_passes/src/input_stats.rs | 11 ++- compiler/rustc_resolve/src/late.rs | 13 ++-- compiler/rustc_resolve/src/late/diagnostics.rs | 37 +++++----- .../src/error_reporting/traits/suggestions.rs | 24 ++++--- .../src/traits/dyn_compatibility.rs | 4 +- src/librustdoc/clean/mod.rs | 10 +-- .../src/extra_unused_type_parameters.rs | 7 +- .../clippy_lints/src/implied_bounds_in_impls.rs | 4 +- src/tools/clippy/clippy_lints/src/lifetimes.rs | 10 +-- .../clippy_lints/src/multiple_bound_locations.rs | 10 +-- .../clippy_lints/src/needless_maybe_sized.rs | 4 +- src/tools/clippy/clippy_lints/src/trait_bounds.rs | 22 +++--- src/tools/clippy/clippy_utils/src/ast_utils.rs | 4 +- src/tools/rustfmt/src/spanned.rs | 6 +- src/tools/rustfmt/src/types.rs | 11 ++- tests/ui/stats/input-stats.stderr | 46 ++++++------ 44 files changed, 395 insertions(+), 409 deletions(-) (limited to 'compiler/rustc_parse/src/parser') diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 792de77e9d4..5b5ffe11379 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -428,7 +428,15 @@ impl Default for WhereClause { /// A single predicate in a where-clause. #[derive(Clone, Encodable, Decodable, Debug)] -pub enum WherePredicate { +pub struct WherePredicate { + pub kind: WherePredicateKind, + pub id: NodeId, + pub span: Span, +} + +/// Predicate kind in where-clause. +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum WherePredicateKind { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate), /// A lifetime predicate (e.g., `'a: 'b + 'c`). @@ -437,22 +445,11 @@ pub enum WherePredicate { EqPredicate(WhereEqPredicate), } -impl WherePredicate { - pub fn span(&self) -> Span { - match self { - WherePredicate::BoundPredicate(p) => p.span, - WherePredicate::RegionPredicate(p) => p.span, - WherePredicate::EqPredicate(p) => p.span, - } - } -} - /// A type bound. /// /// E.g., `for<'c> Foo: Send + Clone + 'c`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereBoundPredicate { - pub span: Span, /// Any generics from a `for` binding. pub bound_generic_params: ThinVec, /// The type being bounded. @@ -466,7 +463,6 @@ pub struct WhereBoundPredicate { /// E.g., `'a: 'b + 'c`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereRegionPredicate { - pub span: Span, pub lifetime: Lifetime, pub bounds: GenericBounds, } @@ -476,7 +472,6 @@ pub struct WhereRegionPredicate { /// E.g., `T = int`. #[derive(Clone, Encodable, Decodable, Debug)] pub struct WhereEqPredicate { - pub span: Span, pub lhs_ty: P, pub rhs_ty: P, } diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 198e1bca774..0aceed45028 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -332,7 +332,11 @@ pub trait MutVisitor: Sized { } fn visit_where_predicate(&mut self, where_predicate: &mut WherePredicate) { - walk_where_predicate(self, where_predicate); + walk_where_predicate(self, where_predicate) + } + + fn visit_where_predicate_kind(&mut self, kind: &mut WherePredicateKind) { + walk_where_predicate_kind(self, kind) } fn visit_vis(&mut self, vis: &mut Visibility) { @@ -1065,26 +1069,30 @@ fn walk_where_clause(vis: &mut T, wc: &mut WhereClause) { vis.visit_span(span); } -fn walk_where_predicate(vis: &mut T, pred: &mut WherePredicate) { - match pred { - WherePredicate::BoundPredicate(bp) => { - let WhereBoundPredicate { span, bound_generic_params, bounded_ty, bounds } = bp; +pub fn walk_where_predicate(vis: &mut T, pred: &mut WherePredicate) { + let WherePredicate { kind, id, span } = pred; + vis.visit_id(id); + vis.visit_where_predicate_kind(kind); + vis.visit_span(span); +} + +pub fn walk_where_predicate_kind(vis: &mut T, kind: &mut WherePredicateKind) { + match kind { + WherePredicateKind::BoundPredicate(bp) => { + let WhereBoundPredicate { bound_generic_params, bounded_ty, bounds } = bp; bound_generic_params.flat_map_in_place(|param| vis.flat_map_generic_param(param)); vis.visit_ty(bounded_ty); visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound)); - vis.visit_span(span); } - WherePredicate::RegionPredicate(rp) => { - let WhereRegionPredicate { span, lifetime, bounds } = rp; + WherePredicateKind::RegionPredicate(rp) => { + let WhereRegionPredicate { lifetime, bounds } = rp; vis.visit_lifetime(lifetime); visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Bound)); - vis.visit_span(span); } - WherePredicate::EqPredicate(ep) => { - let WhereEqPredicate { span, lhs_ty, rhs_ty } = ep; + WherePredicateKind::EqPredicate(ep) => { + let WhereEqPredicate { lhs_ty, rhs_ty } = ep; vis.visit_ty(lhs_ty); vis.visit_ty(rhs_ty); - vis.visit_span(span); } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 20ac9fa02bb..718397e8ca0 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -192,6 +192,9 @@ pub trait Visitor<'ast>: Sized { fn visit_where_predicate(&mut self, p: &'ast WherePredicate) -> Self::Result { walk_where_predicate(self, p) } + fn visit_where_predicate_kind(&mut self, k: &'ast WherePredicateKind) -> Self::Result { + walk_where_predicate_kind(self, k) + } fn visit_fn(&mut self, fk: FnKind<'ast>, _: Span, _: NodeId) -> Self::Result { walk_fn(self, fk) } @@ -794,22 +797,29 @@ pub fn walk_where_predicate<'a, V: Visitor<'a>>( visitor: &mut V, predicate: &'a WherePredicate, ) -> V::Result { - match predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { + let WherePredicate { kind, id: _, span: _ } = predicate; + visitor.visit_where_predicate_kind(kind) +} + +pub fn walk_where_predicate_kind<'a, V: Visitor<'a>>( + visitor: &mut V, + kind: &'a WherePredicateKind, +) -> V::Result { + match kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { bounded_ty, bounds, bound_generic_params, - span: _, }) => { walk_list!(visitor, visit_generic_param, bound_generic_params); try_visit!(visitor.visit_ty(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } - WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span: _ }) => { + WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { try_visit!(visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound)); walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound); } - WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span: _ }) => { + WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => { try_visit!(visitor.visit_ty(lhs_ty)); try_visit!(visitor.visit_ty(rhs_ty)); } diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 6289966561f..65e387de800 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -381,15 +381,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { } fn visit_where_predicate(&mut self, predicate: &'hir WherePredicate<'hir>) { - match predicate { - WherePredicate::BoundPredicate(pred) => { - self.insert(pred.span, pred.hir_id, Node::WhereBoundPredicate(pred)); - self.with_parent(pred.hir_id, |this| { - intravisit::walk_where_predicate(this, predicate) - }) - } - _ => intravisit::walk_where_predicate(self, predicate), - } + self.insert(predicate.span, predicate.hir_id, Node::WherePredicate(predicate)); + self.with_parent(predicate.hir_id, |this| { + intravisit::walk_where_predicate(this, predicate) + }); } fn visit_array_length(&mut self, len: &'hir ArrayLen<'hir>) { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 48b42fa2e97..fb09f1c7fee 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -1401,7 +1401,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // keep track of the Span info. Now, `::add_implicit_sized_bound` // checks both param bounds and where clauses for `?Sized`. for pred in &generics.where_clause.predicates { - let WherePredicate::BoundPredicate(bound_pred) = pred else { + let WherePredicateKind::BoundPredicate(bound_pred) = &pred.kind else { continue; }; let compute_is_param = || { @@ -1538,9 +1538,9 @@ impl<'hir> LoweringContext<'_, 'hir> { } }); let span = self.lower_span(span); - - match kind { - GenericParamKind::Const { .. } => None, + let hir_id = self.next_id(); + let kind = self.arena.alloc(match kind { + GenericParamKind::Const { .. } => return None, GenericParamKind::Type { .. } => { let def_id = self.local_def_id(id).to_def_id(); let hir_id = self.next_id(); @@ -1555,38 +1555,36 @@ impl<'hir> LoweringContext<'_, 'hir> { let ty_id = self.next_id(); let bounded_ty = self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path)); - Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id: self.next_id(), + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounded_ty: self.arena.alloc(bounded_ty), bounds, - span, bound_generic_params: &[], origin, - })) + }) } GenericParamKind::Lifetime => { let ident = self.lower_ident(ident); let lt_id = self.next_node_id(); let lifetime = self.new_named_lifetime(id, lt_id, ident); - Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, - span, bounds, in_where_clause: false, - })) + }) } - } + }); + Some(hir::WherePredicate { hir_id, span, kind }) } fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> { - match pred { - WherePredicate::BoundPredicate(WhereBoundPredicate { + let hir_id = self.lower_node_id(pred.id); + let span = self.lower_span(pred.span); + let kind = self.arena.alloc(match &pred.kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { bound_generic_params, bounded_ty, bounds, - span, - }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id: self.next_id(), + }) => hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bound_generic_params: self .lower_generic_params(bound_generic_params, hir::GenericParamSource::Binder), bounded_ty: self @@ -1595,12 +1593,10 @@ impl<'hir> LoweringContext<'_, 'hir> { bounds, ImplTraitContext::Disallowed(ImplTraitPosition::Bound), ), - span: self.lower_span(*span), origin: PredicateOrigin::WhereClause, }), - WherePredicate::RegionPredicate(WhereRegionPredicate { lifetime, bounds, span }) => { - hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - span: self.lower_span(*span), + WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => { + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime: self.lower_lifetime(lifetime), bounds: self.lower_param_bounds( bounds, @@ -1609,15 +1605,15 @@ impl<'hir> LoweringContext<'_, 'hir> { in_where_clause: true, }) } - WherePredicate::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty, span }) => { - hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { + WherePredicateKind::EqPredicate(WhereEqPredicate { lhs_ty, rhs_ty }) => { + hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { lhs_ty: self .lower_ty(lhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), rhs_ty: self .lower_ty(rhs_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Bound)), - span: self.lower_span(*span), }) } - } + }); + hir::WherePredicate { hir_id, span, kind } } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 07a6f4e5ee7..64e91c91e2c 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1200,14 +1200,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> { validate_generic_param_order(self.dcx(), &generics.params, generics.span); for predicate in &generics.where_clause.predicates { - if let WherePredicate::EqPredicate(predicate) = predicate { - deny_equality_constraints(self, predicate, generics); + let span = predicate.span; + if let WherePredicateKind::EqPredicate(predicate) = &predicate.kind { + deny_equality_constraints(self, predicate, span, generics); } } walk_list!(self, visit_generic_param, &generics.params); for predicate in &generics.where_clause.predicates { - match predicate { - WherePredicate::BoundPredicate(bound_pred) => { + match &predicate.kind { + WherePredicateKind::BoundPredicate(bound_pred) => { // This is slightly complicated. Our representation for poly-trait-refs contains a single // binder and thus we only allow a single level of quantification. However, // the syntax of Rust permits quantification in two places in where clauses, @@ -1504,9 +1505,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn deny_equality_constraints( this: &AstValidator<'_>, predicate: &WhereEqPredicate, + predicate_span: Span, generics: &Generics, ) { - let mut err = errors::EqualityInWhere { span: predicate.span, assoc: None, assoc2: None }; + let mut err = errors::EqualityInWhere { span: predicate_span, assoc: None, assoc2: None }; // Given `::Bar = RhsTy`, suggest `A: Foo`. if let TyKind::Path(Some(qself), full_path) = &predicate.lhs_ty.kind @@ -1550,7 +1552,7 @@ fn deny_equality_constraints( } } err.assoc = Some(errors::AssociatedSuggestion { - span: predicate.span, + span: predicate_span, ident: *ident, param: param.ident, path: pprust::path_to_string(&assoc_path), @@ -1580,23 +1582,23 @@ fn deny_equality_constraints( // We're removing th eonly where bound left, remove the whole thing. generics.where_clause.span } else { - let mut span = predicate.span; + let mut span = predicate_span; let mut prev: Option = None; let mut preds = generics.where_clause.predicates.iter().peekable(); // Find the predicate that shouldn't have been in the where bound list. while let Some(pred) = preds.next() { - if let WherePredicate::EqPredicate(pred) = pred - && pred.span == predicate.span + if let WherePredicateKind::EqPredicate(_) = pred.kind + && pred.span == predicate_span { if let Some(next) = preds.peek() { // This is the first predicate, remove the trailing comma as well. - span = span.with_hi(next.span().lo()); + span = span.with_hi(next.span.lo()); } else if let Some(prev) = prev { // Remove the previous comma as well. span = span.with_lo(prev.hi()); } } - prev = Some(pred.span()); + prev = Some(pred.span); } span }; @@ -1613,8 +1615,8 @@ fn deny_equality_constraints( if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo`. for bounds in generics.params.iter().map(|p| &p.bounds).chain( - generics.where_clause.predicates.iter().filter_map(|pred| match pred { - WherePredicate::BoundPredicate(p) => Some(&p.bounds), + generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind { + WherePredicateKind::BoundPredicate(p) => Some(&p.bounds), _ => None, }), ) { @@ -1637,8 +1639,8 @@ fn deny_equality_constraints( // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo`. if let [potential_param, potential_assoc] = &full_path.segments[..] { for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain( - generics.where_clause.predicates.iter().filter_map(|pred| match pred { - WherePredicate::BoundPredicate(p) + generics.where_clause.predicates.iter().filter_map(|pred| match &pred.kind { + WherePredicateKind::BoundPredicate(p) if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind && let [segment] = &path.segments[..] => { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8a392e4407b..8cdc7133cc0 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -345,8 +345,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_generics(&mut self, g: &'a ast::Generics) { for predicate in &g.where_clause.predicates { - match predicate { - ast::WherePredicate::BoundPredicate(bound_pred) => { + match &predicate.kind { + ast::WherePredicateKind::BoundPredicate(bound_pred) => { // A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). self.check_late_bound_lifetime_defs(&bound_pred.bound_generic_params); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 8279c66836c..1ae765c0130 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -726,11 +726,12 @@ impl<'a> State<'a> { } pub fn print_where_predicate(&mut self, predicate: &ast::WherePredicate) { - match predicate { - ast::WherePredicate::BoundPredicate(where_bound_predicate) => { + let ast::WherePredicate { kind, id: _, span: _ } = predicate; + match kind { + ast::WherePredicateKind::BoundPredicate(where_bound_predicate) => { self.print_where_bound_predicate(where_bound_predicate); } - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { lifetime, bounds, .. @@ -742,7 +743,9 @@ impl<'a> State<'a> { self.print_lifetime_bounds(bounds); } } - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { self.print_type(lhs_ty); self.space(); self.word_space("="); diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index bda96726738..92afad62aa4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1062,8 +1062,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && let spans = hir_generics .predicates .iter() - .filter_map(|pred| match pred { - hir::WherePredicate::BoundPredicate(pred) => Some(pred), + .filter_map(|pred| match pred.kind { + hir::WherePredicateKind::BoundPredicate(pred) => Some(pred), _ => None, }) .filter(|pred| { diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index c38747f6675..6ea5c44dc01 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -5,7 +5,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::GenericBound::Trait; use rustc_hir::QPath::Resolved; -use rustc_hir::WherePredicate::BoundPredicate; +use rustc_hir::WherePredicateKind::BoundPredicate; use rustc_hir::def::Res::Def; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; @@ -236,7 +236,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let mut hrtb_bounds = vec![]; gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| { for pred in generics.predicates { - let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = pred + let BoundPredicate(WhereBoundPredicate { bound_generic_params, bounds, .. }) = + pred.kind else { continue; }; @@ -267,12 +268,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }; debug!(?generics_fn); generics_fn.predicates.iter().for_each(|predicate| { - let BoundPredicate(WhereBoundPredicate { - span: bounded_span, - bounded_ty, - bounds, - .. - }) = predicate + let BoundPredicate(WhereBoundPredicate { bounded_ty, bounds, .. }) = predicate.kind else { return; }; @@ -287,7 +283,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .rfind(|param| param.def_id.to_def_id() == defid) .is_some() { - suggestions.push((bounded_span.shrink_to_hi(), " + 'static".to_string())); + suggestions.push((predicate.span.shrink_to_hi(), " + 'static".to_string())); } }); }); diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 53e938ee216..8adb9a3f4b0 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -288,19 +288,18 @@ pub(crate) fn expand_deriving_coerce_pointee( // // We should also write a few new `where` bounds from `#[pointee] T` to `__S` // as well as any bound that indirectly involves the `#[pointee] T` type. - for bound in &generics.where_clause.predicates { - if let ast::WherePredicate::BoundPredicate(bound) = bound { + for predicate in &generics.where_clause.predicates { + if let ast::WherePredicateKind::BoundPredicate(bound) = &predicate.kind { let mut substitution = TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false, }; - let mut predicate = ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span: bound.span, - bound_generic_params: bound.bound_generic_params.clone(), - bounded_ty: bound.bounded_ty.clone(), - bounds: bound.bounds.clone(), - }); + let mut predicate = ast::WherePredicate { + kind: ast::WherePredicateKind::BoundPredicate(bound.clone()), + span: predicate.span, + id: ast::DUMMY_NODE_ID, + }; substitution.visit_where_predicate(&mut predicate); if substitution.rewritten { impl_generics.where_clause.predicates.push(predicate); @@ -319,7 +318,7 @@ pub(crate) fn expand_deriving_coerce_pointee( fn contains_maybe_sized_bound_on_pointee(predicates: &[WherePredicate], pointee: Symbol) -> bool { for bound in predicates { - if let ast::WherePredicate::BoundPredicate(bound) = bound + if let ast::WherePredicateKind::BoundPredicate(bound) = &bound.kind && bound.bounded_ty.kind.is_simple_path().is_some_and(|name| name == pointee) { for bound in &bound.bounds { @@ -385,8 +384,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { } fn visit_where_predicate(&mut self, where_predicate: &mut ast::WherePredicate) { - match where_predicate { - rustc_ast::WherePredicate::BoundPredicate(bound) => { + match &mut where_predicate.kind { + rustc_ast::WherePredicateKind::BoundPredicate(bound) => { bound .bound_generic_params .flat_map_in_place(|param| self.flat_map_generic_param(param)); @@ -395,8 +394,8 @@ impl<'a> ast::mut_visit::MutVisitor for TypeSubstitution<'a> { self.visit_param_bound(bound, BoundKind::Bound) } } - rustc_ast::WherePredicate::RegionPredicate(_) - | rustc_ast::WherePredicate::EqPredicate(_) => {} + rustc_ast::WherePredicateKind::RegionPredicate(_) + | rustc_ast::WherePredicateKind::EqPredicate(_) => {} } } } diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 79c198ed2d0..f6eea0b21ca 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -690,25 +690,10 @@ impl<'a> TraitDef<'a> { // and similarly for where clauses where_clause.predicates.extend(generics.where_clause.predicates.iter().map(|clause| { - match clause { - ast::WherePredicate::BoundPredicate(wb) => { - let span = wb.span.with_ctxt(ctxt); - ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span, - ..wb.clone() - }) - } - ast::WherePredicate::RegionPredicate(wr) => { - let span = wr.span.with_ctxt(ctxt); - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { - span, - ..wr.clone() - }) - } - ast::WherePredicate::EqPredicate(we) => { - let span = we.span.with_ctxt(ctxt); - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { span, ..we.clone() }) - } + ast::WherePredicate { + kind: clause.kind.clone(), + id: ast::DUMMY_NODE_ID, + span: clause.span.with_ctxt(ctxt), } })); @@ -757,13 +742,14 @@ impl<'a> TraitDef<'a> { if !bounds.is_empty() { let predicate = ast::WhereBoundPredicate { - span: self.span, bound_generic_params: field_ty_param.bound_generic_params, bounded_ty: field_ty_param.ty, bounds, }; - let predicate = ast::WherePredicate::BoundPredicate(predicate); + let kind = ast::WherePredicateKind::BoundPredicate(predicate); + let predicate = + ast::WherePredicate { kind, id: ast::DUMMY_NODE_ID, span: self.span }; where_clause.predicates.push(predicate); } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 16e53a27128..d9be7508cbc 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -690,8 +690,8 @@ impl<'hir> Generics<'hir> { if self.has_where_clause_predicates { self.predicates .iter() - .rfind(|&p| p.in_where_clause()) - .map_or(end, |p| p.span()) + .rfind(|&p| p.kind.in_where_clause()) + .map_or(end, |p| p.span) .shrink_to_hi() .to(end) } else { @@ -714,8 +714,10 @@ impl<'hir> Generics<'hir> { &self, param_def_id: LocalDefId, ) -> impl Iterator> { - self.predicates.iter().filter_map(move |pred| match pred { - WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => { + self.predicates.iter().filter_map(move |pred| match pred.kind { + WherePredicateKind::BoundPredicate(bp) + if bp.is_param_bound(param_def_id.to_def_id()) => + { Some(bp) } _ => None, @@ -726,8 +728,8 @@ impl<'hir> Generics<'hir> { &self, param_def_id: LocalDefId, ) -> impl Iterator> { - self.predicates.iter().filter_map(move |pred| match pred { - WherePredicate::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp), + self.predicates.iter().filter_map(move |pred| match pred.kind { + WherePredicateKind::RegionPredicate(rp) if rp.is_param_bound(param_def_id) => Some(rp), _ => None, }) } @@ -779,9 +781,9 @@ impl<'hir> Generics<'hir> { pub fn span_for_predicate_removal(&self, pos: usize) -> Span { let predicate = &self.predicates[pos]; - let span = predicate.span(); + let span = predicate.span; - if !predicate.in_where_clause() { + if !predicate.kind.in_where_clause() { // // ^^^^^^^^ return span; @@ -790,19 +792,19 @@ impl<'hir> Generics<'hir> { // We need to find out which comma to remove. if pos < self.predicates.len() - 1 { let next_pred = &self.predicates[pos + 1]; - if next_pred.in_where_clause() { + if next_pred.kind.in_where_clause() { // where T: ?Sized, Foo: Bar, // ^^^^^^^^^^^ - return span.until(next_pred.span()); + return span.until(next_pred.span); } } if pos > 0 { let prev_pred = &self.predicates[pos - 1]; - if prev_pred.in_where_clause() { + if prev_pred.kind.in_where_clause() { // where Foo: Bar, T: ?Sized, // ^^^^^^^^^^^ - return prev_pred.span().shrink_to_hi().to(span); + return prev_pred.span.shrink_to_hi().to(span); } } @@ -814,7 +816,7 @@ impl<'hir> Generics<'hir> { pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span { let predicate = &self.predicates[predicate_pos]; - let bounds = predicate.bounds(); + let bounds = predicate.kind.bounds(); if bounds.len() == 1 { return self.span_for_predicate_removal(predicate_pos); @@ -841,7 +843,15 @@ impl<'hir> Generics<'hir> { /// A single predicate in a where-clause. #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub enum WherePredicate<'hir> { +pub struct WherePredicate<'hir> { + pub hir_id: HirId, + pub span: Span, + pub kind: &'hir WherePredicateKind<'hir>, +} + +/// The kind of a single predicate in a where-clause. +#[derive(Debug, Clone, Copy, HashStable_Generic)] +pub enum WherePredicateKind<'hir> { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). BoundPredicate(WhereBoundPredicate<'hir>), /// A lifetime predicate (e.g., `'a: 'b + 'c`). @@ -850,28 +860,20 @@ pub enum WherePredicate<'hir> { EqPredicate(WhereEqPredicate<'hir>), } -impl<'hir> WherePredicate<'hir> { - pub fn span(&self) -> Span { - match self { - WherePredicate::BoundPredicate(p) => p.span, - WherePredicate::RegionPredicate(p) => p.span, - WherePredicate::EqPredicate(p) => p.span, - } - } - +impl<'hir> WherePredicateKind<'hir> { pub fn in_where_clause(&self) -> bool { match self { - WherePredicate::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause, - WherePredicate::RegionPredicate(p) => p.in_where_clause, - WherePredicate::EqPredicate(_) => false, + WherePredicateKind::BoundPredicate(p) => p.origin == PredicateOrigin::WhereClause, + WherePredicateKind::RegionPredicate(p) => p.in_where_clause, + WherePredicateKind::EqPredicate(_) => false, } } pub fn bounds(&self) -> GenericBounds<'hir> { match self { - WherePredicate::BoundPredicate(p) => p.bounds, - WherePredicate::RegionPredicate(p) => p.bounds, - WherePredicate::EqPredicate(_) => &[], + WherePredicateKind::BoundPredicate(p) => p.bounds, + WherePredicateKind::RegionPredicate(p) => p.bounds, + WherePredicateKind::EqPredicate(_) => &[], } } } @@ -886,8 +888,6 @@ pub enum PredicateOrigin { /// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`). #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereBoundPredicate<'hir> { - pub hir_id: HirId, - pub span: Span, /// Origin of the predicate. pub origin: PredicateOrigin, /// Any generics from a `for` binding. @@ -908,7 +908,6 @@ impl<'hir> WhereBoundPredicate<'hir> { /// A lifetime predicate (e.g., `'a: 'b + 'c`). #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereRegionPredicate<'hir> { - pub span: Span, pub in_where_clause: bool, pub lifetime: &'hir Lifetime, pub bounds: GenericBounds<'hir>, @@ -924,7 +923,6 @@ impl<'hir> WhereRegionPredicate<'hir> { /// An equality predicate (e.g., `T = int`); currently unsupported. #[derive(Debug, Clone, Copy, HashStable_Generic)] pub struct WhereEqPredicate<'hir> { - pub span: Span, pub lhs_ty: &'hir Ty<'hir>, pub rhs_ty: &'hir Ty<'hir>, } @@ -3798,7 +3796,7 @@ pub enum Node<'hir> { GenericParam(&'hir GenericParam<'hir>), Crate(&'hir Mod<'hir>), Infer(&'hir InferArg), - WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), + WherePredicate(&'hir WherePredicate<'hir>), // FIXME: Merge into `Node::Infer`. ArrayLenInfer(&'hir InferArg), PreciseCapturingNonLifetimeArg(&'hir PreciseCapturingNonLifetimeArg), @@ -3853,7 +3851,7 @@ impl<'hir> Node<'hir> { | Node::TraitRef(..) | Node::OpaqueTy(..) | Node::Infer(..) - | Node::WhereBoundPredicate(..) + | Node::WherePredicate(..) | Node::ArrayLenInfer(..) | Node::Synthetic | Node::Err(..) => None, diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a453af3f7fd..faaea41047f 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -961,30 +961,28 @@ pub fn walk_where_predicate<'v, V: Visitor<'v>>( visitor: &mut V, predicate: &'v WherePredicate<'v>, ) -> V::Result { - match *predicate { - WherePredicate::BoundPredicate(WhereBoundPredicate { - hir_id, + let &WherePredicate { hir_id, kind, span: _ } = predicate; + try_visit!(visitor.visit_id(hir_id)); + match *kind { + WherePredicateKind::BoundPredicate(WhereBoundPredicate { ref bounded_ty, bounds, bound_generic_params, origin: _, - span: _, }) => { - try_visit!(visitor.visit_id(hir_id)); try_visit!(visitor.visit_ty(bounded_ty)); walk_list!(visitor, visit_param_bound, bounds); walk_list!(visitor, visit_generic_param, bound_generic_params); } - WherePredicate::RegionPredicate(WhereRegionPredicate { + WherePredicateKind::RegionPredicate(WhereRegionPredicate { ref lifetime, bounds, - span: _, in_where_clause: _, }) => { try_visit!(visitor.visit_lifetime(lifetime)); walk_list!(visitor, visit_param_bound, bounds); } - WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, span: _ }) => { + WherePredicateKind::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty }) => { try_visit!(visitor.visit_ty(lhs_ty)); try_visit!(visitor.visit_ty(rhs_ty)); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index f86ca95a954..2891b48a154 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1100,7 +1100,7 @@ fn check_region_bounds_on_impl_item<'tcx>( // FIXME: we could potentially look at the impl's bounds to not point at bounds that // *are* present in the impl. for p in trait_generics.predicates { - if let hir::WherePredicate::BoundPredicate(pred) = p { + if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { for b in pred.bounds { if let hir::GenericBound::Outlives(lt) = b { bounds_span.push(lt.ident.span); @@ -1113,7 +1113,7 @@ fn check_region_bounds_on_impl_item<'tcx>( { let mut impl_bounds = 0; for p in impl_generics.predicates { - if let hir::WherePredicate::BoundPredicate(pred) = p { + if let hir::WherePredicateKind::BoundPredicate(pred) = p.kind { for b in pred.bounds { if let hir::GenericBound::Outlives(_) = b { impl_bounds += 1; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 9a70555fede..8fa797db246 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1921,8 +1921,8 @@ fn check_variances_for_type_defn<'tcx>( hir_generics .predicates .iter() - .filter_map(|predicate| match predicate { - hir::WherePredicate::BoundPredicate(predicate) => { + .filter_map(|predicate| match predicate.kind { + hir::WherePredicateKind::BoundPredicate(predicate) => { match icx.lower_ty(predicate.bounded_ty).kind() { ty::Param(data) => Some(Parameter(data.index)), _ => None, @@ -2202,8 +2202,8 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { span = predicates .iter() // There seems to be no better way to find out which predicate we are in - .find(|pred| pred.span().contains(obligation_span)) - .map(|pred| pred.span()) + .find(|pred| pred.span.contains(obligation_span)) + .map(|pred| pred.span) .unwrap_or(obligation_span); } diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs index b5dee5bd021..bfee5d33598 100644 --- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs @@ -238,11 +238,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen trace!(?predicates); // Add inline `` bounds and bounds in the where clause. for predicate in hir_generics.predicates { - match predicate { - hir::WherePredicate::BoundPredicate(bound_pred) => { + match predicate.kind { + hir::WherePredicateKind::BoundPredicate(bound_pred) => { let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); - let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); + let bound_vars = tcx.late_bound_vars(predicate.hir_id); // Keep the type around in a dummy predicate, in case of no bounds. // That way, `where Ty:` is not a complete noop (see #53696) and `Ty` // is still checked for WF. @@ -275,7 +275,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen predicates.extend(bounds.clauses()); } - hir::WherePredicate::RegionPredicate(region_pred) => { + hir::WherePredicateKind::RegionPredicate(region_pred) => { let r1 = icx .lowerer() .lower_lifetime(region_pred.lifetime, RegionInferReason::RegionPredicate); @@ -298,7 +298,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen })) } - hir::WherePredicate::EqPredicate(..) => { + hir::WherePredicateKind::EqPredicate(..) => { // FIXME(#20041) } } @@ -881,7 +881,8 @@ impl<'tcx> ItemCtxt<'tcx> { let mut bounds = Bounds::default(); for predicate in hir_generics.predicates { - let hir::WherePredicate::BoundPredicate(predicate) = predicate else { + let hir_id = predicate.hir_id; + let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind else { continue; }; @@ -901,7 +902,7 @@ impl<'tcx> ItemCtxt<'tcx> { let bound_ty = self.lowerer().lower_ty_maybe_return_type_notation(predicate.bounded_ty); - let bound_vars = self.tcx.late_bound_vars(predicate.hir_id); + let bound_vars = self.tcx.late_bound_vars(hir_id); self.lowerer().lower_bounds( bound_ty, predicate.bounds, @@ -974,10 +975,10 @@ pub(super) fn const_conditions<'tcx>( let mut bounds = Bounds::default(); for pred in generics.predicates { - match pred { - hir::WherePredicate::BoundPredicate(bound_pred) => { + match pred.kind { + hir::WherePredicateKind::BoundPredicate(bound_pred) => { let ty = icx.lowerer().lower_ty_maybe_return_type_notation(bound_pred.bounded_ty); - let bound_vars = tcx.late_bound_vars(bound_pred.hir_id); + let bound_vars = tcx.late_bound_vars(pred.hir_id); icx.lowerer().lower_bounds( ty, bound_pred.bounds.iter(), diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index b4b3ef31f97..74729ebe488 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -936,9 +936,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { } fn visit_where_predicate(&mut self, predicate: &'tcx hir::WherePredicate<'tcx>) { - match predicate { - &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - hir_id, + let hir_id = predicate.hir_id; + match predicate.kind { + &hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounded_ty, bounds, bound_generic_params, @@ -979,7 +979,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { walk_list!(this, visit_param_bound, bounds); }) } - &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { + &hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { lifetime, bounds, .. @@ -987,7 +987,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> { self.visit_lifetime(lifetime); walk_list!(self, visit_param_bound, bounds); } - &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { lhs_ty, rhs_ty, .. }) => { + &hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { self.visit_ty(lhs_ty); self.visit_ty(rhs_ty); } @@ -2073,7 +2075,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> { // bounds since we'll be emitting a hard error in HIR lowering, so this // is purely speculative. let one_bound = generics.predicates.iter().find_map(|predicate| { - let hir::WherePredicate::BoundPredicate(predicate) = predicate else { + let hir::WherePredicateKind::BoundPredicate(predicate) = predicate.kind + else { return None; }; let hir::TyKind::Path(hir::QPath::Resolved(None, bounded_path)) = diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6ebe1cedcaf..9d60759ae48 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -69,7 +69,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { search_bounds(hir_bounds); if let Some((self_ty, where_clause)) = self_ty_where_predicates { for clause in where_clause { - if let hir::WherePredicate::BoundPredicate(pred) = clause + if let hir::WherePredicateKind::BoundPredicate(pred) = clause.kind && pred.is_param_bound(self_ty.to_def_id()) { search_bounds(pred.bounds); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 0a3aa8fec90..1dedf11f7ad 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -117,11 +117,7 @@ impl<'a> State<'a> { Node::Ctor(..) => panic!("cannot print isolated Ctor"), Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), - Node::WhereBoundPredicate(pred) => { - self.print_formal_generic_params(pred.bound_generic_params); - self.print_type(pred.bounded_ty); - self.print_bounds(":", pred.bounds); - } + Node::WherePredicate(pred) => self.print_where_predicate(pred), Node::ArrayLenInfer(_) => self.word("_"), Node::Synthetic => unreachable!(), Node::Err(_) => self.word("/*ERROR*/"), @@ -2160,47 +2156,50 @@ impl<'a> State<'a> { if i != 0 { self.word_space(","); } + self.print_where_predicate(predicate); + } + } - match *predicate { - hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bound_generic_params, - bounded_ty, - bounds, - .. - }) => { - self.print_formal_generic_params(bound_generic_params); - self.print_type(bounded_ty); - self.print_bounds(":", bounds); - } - hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate { - lifetime, - bounds, - .. - }) => { - self.print_lifetime(lifetime); - self.word(":"); - - for (i, bound) in bounds.iter().enumerate() { - match bound { - GenericBound::Outlives(lt) => { - self.print_lifetime(lt); - } - _ => panic!("unexpected bound on lifetime param: {bound:?}"), - } + fn print_where_predicate(&mut self, predicate: &hir::WherePredicate<'_>) { + match *predicate.kind { + hir::WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { + bound_generic_params, + bounded_ty, + bounds, + .. + }) => { + self.print_formal_generic_params(bound_generic_params); + self.print_type(bounded_ty); + self.print_bounds(":", bounds); + } + hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate { + lifetime, + bounds, + .. + }) => { + self.print_lifetime(lifetime); + self.word(":"); - if i != 0 { - self.word(":"); + for (i, bound) in bounds.iter().enumerate() { + match bound { + GenericBound::Outlives(lt) => { + self.print_lifetime(lt); } + _ => panic!("unexpected bound on lifetime param: {bound:?}"), + } + + if i != 0 { + self.word(":"); } } - hir::WherePredicate::EqPredicate(hir::WhereEqPredicate { - lhs_ty, rhs_ty, .. - }) => { - self.print_type(lhs_ty); - self.space(); - self.word_space("="); - self.print_type(rhs_ty); - } + } + hir::WherePredicateKind::EqPredicate(hir::WhereEqPredicate { + lhs_ty, rhs_ty, .. + }) => { + self.print_type(lhs_ty); + self.space(); + self.word_space("="); + self.print_type(rhs_ty); } } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 1610848958e..6599b49baa3 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -420,7 +420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | hir::Node::GenericParam(_) | hir::Node::Crate(_) | hir::Node::Infer(_) - | hir::Node::WhereBoundPredicate(_) + | hir::Node::WherePredicate(_) | hir::Node::ArrayLenInfer(_) | hir::Node::PreciseCapturingNonLifetimeArg(_) | hir::Node::OpaqueTy(_) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index c4c4c2f200b..707d98d8311 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -10,7 +10,7 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, + Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -1004,8 +1004,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // get all where BoundPredicates here, because they are used in two cases below let where_predicates = predicates .iter() - .filter_map(|p| match p { - WherePredicate::BoundPredicate(hir::WhereBoundPredicate { + .filter_map(|p| match p.kind { + WherePredicateKind::BoundPredicate(hir::WhereBoundPredicate { bounds, bounded_ty, .. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index bda982a3675..e130cfc1d73 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1407,7 +1407,7 @@ declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]); impl TypeAliasBounds { pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults. - if let hir::WherePredicate::BoundPredicate(pred) = pred + if let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS && pred.bounded_ty.as_generic_param().is_some() @@ -1451,11 +1451,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut inline_sugg = Vec::new(); for p in generics.predicates { - let span = p.span(); - if p.in_where_clause() { + let span = p.span; + if p.kind.in_where_clause() { where_spans.push(span); } else { - for b in p.bounds() { + for b in p.kind.bounds() { inline_spans.push(b.span()); } inline_sugg.push((span, String::new())); @@ -2071,7 +2071,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let num_where_predicates = hir_generics .predicates .iter() - .filter(|predicate| predicate.in_where_clause()) + .filter(|predicate| predicate.kind.in_where_clause()) .count(); let mut bound_count = 0; @@ -2080,8 +2080,8 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { let mut dropped_where_predicate_count = 0; for (i, where_predicate) in hir_generics.predicates.iter().enumerate() { let (relevant_lifetimes, bounds, predicate_span, in_where_clause) = - match where_predicate { - hir::WherePredicate::RegionPredicate(predicate) => { + match where_predicate.kind { + hir::WherePredicateKind::RegionPredicate(predicate) => { if let Some(ResolvedArg::EarlyBound(region_def_id)) = cx.tcx.named_bound_var(predicate.lifetime.hir_id) { @@ -2090,21 +2090,21 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { cx.tcx, // don't warn if the inferred span actually came from the predicate we're looking at // this happens if the type is recursively defined - inferred_outlives - .iter() - .filter(|(_, span)| !predicate.span.contains(*span)), + inferred_outlives.iter().filter(|(_, span)| { + !where_predicate.span.contains(*span) + }), item.owner_id.def_id, region_def_id, ), &predicate.bounds, - predicate.span, + where_predicate.span, predicate.in_where_clause, ) } else { continue; } } - hir::WherePredicate::BoundPredicate(predicate) => { + hir::WherePredicateKind::BoundPredicate(predicate) => { // FIXME we can also infer bounds on associated types, // and should check for them here. match predicate.bounded_ty.kind { @@ -2118,12 +2118,12 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { // don't warn if the inferred span actually came from the predicate we're looking at // this happens if the type is recursively defined inferred_outlives.iter().filter(|(_, span)| { - !predicate.span.contains(*span) + !where_predicate.span.contains(*span) }), index, ), &predicate.bounds, - predicate.span, + where_predicate.span, predicate.origin == PredicateOrigin::WhereClause, ) } @@ -2161,7 +2161,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { } else if i + 1 < num_where_predicates { // If all the bounds on a predicate were inferable and there are // further predicates, we want to eat the trailing comma. - let next_predicate_span = hir_generics.predicates[i + 1].span(); + let next_predicate_span = hir_generics.predicates[i + 1].span; if next_predicate_span.from_expansion() { where_lint_spans.push(predicate_span); } else { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 352155729e5..dce6010a2c1 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -300,7 +300,7 @@ impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_> { let affect_object_lifetime_defaults = self .preds .iter() - .filter(|pred| pred.in_where_clause() == self.in_where_clause) + .filter(|pred| pred.kind.in_where_clause() == self.in_where_clause) .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred)); // If there are any shorthand assoc tys, then the bounds can't be removed automatically. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5ec920d39f4..e1a0e1ec579 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1304,12 +1304,12 @@ impl EarlyLintPass for UnusedParens { } fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) { - use rustc_ast::{WhereBoundPredicate, WherePredicate}; - if let WherePredicate::BoundPredicate(WhereBoundPredicate { + use rustc_ast::{WhereBoundPredicate, WherePredicateKind}; + if let WherePredicateKind::BoundPredicate(WhereBoundPredicate { bounded_ty, bound_generic_params, .. - }) = pred + }) = &pred.kind && let ast::TyKind::Paren(_) = &bounded_ty.kind && bound_generic_params.is_empty() { diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 007e6f46006..4900575c36e 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -947,7 +947,7 @@ impl<'hir> Map<'hir> { Node::Infer(i) => i.span, Node::LetStmt(local) => local.span, Node::Crate(item) => item.spans.inner_span, - Node::WhereBoundPredicate(pred) => pred.span, + Node::WherePredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, Node::PreciseCapturingNonLifetimeArg(param) => param.ident.span, Node::Synthetic => unreachable!(), @@ -1225,7 +1225,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { format!("{id} (generic_param {})", path_str(param.def_id)) } Node::Crate(..) => String::from("(root_crate)"), - Node::WhereBoundPredicate(_) => node_str("where bound predicate"), + Node::WherePredicate(_) => node_str("where predicate"), Node::ArrayLenInfer(_) => node_str("array len infer"), Node::Synthetic => unreachable!(), Node::Err(_) => node_str("error"), diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 84f52bfe48f..fd807882e0f 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag, DiagArgValue, IntoDiagArg, into_diag_arg_using_display}; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicate}; +use rustc_hir::{self as hir, LangItem, PredicateOrigin, WherePredicateKind}; use rustc_span::{BytePos, Span}; use rustc_type_ir::TyKind::*; @@ -180,7 +180,7 @@ fn suggest_changing_unsized_bound( // First look at the `where` clause because we can have `where T: ?Sized`, // then look at params. for (where_pos, predicate) in generics.predicates.iter().enumerate() { - let WherePredicate::BoundPredicate(predicate) = predicate else { + let WherePredicateKind::BoundPredicate(predicate) = predicate.kind else { continue; }; if !predicate.is_param_bound(param.def_id.to_def_id()) { diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs index 5aebe716b0a..76ecb77d750 100644 --- a/compiler/rustc_parse/src/parser/generics.rs +++ b/compiler/rustc_parse/src/parser/generics.rs @@ -1,6 +1,7 @@ use ast::token::Delimiter; use rustc_ast::{ - self as ast, AttrVec, GenericBounds, GenericParam, GenericParamKind, TyKind, WhereClause, token, + self as ast, AttrVec, DUMMY_NODE_ID, GenericBounds, GenericParam, GenericParamKind, TyKind, + WhereClause, token, }; use rustc_errors::{Applicability, PResult}; use rustc_span::Span; @@ -14,8 +15,8 @@ use crate::errors::{ WhereClauseBeforeTupleStructBodySugg, }; -enum PredicateOrStructBody { - Predicate(ast::WherePredicate), +enum PredicateKindOrStructBody { + PredicateKind(ast::WherePredicateKind), StructBody(ThinVec), } @@ -218,10 +219,11 @@ impl<'a> Parser<'a> { } else if this.token.can_begin_type() { // Trying to write an associated type bound? (#26271) let snapshot = this.create_snapshot_for_diagnostic(); - match this.parse_ty_where_predicate() { - Ok(where_predicate) => { + let lo = this.token.span; + match this.parse_ty_where_predicate_kind() { + Ok(_) => { this.dcx().emit_err(errors::BadAssocTypeBounds { - span: where_predicate.span(), + span: lo.to(this.prev_token.span), }); // FIXME - try to continue parsing other generics? } @@ -340,31 +342,33 @@ impl<'a> Parser<'a> { loop { let where_sp = where_lo.to(self.prev_token.span); let pred_lo = self.token.span; - if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { + let kind = if self.check_lifetime() && self.look_ahead(1, |t| !t.is_like_plus()) { let lifetime = self.expect_lifetime(); // Bounds starting with a colon are mandatory, but possibly empty. self.expect(&token::Colon)?; let bounds = self.parse_lt_param_bounds(); - where_clause.predicates.push(ast::WherePredicate::RegionPredicate( - ast::WhereRegionPredicate { - span: pred_lo.to(self.prev_token.span), - lifetime, - bounds, - }, - )); + ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { + lifetime, + bounds, + }) } else if self.check_type() { - match self.parse_ty_where_predicate_or_recover_tuple_struct_body( + match self.parse_ty_where_predicate_kind_or_recover_tuple_struct_body( struct_, pred_lo, where_sp, )? { - PredicateOrStructBody::Predicate(pred) => where_clause.predicates.push(pred), - PredicateOrStructBody::StructBody(body) => { + PredicateKindOrStructBody::PredicateKind(kind) => kind, + PredicateKindOrStructBody::StructBody(body) => { tuple_struct_body = Some(body); break; } } } else { break; - } + }; + where_clause.predicates.push(ast::WherePredicate { + kind, + id: DUMMY_NODE_ID, + span: pred_lo.to(self.prev_token.span), + }); let prev_token = self.prev_token.span; let ate_comma = self.eat(&token::Comma); @@ -384,12 +388,12 @@ impl<'a> Parser<'a> { Ok((where_clause, tuple_struct_body)) } - fn parse_ty_where_predicate_or_recover_tuple_struct_body( + fn parse_ty_where_predicate_kind_or_recover_tuple_struct_body( &mut self, struct_: Option<(Ident, Span)>, pred_lo: Span, where_sp: Span, - ) -> PResult<'a, PredicateOrStructBody> { + ) -> PResult<'a, PredicateKindOrStructBody> { let mut snapshot = None; if let Some(struct_) = struct_ @@ -399,8 +403,8 @@ impl<'a> Parser<'a> { snapshot = Some((struct_, self.create_snapshot_for_diagnostic())); }; - match self.parse_ty_where_predicate() { - Ok(pred) => Ok(PredicateOrStructBody::Predicate(pred)), + match self.parse_ty_where_predicate_kind() { + Ok(pred) => Ok(PredicateKindOrStructBody::PredicateKind(pred)), Err(type_err) => { let Some(((struct_name, body_insertion_point), mut snapshot)) = snapshot else { return Err(type_err); @@ -436,7 +440,7 @@ impl<'a> Parser<'a> { }); self.restore_snapshot(snapshot); - Ok(PredicateOrStructBody::StructBody(body)) + Ok(PredicateKindOrStructBody::StructBody(body)) } Ok(_) => Err(type_err), Err(body_err) => { @@ -448,8 +452,7 @@ impl<'a> Parser<'a> { } } - fn parse_ty_where_predicate(&mut self) -> PResult<'a, ast::WherePredicate> { - let lo = self.token.span; + fn parse_ty_where_predicate_kind(&mut self) -> PResult<'a, ast::WherePredicateKind> { // Parse optional `for<'a, 'b>`. // This `for` is parsed greedily and applies to the whole predicate, // the bounded type can have its own `for` applying only to it. @@ -464,8 +467,7 @@ impl<'a> Parser<'a> { let ty = self.parse_ty_for_where_clause()?; if self.eat(&token::Colon) { let bounds = self.parse_generic_bounds()?; - Ok(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - span: lo.to(self.prev_token.span), + Ok(ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { bound_generic_params: lifetime_defs, bounded_ty: ty, bounds, @@ -474,11 +476,7 @@ impl<'a> Parser<'a> { // FIXME: We are just dropping the binders in lifetime_defs on the floor here. } else if self.eat(&token::Eq) || self.eat(&token::EqEq) { let rhs_ty = self.parse_ty()?; - Ok(ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { - span: lo.to(self.prev_token.span), - lhs_ty: ty, - rhs_ty, - })) + Ok(ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { lhs_ty: ty, rhs_ty })) } else { self.maybe_recover_bounds_doubled_colon(&ty)?; self.unexpected_any() diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index db34189be2a..b8f66a2b2ec 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -360,11 +360,10 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) { - record_variants!((self, p, p, None, hir, WherePredicate, WherePredicate), [ - BoundPredicate, - RegionPredicate, - EqPredicate - ]); + record_variants!( + (self, p, p.kind, Some(p.hir_id), hir, WherePredicate, WherePredicateKind), + [BoundPredicate, RegionPredicate, EqPredicate] + ); hir_visit::walk_where_predicate(self, p) } @@ -611,7 +610,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { } fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) { - record_variants!((self, p, p, None, ast, WherePredicate, WherePredicate), [ + record_variants!((self, p, &p.kind, None, ast, WherePredicate, WherePredicateKind), [ BoundPredicate, RegionPredicate, EqPredicate diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 235434326aa..60815ffdb1b 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1268,15 +1268,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r debug!("visit_where_predicate {:?}", p); let previous_value = replace(&mut self.diag_metadata.current_where_predicate, Some(p)); self.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| { - if let WherePredicate::BoundPredicate(WhereBoundPredicate { - ref bounded_ty, - ref bounds, - ref bound_generic_params, - span: predicate_span, + if let WherePredicateKind::BoundPredicate(WhereBoundPredicate { + bounded_ty, + bounds, + bound_generic_params, .. - }) = p + }) = &p.kind { - let span = predicate_span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); + let span = p.span.shrink_to_lo().to(bounded_ty.span.shrink_to_lo()); this.with_generic_param_rib( bound_generic_params, RibKind::Normal, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4a157049964..663c3ac0045 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1317,21 +1317,24 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { /// Given `where ::Baz: String`, suggest `where T: Bar`. fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diag<'_>) -> bool { // Detect that we are actually in a `where` predicate. - let (bounded_ty, bounds, where_span) = - if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { - bounded_ty, - bound_generic_params, - bounds, - span, - })) = self.diag_metadata.current_where_predicate - { - if !bound_generic_params.is_empty() { - return false; - } - (bounded_ty, bounds, span) - } else { + let (bounded_ty, bounds, where_span) = if let Some(ast::WherePredicate { + kind: + ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { + bounded_ty, + bound_generic_params, + bounds, + }), + span, + .. + }) = self.diag_metadata.current_where_predicate + { + if !bound_generic_params.is_empty() { return false; - }; + } + (bounded_ty, bounds, span) + } else { + return false; + }; // Confirm that the target is an associated type. let (ty, _, path) = if let ast::TyKind::Path(Some(qself), path) = &bounded_ty.kind { @@ -2840,9 +2843,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // for<'a, 'b> T: Trait + 'b // ^^^^^^^^^^^ suggest outer binder `for<'a, 'b>` if let LifetimeBinderKind::WhereBound = kind - && let Some(ast::WherePredicate::BoundPredicate( + && let Some(predicate) = self.diag_metadata.current_where_predicate + && let ast::WherePredicateKind::BoundPredicate( ast::WhereBoundPredicate { bounded_ty, bounds, .. }, - )) = self.diag_metadata.current_where_predicate + ) = &predicate.kind && bounded_ty.id == binder { for bound in bounds { @@ -3473,7 +3477,6 @@ fn mk_where_bound_predicate( }; let new_where_bound_predicate = ast::WhereBoundPredicate { - span: DUMMY_SP, bound_generic_params: ThinVec::new(), bounded_ty: ast::ptr::P(ty.clone()), bounds: vec![ast::GenericBound::Trait(ast::PolyTraitRef { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 5ad15feadff..27b45f70946 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5312,9 +5312,10 @@ fn point_at_assoc_type_restriction( }; let name = tcx.item_name(proj.projection_term.def_id); let mut predicates = generics.predicates.iter().peekable(); - let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; + let mut prev: Option<(&hir::WhereBoundPredicate<'_>, Span)> = None; while let Some(pred) = predicates.next() { - let hir::WherePredicate::BoundPredicate(pred) = pred else { + let curr_span = pred.span; + let hir::WherePredicateKind::BoundPredicate(pred) = pred.kind else { continue; }; let mut bounds = pred.bounds.iter(); @@ -5340,8 +5341,8 @@ fn point_at_assoc_type_restriction( .iter() .filter(|p| { matches!( - p, - hir::WherePredicate::BoundPredicate(p) + p.kind, + hir::WherePredicateKind::BoundPredicate(p) if hir::PredicateOrigin::WhereClause == p.origin ) }) @@ -5351,20 +5352,21 @@ fn point_at_assoc_type_restriction( // There's only one `where` bound, that needs to be removed. Remove the whole // `where` clause. generics.where_clause_span - } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() + } else if let Some(next_pred) = predicates.peek() + && let hir::WherePredicateKind::BoundPredicate(next) = next_pred.kind && pred.origin == next.origin { // There's another bound, include the comma for the current one. - pred.span.until(next.span) - } else if let Some(prev) = prev + curr_span.until(next_pred.span) + } else if let Some((prev, prev_span)) = prev && pred.origin == prev.origin { // Last bound, try to remove the previous comma. - prev.span.shrink_to_hi().to(pred.span) + prev_span.shrink_to_hi().to(curr_span) } else if pred.origin == hir::PredicateOrigin::WhereClause { - pred.span.with_hi(generics.where_clause_span.hi()) + curr_span.with_hi(generics.where_clause_span.hi()) } else { - pred.span + curr_span }; err.span_suggestion_verbose( @@ -5417,7 +5419,7 @@ fn point_at_assoc_type_restriction( ); } } - prev = Some(pred); + prev = Some((pred, curr_span)); } } diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index 78e92e60b23..e0a9ddf1876 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -150,8 +150,8 @@ fn get_sized_bounds(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span; 1]> .predicates .iter() .filter_map(|pred| { - match pred { - hir::WherePredicate::BoundPredicate(pred) + match pred.kind { + hir::WherePredicateKind::BoundPredicate(pred) if pred.bounded_ty.hir_id.owner.to_def_id() == trait_def_id => { // Fetch spans for trait bounds that are Sized: diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 031696d445d..4bf299cb1fe 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -325,11 +325,11 @@ fn clean_where_predicate<'tcx>( predicate: &hir::WherePredicate<'tcx>, cx: &mut DocContext<'tcx>, ) -> Option { - if !predicate.in_where_clause() { + if !predicate.kind.in_where_clause() { return None; } - Some(match *predicate { - hir::WherePredicate::BoundPredicate(ref wbp) => { + Some(match *predicate.kind { + hir::WherePredicateKind::BoundPredicate(ref wbp) => { let bound_params = wbp .bound_generic_params .iter() @@ -342,12 +342,12 @@ fn clean_where_predicate<'tcx>( } } - hir::WherePredicate::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { + hir::WherePredicateKind::RegionPredicate(ref wrp) => WherePredicate::RegionPredicate { lifetime: clean_lifetime(wrp.lifetime, cx), bounds: wrp.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(), }, - hir::WherePredicate::EqPredicate(ref wrp) => WherePredicate::EqPredicate { + hir::WherePredicateKind::EqPredicate(ref wrp) => WherePredicate::EqPredicate { lhs: clean_ty(wrp.lhs_ty, cx), rhs: clean_ty(wrp.rhs_ty, cx).into(), }, diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 6ad879b9fe7..81152da8c85 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; use rustc_hir::{ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, - PredicateOrigin, Ty, WherePredicate, + PredicateOrigin, Ty, WherePredicate, WherePredicateKind }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; @@ -205,12 +205,13 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> { } fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) { - if let WherePredicate::BoundPredicate(predicate) = predicate { + let span = predicate.span; + if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind { // Collect spans for any bounds on type parameters. if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() { match predicate.origin { PredicateOrigin::GenericParam => { - self.inline_bounds.insert(def_id, predicate.span); + self.inline_bounds.insert(def_id, span); }, PredicateOrigin::WhereClause => { self.where_bounds.insert(def_id); diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs index 65fdc93e0ed..4427edb752e 100644 --- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs +++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs @@ -4,7 +4,7 @@ use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::def_id::DefId; use rustc_hir::{ AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind, - WherePredicate, + WherePredicateKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -324,7 +324,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) { impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls { fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &rustc_hir::Generics<'tcx>) { for predicate in generics.predicates { - if let WherePredicate::BoundPredicate(predicate) = predicate + if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind // In theory, the origin doesn't really matter, // we *could* also lint on explicit where clauses written out by the user, // not just impl trait desugared ones, but that contradicts with the lint name... diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index ce0e1a24a7b..6ff1a1e5ec7 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{ use rustc_hir::{ BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, - PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, WherePredicateKind, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; @@ -442,9 +442,9 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> { /// reason about elision. fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool { for predicate in generics.predicates { - match *predicate { - WherePredicate::RegionPredicate(..) => return true, - WherePredicate::BoundPredicate(ref pred) => { + match *predicate.kind { + WherePredicateKind::RegionPredicate(..) => return true, + WherePredicateKind::BoundPredicate(ref pred) => { // a predicate like F: Trait or F: for<'a> Trait<'a> let mut visitor = RefVisitor::new(cx); // walk the type F, it may not contain LT refs @@ -467,7 +467,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ } } }, - WherePredicate::EqPredicate(ref pred) => { + WherePredicateKind::EqPredicate(ref pred) => { let mut visitor = RefVisitor::new(cx); walk_ty(&mut visitor, pred.lhs_ty); walk_ty(&mut visitor, pred.rhs_ty); diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index d276e29bace..882ab2dda7a 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -1,5 +1,5 @@ use rustc_ast::visit::FnKind; -use rustc_ast::{NodeId, WherePredicate}; +use rustc_ast::{NodeId, WherePredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -51,8 +51,8 @@ impl EarlyLintPass for MultipleBoundLocations { } } for clause in &generics.where_clause.predicates { - match clause { - WherePredicate::BoundPredicate(pred) => { + match &clause.kind { + WherePredicateKind::BoundPredicate(pred) => { if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty()) && let Some(Some(bound_span)) = pred .bounded_ty @@ -62,14 +62,14 @@ impl EarlyLintPass for MultipleBoundLocations { emit_lint(cx, *bound_span, pred.bounded_ty.span); } }, - WherePredicate::RegionPredicate(pred) => { + WherePredicateKind::RegionPredicate(pred) => { if !pred.bounds.is_empty() && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str()) { emit_lint(cx, *bound_span, pred.lifetime.ident.span); } }, - WherePredicate::EqPredicate(_) => {}, + WherePredicateKind::EqPredicate(_) => {}, } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index 852a0ce8c6d..ad6313e391b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; use rustc_hir::def_id::{DefId, DefIdMap}; -use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicate}; +use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ClauseKind, PredicatePolarity}; use rustc_session::declare_lint_pass; @@ -52,7 +52,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator LateLintPass<'tcx> for TraitBounds { let mut self_bounds_map = FxHashMap::default(); for predicate in item.generics.predicates { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate + if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind && bound_predicate.origin != PredicateOrigin::ImplTrait - && !bound_predicate.span.from_expansion() + && !predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind && let Some(PathSegment { res: Res::SelfTyParam { trait_: def_id }, @@ -268,10 +268,10 @@ impl TraitBounds { let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in generics.predicates { - if let WherePredicate::BoundPredicate(p) = bound + if let WherePredicateKind::BoundPredicate(p) = bound.kind && p.origin != PredicateOrigin::ImplTrait && p.bounds.len() as u64 <= self.max_trait_bounds - && !p.span.from_expansion() + && !bound.span.from_expansion() && let bounds = p .bounds .iter() @@ -295,7 +295,7 @@ impl TraitBounds { span_lint_and_help( cx, TYPE_REPETITION_IN_BOUNDS, - p.span, + bound.span, "this type has already been used as a bound predicate", None, hint_string, @@ -322,8 +322,8 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen .predicates .iter() .filter_map(|pred| { - if pred.in_where_clause() - && let WherePredicate::BoundPredicate(bound_predicate) = pred + if pred.kind.in_where_clause() + && let WherePredicateKind::BoundPredicate(bound_predicate) = pred.kind && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind { return Some( @@ -347,10 +347,10 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen // | // compare trait bounds keyed by generic name and comparable trait to collected where // predicates eg. (T, Clone) - for predicate in generics.predicates.iter().filter(|pred| !pred.in_where_clause()) { - if let WherePredicate::BoundPredicate(bound_predicate) = predicate + for predicate in generics.predicates.iter().filter(|pred| !pred.kind.in_where_clause()) { + if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind && bound_predicate.origin != PredicateOrigin::ImplTrait - && !bound_predicate.span.from_expansion() + && !predicate.span.from_expansion() && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind { let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements"); diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 0be6dc15a8e..c90f4a6ebfe 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -661,8 +661,8 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool { } pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool { - use WherePredicate::*; - match (l, r) { + use WherePredicateKind::*; + match (&l.kind, &r.kind) { (BoundPredicate(l), BoundPredicate(r)) => { over(&l.bound_generic_params, &r.bound_generic_params, |l, r| { eq_generic_param(l, r) diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs index 4d684f3c635..db7c3486e71 100644 --- a/src/tools/rustfmt/src/spanned.rs +++ b/src/tools/rustfmt/src/spanned.rs @@ -150,11 +150,7 @@ impl Spanned for ast::FieldDef { impl Spanned for ast::WherePredicate { fn span(&self) -> Span { - match *self { - ast::WherePredicate::BoundPredicate(ref p) => p.span, - ast::WherePredicate::RegionPredicate(ref p) => p.span, - ast::WherePredicate::EqPredicate(ref p) => p.span, - } + self.span } } diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index e237662f5aa..dd4a788c002 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -463,8 +463,8 @@ impl Rewrite for ast::WherePredicate { fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult { // FIXME: dead spans? - let result = match *self { - ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { + let result = match self.kind { + ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate { ref bound_generic_params, ref bounded_ty, ref bounds, @@ -482,12 +482,11 @@ impl Rewrite for ast::WherePredicate { rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)? } - ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { + ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate { ref lifetime, ref bounds, - span, - }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?, - ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { + }) => rewrite_bounded_lifetime(lifetime, bounds, self.span, context, shape)?, + ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index f2dd9043759..2adbcfab612 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -5,11 +5,11 @@ ast-stats-1 Crate 40 ( 0.6%) 1 40 ast-stats-1 GenericArgs 40 ( 0.6%) 1 40 ast-stats-1 - AngleBracketed 40 ( 0.6%) 1 ast-stats-1 ExprField 48 ( 0.7%) 1 48 -ast-stats-1 WherePredicate 56 ( 0.8%) 1 56 -ast-stats-1 - BoundPredicate 56 ( 0.8%) 1 ast-stats-1 Attribute 64 ( 1.0%) 2 32 ast-stats-1 - DocComment 32 ( 0.5%) 1 ast-stats-1 - Normal 32 ( 0.5%) 1 +ast-stats-1 WherePredicate 64 ( 1.0%) 1 64 +ast-stats-1 - BoundPredicate 64 ( 1.0%) 1 ast-stats-1 Local 80 ( 1.2%) 1 80 ast-stats-1 ForeignItem 88 ( 1.3%) 1 88 ast-stats-1 - Fn 88 ( 1.3%) 1 @@ -33,14 +33,14 @@ ast-stats-1 Pat 504 ( 7.6%) 7 72 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Wild 72 ( 1.1%) 1 ast-stats-1 - Ident 360 ( 5.4%) 5 -ast-stats-1 Expr 576 ( 8.7%) 8 72 +ast-stats-1 Expr 576 ( 8.6%) 8 72 ast-stats-1 - Path 72 ( 1.1%) 1 ast-stats-1 - Match 72 ( 1.1%) 1 ast-stats-1 - Struct 72 ( 1.1%) 1 ast-stats-1 - Lit 144 ( 2.2%) 2 ast-stats-1 - Block 216 ( 3.2%) 3 ast-stats-1 PathSegment 744 (11.2%) 31 24 -ast-stats-1 Ty 896 (13.5%) 14 64 +ast-stats-1 Ty 896 (13.4%) 14 64 ast-stats-1 - Ref 64 ( 1.0%) 1 ast-stats-1 - Ptr 64 ( 1.0%) 1 ast-stats-1 - ImplicitSelf 128 ( 1.9%) 2 @@ -53,7 +53,7 @@ ast-stats-1 - Enum 136 ( 2.0%) 1 ast-stats-1 - Fn 272 ( 4.1%) 2 ast-stats-1 - Use 408 ( 6.1%) 3 ast-stats-1 ---------------------------------------------------------------- -ast-stats-1 Total 6_656 116 +ast-stats-1 Total 6_664 116 ast-stats-1 ast-stats-2 POST EXPANSION AST STATS ast-stats-2 Name Accumulated Size Count Item Size @@ -62,8 +62,8 @@ ast-stats-2 Crate 40 ( 0.5%) 1 40 ast-stats-2 GenericArgs 40 ( 0.5%) 1 40 ast-stats-2 - AngleBracketed 40 ( 0.5%) 1 ast-stats-2 ExprField 48 ( 0.7%) 1 48 -ast-stats-2 WherePredicate 56 ( 0.8%) 1 56 -ast-stats-2 - BoundPredicate 56 ( 0.8%) 1 +ast-stats-2 WherePredicate 64 ( 0.9%) 1 64 +ast-stats-2 - BoundPredicate 64 ( 0.9%) 1 ast-stats-2 Local 80 ( 1.1%) 1 80 ast-stats-2 ForeignItem 88 ( 1.2%) 1 88 ast-stats-2 - Fn 88 ( 1.2%) 1 @@ -113,7 +113,7 @@ ast-stats-2 - ForeignMod 136 ( 1.9%) 1 ast-stats-2 - Fn 272 ( 3.7%) 2 ast-stats-2 - Use 544 ( 7.4%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_304 127 +ast-stats-2 Total 7_312 127 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size @@ -131,48 +131,48 @@ hir-stats Param 64 ( 0.7%) 2 32 hir-stats Body 72 ( 0.8%) 3 24 hir-stats ImplItemRef 72 ( 0.8%) 2 36 hir-stats InlineAsm 72 ( 0.8%) 1 72 +hir-stats WherePredicate 72 ( 0.8%) 3 24 +hir-stats - BoundPredicate 72 ( 0.8%) 3 hir-stats Arm 80 ( 0.9%) 2 40 hir-stats Stmt 96 ( 1.1%) 3 32 hir-stats - Let 32 ( 0.4%) 1 hir-stats - Semi 32 ( 0.4%) 1 hir-stats - Expr 32 ( 0.4%) 1 -hir-stats FieldDef 112 ( 1.2%) 2 56 +hir-stats FieldDef 112 ( 1.3%) 2 56 hir-stats FnDecl 120 ( 1.3%) 3 40 hir-stats Attribute 128 ( 1.4%) 4 32 hir-stats GenericArgs 144 ( 1.6%) 3 48 hir-stats Variant 144 ( 1.6%) 2 72 -hir-stats WherePredicate 192 ( 2.1%) 3 64 -hir-stats - BoundPredicate 192 ( 2.1%) 3 -hir-stats GenericBound 256 ( 2.8%) 4 64 -hir-stats - Trait 256 ( 2.8%) 4 +hir-stats GenericBound 256 ( 2.9%) 4 64 +hir-stats - Trait 256 ( 2.9%) 4 hir-stats Block 288 ( 3.2%) 6 48 hir-stats GenericParam 360 ( 4.0%) 5 72 hir-stats Pat 360 ( 4.0%) 5 72 hir-stats - Struct 72 ( 0.8%) 1 hir-stats - Wild 72 ( 0.8%) 1 hir-stats - Binding 216 ( 2.4%) 3 -hir-stats Generics 560 ( 6.2%) 10 56 -hir-stats Ty 720 ( 8.0%) 15 48 +hir-stats Generics 560 ( 6.3%) 10 56 +hir-stats Ty 720 ( 8.1%) 15 48 hir-stats - Ref 48 ( 0.5%) 1 hir-stats - Ptr 48 ( 0.5%) 1 -hir-stats - Path 624 ( 6.9%) 13 -hir-stats Expr 768 ( 8.5%) 12 64 +hir-stats - Path 624 ( 7.0%) 13 +hir-stats Expr 768 ( 8.6%) 12 64 hir-stats - Path 64 ( 0.7%) 1 hir-stats - Match 64 ( 0.7%) 1 hir-stats - Struct 64 ( 0.7%) 1 hir-stats - InlineAsm 64 ( 0.7%) 1 hir-stats - Lit 128 ( 1.4%) 2 -hir-stats - Block 384 ( 4.2%) 6 -hir-stats Item 968 (10.7%) 11 88 +hir-stats - Block 384 ( 4.3%) 6 +hir-stats Item 968 (10.9%) 11 88 hir-stats - Enum 88 ( 1.0%) 1 hir-stats - Trait 88 ( 1.0%) 1 hir-stats - Impl 88 ( 1.0%) 1 hir-stats - ExternCrate 88 ( 1.0%) 1 hir-stats - ForeignMod 88 ( 1.0%) 1 -hir-stats - Fn 176 ( 1.9%) 2 +hir-stats - Fn 176 ( 2.0%) 2 hir-stats - Use 352 ( 3.9%) 4 -hir-stats Path 1_240 (13.7%) 31 40 -hir-stats PathSegment 1_920 (21.2%) 40 48 +hir-stats Path 1_240 (13.9%) 31 40 +hir-stats PathSegment 1_920 (21.5%) 40 48 hir-stats ---------------------------------------------------------------- -hir-stats Total 9_040 180 +hir-stats Total 8_920 180 hir-stats -- cgit 1.4.1-3-g733a5