Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 09ae784

Browse files
committedSep 28, 2022
Auto merge of #101619 - Xiretza:rustc_parse-session-diagnostics, r=davidtwco
Migrate more of rustc_parse to SessionDiagnostic Still far from complete, but I thought I'd add a checkpoint here because rebasing was starting to get annoying.
2 parents 6201eab + d7c6457 commit 09ae784

File tree

28 files changed

+2047
-1285
lines changed

28 files changed

+2047
-1285
lines changed
 

‎Cargo.lock

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3450,6 +3450,8 @@ version = "0.0.0"
34503450
dependencies = [
34513451
"annotate-snippets",
34523452
"atty",
3453+
"rustc_ast",
3454+
"rustc_ast_pretty",
34533455
"rustc_data_structures",
34543456
"rustc_error_messages",
34553457
"rustc_hir",

‎compiler/rustc_error_messages/locales/en-US/parser.ftl

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ parser_field_expression_with_generic = field expressions cannot have generic arg
7171
parser_macro_invocation_with_qualified_path = macros cannot use qualified paths
7272
7373
parser_unexpected_token_after_label = expected `while`, `for`, `loop` or `{"{"}` after a label
74+
.suggestion_remove_label = consider removing the label
75+
.suggestion_enclose_in_block = consider enclosing expression in a block
7476
7577
parser_require_colon_after_labeled_expression = labeled expression must be followed by `:`
7678
.note = labels are used before loops and blocks, allowing e.g., `break 'label` to them
@@ -161,3 +163,209 @@ parser_use_eq_instead = unexpected `==`
161163
162164
parser_use_empty_block_not_semi = expected { "`{}`" }, found `;`
163165
.suggestion = try using { "`{}`" } instead
166+
167+
parser_comparison_interpreted_as_generic =
168+
`<` is interpreted as a start of generic arguments for `{$type}`, not a comparison
169+
.label_args = interpreted as generic arguments
170+
.label_comparison = not interpreted as comparison
171+
.suggestion = try comparing the cast value
172+
173+
parser_shift_interpreted_as_generic =
174+
`<<` is interpreted as a start of generic arguments for `{$type}`, not a shift
175+
.label_args = interpreted as generic arguments
176+
.label_comparison = not interpreted as shift
177+
.suggestion = try shifting the cast value
178+
179+
parser_found_expr_would_be_stmt = expected expression, found `{$token}`
180+
.label = expected expression
181+
182+
parser_leading_plus_not_supported = leading `+` is not supported
183+
.label = unexpected `+`
184+
.suggestion_remove_plus = try removing the `+`
185+
186+
parser_parentheses_with_struct_fields = invalid `struct` delimiters or `fn` call arguments
187+
.suggestion_braces_for_struct = if `{$type}` is a struct, use braces as delimiters
188+
.suggestion_no_fields_for_fn = if `{$type}` is a function, use the arguments directly
189+
190+
parser_labeled_loop_in_break = parentheses are required around this expression to avoid confusion with a labeled break expression
191+
192+
parser_sugg_wrap_expression_in_parentheses = wrap the expression in parentheses
193+
194+
parser_array_brackets_instead_of_braces = this is a block expression, not an array
195+
.suggestion = to make an array, use square brackets instead of curly braces
196+
197+
parser_match_arm_body_without_braces = `match` arm body without braces
198+
.label_statements = {$num_statements ->
199+
[one] this statement is not surrounded by a body
200+
*[other] these statements are not surrounded by a body
201+
}
202+
.label_arrow = while parsing the `match` arm starting here
203+
.suggestion_add_braces = surround the {$num_statements ->
204+
[one] statement
205+
*[other] statements
206+
} with a body
207+
.suggestion_use_comma_not_semicolon = use a comma to end a `match` arm expression
208+
209+
parser_struct_literal_not_allowed_here = struct literals are not allowed here
210+
.suggestion = surround the struct literal with parentheses
211+
212+
parser_invalid_interpolated_expression = invalid interpolated expression
213+
214+
parser_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported
215+
parser_octal_float_literal_not_supported = octal float literal is not supported
216+
parser_binary_float_literal_not_supported = binary float literal is not supported
217+
parser_not_supported = not supported
218+
219+
parser_invalid_literal_suffix = suffixes on {$kind} literals are invalid
220+
.label = invalid suffix `{$suffix}`
221+
222+
parser_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
223+
.label = invalid suffix `{$suffix}`
224+
.tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
225+
.tuple_exception_line_2 = on proc macros, you'll want to use `syn::Index::from` or `proc_macro::Literal::*_unsuffixed` for code that will desugar to tuple field access
226+
.tuple_exception_line_3 = see issue #60210 <https://github.com/rust-lang/rust/issues/60210> for more information
227+
228+
parser_non_string_abi_literal = non-string ABI literal
229+
.suggestion = specify the ABI with a string literal
230+
231+
parser_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter}`
232+
.label_unmatched = mismatched closing delimiter
233+
.label_opening_candidate = closing delimiter possibly meant for this
234+
.label_unclosed = unclosed delimiter
235+
236+
parser_incorrect_visibility_restriction = incorrect visibility restriction
237+
.help = some possible visibility restrictions are:
238+
`pub(crate)`: visible only on the current crate
239+
`pub(super)`: visible only in the current module's parent
240+
`pub(in path::to::module)`: visible only on the specified path
241+
.suggestion = make this visible only to module `{$inner_str}` with `in`
242+
243+
parser_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is not allowed
244+
245+
parser_expected_statement_after_outer_attr = expected statement after outer attribute
246+
247+
parser_doc_comment_does_not_document_anything = found a documentation comment that doesn't document anything
248+
.help = doc comments must come before what they document, maybe a comment was intended with `//`?
249+
.suggestion = missing comma here
250+
251+
parser_const_let_mutually_exclusive = `const` and `let` are mutually exclusive
252+
.suggestion = remove `let`
253+
254+
parser_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
255+
parser_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed
256+
257+
parser_compound_assignment_expression_in_let = can't reassign to an uninitialized variable
258+
.suggestion = initialize the variable
259+
.help = if you meant to overwrite, remove the `let` binding
260+
261+
parser_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
262+
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
263+
264+
parser_invalid_meta_item = expected unsuffixed literal or identifier, found `{$token}`
265+
266+
parser_label_inner_attr_does_not_annotate_this = the inner attribute doesn't annotate this {$item}
267+
parser_sugg_change_inner_attr_to_outer = to annotate the {$item}, change the attribute from inner to outer style
268+
269+
parser_inner_attr_not_permitted_after_outer_doc_comment = an inner attribute is not permitted following an outer doc comment
270+
.label_attr = not permitted following an outer doc comment
271+
.label_prev_doc_comment = previous doc comment
272+
.label_does_not_annotate_this = {parser_label_inner_attr_does_not_annotate_this}
273+
.sugg_change_inner_to_outer = {parser_sugg_change_inner_attr_to_outer}
274+
275+
parser_inner_attr_not_permitted_after_outer_attr = an inner attribute is not permitted following an outer attribute
276+
.label_attr = not permitted following an outer attribute
277+
.label_prev_attr = previous outer attribute
278+
.label_does_not_annotate_this = {parser_label_inner_attr_does_not_annotate_this}
279+
.sugg_change_inner_to_outer = {parser_sugg_change_inner_attr_to_outer}
280+
281+
parser_inner_attr_not_permitted = an inner attribute is not permitted in this context
282+
.label_does_not_annotate_this = {parser_label_inner_attr_does_not_annotate_this}
283+
.sugg_change_inner_to_outer = {parser_sugg_change_inner_attr_to_outer}
284+
285+
parser_inner_attr_explanation = inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files
286+
parser_outer_attr_explanation = outer attributes, like `#[test]`, annotate the item following them
287+
288+
parser_inner_doc_comment_not_permitted = expected outer doc comment
289+
.note = inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
290+
.suggestion = you might have meant to write a regular comment
291+
.label_does_not_annotate_this = the inner doc comment doesn't annotate this {$item}
292+
.sugg_change_inner_to_outer = to annotate the {$item}, change the doc comment from inner to outer style
293+
294+
parser_expected_identifier_found_reserved_identifier_str = expected identifier, found reserved identifier `{$token}`
295+
parser_expected_identifier_found_keyword_str = expected identifier, found keyword `{$token}`
296+
parser_expected_identifier_found_reserved_keyword_str = expected identifier, found reserved keyword `{$token}`
297+
parser_expected_identifier_found_doc_comment_str = expected identifier, found doc comment `{$token}`
298+
parser_expected_identifier_found_str = expected identifier, found `{$token}`
299+
300+
parser_expected_identifier_found_reserved_identifier = expected identifier, found reserved identifier
301+
parser_expected_identifier_found_keyword = expected identifier, found keyword
302+
parser_expected_identifier_found_reserved_keyword = expected identifier, found reserved keyword
303+
parser_expected_identifier_found_doc_comment = expected identifier, found doc comment
304+
parser_expected_identifier = expected identifier
305+
306+
parser_sugg_escape_to_use_as_identifier = escape `{$ident_name}` to use it as an identifier
307+
308+
parser_sugg_remove_comma = remove this comma
309+
310+
parser_expected_semi_found_reserved_identifier_str = expected `;`, found reserved identifier `{$token}`
311+
parser_expected_semi_found_keyword_str = expected `;`, found keyword `{$token}`
312+
parser_expected_semi_found_reserved_keyword_str = expected `;`, found reserved keyword `{$token}`
313+
parser_expected_semi_found_doc_comment_str = expected `;`, found doc comment `{$token}`
314+
parser_expected_semi_found_str = expected `;`, found `{$token}`
315+
316+
parser_sugg_change_this_to_semi = change this to `;`
317+
parser_sugg_add_semi = add `;` here
318+
parser_label_unexpected_token = unexpected token
319+
320+
parser_unmatched_angle_brackets = {$num_extra_brackets ->
321+
[one] unmatched angle bracket
322+
*[other] unmatched angle brackets
323+
}
324+
.suggestion = {$num_extra_brackets ->
325+
[one] remove extra angle bracket
326+
*[other] remove extra angle brackets
327+
}
328+
329+
parser_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets
330+
.suggestion = surround the type parameters with angle brackets
331+
332+
parser_comparison_operators_cannot_be_chained = comparison operators cannot be chained
333+
.sugg_parentheses_for_function_args = or use `(...)` if you meant to specify fn arguments
334+
.sugg_split_comparison = split the comparison into two
335+
.sugg_parenthesize = parenthesize the comparison
336+
parser_sugg_turbofish_syntax = use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
337+
338+
parser_question_mark_in_type = invalid `?` in type
339+
.label = `?` is only allowed on expressions, not types
340+
.suggestion = if you meant to express that the type might not contain a value, use the `Option` wrapper type
341+
342+
parser_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head
343+
.suggestion = remove parentheses in `for` loop
344+
345+
parser_doc_comment_on_param_type = documentation comments cannot be applied to a function parameter's type
346+
.label = doc comments are not allowed here
347+
348+
parser_attribute_on_param_type = attributes cannot be applied to a function parameter's type
349+
.label = attributes are not allowed here
350+
351+
parser_pattern_method_param_without_body = patterns aren't allowed in methods without bodies
352+
.suggestion = give this argument a name or use an underscore to ignore it
353+
354+
parser_self_param_not_first = unexpected `self` parameter in function
355+
.label = must be the first parameter of an associated function
356+
357+
parser_const_generic_without_braces = expressions must be enclosed in braces to be used as const generic arguments
358+
.suggestion = enclose the `const` expression in braces
359+
360+
parser_unexpected_const_param_declaration = unexpected `const` parameter declaration
361+
.label = expected a `const` expression, not a parameter declaration
362+
.suggestion = `const` parameters must be declared for the `impl`
363+
364+
parser_unexpected_const_in_generic_param = expected lifetime, type, or constant, found keyword `const`
365+
.suggestion = the `const` keyword is only needed in the definition of the type
366+
367+
parser_async_move_order_incorrect = the order of `move` and `async` is incorrect
368+
.suggestion = try switching the order
369+
370+
parser_double_colon_in_bound = expected `:` followed by trait or lifetime
371+
.suggestion = use single colon

‎compiler/rustc_error_messages/locales/en-US/session.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,5 @@ session_crate_name_invalid = crate names cannot start with a `-`, but `{$s}` has
6666
session_crate_name_empty = crate name must not be empty
6767
6868
session_invalid_character_in_create_name = invalid character `{$character}` in crate name: `{$crate_name}`
69+
70+
session_expr_parentheses_needed = parentheses are required to parse this as an expression

‎compiler/rustc_errors/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ doctest = false
88

99
[dependencies]
1010
tracing = "0.1"
11+
rustc_ast = { path = "../rustc_ast" }
12+
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
1113
rustc_error_messages = { path = "../rustc_error_messages" }
1214
rustc_serialize = { path = "../rustc_serialize" }
1315
rustc_span = { path = "../rustc_span" }

‎compiler/rustc_errors/src/diagnostic.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use crate::{
33
CodeSuggestion, DiagnosticMessage, EmissionGuarantee, Level, LintDiagnosticBuilder, MultiSpan,
44
SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
55
};
6+
use rustc_ast as ast;
7+
use rustc_ast_pretty::pprust;
68
use rustc_data_structures::fx::FxHashMap;
79
use rustc_error_messages::FluentValue;
810
use rustc_hir as hir;
@@ -175,6 +177,24 @@ impl IntoDiagnosticArg for hir::ConstContext {
175177
}
176178
}
177179

180+
impl IntoDiagnosticArg for ast::Path {
181+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
182+
DiagnosticArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))
183+
}
184+
}
185+
186+
impl IntoDiagnosticArg for ast::token::Token {
187+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
188+
DiagnosticArgValue::Str(pprust::token_to_string(&self))
189+
}
190+
}
191+
192+
impl IntoDiagnosticArg for ast::token::TokenKind {
193+
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
194+
DiagnosticArgValue::Str(pprust::token_kind_to_string(&self))
195+
}
196+
}
197+
178198
/// Trait implemented by error types. This should not be implemented manually. Instead, use
179199
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
180200
#[cfg_attr(bootstrap, rustc_diagnostic_item = "AddSubdiagnostic")]

‎compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
281281
if should_generate_set_arg(&field) {
282282
let diag = &self.parent.diag;
283283
let ident = field.ident.as_ref().unwrap();
284+
// strip `r#` prefix, if present
285+
let ident = format_ident!("{}", ident);
284286
return quote! {
285287
#diag.set_arg(
286288
stringify!(#ident),

‎compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
189189

190190
let diag = &self.diag;
191191
let ident = ast.ident.as_ref().unwrap();
192+
// strip `r#` prefix, if present
193+
let ident = format_ident!("{}", ident);
194+
192195
quote! {
193196
#diag.set_arg(
194197
stringify!(#ident),

‎compiler/rustc_parse/src/errors.rs

Lines changed: 1251 additions & 0 deletions
Large diffs are not rendered by default.

‎compiler/rustc_parse/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ use parser::{emit_unclosed_delims, make_unclosed_delims_error, Parser};
3232
pub mod lexer;
3333
pub mod validate_attr;
3434

35+
mod errors;
36+
3537
// A bunch of utility functions of the form `parse_<thing>_from_<source>`
3638
// where <thing> includes crate, expr, item, stmt, tts, and one that
3739
// uses a HOF to parse anything, and <source> includes file and

‎compiler/rustc_parse/src/parser/attr.rs

Lines changed: 68 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,26 @@
1+
use crate::errors::{InvalidMetaItem, SuffixedLiteralInAttribute};
2+
13
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
24
use rustc_ast as ast;
35
use rustc_ast::attr;
46
use rustc_ast::token::{self, Delimiter, Nonterminal};
5-
use rustc_ast_pretty::pprust;
6-
use rustc_errors::{error_code, Diagnostic, PResult};
7+
use rustc_errors::{error_code, fluent, Diagnostic, IntoDiagnostic, PResult};
78
use rustc_span::{sym, BytePos, Span};
89
use std::convert::TryInto;
910

1011
// Public for rustfmt usage
1112
#[derive(Debug)]
12-
pub enum InnerAttrPolicy<'a> {
13+
pub enum InnerAttrPolicy {
1314
Permitted,
14-
Forbidden { reason: &'a str, saw_doc_comment: bool, prev_outer_attr_sp: Option<Span> },
15+
Forbidden(Option<InnerAttrForbiddenReason>),
1516
}
1617

17-
const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \
18-
permitted in this context";
19-
20-
pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden {
21-
reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG,
22-
saw_doc_comment: false,
23-
prev_outer_attr_sp: None,
24-
};
18+
#[derive(Clone, Copy, Debug)]
19+
pub enum InnerAttrForbiddenReason {
20+
InCodeBlock,
21+
AfterOuterDocComment { prev_doc_comment_span: Span },
22+
AfterOuterAttribute { prev_outer_attr_sp: Span },
23+
}
2524

2625
enum OuterAttributeType {
2726
DocComment,
@@ -40,25 +39,23 @@ impl<'a> Parser<'a> {
4039
let prev_outer_attr_sp = outer_attrs.last().map(|attr| attr.span);
4140

4241
let inner_error_reason = if just_parsed_doc_comment {
43-
"an inner attribute is not permitted following an outer doc comment"
44-
} else if prev_outer_attr_sp.is_some() {
45-
"an inner attribute is not permitted following an outer attribute"
42+
Some(InnerAttrForbiddenReason::AfterOuterDocComment {
43+
prev_doc_comment_span: prev_outer_attr_sp.unwrap(),
44+
})
45+
} else if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
46+
Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp })
4647
} else {
47-
DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG
48-
};
49-
let inner_parse_policy = InnerAttrPolicy::Forbidden {
50-
reason: inner_error_reason,
51-
saw_doc_comment: just_parsed_doc_comment,
52-
prev_outer_attr_sp,
48+
None
5349
};
50+
let inner_parse_policy = InnerAttrPolicy::Forbidden(inner_error_reason);
5451
just_parsed_doc_comment = false;
5552
Some(self.parse_attribute(inner_parse_policy)?)
5653
} else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
5754
if attr_style != ast::AttrStyle::Outer {
5855
let span = self.token.span;
5956
let mut err = self.sess.span_diagnostic.struct_span_err_with_code(
6057
span,
61-
"expected outer doc comment",
58+
fluent::parser::inner_doc_comment_not_permitted,
6259
error_code!(E0753),
6360
);
6461
if let Some(replacement_span) = self.annotate_following_item_if_applicable(
@@ -69,13 +66,10 @@ impl<'a> Parser<'a> {
6966
token::CommentKind::Block => OuterAttributeType::DocBlockComment,
7067
},
7168
) {
72-
err.note(
73-
"inner doc comments like this (starting with `//!` or `/*!`) can \
74-
only appear before items",
75-
);
69+
err.note(fluent::parser::note);
7670
err.span_suggestion_verbose(
7771
replacement_span,
78-
"you might have meant to write a regular comment",
72+
fluent::parser::suggestion,
7973
"",
8074
rustc_errors::Applicability::MachineApplicable,
8175
);
@@ -113,7 +107,7 @@ impl<'a> Parser<'a> {
113107
// Public for rustfmt usage.
114108
pub fn parse_attribute(
115109
&mut self,
116-
inner_parse_policy: InnerAttrPolicy<'_>,
110+
inner_parse_policy: InnerAttrPolicy,
117111
) -> PResult<'a, ast::Attribute> {
118112
debug!(
119113
"parse_attribute: inner_parse_policy={:?} self.token={:?}",
@@ -122,35 +116,22 @@ impl<'a> Parser<'a> {
122116
let lo = self.token.span;
123117
// Attributes can't have attributes of their own [Editor's note: not with that attitude]
124118
self.collect_tokens_no_attrs(|this| {
125-
if this.eat(&token::Pound) {
126-
let style = if this.eat(&token::Not) {
127-
ast::AttrStyle::Inner
128-
} else {
129-
ast::AttrStyle::Outer
130-
};
119+
assert!(this.eat(&token::Pound), "parse_attribute called in non-attribute position");
131120

132-
this.expect(&token::OpenDelim(Delimiter::Bracket))?;
133-
let item = this.parse_attr_item(false)?;
134-
this.expect(&token::CloseDelim(Delimiter::Bracket))?;
135-
let attr_sp = lo.to(this.prev_token.span);
121+
let style =
122+
if this.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer };
136123

137-
// Emit error if inner attribute is encountered and forbidden.
138-
if style == ast::AttrStyle::Inner {
139-
this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
140-
}
124+
this.expect(&token::OpenDelim(Delimiter::Bracket))?;
125+
let item = this.parse_attr_item(false)?;
126+
this.expect(&token::CloseDelim(Delimiter::Bracket))?;
127+
let attr_sp = lo.to(this.prev_token.span);
141128

142-
Ok(attr::mk_attr_from_item(
143-
&self.sess.attr_id_generator,
144-
item,
145-
None,
146-
style,
147-
attr_sp,
148-
))
149-
} else {
150-
let token_str = pprust::token_to_string(&this.token);
151-
let msg = &format!("expected `#`, found `{token_str}`");
152-
Err(this.struct_span_err(this.token.span, msg))
129+
// Emit error if inner attribute is encountered and forbidden.
130+
if style == ast::AttrStyle::Inner {
131+
this.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy);
153132
}
133+
134+
Ok(attr::mk_attr_from_item(&self.sess.attr_id_generator, item, None, style, attr_sp))
154135
})
155136
}
156137

@@ -190,21 +171,12 @@ impl<'a> Parser<'a> {
190171
ForceCollect::No,
191172
) {
192173
Ok(Some(item)) => {
193-
let attr_name = match attr_type {
194-
OuterAttributeType::Attribute => "attribute",
195-
_ => "doc comment",
196-
};
197-
err.span_label(
198-
item.span,
199-
&format!("the inner {} doesn't annotate this {}", attr_name, item.kind.descr()),
200-
);
174+
// FIXME(#100717)
175+
err.set_arg("item", item.kind.descr());
176+
err.span_label(item.span, fluent::parser::label_does_not_annotate_this);
201177
err.span_suggestion_verbose(
202178
replacement_span,
203-
&format!(
204-
"to annotate the {}, change the {} from inner to outer style",
205-
item.kind.descr(),
206-
attr_name
207-
),
179+
fluent::parser::sugg_change_inner_to_outer,
208180
match attr_type {
209181
OuterAttributeType::Attribute => "",
210182
OuterAttributeType::DocBlockComment => "*",
@@ -222,22 +194,33 @@ impl<'a> Parser<'a> {
222194
Some(replacement_span)
223195
}
224196

225-
pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) {
226-
if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_outer_attr_sp } = policy {
227-
let prev_outer_attr_note =
228-
if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" };
229-
230-
let mut diag = self.struct_span_err(attr_sp, reason);
231-
232-
if let Some(prev_outer_attr_sp) = prev_outer_attr_sp {
233-
diag.span_label(attr_sp, "not permitted following an outer attribute")
234-
.span_label(prev_outer_attr_sp, prev_outer_attr_note);
235-
}
197+
pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy) {
198+
if let InnerAttrPolicy::Forbidden(reason) = policy {
199+
let mut diag = match reason.as_ref().copied() {
200+
Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => {
201+
let mut diag = self.struct_span_err(
202+
attr_sp,
203+
fluent::parser::inner_attr_not_permitted_after_outer_doc_comment,
204+
);
205+
diag.span_label(attr_sp, fluent::parser::label_attr)
206+
.span_label(prev_doc_comment_span, fluent::parser::label_prev_doc_comment);
207+
diag
208+
}
209+
Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => {
210+
let mut diag = self.struct_span_err(
211+
attr_sp,
212+
fluent::parser::inner_attr_not_permitted_after_outer_attr,
213+
);
214+
diag.span_label(attr_sp, fluent::parser::label_attr)
215+
.span_label(prev_outer_attr_sp, fluent::parser::label_prev_attr);
216+
diag
217+
}
218+
Some(InnerAttrForbiddenReason::InCodeBlock) | None => {
219+
self.struct_span_err(attr_sp, fluent::parser::inner_attr_not_permitted)
220+
}
221+
};
236222

237-
diag.note(
238-
"inner attributes, like `#![no_std]`, annotate the item enclosing them, and \
239-
are usually found at the beginning of source files",
240-
);
223+
diag.note(fluent::parser::inner_attr_explanation);
241224
if self
242225
.annotate_following_item_if_applicable(
243226
&mut diag,
@@ -246,7 +229,7 @@ impl<'a> Parser<'a> {
246229
)
247230
.is_some()
248231
{
249-
diag.note("outer attributes, like `#[test]`, annotate the item following them");
232+
diag.note(fluent::parser::outer_attr_explanation);
250233
};
251234
diag.emit();
252235
}
@@ -337,12 +320,7 @@ impl<'a> Parser<'a> {
337320
debug!("checking if {:?} is unusuffixed", lit);
338321

339322
if !lit.kind.is_unsuffixed() {
340-
self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes")
341-
.help(
342-
"instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \
343-
use an unsuffixed version (`1`, `1.0`, etc.)",
344-
)
345-
.emit();
323+
self.sess.emit_err(SuffixedLiteralInAttribute { span: lit.span });
346324
}
347325

348326
Ok(lit)
@@ -435,9 +413,8 @@ impl<'a> Parser<'a> {
435413
Err(err) => err.cancel(),
436414
}
437415

438-
let found = pprust::token_to_string(&self.token);
439-
let msg = format!("expected unsuffixed literal or identifier, found `{found}`");
440-
Err(self.struct_span_err(self.token.span, &msg))
416+
Err(InvalidMetaItem { span: self.token.span, token: self.token.clone() }
417+
.into_diagnostic(&self.sess.span_diagnostic))
441418
}
442419
}
443420

‎compiler/rustc_parse/src/parser/diagnostics.rs

Lines changed: 158 additions & 789 deletions
Large diffs are not rendered by default.

‎compiler/rustc_parse/src/parser/expr.rs

Lines changed: 167 additions & 234 deletions
Large diffs are not rendered by default.

‎compiler/rustc_parse/src/parser/item.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use super::diagnostics::{dummy_arg, ConsumeClosingDelim, Error, UseEmptyBlockNotSemi};
1+
use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi};
2+
3+
use super::diagnostics::{dummy_arg, ConsumeClosingDelim};
24
use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
35
use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken};
46

@@ -13,7 +15,7 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari
1315
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
1416
use rustc_ast::{MacArgs, MacCall, MacDelimiter};
1517
use rustc_ast_pretty::pprust;
16-
use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
18+
use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey};
1719
use rustc_span::edition::Edition;
1820
use rustc_span::lev_distance::lev_distance;
1921
use rustc_span::source_map::{self, Span};
@@ -1584,7 +1586,10 @@ impl<'a> Parser<'a> {
15841586
token::CloseDelim(Delimiter::Brace) => {}
15851587
token::DocComment(..) => {
15861588
let previous_span = self.prev_token.span;
1587-
let mut err = self.span_err(self.token.span, Error::UselessDocComment);
1589+
let mut err = DocCommentDoesNotDocumentAnything {
1590+
span: self.token.span,
1591+
missing_comma: None,
1592+
};
15881593
self.bump(); // consume the doc comment
15891594
let comma_after_doc_seen = self.eat(&token::Comma);
15901595
// `seen_comma` is always false, because we are inside doc block
@@ -1593,18 +1598,13 @@ impl<'a> Parser<'a> {
15931598
seen_comma = true;
15941599
}
15951600
if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
1596-
err.emit();
1601+
self.sess.emit_err(err);
15971602
} else {
15981603
if !seen_comma {
15991604
let sp = self.sess.source_map().next_point(previous_span);
1600-
err.span_suggestion(
1601-
sp,
1602-
"missing comma here",
1603-
",",
1604-
Applicability::MachineApplicable,
1605-
);
1605+
err.missing_comma = Some(sp);
16061606
}
1607-
return Err(err);
1607+
return Err(err.into_diagnostic(&self.sess.span_diagnostic));
16081608
}
16091609
}
16101610
_ => {

‎compiler/rustc_parse/src/parser/mod.rs

Lines changed: 54 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ mod ty;
1313
use crate::lexer::UnmatchedBrace;
1414
pub use attr_wrapper::AttrWrapper;
1515
pub use diagnostics::AttemptLocalParseRecovery;
16-
use diagnostics::Error;
1716
pub(crate) use item::FnParseMode;
1817
pub use pat::{CommaRecoveryMode, RecoverColon, RecoverComma};
1918
pub use path::PathStyle;
@@ -32,7 +31,7 @@ use rustc_ast_pretty::pprust;
3231
use rustc_data_structures::fx::FxHashMap;
3332
use rustc_errors::PResult;
3433
use rustc_errors::{
35-
struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, MultiSpan,
34+
Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError, IntoDiagnostic, MultiSpan,
3635
};
3736
use rustc_session::parse::ParseSess;
3837
use rustc_span::source_map::{Span, DUMMY_SP};
@@ -41,6 +40,11 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
4140
use std::ops::Range;
4241
use std::{cmp, mem, slice};
4342

43+
use crate::errors::{
44+
DocCommentDoesNotDocumentAnything, IncorrectVisibilityRestriction, MismatchedClosingDelimiter,
45+
NonStringAbiLiteral,
46+
};
47+
4448
bitflags::bitflags! {
4549
struct Restrictions: u8 {
4650
const STMT_EXPR = 1 << 0;
@@ -406,24 +410,39 @@ pub enum FollowedByType {
406410
No,
407411
}
408412

409-
fn token_descr_opt(token: &Token) -> Option<&'static str> {
410-
Some(match token.kind {
411-
_ if token.is_special_ident() => "reserved identifier",
412-
_ if token.is_used_keyword() => "keyword",
413-
_ if token.is_unused_keyword() => "reserved keyword",
414-
token::DocComment(..) => "doc comment",
415-
_ => return None,
416-
})
413+
#[derive(Clone, Copy, PartialEq, Eq)]
414+
pub enum TokenDescription {
415+
ReservedIdentifier,
416+
Keyword,
417+
ReservedKeyword,
418+
DocComment,
417419
}
418420

419-
pub(super) fn token_descr(token: &Token) -> String {
420-
let token_str = pprust::token_to_string(token);
421-
match token_descr_opt(token) {
422-
Some(prefix) => format!("{} `{}`", prefix, token_str),
423-
_ => format!("`{}`", token_str),
421+
impl TokenDescription {
422+
pub fn from_token(token: &Token) -> Option<Self> {
423+
match token.kind {
424+
_ if token.is_special_ident() => Some(TokenDescription::ReservedIdentifier),
425+
_ if token.is_used_keyword() => Some(TokenDescription::Keyword),
426+
_ if token.is_unused_keyword() => Some(TokenDescription::ReservedKeyword),
427+
token::DocComment(..) => Some(TokenDescription::DocComment),
428+
_ => None,
429+
}
424430
}
425431
}
426432

433+
pub(super) fn token_descr(token: &Token) -> String {
434+
let name = pprust::token_to_string(token).to_string();
435+
436+
let kind = TokenDescription::from_token(token).map(|kind| match kind {
437+
TokenDescription::ReservedIdentifier => "reserved identifier",
438+
TokenDescription::Keyword => "keyword",
439+
TokenDescription::ReservedKeyword => "reserved keyword",
440+
TokenDescription::DocComment => "doc comment",
441+
});
442+
443+
if let Some(kind) = kind { format!("{} `{}`", kind, name) } else { format!("`{}`", name) }
444+
}
445+
427446
impl<'a> Parser<'a> {
428447
pub fn new(
429448
sess: &'a ParseSess,
@@ -518,9 +537,11 @@ impl<'a> Parser<'a> {
518537

519538
fn ident_or_err(&mut self) -> PResult<'a, (Ident, /* is_raw */ bool)> {
520539
self.token.ident().ok_or_else(|| match self.prev_token.kind {
521-
TokenKind::DocComment(..) => {
522-
self.span_err(self.prev_token.span, Error::UselessDocComment)
540+
TokenKind::DocComment(..) => DocCommentDoesNotDocumentAnything {
541+
span: self.prev_token.span,
542+
missing_comma: None,
523543
}
544+
.into_diagnostic(&self.sess.span_diagnostic),
524545
_ => self.expected_ident_found(),
525546
})
526547
}
@@ -1144,7 +1165,9 @@ impl<'a> Parser<'a> {
11441165
fn parse_field_name(&mut self) -> PResult<'a, Ident> {
11451166
if let token::Literal(token::Lit { kind: token::Integer, symbol, suffix }) = self.token.kind
11461167
{
1147-
self.expect_no_suffix(self.token.span, "a tuple index", suffix);
1168+
if let Some(suffix) = suffix {
1169+
self.expect_no_tuple_index_suffix(self.token.span, suffix);
1170+
}
11481171
self.bump();
11491172
Ok(Ident::new(symbol, self.prev_token.span))
11501173
} else {
@@ -1342,23 +1365,8 @@ impl<'a> Parser<'a> {
13421365
let path = self.parse_path(PathStyle::Mod)?;
13431366
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
13441367

1345-
let msg = "incorrect visibility restriction";
1346-
let suggestion = r##"some possible visibility restrictions are:
1347-
`pub(crate)`: visible only on the current crate
1348-
`pub(super)`: visible only in the current module's parent
1349-
`pub(in path::to::module)`: visible only on the specified path"##;
1350-
13511368
let path_str = pprust::path_to_string(&path);
1352-
1353-
struct_span_err!(self.sess.span_diagnostic, path.span, E0704, "{}", msg)
1354-
.help(suggestion)
1355-
.span_suggestion(
1356-
path.span,
1357-
&format!("make this visible only to module `{}` with `in`", path_str),
1358-
format!("in {}", path_str),
1359-
Applicability::MachineApplicable,
1360-
)
1361-
.emit();
1369+
self.sess.emit_err(IncorrectVisibilityRestriction { span: path.span, inner_str: path_str });
13621370

13631371
Ok(())
13641372
}
@@ -1384,14 +1392,7 @@ impl<'a> Parser<'a> {
13841392
Err(Some(lit)) => match lit.kind {
13851393
ast::LitKind::Err => None,
13861394
_ => {
1387-
self.struct_span_err(lit.span, "non-string ABI literal")
1388-
.span_suggestion(
1389-
lit.span,
1390-
"specify the ABI with a string literal",
1391-
"\"C\"",
1392-
Applicability::MaybeIncorrect,
1393-
)
1394-
.emit();
1395+
self.sess.emit_err(NonStringAbiLiteral { span: lit.span });
13951396
None
13961397
}
13971398
},
@@ -1432,25 +1433,18 @@ pub(crate) fn make_unclosed_delims_error(
14321433
// `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
14331434
// `unmatched_braces` only for error recovery in the `Parser`.
14341435
let found_delim = unmatched.found_delim?;
1435-
let span: MultiSpan = if let Some(sp) = unmatched.unclosed_span {
1436-
vec![unmatched.found_span, sp].into()
1437-
} else {
1438-
unmatched.found_span.into()
1439-
};
1440-
let mut err = sess.span_diagnostic.struct_span_err(
1441-
span,
1442-
&format!(
1443-
"mismatched closing delimiter: `{}`",
1444-
pprust::token_kind_to_string(&token::CloseDelim(found_delim)),
1445-
),
1446-
);
1447-
err.span_label(unmatched.found_span, "mismatched closing delimiter");
1448-
if let Some(sp) = unmatched.candidate_span {
1449-
err.span_label(sp, "closing delimiter possibly meant for this");
1450-
}
1436+
let mut spans = vec![unmatched.found_span];
14511437
if let Some(sp) = unmatched.unclosed_span {
1452-
err.span_label(sp, "unclosed delimiter");
1453-
}
1438+
spans.push(sp);
1439+
};
1440+
let err = MismatchedClosingDelimiter {
1441+
spans,
1442+
delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
1443+
unmatched: unmatched.found_span,
1444+
opening_candidate: unmatched.candidate_span,
1445+
unclosed: unmatched.unclosed_span,
1446+
}
1447+
.into_diagnostic(&sess.span_diagnostic);
14541448
Some(err)
14551449
}
14561450

‎compiler/rustc_parse/src/parser/pat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{ForceCollect, Parser, PathStyle, TrailingToken};
2-
use crate::parser::diagnostics::RemoveLet;
2+
use crate::errors::RemoveLet;
33
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
44
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
55
use rustc_ast::ptr::P;

‎compiler/rustc_parse/src/parser/stmt.rs

Lines changed: 37 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN;
2-
use super::diagnostics::{
3-
AttemptLocalParseRecovery, Error, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
4-
};
1+
use super::attr::InnerAttrForbiddenReason;
2+
use super::diagnostics::AttemptLocalParseRecovery;
53
use super::expr::LhsExpr;
64
use super::pat::RecoverComma;
75
use super::path::PathStyle;
86
use super::TrailingToken;
97
use super::{
108
AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
119
};
10+
use crate::errors::{
11+
AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
12+
DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
13+
InvalidExpressionInLetElse, InvalidVariableDeclaration, InvalidVariableDeclarationSub,
14+
WrapExpressionInParentheses,
15+
};
1216
use crate::maybe_whole;
1317

1418
use rustc_ast as ast;
@@ -112,11 +116,7 @@ impl<'a> Parser<'a> {
112116
let bl = self.parse_block()?;
113117
// Destructuring assignment ... else.
114118
// This is not allowed, but point it out in a nice way.
115-
let mut err = self.struct_span_err(
116-
e.span.to(bl.span),
117-
"<assignment> ... else { ... } is not allowed",
118-
);
119-
err.emit();
119+
self.sess.emit_err(AssignmentElseNotAllowed { span: e.span.to(bl.span) });
120120
}
121121
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
122122
} else {
@@ -202,9 +202,12 @@ impl<'a> Parser<'a> {
202202
fn error_outer_attrs(&self, attrs: &[Attribute]) {
203203
if let [.., last] = attrs {
204204
if last.is_doc_comment() {
205-
self.span_err(last.span, Error::UselessDocComment).emit();
205+
self.sess.emit_err(DocCommentDoesNotDocumentAnything {
206+
span: last.span,
207+
missing_comma: None,
208+
});
206209
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
207-
self.struct_span_err(last.span, "expected statement after outer attribute").emit();
210+
self.sess.emit_err(ExpectedStatementAfterOuterAttr { span: last.span });
208211
}
209212
}
210213
}
@@ -255,17 +258,7 @@ impl<'a> Parser<'a> {
255258
let lo = self.prev_token.span;
256259

257260
if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
258-
self.struct_span_err(
259-
lo.to(self.token.span),
260-
"`const` and `let` are mutually exclusive",
261-
)
262-
.span_suggestion(
263-
lo.to(self.token.span),
264-
"remove `let`",
265-
"const",
266-
Applicability::MaybeIncorrect,
267-
)
268-
.emit();
261+
self.sess.emit_err(ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
269262
self.bump();
270263
}
271264

@@ -363,44 +356,27 @@ impl<'a> Parser<'a> {
363356
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
364357
if let ast::ExprKind::Binary(op, ..) = init.kind {
365358
if op.node.lazy() {
366-
let suggs = vec![
367-
(init.span.shrink_to_lo(), "(".to_string()),
368-
(init.span.shrink_to_hi(), ")".to_string()),
369-
];
370-
self.struct_span_err(
371-
init.span,
372-
&format!(
373-
"a `{}` expression cannot be directly assigned in `let...else`",
374-
op.node.to_string()
375-
),
376-
)
377-
.multipart_suggestion(
378-
"wrap the expression in parentheses",
379-
suggs,
380-
Applicability::MachineApplicable,
381-
)
382-
.emit();
359+
self.sess.emit_err(InvalidExpressionInLetElse {
360+
span: init.span,
361+
operator: op.node.to_string(),
362+
sugg: WrapExpressionInParentheses {
363+
left: init.span.shrink_to_lo(),
364+
right: init.span.shrink_to_hi(),
365+
},
366+
});
383367
}
384368
}
385369
}
386370

387371
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
388372
if let Some(trailing) = classify::expr_trailing_brace(init) {
389-
let err_span = trailing.span.with_lo(trailing.span.hi() - BytePos(1));
390-
let suggs = vec![
391-
(trailing.span.shrink_to_lo(), "(".to_string()),
392-
(trailing.span.shrink_to_hi(), ")".to_string()),
393-
];
394-
self.struct_span_err(
395-
err_span,
396-
"right curly brace `}` before `else` in a `let...else` statement not allowed",
397-
)
398-
.multipart_suggestion(
399-
"try wrapping the expression in parentheses",
400-
suggs,
401-
Applicability::MachineApplicable,
402-
)
403-
.emit();
373+
self.sess.emit_err(InvalidCurlyInLetElse {
374+
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
375+
sugg: WrapExpressionInParentheses {
376+
left: trailing.span.shrink_to_lo(),
377+
right: trailing.span.shrink_to_hi(),
378+
},
379+
});
404380
}
405381
}
406382

@@ -409,18 +385,7 @@ impl<'a> Parser<'a> {
409385
let eq_consumed = match self.token.kind {
410386
token::BinOpEq(..) => {
411387
// Recover `let x <op>= 1` as `let x = 1`
412-
self.struct_span_err(
413-
self.token.span,
414-
"can't reassign to an uninitialized variable",
415-
)
416-
.span_suggestion_short(
417-
self.token.span,
418-
"initialize the variable",
419-
"=",
420-
Applicability::MaybeIncorrect,
421-
)
422-
.help("if you meant to overwrite, remove the `let` binding")
423-
.emit();
388+
self.sess.emit_err(CompoundAssignmentExpressionInLet { span: self.token.span });
424389
self.bump();
425390
true
426391
}
@@ -434,7 +399,12 @@ impl<'a> Parser<'a> {
434399
pub(super) fn parse_block(&mut self) -> PResult<'a, P<Block>> {
435400
let (attrs, block) = self.parse_inner_attrs_and_block()?;
436401
if let [.., last] = &*attrs {
437-
self.error_on_forbidden_inner_attr(last.span, DEFAULT_INNER_ATTR_FORBIDDEN);
402+
self.error_on_forbidden_inner_attr(
403+
last.span,
404+
super::attr::InnerAttrPolicy::Forbidden(Some(
405+
InnerAttrForbiddenReason::InCodeBlock,
406+
)),
407+
);
438408
}
439409
Ok(block)
440410
}

‎compiler/rustc_session/src/errors.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,18 @@ impl IntoDiagnostic<'_> for InvalidCharacterInCrateName<'_> {
219219
diag
220220
}
221221
}
222+
223+
#[derive(Subdiagnostic)]
224+
#[multipart_suggestion(session::expr_parentheses_needed, applicability = "machine-applicable")]
225+
pub struct ExprParenthesesNeeded {
226+
#[suggestion_part(code = "(")]
227+
pub left: Span,
228+
#[suggestion_part(code = ")")]
229+
pub right: Span,
230+
}
231+
232+
impl ExprParenthesesNeeded {
233+
pub fn surrounding(s: Span) -> Self {
234+
ExprParenthesesNeeded { left: s.shrink_to_lo(), right: s.shrink_to_hi() }
235+
}
236+
}

‎compiler/rustc_session/src/parse.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
//! It also serves as an input to the parser itself.
33
44
use crate::config::CheckCfg;
5-
use crate::errors::{FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError};
5+
use crate::errors::{
6+
ExprParenthesesNeeded, FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError,
7+
};
68
use crate::lint::{
79
builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId,
810
};
@@ -11,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
1113
use rustc_data_structures::sync::{Lock, Lrc};
1214
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
1315
use rustc_errors::{
14-
fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
16+
fallback_fluent_bundle, AddToDiagnostic, Diagnostic, DiagnosticBuilder, DiagnosticId,
1517
DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, IntoDiagnostic, MultiSpan, StashKey,
1618
};
1719
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
@@ -325,11 +327,7 @@ impl ParseSess {
325327
/// Extend an error with a suggestion to wrap an expression with parentheses to allow the
326328
/// parser to continue parsing the following operation as part of the same expression.
327329
pub fn expr_parentheses_needed(&self, err: &mut Diagnostic, span: Span) {
328-
err.multipart_suggestion(
329-
"parentheses are required to parse this as an expression",
330-
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), ")".to_string())],
331-
Applicability::MachineApplicable,
332-
);
330+
ExprParenthesesNeeded::surrounding(span).add_to_diagnostic(err);
333331
}
334332

335333
pub fn save_proc_macro_span(&self, span: Span) -> usize {

‎src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,3 +671,9 @@ enum ExampleEnum {
671671
#[diag(typeck::ambiguous_lifetime_bound)]
672672
Baz,
673673
}
674+
675+
#[derive(Diagnostic)]
676+
#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")]
677+
struct RawIdentDiagnosticArg {
678+
pub r#type: String,
679+
}

‎src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,11 @@ struct BI {
633633
#[suggestion_part(code = "")]
634634
spans: Vec<Span>,
635635
}
636+
637+
#[derive(Subdiagnostic)]
638+
#[label(parser::add_paren)]
639+
struct BJ {
640+
#[primary_span]
641+
span: Span,
642+
r#type: String,
643+
}

‎src/test/ui/extenv/issue-55897.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod nonexistent_env {
1414

1515
mod erroneous_literal {
1616
include!(concat!("NON_EXISTENT"suffix, "/data.rs"));
17-
//~^ ERROR suffixes on a string literal are invalid
17+
//~^ ERROR suffixes on string literals are invalid
1818
}
1919

2020
fn main() {}

‎src/test/ui/extenv/issue-55897.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | include!(concat!(env!("NON_EXISTENT"), "/data.rs"));
66
|
77
= note: this error originates in the macro `env` (in Nightly builds, run with -Z macro-backtrace for more info)
88

9-
error: suffixes on a string literal are invalid
9+
error: suffixes on string literals are invalid
1010
--> $DIR/issue-55897.rs:16:22
1111
|
1212
LL | include!(concat!("NON_EXISTENT"suffix, "/data.rs"));

‎src/test/ui/issues/issue-22644.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn main() {
2929
< //~ ERROR `<` is interpreted as a start of generic
3030
5);
3131

32-
println!("{}", a as usize << long_name); //~ ERROR `<` is interpreted as a start of generic
32+
println!("{}", a as usize << long_name); //~ ERROR `<<` is interpreted as a start of generic
3333

3434
println!("{}", a: &mut 4); //~ ERROR expected type, found `4`
3535
}

‎src/test/ui/issues/issue-22644.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ LL |
9595
LL ~ usize)
9696
|
9797

98-
error: `<` is interpreted as a start of generic arguments for `usize`, not a shift
98+
error: `<<` is interpreted as a start of generic arguments for `usize`, not a shift
9999
--> $DIR/issue-22644.rs:32:31
100100
|
101101
LL | println!("{}", a as usize << long_name);

‎src/test/ui/let-else/let-else-brace-before-else.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
44
LL | let Some(1) = { Some(1) } else {
55
| ^
66
|
7-
help: try wrapping the expression in parentheses
7+
help: wrap the expression in parentheses
88
|
99
LL | let Some(1) = ({ Some(1) }) else {
1010
| + +
@@ -15,7 +15,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
1515
LL | let Some(1) = loop { break Some(1) } else {
1616
| ^
1717
|
18-
help: try wrapping the expression in parentheses
18+
help: wrap the expression in parentheses
1919
|
2020
LL | let Some(1) = (loop { break Some(1) }) else {
2121
| + +
@@ -26,7 +26,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
2626
LL | let 2 = 1 + match 1 { n => n } else {
2727
| ^
2828
|
29-
help: try wrapping the expression in parentheses
29+
help: wrap the expression in parentheses
3030
|
3131
LL | let 2 = 1 + (match 1 { n => n }) else {
3232
| + +
@@ -37,7 +37,7 @@ error: right curly brace `}` before `else` in a `let...else` statement not allow
3737
LL | let Some(1) = unsafe { unsafe_fn() } else {
3838
| ^
3939
|
40-
help: try wrapping the expression in parentheses
40+
help: wrap the expression in parentheses
4141
|
4242
LL | let Some(1) = (unsafe { unsafe_fn() }) else {
4343
| + +

‎src/test/ui/parser/bad-lit-suffixes.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
extern
2-
"C"suffix //~ ERROR suffixes on a string literal are invalid
2+
"C"suffix //~ ERROR suffixes on string literals are invalid
33
fn foo() {}
44

55
extern
6-
"C"suffix //~ ERROR suffixes on a string literal are invalid
6+
"C"suffix //~ ERROR suffixes on string literals are invalid
77
{}
88

99
fn main() {
10-
""suffix; //~ ERROR suffixes on a string literal are invalid
11-
b""suffix; //~ ERROR suffixes on a byte string literal are invalid
12-
r#""#suffix; //~ ERROR suffixes on a string literal are invalid
13-
br#""#suffix; //~ ERROR suffixes on a byte string literal are invalid
14-
'a'suffix; //~ ERROR suffixes on a char literal are invalid
15-
b'a'suffix; //~ ERROR suffixes on a byte literal are invalid
10+
""suffix; //~ ERROR suffixes on string literals are invalid
11+
b""suffix; //~ ERROR suffixes on byte string literals are invalid
12+
r#""#suffix; //~ ERROR suffixes on string literals are invalid
13+
br#""#suffix; //~ ERROR suffixes on byte string literals are invalid
14+
'a'suffix; //~ ERROR suffixes on char literals are invalid
15+
b'a'suffix; //~ ERROR suffixes on byte literals are invalid
1616

1717
1234u1024; //~ ERROR invalid width `1024` for integer literal
1818
1234i1024; //~ ERROR invalid width `1024` for integer literal

‎src/test/ui/parser/bad-lit-suffixes.stderr

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,46 @@
1-
error: suffixes on a string literal are invalid
1+
error: suffixes on string literals are invalid
22
--> $DIR/bad-lit-suffixes.rs:2:5
33
|
44
LL | "C"suffix
55
| ^^^^^^^^^ invalid suffix `suffix`
66

7-
error: suffixes on a string literal are invalid
7+
error: suffixes on string literals are invalid
88
--> $DIR/bad-lit-suffixes.rs:6:5
99
|
1010
LL | "C"suffix
1111
| ^^^^^^^^^ invalid suffix `suffix`
1212

13-
error: suffixes on a string literal are invalid
13+
error: suffixes on string literals are invalid
1414
--> $DIR/bad-lit-suffixes.rs:10:5
1515
|
1616
LL | ""suffix;
1717
| ^^^^^^^^ invalid suffix `suffix`
1818

19-
error: suffixes on a byte string literal are invalid
19+
error: suffixes on byte string literals are invalid
2020
--> $DIR/bad-lit-suffixes.rs:11:5
2121
|
2222
LL | b""suffix;
2323
| ^^^^^^^^^ invalid suffix `suffix`
2424

25-
error: suffixes on a string literal are invalid
25+
error: suffixes on string literals are invalid
2626
--> $DIR/bad-lit-suffixes.rs:12:5
2727
|
2828
LL | r#""#suffix;
2929
| ^^^^^^^^^^^ invalid suffix `suffix`
3030

31-
error: suffixes on a byte string literal are invalid
31+
error: suffixes on byte string literals are invalid
3232
--> $DIR/bad-lit-suffixes.rs:13:5
3333
|
3434
LL | br#""#suffix;
3535
| ^^^^^^^^^^^^ invalid suffix `suffix`
3636

37-
error: suffixes on a char literal are invalid
37+
error: suffixes on char literals are invalid
3838
--> $DIR/bad-lit-suffixes.rs:14:5
3939
|
4040
LL | 'a'suffix;
4141
| ^^^^^^^^^ invalid suffix `suffix`
4242

43-
error: suffixes on a byte literal are invalid
43+
error: suffixes on byte literals are invalid
4444
--> $DIR/bad-lit-suffixes.rs:15:5
4545
|
4646
LL | b'a'suffix;

‎src/test/ui/parser/inner-attr-after-doc-comment.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | | */
77
| |___- previous doc comment
88
LL |
99
LL | #![recursion_limit="100"]
10-
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer attribute
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ not permitted following an outer doc comment
1111
LL |
1212
LL | fn main() {}
1313
| ------------ the inner attribute doesn't annotate this function

0 commit comments

Comments
 (0)
Please sign in to comment.