From ee976bbbcaf85390a00f03dedacd035e7e274e8e Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Tue, 4 Mar 2025 14:17:06 +0100 Subject: fix bugs in inline/force_inline and diagnostics of all attr parsers --- compiler/rustc_parse/src/validate_attr.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 555ab3cdb2b..1c12e8c189d 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -282,11 +282,15 @@ fn emit_malformed_attribute( name: Symbol, template: AttributeTemplate, ) { + // attrs with new parsers are locally validated so excluded here + if matches!(name, sym::inline | sym::rustc_force_inline) { + return; + } + // Some of previously accepted forms were used in practice, // report them as warnings for now. - let should_warn = |name| { - matches!(name, sym::doc | sym::ignore | sym::inline | sym::link | sym::test | sym::bench) - }; + let should_warn = + |name| matches!(name, sym::doc | sym::ignore | sym::link | sym::test | sym::bench); let error_msg = format!("malformed `{name}` attribute input"); let mut suggestions = vec![]; -- cgit 1.4.1-3-g733a5 From 672452d573e3f499a8edc4308400a6d116974938 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Wed, 5 Mar 2025 14:35:54 +0100 Subject: use consistent attr errors in all attribute parsers --- compiler/rustc_attr_parsing/messages.ftl | 5 -- .../src/attributes/confusables.rs | 12 +-- .../src/attributes/deprecation.rs | 15 ++-- .../rustc_attr_parsing/src/attributes/inline.rs | 2 +- .../rustc_attr_parsing/src/attributes/stability.rs | 39 +++++---- .../src/attributes/transparency.rs | 13 ++- compiler/rustc_attr_parsing/src/context.rs | 73 ++++++++++++++-- .../rustc_attr_parsing/src/session_diagnostics.rs | 74 ++++++++--------- compiler/rustc_codegen_ssa/src/errors.rs | 15 ---- compiler/rustc_parse/src/validate_attr.rs | 2 +- tests/ui/attributes/rustc_confusables.rs | 4 +- tests/ui/attributes/rustc_confusables.stderr | 31 ++++--- tests/ui/deprecation/deprecation-sanity.rs | 10 +-- tests/ui/deprecation/deprecation-sanity.stderr | 97 ++++++++++++++++++++-- tests/ui/error-codes/E0534.stderr | 2 +- tests/ui/force-inlining/invalid.stderr | 6 +- tests/ui/invalid/invalid-inline.stderr | 4 +- tests/ui/issues/issue-43988.stderr | 4 +- tests/ui/span/E0535.stderr | 2 +- .../stability-attribute-sanity-2.rs | 2 +- .../stability-attribute-sanity-2.stderr | 10 ++- .../stability-attribute-sanity.rs | 8 +- .../stability-attribute-sanity.stderr | 32 ++++++- 23 files changed, 308 insertions(+), 154 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 2687bfdc716..70de83f2f74 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -27,8 +27,6 @@ attr_parsing_ill_formed_attribute_input = {$num_suggestions -> [1] attribute must be of the form {$suggestions} *[other] valid forms for the attribute are {$suggestions} } -attr_parsing_incorrect_meta_item = expected a quoted string literal -attr_parsing_incorrect_meta_item_suggestion = consider surrounding this with quotes attr_parsing_incorrect_repr_format_align_one_arg = incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses @@ -85,9 +83,6 @@ attr_parsing_missing_note = attr_parsing_missing_since = missing 'since' -attr_parsing_multiple_item = - multiple '{$item}' items - attr_parsing_multiple_stability_levels = multiple stability levels diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index 0be9d690c32..f4505cbc0e1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -19,9 +19,7 @@ impl AttributeParser for ConfusablesParser { template!(List: r#""name1", "name2", ..."#), |this, cx, args| { let Some(list) = args.list() else { - // FIXME(jdonszelmann): error when not a list? Bring validation code here. - // NOTE: currently subsequent attributes are silently ignored using - // tcx.get_attr(). + cx.expected_list(cx.attr_span); return; }; @@ -33,13 +31,7 @@ impl AttributeParser for ConfusablesParser { let span = param.span(); let Some(lit) = param.lit() else { - cx.emit_err(session_diagnostics::IncorrectMetaItem { - span, - suggestion: Some(session_diagnostics::IncorrectMetaItemSuggestion { - lo: span.shrink_to_lo(), - hi: span.shrink_to_hi(), - }), - }); + cx.expected_string_literal(span); continue; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 692f587ba03..0983a153efa 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -19,7 +19,7 @@ fn get( item: &Option, ) -> Option { if item.is_some() { - cx.emit_err(session_diagnostics::MultipleItem { span: param_span, item: name.to_string() }); + cx.duplicate_key(param_span, name); return None; } if let Some(v) = arg.name_value() { @@ -36,8 +36,7 @@ fn get( None } } else { - // FIXME(jdonszelmann): suggestion? - cx.emit_err(session_diagnostics::IncorrectMetaItem { span: param_span, suggestion: None }); + cx.expected_name_value(param_span, Some(name)); None } } @@ -99,15 +98,15 @@ impl SingleAttributeParser for DeprecationParser { suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?); } _ => { - cx.emit_err(session_diagnostics::UnknownMetaItem { - span: param_span, - item: param.path().to_string(), - expected: if features.deprecated_suggestion() { + cx.unknown_key( + param_span, + param.path().to_string(), + if features.deprecated_suggestion() { &["since", "note", "suggestion"] } else { &["since", "note"] }, - }); + ); return None; } } diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index da56656e3ed..75ae1d6a6c1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -29,7 +29,7 @@ impl SingleAttributeParser for InlineParser { return None; }; - match l.meta_item().and_then(|i| i.path().word().map(|i| i.name)) { + match l.meta_item().and_then(|i| i.path().word_sym()) { Some(sym::always) => { Some(AttributeKind::Inline(InlineAttr::Always, cx.attr_span)) } diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index fba725ae164..65de13972a0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -6,7 +6,7 @@ use rustc_attr_data_structures::{ }; use rustc_errors::ErrorGuaranteed; use rustc_feature::{AttributeTemplate, template}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use super::util::parse_version; use super::{AcceptMapping, AttributeOrder, AttributeParser, OnDuplicate, SingleAttributeParser}; @@ -217,12 +217,10 @@ fn insert_value_into_option_or_error( cx: &AcceptContext<'_, '_, S>, param: &MetaItemParser<'_>, item: &mut Option, + name: Ident, ) -> Option<()> { if item.is_some() { - cx.emit_err(session_diagnostics::MultipleItem { - span: param.span(), - item: param.path().to_string(), - }); + cx.duplicate_key(name.span, name.name); None } else if let Some(v) = param.args().name_value() && let Some(s) = v.value_as_str() @@ -230,10 +228,7 @@ fn insert_value_into_option_or_error( *item = Some(s); Some(()) } else { - cx.emit_err(session_diagnostics::IncorrectMetaItem { - span: param.span(), - suggestion: None, - }); + cx.expected_name_value(param.span(), Some(name.name)); None } } @@ -259,9 +254,14 @@ pub(crate) fn parse_stability( return None; }; - match param.path().word_sym() { - Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - Some(sym::since) => insert_value_into_option_or_error(cx, ¶m, &mut since)?, + let word = param.path().word(); + match word.map(|i| i.name) { + Some(sym::feature) => { + insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + } + Some(sym::since) => { + insert_value_into_option_or_error(cx, ¶m, &mut since, word.unwrap())? + } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param_span, @@ -328,11 +328,16 @@ pub(crate) fn parse_unstability( return None; }; - match param.path().word_sym() { - Some(sym::feature) => insert_value_into_option_or_error(cx, ¶m, &mut feature)?, - Some(sym::reason) => insert_value_into_option_or_error(cx, ¶m, &mut reason)?, + let word = param.path().word(); + match word.map(|i| i.name) { + Some(sym::feature) => { + insert_value_into_option_or_error(cx, ¶m, &mut feature, word.unwrap())? + } + Some(sym::reason) => { + insert_value_into_option_or_error(cx, ¶m, &mut reason, word.unwrap())? + } Some(sym::issue) => { - insert_value_into_option_or_error(cx, ¶m, &mut issue)?; + insert_value_into_option_or_error(cx, ¶m, &mut issue, word.unwrap())?; // These unwraps are safe because `insert_value_into_option_or_error` ensures the meta item // is a name/value pair string literal. @@ -362,7 +367,7 @@ pub(crate) fn parse_unstability( is_soft = true; } Some(sym::implied_by) => { - insert_value_into_option_or_error(cx, ¶m, &mut implied_by)? + insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())? } Some(sym::old_name) => insert_value_into_option_or_error(cx, ¶m, &mut old_name)?, _ => { diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs index b0f5dac7fe2..ce5ceb9139a 100644 --- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs +++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs @@ -22,12 +22,19 @@ impl SingleAttributeParser for TransparencyParser { template!(NameValueStr: "transparent|semitransparent|opaque"); fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { - match args.name_value().and_then(|nv| nv.value_as_str()) { + let Some(nv) = args.name_value() else { + cx.expected_name_value(cx.attr_span, None); + return None; + }; + match nv.value_as_str() { Some(sym::transparent) => Some(Transparency::Transparent), Some(sym::semiopaque | sym::semitransparent) => Some(Transparency::SemiOpaque), Some(sym::opaque) => Some(Transparency::Opaque), - Some(other) => { - cx.dcx().span_err(cx.attr_span, format!("unknown macro transparency: `{other}`")); + Some(_) => { + cx.expected_specific_argument_strings( + nv.value_span, + vec!["transparent", "semitransparent", "opaque"], + ); None } None => None, diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index ca95c67b418..d6dfa2c7477 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -27,7 +27,7 @@ use crate::attributes::stability::{ use crate::attributes::transparency::TransparencyParser; use crate::attributes::{AttributeParser as _, Combine, Single}; use crate::parser::{ArgParser, MetaItemParser}; -use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason}; +use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem}; macro_rules! group_type { ($stage: ty) => { @@ -191,8 +191,16 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { (self.emit_lint)(AttributeLint { id, span, kind: lint }); } + pub(crate) fn unknown_key( + &self, + span: Span, + found: String, + options: &'static [&'static str], + ) -> ErrorGuaranteed { + self.emit_err(UnknownMetaItem { span, item: found, expected: options }) + } + pub(crate) fn expected_string_literal(&self, span: Span) -> ErrorGuaranteed { - // 539? self.emit_err(AttributeParseError { span, attr_span: self.attr_span, @@ -202,12 +210,40 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } - // pub(crate) fn expected_any_arguments(&self, span: Span) -> ErrorGuaranteed { - // - // } + pub(crate) fn expected_list(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedList, + }) + } + + /// emit an error that a `name = value` pair was expected at this span. The symbol can be given for + /// a nicer error message talking about the specific name that was found lacking a value. + pub(crate) fn expected_name_value(&self, span: Span, name: Option) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedNameValue(name), + }) + } + + /// emit an error that a `name = value` pair was found where that name was already seen. + pub(crate) fn duplicate_key(&self, span: Span, key: Symbol) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::DuplicateKey(key), + }) + } pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { - // E534? self.emit_err(AttributeParseError { span, attr_span: self.attr_span, @@ -220,15 +256,34 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { pub(crate) fn expected_specific_argument( &self, span: Span, - options: Vec<&'static str>, + possibilities: Vec<&'static str>, + ) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: false, + }, + }) + } + + pub(crate) fn expected_specific_argument_strings( + &self, + span: Span, + possibilities: Vec<&'static str>, ) -> ErrorGuaranteed { - // E535? self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), - reason: AttributeParseErrorReason::ExpectedSpecificArgument(options), + reason: AttributeParseErrorReason::ExpectedSpecificArgument { + possibilities, + strings: true, + }, }) } } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 08cd8b5df2d..3d865c1d14e 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -36,37 +36,6 @@ pub(crate) struct InvalidPredicate { pub predicate: String, } -#[derive(Diagnostic)] -#[diag(attr_parsing_multiple_item, code = E0538)] -pub(crate) struct MultipleItem { - #[primary_span] - pub span: Span, - - pub item: String, -} - -#[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_meta_item, code = E0539)] -pub(crate) struct IncorrectMetaItem { - #[primary_span] - pub span: Span, - - #[subdiagnostic] - pub suggestion: Option, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - attr_parsing_incorrect_meta_item_suggestion, - applicability = "maybe-incorrect" -)] -pub(crate) struct IncorrectMetaItemSuggestion { - #[suggestion_part(code = "\"")] - pub lo: Span, - #[suggestion_part(code = "\"")] - pub hi: Span, -} - /// Error code: E0541 pub(crate) struct UnknownMetaItem<'a> { pub span: Span, @@ -506,7 +475,10 @@ pub(crate) struct UnrecognizedReprHint { pub(crate) enum AttributeParseErrorReason { ExpectedStringLiteral, ExpectedSingleArgument, - ExpectedSpecificArgument(Vec<&'static str>), + ExpectedList, + ExpectedNameValue(Option), + DuplicateKey(Symbol), + ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, } pub(crate) struct AttributeParseError { @@ -531,24 +503,46 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { AttributeParseErrorReason::ExpectedSingleArgument => { diag.span_note(self.span, "expected a single argument here"); } - AttributeParseErrorReason::ExpectedSpecificArgument(possibilities) => { + AttributeParseErrorReason::ExpectedList => { + diag.span_note(self.span, "expected this to be a list"); + } + AttributeParseErrorReason::DuplicateKey(key) => { + diag.span_note(self.span, format!("found `{key}` used as a key more than once")); + diag.code(E0538); + } + AttributeParseErrorReason::ExpectedNameValue(None) => { + diag.span_note( + self.span, + format!("expected this to be of the form `{name} = \"...\"`"), + ); + } + AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { + diag.span_note( + self.span, + format!("expected this to be of the form `{name} = \"...\"`"), + ); + } + AttributeParseErrorReason::ExpectedSpecificArgument { possibilities, strings } => { + let quote = if strings { '"' } else { '`' }; match possibilities.as_slice() { &[] => {} &[x] => { - diag.span_note(self.span, format!("the only valid argument here is `{x}`")); - } - [first, second] => { diag.span_note( self.span, - format!("valid arguments are `{first}` or `{second}`"), + format!("the only valid argument here is {quote}{x}{quote}"), ); } + [first, second] => { + diag.span_note(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}")); + } [first @ .., second_to_last, last] => { let mut res = String::new(); for i in first { - res.push_str(&format!("`{i}`, ")); + res.push_str(&format!("{quote}{i}{quote}, ")); } - res.push_str(&format!("`{second_to_last}` or `{last}`")); + res.push_str(&format!( + "{quote}{second_to_last}{quote} or {quote}{last}{quote}" + )); diag.span_note(self.span, format!("valid arguments are {res}")); } @@ -562,7 +556,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { if suggestions.len() == 1 { "must be of the form" } else { - "the following are possible correct uses" + "try changing it to one of the following valid forms of the attribute" }, suggestions, Applicability::HasPlaceholders, diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index bac02bdf983..5387b2a7f81 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -208,13 +208,6 @@ pub(crate) struct OutOfRangeInteger { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_expected_one_argument, code = E0534)] -pub(crate) struct ExpectedOneArgument { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_expected_one_argument, code = E0722)] pub(crate) struct ExpectedOneArgumentOptimize { @@ -222,14 +215,6 @@ pub(crate) struct ExpectedOneArgumentOptimize { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_argument, code = E0535)] -#[help] -pub(crate) struct InvalidArgument { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_invalid_argument, code = E0722)] pub(crate) struct InvalidArgumentOptimize { diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 1c12e8c189d..2296ec10432 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -283,7 +283,7 @@ fn emit_malformed_attribute( template: AttributeTemplate, ) { // attrs with new parsers are locally validated so excluded here - if matches!(name, sym::inline | sym::rustc_force_inline) { + if matches!(name, sym::inline | sym::rustc_force_inline | sym::rustc_confusables) { return; } diff --git a/tests/ui/attributes/rustc_confusables.rs b/tests/ui/attributes/rustc_confusables.rs index 93d9a7d572c..a8095936cff 100644 --- a/tests/ui/attributes/rustc_confusables.rs +++ b/tests/ui/attributes/rustc_confusables.rs @@ -37,8 +37,8 @@ impl Bar { fn qux() {} #[rustc_confusables(invalid_meta_item)] - //~^ ERROR expected a quoted string literal - //~| HELP consider surrounding this with quotes + //~^ ERROR malformed `rustc_confusables` attribute input [E0539] + //~| HELP must be of the form fn quux() {} } diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index aba384ff8ac..be16da2f873 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -1,25 +1,32 @@ -error: malformed `rustc_confusables` attribute input - --> $DIR/rustc_confusables.rs:34:5 - | -LL | #[rustc_confusables] - | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` - error: expected at least one confusable name --> $DIR/rustc_confusables.rs:30:5 | LL | #[rustc_confusables()] | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: expected a quoted string literal - --> $DIR/rustc_confusables.rs:39:25 +error[E0539]: malformed `rustc_confusables` attribute input + --> $DIR/rustc_confusables.rs:34:5 + | +LL | #[rustc_confusables] + | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` + | +note: expected this to be a list + --> $DIR/rustc_confusables.rs:34:5 + | +LL | #[rustc_confusables] + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0539]: malformed `rustc_confusables` attribute input + --> $DIR/rustc_confusables.rs:39:5 | LL | #[rustc_confusables(invalid_meta_item)] - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` | -help: consider surrounding this with quotes +note: expected a string literal here + --> $DIR/rustc_confusables.rs:39:25 | -LL | #[rustc_confusables("invalid_meta_item")] - | + + +LL | #[rustc_confusables(invalid_meta_item)] + | ^^^^^^^^^^^^^^^^^ error: attribute should be applied to an inherent method --> $DIR/rustc_confusables.rs:45:1 diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index d5b149b18ed..e40b017378a 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -4,16 +4,16 @@ mod bogus_attribute_types_1 { #[deprecated(since = "a", note = "a", reason)] //~ ERROR unknown meta item 'reason' fn f1() { } - #[deprecated(since = "a", note)] //~ ERROR expected a quoted string literal + #[deprecated(since = "a", note)] //~ ERROR malformed `deprecated` attribute input [E0539] fn f2() { } - #[deprecated(since, note = "a")] //~ ERROR expected a quoted string literal + #[deprecated(since, note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539] fn f3() { } - #[deprecated(since = "a", note(b))] //~ ERROR expected a quoted string literal + #[deprecated(since = "a", note(b))] //~ ERROR malformed `deprecated` attribute input [E0539] fn f5() { } - #[deprecated(since(b), note = "a")] //~ ERROR expected a quoted string literal + #[deprecated(since(b), note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539] fn f6() { } #[deprecated(note = b"test")] //~ ERROR literal in `deprecated` value must be a string @@ -27,7 +27,7 @@ mod bogus_attribute_types_1 { #[deprecated(since = "a", note = "b")] //~ ERROR multiple `deprecated` attributes fn multiple1() { } -#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR multiple 'since' items +#[deprecated(since = "a", since = "b", note = "c")] //~ ERROR malformed `deprecated` attribute input [E0538] fn f1() { } struct X; diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index 53047d40cb2..8854ebb70d2 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -4,29 +4,97 @@ error[E0541]: unknown meta item 'reason' LL | #[deprecated(since = "a", note = "a", reason)] | ^^^^^^ expected one of `since`, `note` -error[E0539]: expected a quoted string literal +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:7:5 + | +LL | #[deprecated(since = "a", note)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this to be of the form `note = "..."` --> $DIR/deprecation-sanity.rs:7:31 | LL | #[deprecated(since = "a", note)] | ^^^^ +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated(since = "a", note)] +LL + #[deprecated = "reason"] + | +LL - #[deprecated(since = "a", note)] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated(since = "a", note)] +LL + #[deprecated] + | -error[E0539]: expected a quoted string literal +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:10:5 + | +LL | #[deprecated(since, note = "a")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this to be of the form `since = "..."` --> $DIR/deprecation-sanity.rs:10:18 | LL | #[deprecated(since, note = "a")] | ^^^^^ +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated(since, note = "a")] +LL + #[deprecated = "reason"] + | +LL - #[deprecated(since, note = "a")] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated(since, note = "a")] +LL + #[deprecated] + | -error[E0539]: expected a quoted string literal +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:13:5 + | +LL | #[deprecated(since = "a", note(b))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this to be of the form `note = "..."` --> $DIR/deprecation-sanity.rs:13:31 | LL | #[deprecated(since = "a", note(b))] | ^^^^^^^ +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated(since = "a", note(b))] +LL + #[deprecated = "reason"] + | +LL - #[deprecated(since = "a", note(b))] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated(since = "a", note(b))] +LL + #[deprecated] + | -error[E0539]: expected a quoted string literal +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:16:5 + | +LL | #[deprecated(since(b), note = "a")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: expected this to be of the form `since = "..."` --> $DIR/deprecation-sanity.rs:16:18 | LL | #[deprecated(since(b), note = "a")] | ^^^^^^^^ +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated(since(b), note = "a")] +LL + #[deprecated = "reason"] + | +LL - #[deprecated(since(b), note = "a")] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated(since(b), note = "a")] +LL + #[deprecated] + | error[E0565]: literal in `deprecated` value must be a string --> $DIR/deprecation-sanity.rs:19:25 @@ -54,11 +122,28 @@ note: attribute also specified here LL | #[deprecated(since = "a", note = "b")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0538]: multiple 'since' items +error[E0538]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:30:1 + | +LL | #[deprecated(since = "a", since = "b", note = "c")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: found `since` used as a key more than once --> $DIR/deprecation-sanity.rs:30:27 | LL | #[deprecated(since = "a", since = "b", note = "c")] - | ^^^^^^^^^^^ + | ^^^^^ +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated(since = "a", since = "b", note = "c")] +LL + #[deprecated = "reason"] + | +LL - #[deprecated(since = "a", since = "b", note = "c")] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated(since = "a", since = "b", note = "c")] +LL + #[deprecated] + | error: this `#[deprecated]` annotation has no effect --> $DIR/deprecation-sanity.rs:35:1 diff --git a/tests/ui/error-codes/E0534.stderr b/tests/ui/error-codes/E0534.stderr index f63d80c220c..de97ef18b73 100644 --- a/tests/ui/error-codes/E0534.stderr +++ b/tests/ui/error-codes/E0534.stderr @@ -9,7 +9,7 @@ note: expected a single argument here | LL | #[inline()] | ^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[inline(always|never)] | ++++++++++++ diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr index 26259a4d61f..a12f633b3f2 100644 --- a/tests/ui/force-inlining/invalid.stderr +++ b/tests/ui/force-inlining/invalid.stderr @@ -15,7 +15,7 @@ note: expected a single argument here | LL | #[rustc_force_inline(bar, baz)] | ^^^^^^^^^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline(bar, baz)] LL + #[rustc_force_inline = "reason"] @@ -38,7 +38,7 @@ note: expected a string literal here | LL | #[rustc_force_inline(2)] | ^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline(2)] LL + #[rustc_force_inline = "reason"] @@ -61,7 +61,7 @@ note: expected a string literal here | LL | #[rustc_force_inline = 2] | ^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline = 2] LL + #[rustc_force_inline = "reason"] diff --git a/tests/ui/invalid/invalid-inline.stderr b/tests/ui/invalid/invalid-inline.stderr index 965da7e7567..8a230351d32 100644 --- a/tests/ui/invalid/invalid-inline.stderr +++ b/tests/ui/invalid/invalid-inline.stderr @@ -9,7 +9,7 @@ note: expected a single argument here | LL | #[inline(please,no)] | ^^^^^^^^^^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[inline(please,no)] LL + #[inline(always|never)] @@ -29,7 +29,7 @@ note: expected a single argument here | LL | #[inline()] | ^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL | #[inline(always|never)] | ++++++++++++ diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr index 74c3e81cb1c..8e731db6f2e 100644 --- a/tests/ui/issues/issue-43988.stderr +++ b/tests/ui/issues/issue-43988.stderr @@ -21,7 +21,7 @@ note: valid arguments are `always` or `never` | LL | #[inline(XYZ)] | ^^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[inline(XYZ)] LL + #[inline(always|never)] @@ -57,7 +57,7 @@ note: valid arguments are `always` or `never` | LL | #[inline(ABC)] | ^^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[inline(ABC)] LL + #[inline(always|never)] diff --git a/tests/ui/span/E0535.stderr b/tests/ui/span/E0535.stderr index 54dcdd89579..fb0b72c0001 100644 --- a/tests/ui/span/E0535.stderr +++ b/tests/ui/span/E0535.stderr @@ -9,7 +9,7 @@ note: valid arguments are `always` or `never` | LL | #[inline(unknown)] | ^^^^^^^ -help: the following are possible correct uses +help: try changing it to one of the following valid forms of the attribute | LL - #[inline(unknown)] LL + #[inline(always|never)] diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs index de3ea4eaca9..92e300d33d6 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.rs @@ -4,7 +4,7 @@ #![stable(feature = "stable_test_feature", since = "1.0.0")] -#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR multiple 'feature' items +#[stable(feature = "a", feature = "b", since = "1.0.0")] //~ ERROR malformed `stable` attribute input [E0538] fn f1() { } #[stable(feature = "a", sinse = "1.0.0")] //~ ERROR unknown meta item 'sinse' diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr index 8dbcc6c97ef..412af87bad6 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr @@ -1,8 +1,14 @@ -error[E0538]: multiple 'feature' items +error[E0538]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity-2.rs:7:1 + | +LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` + | +note: found `feature` used as a key more than once --> $DIR/stability-attribute-sanity-2.rs:7:25 | LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] - | ^^^^^^^^^^^^^ + | ^^^^^^^ error[E0541]: unknown meta item 'sinse' --> $DIR/stability-attribute-sanity-2.rs:10:25 diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.rs b/tests/ui/stability-attribute/stability-attribute-sanity.rs index f46e35e1a72..c4c86e12d26 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.rs +++ b/tests/ui/stability-attribute/stability-attribute-sanity.rs @@ -8,16 +8,16 @@ mod bogus_attribute_types_1 { #[stable(feature = "a", since = "4.4.4", reason)] //~ ERROR unknown meta item 'reason' [E0541] fn f1() { } - #[stable(feature = "a", since)] //~ ERROR expected a quoted string literal [E0539] + #[stable(feature = "a", since)] //~ ERROR malformed `stable` attribute input [E0539] fn f2() { } - #[stable(feature, since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539] + #[stable(feature, since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539] fn f3() { } - #[stable(feature = "a", since(b))] //~ ERROR expected a quoted string literal [E0539] + #[stable(feature = "a", since(b))] //~ ERROR malformed `stable` attribute input [E0539] fn f5() { } - #[stable(feature(b), since = "3.3.3")] //~ ERROR expected a quoted string literal [E0539] + #[stable(feature(b), since = "3.3.3")] //~ ERROR malformed `stable` attribute input [E0539] fn f6() { } } diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index 2e2b5b509c8..86284b0c1b8 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -4,25 +4,49 @@ error[E0541]: unknown meta item 'reason' LL | #[stable(feature = "a", since = "4.4.4", reason)] | ^^^^^^ expected one of `feature`, `since` -error[E0539]: expected a quoted string literal +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:11:5 + | +LL | #[stable(feature = "a", since)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` + | +note: expected this to be of the form `since = "..."` --> $DIR/stability-attribute-sanity.rs:11:29 | LL | #[stable(feature = "a", since)] | ^^^^^ -error[E0539]: expected a quoted string literal +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:14:5 + | +LL | #[stable(feature, since = "3.3.3")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` + | +note: expected this to be of the form `feature = "..."` --> $DIR/stability-attribute-sanity.rs:14:14 | LL | #[stable(feature, since = "3.3.3")] | ^^^^^^^ -error[E0539]: expected a quoted string literal +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:17:5 + | +LL | #[stable(feature = "a", since(b))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` + | +note: expected this to be of the form `since = "..."` --> $DIR/stability-attribute-sanity.rs:17:29 | LL | #[stable(feature = "a", since(b))] | ^^^^^^^^ -error[E0539]: expected a quoted string literal +error[E0539]: malformed `stable` attribute input + --> $DIR/stability-attribute-sanity.rs:20:5 + | +LL | #[stable(feature(b), since = "3.3.3")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` + | +note: expected this to be of the form `feature = "..."` --> $DIR/stability-attribute-sanity.rs:20:14 | LL | #[stable(feature(b), since = "3.3.3")] -- cgit 1.4.1-3-g733a5 From 5ab5f8a24a99cdbb178c886487d4c1971b86a32b Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Sat, 8 Mar 2025 18:58:05 +0100 Subject: make error codes reflect reality better --- compiler/rustc_attr_parsing/messages.ftl | 4 - .../src/attributes/confusables.rs | 6 +- .../src/attributes/deprecation.rs | 100 ++++++++++----------- .../rustc_attr_parsing/src/attributes/inline.rs | 4 +- .../src/attributes/lint_helpers.rs | 4 +- compiler/rustc_attr_parsing/src/attributes/repr.rs | 1 + .../rustc_attr_parsing/src/attributes/stability.rs | 4 +- compiler/rustc_attr_parsing/src/context.rs | 31 ++++++- .../rustc_attr_parsing/src/session_diagnostics.rs | 49 ++++++---- .../rustc_error_codes/src/error_codes/E0534.md | 8 +- .../rustc_error_codes/src/error_codes/E0535.md | 9 +- .../rustc_error_codes/src/error_codes/E0539.md | 29 +++++- .../rustc_error_codes/src/error_codes/E0565.md | 7 +- .../rustc_error_codes/src/error_codes/E0805.md | 26 ++++++ compiler/rustc_error_codes/src/lib.rs | 1 + compiler/rustc_parse/src/validate_attr.rs | 9 +- compiler/rustc_passes/src/check_attr.rs | 7 -- tests/ui/attributes/rustc_confusables.stderr | 20 ++--- tests/ui/deprecation/deprecation-sanity.rs | 4 +- tests/ui/deprecation/deprecation-sanity.stderr | 73 +++++++-------- tests/ui/deprecation/invalid-literal.stderr | 19 ++-- tests/ui/error-codes/E0534.rs | 6 -- tests/ui/error-codes/E0534.stderr | 22 ----- tests/ui/error-codes/E0539.rs | 5 ++ tests/ui/error-codes/E0539.stderr | 13 +++ tests/ui/error-codes/E0540.rs | 6 ++ tests/ui/error-codes/E0540.stderr | 19 ++++ tests/ui/error-codes/E0565-1.stderr | 20 ++++- tests/ui/error-codes/E0565-2.rs | 5 -- tests/ui/error-codes/E0565-2.stderr | 11 --- ...ssue-43106-gating-of-builtin-attrs-error.stderr | 20 ++--- tests/ui/force-inlining/invalid.stderr | 32 +++---- tests/ui/invalid/invalid-inline.stderr | 24 ++--- tests/ui/issues/issue-43988.stderr | 48 +++++----- tests/ui/lint/unused/unused-attr-duplicate.stderr | 36 ++++---- .../macros/genercs-in-path-with-prettry-hir.stdout | 2 +- tests/ui/repr/repr.stderr | 22 +++-- tests/ui/span/E0535.rs | 6 -- tests/ui/span/E0535.stderr | 23 ----- tests/ui/span/E0539.rs | 6 ++ tests/ui/span/E0539.stderr | 20 +++++ .../stability-attribute-sanity-2.stderr | 11 +-- .../stability-attribute-sanity.stderr | 44 ++++----- 43 files changed, 443 insertions(+), 373 deletions(-) create mode 100644 compiler/rustc_error_codes/src/error_codes/E0805.md delete mode 100644 tests/ui/error-codes/E0534.rs delete mode 100644 tests/ui/error-codes/E0534.stderr create mode 100644 tests/ui/error-codes/E0539.rs create mode 100644 tests/ui/error-codes/E0539.stderr create mode 100644 tests/ui/error-codes/E0540.rs create mode 100644 tests/ui/error-codes/E0540.stderr delete mode 100644 tests/ui/error-codes/E0565-2.rs delete mode 100644 tests/ui/error-codes/E0565-2.stderr delete mode 100644 tests/ui/span/E0535.rs delete mode 100644 tests/ui/span/E0535.stderr create mode 100644 tests/ui/span/E0539.rs create mode 100644 tests/ui/span/E0539.stderr (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index 70de83f2f74..b9b386635f6 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -121,10 +121,6 @@ attr_parsing_unsupported_literal_cfg_boolean = literal in `cfg` predicate value must be a boolean attr_parsing_unsupported_literal_cfg_string = literal in `cfg` predicate value must be a string -attr_parsing_unsupported_literal_deprecated_kv_pair = - item in `deprecated` must be a key/value pair -attr_parsing_unsupported_literal_deprecated_string = - literal in `deprecated` value must be a string attr_parsing_unsupported_literal_generic = unsupported literal attr_parsing_unsupported_literal_suggestion = diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs index f4505cbc0e1..c911908dfb3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs +++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs @@ -30,12 +30,12 @@ impl AttributeParser for ConfusablesParser { for param in list.mixed() { let span = param.span(); - let Some(lit) = param.lit() else { - cx.expected_string_literal(span); + let Some(lit) = param.lit().and_then(|i| i.value_str()) else { + cx.expected_string_literal(span, param.lit()); continue; }; - this.confusables.push(lit.symbol); + this.confusables.push(lit); } this.first_span.get_or_insert(cx.attr_span); diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs index 0983a153efa..702ad66f578 100644 --- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs +++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs @@ -7,7 +7,6 @@ use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; use crate::context::{AcceptContext, Stage}; use crate::parser::ArgParser; use crate::session_diagnostics; -use crate::session_diagnostics::UnsupportedLiteralReason; pub(crate) struct DeprecationParser; @@ -26,13 +25,7 @@ fn get( if let Some(value_str) = v.value_as_str() { Some(value_str) } else { - let lit = v.value_as_lit(); - cx.emit_err(session_diagnostics::UnsupportedLiteral { - span: v.value_span, - reason: UnsupportedLiteralReason::DeprecatedString, - is_bytestr: lit.kind.is_bytestr(), - start_point_span: cx.sess().source_map().start_point(lit.span), - }); + cx.expected_string_literal(v.value_span, Some(&v.value_as_lit())); None } } else { @@ -60,57 +53,60 @@ impl SingleAttributeParser for DeprecationParser { let is_rustc = features.staged_api(); - if let Some(value) = args.name_value() - && let Some(value_str) = value.value_as_str() - { - note = Some(value_str) - } else if let Some(list) = args.list() { - for param in list.mixed() { - let param_span = param.span(); - let Some(param) = param.meta_item() else { - cx.emit_err(session_diagnostics::UnsupportedLiteral { - span: param_span, - reason: UnsupportedLiteralReason::DeprecatedKvPair, - is_bytestr: false, - start_point_span: cx.sess().source_map().start_point(param_span), - }); - return None; - }; + match args { + ArgParser::NoArgs => { + // ok + } + ArgParser::List(list) => { + for param in list.mixed() { + let Some(param) = param.meta_item() else { + cx.unexpected_literal(param.span()); + return None; + }; - let ident_name = param.path().word_sym(); + let ident_name = param.path().word_sym(); - match ident_name { - Some(name @ sym::since) => { - since = Some(get(cx, name, param_span, param.args(), &since)?); - } - Some(name @ sym::note) => { - note = Some(get(cx, name, param_span, param.args(), ¬e)?); - } - Some(name @ sym::suggestion) => { - if !features.deprecated_suggestion() { - cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { - span: param_span, - is_nightly: cx.sess().is_nightly_build(), - details: (), - }); + match ident_name { + Some(name @ sym::since) => { + since = Some(get(cx, name, param.span(), param.args(), &since)?); + } + Some(name @ sym::note) => { + note = Some(get(cx, name, param.span(), param.args(), ¬e)?); } + Some(name @ sym::suggestion) => { + if !features.deprecated_suggestion() { + cx.emit_err(session_diagnostics::DeprecatedItemSuggestion { + span: param.span(), + is_nightly: cx.sess().is_nightly_build(), + details: (), + }); + } - suggestion = Some(get(cx, name, param_span, param.args(), &suggestion)?); - } - _ => { - cx.unknown_key( - param_span, - param.path().to_string(), - if features.deprecated_suggestion() { - &["since", "note", "suggestion"] - } else { - &["since", "note"] - }, - ); - return None; + suggestion = + Some(get(cx, name, param.span(), param.args(), &suggestion)?); + } + _ => { + cx.unknown_key( + param.span(), + param.path().to_string(), + if features.deprecated_suggestion() { + &["since", "note", "suggestion"] + } else { + &["since", "note"] + }, + ); + return None; + } } } } + ArgParser::NameValue(v) => { + let Some(value) = v.value_as_str() else { + cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); + return None; + }; + note = Some(value); + } } let since = if let Some(since) = since { diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs index 75ae1d6a6c1..25efc3ae49b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/inline.rs +++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs @@ -73,7 +73,7 @@ impl SingleAttributeParser for RustcForceInlineParser { }; let Some(reason) = l.lit().and_then(|i| i.kind.str()) else { - cx.expected_string_literal(l.span()); + cx.expected_string_literal(l.span(), l.lit()); return None; }; @@ -81,7 +81,7 @@ impl SingleAttributeParser for RustcForceInlineParser { } ArgParser::NameValue(v) => { let Some(reason) = v.value_as_str() else { - cx.expected_string_literal(v.value_span); + cx.expected_string_literal(v.value_span, Some(v.value_as_lit())); return None; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs index 32a20d4c5b5..d4c846de56e 100644 --- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs +++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs @@ -1,4 +1,5 @@ use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; use rustc_span::{Symbol, sym}; use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; @@ -9,10 +10,9 @@ pub(crate) struct AsPtrParser; impl SingleAttributeParser for AsPtrParser { const PATH: &[Symbol] = &[sym::rustc_as_ptr]; - const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepFirst; - const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = template!(Word); fn convert(cx: &mut AcceptContext<'_, '_, S>, _args: &ArgParser<'_>) -> Option { // FIXME: check that there's no args (this is currently checked elsewhere) diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index 6fb618541e3..ae9e7871874 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -34,6 +34,7 @@ impl CombineAttributeParser for ReprParser { let mut reprs = Vec::new(); let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); return reprs; }; diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs index 65de13972a0..6871ff4ec9f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/stability.rs +++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs @@ -369,7 +369,9 @@ pub(crate) fn parse_unstability( Some(sym::implied_by) => { insert_value_into_option_or_error(cx, ¶m, &mut implied_by, word.unwrap())? } - Some(sym::old_name) => insert_value_into_option_or_error(cx, ¶m, &mut old_name)?, + Some(sym::old_name) => { + insert_value_into_option_or_error(cx, ¶m, &mut old_name, word.unwrap())? + } _ => { cx.emit_err(session_diagnostics::UnknownMetaItem { span: param.span(), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index d6dfa2c7477..51c1760da30 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,8 +5,7 @@ use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; use private::Sealed; -use rustc_ast as ast; -use rustc_ast::NodeId; +use rustc_ast::{self as ast, MetaItemLit, NodeId}; use rustc_attr_data_structures::AttributeKind; use rustc_attr_data_structures::lints::{AttributeLint, AttributeLintKind}; use rustc_errors::{DiagCtxtHandle, Diagnostic}; @@ -200,13 +199,25 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { self.emit_err(UnknownMetaItem { span, item: found, expected: options }) } - pub(crate) fn expected_string_literal(&self, span: Span) -> ErrorGuaranteed { + /// error that a string literal was expected. + /// You can optionally give the literal you did find (which you found not to be a string literal) + /// which can make better errors. For example, if the literal was a byte string it will suggest + /// removing the `b` prefix. + pub(crate) fn expected_string_literal( + &self, + span: Span, + actual_literal: Option<&MetaItemLit>, + ) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), - reason: AttributeParseErrorReason::ExpectedStringLiteral, + reason: AttributeParseErrorReason::ExpectedStringLiteral { + byte_string: actual_literal.and_then(|i| { + i.kind.is_bytestr().then(|| self.sess().source_map().start_point(i.span)) + }), + }, }) } @@ -243,6 +254,18 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } + /// an error that should be emitted when a [`MetaItemOrLitParser`](crate::parser::MetaItemOrLitParser) + /// was expected *not* to be a literal, but instead a meta item. + pub(crate) fn unexpected_literal(&self, span: Span) -> ErrorGuaranteed { + self.emit_err(AttributeParseError { + span, + attr_span: self.attr_span, + template: self.template.clone(), + attribute: self.attr_path.clone(), + reason: AttributeParseErrorReason::UnexpectedLiteral, + }) + } + pub(crate) fn expected_single_argument(&self, span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { span, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 3d865c1d14e..57ac92a0ca1 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -16,8 +16,6 @@ pub(crate) enum UnsupportedLiteralReason { Generic, CfgString, CfgBoolean, - DeprecatedString, - DeprecatedKvPair, } #[derive(Diagnostic)] @@ -190,6 +188,7 @@ pub(crate) struct InvalidReprHintNoValue { } /// Error code: E0565 +// FIXME(jdonszelmann): slowly phased out pub(crate) struct UnsupportedLiteral { pub span: Span, pub reason: UnsupportedLiteralReason, @@ -212,12 +211,6 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { UnsupportedLiteralReason::CfgBoolean => { fluent::attr_parsing_unsupported_literal_cfg_boolean } - UnsupportedLiteralReason::DeprecatedString => { - fluent::attr_parsing_unsupported_literal_deprecated_string - } - UnsupportedLiteralReason::DeprecatedKvPair => { - fluent::attr_parsing_unsupported_literal_deprecated_kv_pair - } }, ); diag.span(self.span); @@ -473,9 +466,10 @@ pub(crate) struct UnrecognizedReprHint { } pub(crate) enum AttributeParseErrorReason { - ExpectedStringLiteral, + ExpectedStringLiteral { byte_string: Option }, ExpectedSingleArgument, ExpectedList, + UnexpectedLiteral, ExpectedNameValue(Option), DuplicateKey(Symbol), ExpectedSpecificArgument { possibilities: Vec<&'static str>, strings: bool }, @@ -497,27 +491,44 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { diag.span(self.attr_span); diag.code(E0539); match self.reason { - AttributeParseErrorReason::ExpectedStringLiteral => { - diag.span_note(self.span, "expected a string literal here"); + AttributeParseErrorReason::ExpectedStringLiteral { byte_string } => { + if let Some(start_point_span) = byte_string { + diag.span_suggestion( + start_point_span, + fluent::attr_parsing_unsupported_literal_suggestion, + "", + Applicability::MaybeIncorrect, + ); + diag.note("expected a normal string literal, not a byte string literal"); + + return diag; + } else { + diag.span_label(self.span, "expected a string literal here"); + } } AttributeParseErrorReason::ExpectedSingleArgument => { - diag.span_note(self.span, "expected a single argument here"); + diag.span_label(self.span, "expected a single argument here"); + diag.code(E0805); } AttributeParseErrorReason::ExpectedList => { - diag.span_note(self.span, "expected this to be a list"); + diag.span_label(self.span, "expected this to be a list"); } AttributeParseErrorReason::DuplicateKey(key) => { - diag.span_note(self.span, format!("found `{key}` used as a key more than once")); + diag.span_label(self.span, format!("found `{key}` used as a key more than once")); diag.code(E0538); } + AttributeParseErrorReason::UnexpectedLiteral => { + diag.span_label(self.span, format!("didn't expect a literal here")); + diag.code(E0565); + } AttributeParseErrorReason::ExpectedNameValue(None) => { - diag.span_note( + diag.span_label( self.span, format!("expected this to be of the form `{name} = \"...\"`"), ); } AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { - diag.span_note( + diag.span_label( self.span, format!("expected this to be of the form `{name} = \"...\"`"), ); @@ -527,13 +538,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { match possibilities.as_slice() { &[] => {} &[x] => { - diag.span_note( + diag.span_label( self.span, format!("the only valid argument here is {quote}{x}{quote}"), ); } [first, second] => { - diag.span_note(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}")); + diag.span_label(self.span, format!("valid arguments are {quote}{first}{quote} or {quote}{second}{quote}")); } [first @ .., second_to_last, last] => { let mut res = String::new(); @@ -544,7 +555,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { "{quote}{second_to_last}{quote} or {quote}{last}{quote}" )); - diag.span_note(self.span, format!("valid arguments are {res}")); + diag.span_label(self.span, format!("valid arguments are {res}")); } } } diff --git a/compiler/rustc_error_codes/src/error_codes/E0534.md b/compiler/rustc_error_codes/src/error_codes/E0534.md index 1ca9411b8d4..023c38c730c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0534.md +++ b/compiler/rustc_error_codes/src/error_codes/E0534.md @@ -1,8 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `inline` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0805` + The `inline` attribute was malformed. Erroneous code example: -```compile_fail,E0534 +```compile_fail,E0805 #[inline()] // error: expected one argument pub fn something() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0535.md b/compiler/rustc_error_codes/src/error_codes/E0535.md index 0cf3118b02c..93e2ba53826 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0535.md +++ b/compiler/rustc_error_codes/src/error_codes/E0535.md @@ -1,8 +1,13 @@ -An unknown argument was given to the `inline` attribute. +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `inline` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0539` + Erroneous code example: -```compile_fail,E0535 +```compile_fail,E0539 #[inline(unknown)] // error: invalid argument pub fn something() {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0539.md b/compiler/rustc_error_codes/src/error_codes/E0539.md index 6b2e23ba2d8..c76b60ac108 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0539.md +++ b/compiler/rustc_error_codes/src/error_codes/E0539.md @@ -24,8 +24,7 @@ struct Stable; const fn stable_fn() {} ``` -Meta items are the key-value pairs inside of an attribute. -To fix these issues you need to give required key-value pairs. +To fix the above example, you can write the following: ``` #![feature(staged_api)] @@ -49,3 +48,29 @@ struct Stable; #[rustc_const_stable(feature = "stable_fn", since = "1.39.0")] // ok! const fn stable_fn() {} ``` + +Several causes of this are, +an attribute may have expected you to give a list but you gave a +`name = value` pair: + +```compile_fail,E0539 +// wrong, should be `#[repr(C)]` +#[repr = "C"] +struct Foo {} +``` + +Or a `name = value` pair, but you gave a list: + +```compile_fail,E0539 +// wrong, should be `note = "reason"` +#[deprecated(since = "1.0.0", note("reason"))] +struct Foo {} +``` + +Or it expected some specific word but you gave an unexpected one: + +```compile_fail,E0539 +// should be `always` or `never` +#[inline(maybe_if_you_feel_like_it)] +fn foo() {} +``` diff --git a/compiler/rustc_error_codes/src/error_codes/E0565.md b/compiler/rustc_error_codes/src/error_codes/E0565.md index d5bba941c1d..34152eb7cfe 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0565.md +++ b/compiler/rustc_error_codes/src/error_codes/E0565.md @@ -9,10 +9,9 @@ struct Repr {} fn main() {} ``` -Literals in attributes are new and largely unsupported in built-in attributes. -Work to support literals where appropriate is ongoing. Try using an unquoted -name instead: - +Not all attributes support literals in their input, +and in some cases they expect an identifier instead. +That would be the solution in the case of `repr`: ``` #[repr(C)] // ok! struct Repr {} diff --git a/compiler/rustc_error_codes/src/error_codes/E0805.md b/compiler/rustc_error_codes/src/error_codes/E0805.md new file mode 100644 index 00000000000..b1ed3a11d48 --- /dev/null +++ b/compiler/rustc_error_codes/src/error_codes/E0805.md @@ -0,0 +1,26 @@ +An attribute was given an invalid number of arguments + +Erroneous code example: + +```compile_fail,E0805 +#[inline()] // error! should either have a single argument, or no parentheses +fn foo() {} + +#[inline(always, never)] // error! should have only one argument, not two +fn bar() {} +``` + +To fix this, either give the right number of arguments the attribute needs. +In the case of inline, this could be none at all: + +``` +#[inline] +fn foo() {} +``` + +or only one: + +``` +#[inline(always)] +fn foo() {} +``` diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 2488d870899..6f5e4829802 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -547,6 +547,7 @@ E0801: 0801, E0802: 0802, E0803: 0803, E0804: 0804, +E0805: 0805, ); ) } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 2296ec10432..b3096e46b09 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -283,7 +283,14 @@ fn emit_malformed_attribute( template: AttributeTemplate, ) { // attrs with new parsers are locally validated so excluded here - if matches!(name, sym::inline | sym::rustc_force_inline | sym::rustc_confusables) { + if matches!( + name, + sym::inline + | sym::rustc_force_inline + | sym::rustc_confusables + | sym::repr + | sym::deprecated + ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d543abb5411..5ce803aa1f8 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -728,13 +728,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } } - // FIXME(#80564): We permit struct fields, match arms and macro defs to have an - // `#[naked]` attribute with just a lint, because we previously - // erroneously allowed it and some crates used it accidentally, to be compatible - // with crates depending on them, we can't throw an error here. - Target::Field | Target::Arm | Target::MacroDef => { - self.inline_attr_str_error_with_macro_def(hir_id, attr.span(), "naked") - } _ => { self.dcx().emit_err(errors::AttrShouldBeAppliedToFn { attr_span: attr.span(), diff --git a/tests/ui/attributes/rustc_confusables.stderr b/tests/ui/attributes/rustc_confusables.stderr index be16da2f873..3ed4efeb4db 100644 --- a/tests/ui/attributes/rustc_confusables.stderr +++ b/tests/ui/attributes/rustc_confusables.stderr @@ -7,26 +7,20 @@ LL | #[rustc_confusables()] error[E0539]: malformed `rustc_confusables` attribute input --> $DIR/rustc_confusables.rs:34:5 | -LL | #[rustc_confusables] - | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` - | -note: expected this to be a list - --> $DIR/rustc_confusables.rs:34:5 - | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` error[E0539]: malformed `rustc_confusables` attribute input --> $DIR/rustc_confusables.rs:39:5 | LL | #[rustc_confusables(invalid_meta_item)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` - | -note: expected a string literal here - --> $DIR/rustc_confusables.rs:39:25 - | -LL | #[rustc_confusables(invalid_meta_item)] - | ^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^-----------------^^ + | | | + | | expected a string literal here + | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` error: attribute should be applied to an inherent method --> $DIR/rustc_confusables.rs:45:1 diff --git a/tests/ui/deprecation/deprecation-sanity.rs b/tests/ui/deprecation/deprecation-sanity.rs index e40b017378a..80198ab8196 100644 --- a/tests/ui/deprecation/deprecation-sanity.rs +++ b/tests/ui/deprecation/deprecation-sanity.rs @@ -16,10 +16,10 @@ mod bogus_attribute_types_1 { #[deprecated(since(b), note = "a")] //~ ERROR malformed `deprecated` attribute input [E0539] fn f6() { } - #[deprecated(note = b"test")] //~ ERROR literal in `deprecated` value must be a string + #[deprecated(note = b"test")] //~ ERROR malformed `deprecated` attribute input [E0539] fn f7() { } - #[deprecated("test")] //~ ERROR item in `deprecated` must be a key/value pair + #[deprecated("test")] //~ ERROR malformed `deprecated` attribute input [E0565] fn f8() { } } diff --git a/tests/ui/deprecation/deprecation-sanity.stderr b/tests/ui/deprecation/deprecation-sanity.stderr index 8854ebb70d2..f1b4697485c 100644 --- a/tests/ui/deprecation/deprecation-sanity.stderr +++ b/tests/ui/deprecation/deprecation-sanity.stderr @@ -8,13 +8,10 @@ error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:7:5 | LL | #[deprecated(since = "a", note)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^----^^ + | | + | expected this to be of the form `note = "..."` | -note: expected this to be of the form `note = "..."` - --> $DIR/deprecation-sanity.rs:7:31 - | -LL | #[deprecated(since = "a", note)] - | ^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[deprecated(since = "a", note)] @@ -31,13 +28,10 @@ error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:10:5 | LL | #[deprecated(since, note = "a")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: expected this to be of the form `since = "..."` - --> $DIR/deprecation-sanity.rs:10:18 + | ^^^^^^^^^^^^^-----^^^^^^^^^^^^^^ + | | + | expected this to be of the form `since = "..."` | -LL | #[deprecated(since, note = "a")] - | ^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[deprecated(since, note = "a")] @@ -54,13 +48,10 @@ error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:13:5 | LL | #[deprecated(since = "a", note(b))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: expected this to be of the form `note = "..."` - --> $DIR/deprecation-sanity.rs:13:31 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^-------^^ + | | + | expected this to be of the form `note = "..."` | -LL | #[deprecated(since = "a", note(b))] - | ^^^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[deprecated(since = "a", note(b))] @@ -77,13 +68,10 @@ error[E0539]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:16:5 | LL | #[deprecated(since(b), note = "a")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^--------^^^^^^^^^^^^^^ + | | + | expected this to be of the form `since = "..."` | -note: expected this to be of the form `since = "..."` - --> $DIR/deprecation-sanity.rs:16:18 - | -LL | #[deprecated(since(b), note = "a")] - | ^^^^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[deprecated(since(b), note = "a")] @@ -96,19 +84,35 @@ LL - #[deprecated(since(b), note = "a")] LL + #[deprecated] | -error[E0565]: literal in `deprecated` value must be a string - --> $DIR/deprecation-sanity.rs:19:25 +error[E0539]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:19:5 | LL | #[deprecated(note = b"test")] - | -^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^-^^^^^^^^ | | | help: consider removing the prefix + | + = note: expected a normal string literal, not a byte string literal -error[E0565]: item in `deprecated` must be a key/value pair - --> $DIR/deprecation-sanity.rs:22:18 +error[E0565]: malformed `deprecated` attribute input + --> $DIR/deprecation-sanity.rs:22:5 | LL | #[deprecated("test")] - | ^^^^^^ + | ^^^^^^^^^^^^^------^^ + | | + | didn't expect a literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated("test")] +LL + #[deprecated = "reason"] + | +LL - #[deprecated("test")] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated("test")] +LL + #[deprecated] + | error: multiple `deprecated` attributes --> $DIR/deprecation-sanity.rs:27:1 @@ -126,13 +130,10 @@ error[E0538]: malformed `deprecated` attribute input --> $DIR/deprecation-sanity.rs:30:1 | LL | #[deprecated(since = "a", since = "b", note = "c")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^^^^^^^^^^^ + | | + | found `since` used as a key more than once | -note: found `since` used as a key more than once - --> $DIR/deprecation-sanity.rs:30:27 - | -LL | #[deprecated(since = "a", since = "b", note = "c")] - | ^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[deprecated(since = "a", since = "b", note = "c")] diff --git a/tests/ui/deprecation/invalid-literal.stderr b/tests/ui/deprecation/invalid-literal.stderr index cbe1fcca023..6f25aebc315 100644 --- a/tests/ui/deprecation/invalid-literal.stderr +++ b/tests/ui/deprecation/invalid-literal.stderr @@ -1,20 +1,13 @@ -error: malformed `deprecated` attribute input +error[E0539]: malformed `deprecated` attribute input --> $DIR/invalid-literal.rs:1:1 | LL | #[deprecated = b"test"] - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL - #[deprecated = b"test"] -LL + #[deprecated = "reason"] - | -LL - #[deprecated = b"test"] -LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] - | -LL - #[deprecated = b"test"] -LL + #[deprecated] + | ^^^^^^^^^^^^^^^-^^^^^^^ + | | + | help: consider removing the prefix | + = note: expected a normal string literal, not a byte string literal error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/error-codes/E0534.rs b/tests/ui/error-codes/E0534.rs deleted file mode 100644 index db29e6801f5..00000000000 --- a/tests/ui/error-codes/E0534.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[inline()] //~ ERROR malformed `inline` attribute input -pub fn something() {} - -fn main() { - something(); -} diff --git a/tests/ui/error-codes/E0534.stderr b/tests/ui/error-codes/E0534.stderr deleted file mode 100644 index de97ef18b73..00000000000 --- a/tests/ui/error-codes/E0534.stderr +++ /dev/null @@ -1,22 +0,0 @@ -error[E0539]: malformed `inline` attribute input - --> $DIR/E0534.rs:1:1 - | -LL | #[inline()] - | ^^^^^^^^^^^ - | -note: expected a single argument here - --> $DIR/E0534.rs:1:9 - | -LL | #[inline()] - | ^^ -help: try changing it to one of the following valid forms of the attribute - | -LL | #[inline(always|never)] - | ++++++++++++ -LL - #[inline()] -LL + #[inline] - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/error-codes/E0539.rs b/tests/ui/error-codes/E0539.rs new file mode 100644 index 00000000000..0e2c537f473 --- /dev/null +++ b/tests/ui/error-codes/E0539.rs @@ -0,0 +1,5 @@ +// repr currently doesn't support literals +#[deprecated(since = b"1.29", note = "hi")] //~ ERROR E0539 +struct A { } + +fn main() { } diff --git a/tests/ui/error-codes/E0539.stderr b/tests/ui/error-codes/E0539.stderr new file mode 100644 index 00000000000..18ed1c23b40 --- /dev/null +++ b/tests/ui/error-codes/E0539.stderr @@ -0,0 +1,13 @@ +error[E0539]: malformed `deprecated` attribute input + --> $DIR/E0539.rs:2:1 + | +LL | #[deprecated(since = b"1.29", note = "hi")] + | ^^^^^^^^^^^^^^^^^^^^^-^^^^^^^^^^^^^^^^^^^^^ + | | + | help: consider removing the prefix + | + = note: expected a normal string literal, not a byte string literal + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/error-codes/E0540.rs b/tests/ui/error-codes/E0540.rs new file mode 100644 index 00000000000..db29e6801f5 --- /dev/null +++ b/tests/ui/error-codes/E0540.rs @@ -0,0 +1,6 @@ +#[inline()] //~ ERROR malformed `inline` attribute input +pub fn something() {} + +fn main() { + something(); +} diff --git a/tests/ui/error-codes/E0540.stderr b/tests/ui/error-codes/E0540.stderr new file mode 100644 index 00000000000..3e5f408feb5 --- /dev/null +++ b/tests/ui/error-codes/E0540.stderr @@ -0,0 +1,19 @@ +error[E0805]: malformed `inline` attribute input + --> $DIR/E0540.rs:1:1 + | +LL | #[inline()] + | ^^^^^^^^--^ + | | + | expected a single argument here + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[inline(always|never)] + | ++++++++++++ +LL - #[inline()] +LL + #[inline] + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/ui/error-codes/E0565-1.stderr b/tests/ui/error-codes/E0565-1.stderr index 806eed2a632..6277e6400d7 100644 --- a/tests/ui/error-codes/E0565-1.stderr +++ b/tests/ui/error-codes/E0565-1.stderr @@ -1,8 +1,22 @@ -error[E0565]: item in `deprecated` must be a key/value pair - --> $DIR/E0565-1.rs:2:14 +error[E0565]: malformed `deprecated` attribute input + --> $DIR/E0565-1.rs:2:1 | LL | #[deprecated("since")] - | ^^^^^^^ + | ^^^^^^^^^^^^^-------^^ + | | + | didn't expect a literal here + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[deprecated("since")] +LL + #[deprecated = "reason"] + | +LL - #[deprecated("since")] +LL + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")] + | +LL - #[deprecated("since")] +LL + #[deprecated] + | error: aborting due to 1 previous error diff --git a/tests/ui/error-codes/E0565-2.rs b/tests/ui/error-codes/E0565-2.rs deleted file mode 100644 index 0319ecb11f9..00000000000 --- a/tests/ui/error-codes/E0565-2.rs +++ /dev/null @@ -1,5 +0,0 @@ -// repr currently doesn't support literals -#[deprecated(since = b"1.29", note = "hi")] //~ ERROR E0565 -struct A { } - -fn main() { } diff --git a/tests/ui/error-codes/E0565-2.stderr b/tests/ui/error-codes/E0565-2.stderr deleted file mode 100644 index 42199351c3d..00000000000 --- a/tests/ui/error-codes/E0565-2.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0565]: literal in `deprecated` value must be a string - --> $DIR/E0565-2.rs:2:22 - | -LL | #[deprecated(since = b"1.29", note = "hi")] - | -^^^^^^ - | | - | help: consider removing the prefix - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr index a9ef0bbc6e7..1620bf72922 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs-error.stderr @@ -8,16 +8,6 @@ LL | #![rustc_main] = note: the `#[rustc_main]` attribute is an internal implementation detail that will never be stable = note: the `#[rustc_main]` attribute is used internally to specify test entry point function -error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]` - --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 - | -LL | #[inline = "2100"] fn f() { } - | ^^^^^^^^^^^^^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` on by default - error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:32:1 | @@ -314,6 +304,16 @@ error[E0517]: attribute should be applied to a struct, enum, or union LL | #[repr(Rust)] impl S { } | ^^^^ ---------- not a struct, enum, or union +error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]` + --> $DIR/issue-43106-gating-of-builtin-attrs-error.rs:46:5 + | +LL | #[inline = "2100"] fn f() { } + | ^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` on by default + error: aborting due to 38 previous errors Some errors have detailed explanations: E0517, E0518, E0658. diff --git a/tests/ui/force-inlining/invalid.stderr b/tests/ui/force-inlining/invalid.stderr index a12f633b3f2..3b3da00ae88 100644 --- a/tests/ui/force-inlining/invalid.stderr +++ b/tests/ui/force-inlining/invalid.stderr @@ -4,17 +4,14 @@ error: allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed LL | fn barqux(#[rustc_force_inline] _x: u32) {} | ^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `rustc_force_inline` attribute input +error[E0805]: malformed `rustc_force_inline` attribute input --> $DIR/invalid.rs:15:1 | LL | #[rustc_force_inline(bar, baz)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: expected a single argument here - --> $DIR/invalid.rs:15:21 + | ^^^^^^^^^^^^^^^^^^^^----------^ + | | + | expected a single argument here | -LL | #[rustc_force_inline(bar, baz)] - | ^^^^^^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline(bar, baz)] @@ -31,13 +28,10 @@ error[E0539]: malformed `rustc_force_inline` attribute input --> $DIR/invalid.rs:20:1 | LL | #[rustc_force_inline(2)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: expected a string literal here - --> $DIR/invalid.rs:20:22 + | ^^^^^^^^^^^^^^^^^^^^^-^^ + | | + | expected a string literal here | -LL | #[rustc_force_inline(2)] - | ^ help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline(2)] @@ -54,13 +48,10 @@ error[E0539]: malformed `rustc_force_inline` attribute input --> $DIR/invalid.rs:25:1 | LL | #[rustc_force_inline = 2] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: expected a string literal here - --> $DIR/invalid.rs:25:24 + | ^^^^^^^^^^^^^^^^^^^^^^^-^ + | | + | expected a string literal here | -LL | #[rustc_force_inline = 2] - | ^ help: try changing it to one of the following valid forms of the attribute | LL - #[rustc_force_inline = 2] @@ -392,4 +383,5 @@ LL | fn foo(); error: aborting due to 37 previous errors -For more information about this error, try `rustc --explain E0539`. +Some errors have detailed explanations: E0539, E0805. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/invalid/invalid-inline.stderr b/tests/ui/invalid/invalid-inline.stderr index 8a230351d32..54e6b2b5408 100644 --- a/tests/ui/invalid/invalid-inline.stderr +++ b/tests/ui/invalid/invalid-inline.stderr @@ -1,14 +1,11 @@ -error[E0539]: malformed `inline` attribute input +error[E0805]: malformed `inline` attribute input --> $DIR/invalid-inline.rs:3:1 | LL | #[inline(please,no)] - | ^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^-----------^ + | | + | expected a single argument here | -note: expected a single argument here - --> $DIR/invalid-inline.rs:3:9 - | -LL | #[inline(please,no)] - | ^^^^^^^^^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[inline(please,no)] @@ -18,17 +15,14 @@ LL - #[inline(please,no)] LL + #[inline] | -error[E0539]: malformed `inline` attribute input +error[E0805]: malformed `inline` attribute input --> $DIR/invalid-inline.rs:7:1 | LL | #[inline()] - | ^^^^^^^^^^^ + | ^^^^^^^^--^ + | | + | expected a single argument here | -note: expected a single argument here - --> $DIR/invalid-inline.rs:7:9 - | -LL | #[inline()] - | ^^ help: try changing it to one of the following valid forms of the attribute | LL | #[inline(always|never)] @@ -39,4 +33,4 @@ LL + #[inline] error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0539`. +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/ui/issues/issue-43988.stderr b/tests/ui/issues/issue-43988.stderr index 8e731db6f2e..bd4eb8bbed3 100644 --- a/tests/ui/issues/issue-43988.stderr +++ b/tests/ui/issues/issue-43988.stderr @@ -1,26 +1,11 @@ -error: malformed `repr` attribute input - --> $DIR/issue-43988.rs:24:5 - | -LL | #[repr] - | ^^^^^^^ help: must be of the form: `#[repr(C)]` - -error: malformed `repr` attribute input - --> $DIR/issue-43988.rs:34:14 - | -LL | let _z = #[repr] 1; - | ^^^^^^^ help: must be of the form: `#[repr(C)]` - error[E0539]: malformed `inline` attribute input --> $DIR/issue-43988.rs:10:5 | LL | #[inline(XYZ)] - | ^^^^^^^^^^^^^^ + | ^^^^^^^^^---^^ + | | + | valid arguments are `always` or `never` | -note: valid arguments are `always` or `never` - --> $DIR/issue-43988.rs:10:14 - | -LL | #[inline(XYZ)] - | ^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[inline(XYZ)] @@ -46,17 +31,23 @@ LL | #[repr(something_not_real)] | = help: valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` +error[E0539]: malformed `repr` attribute input + --> $DIR/issue-43988.rs:24:5 + | +LL | #[repr] + | ^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C)]` + error[E0539]: malformed `inline` attribute input --> $DIR/issue-43988.rs:30:5 | LL | #[inline(ABC)] - | ^^^^^^^^^^^^^^ - | -note: valid arguments are `always` or `never` - --> $DIR/issue-43988.rs:30:14 + | ^^^^^^^^^---^^ + | | + | valid arguments are `always` or `never` | -LL | #[inline(ABC)] - | ^^^ help: try changing it to one of the following valid forms of the attribute | LL - #[inline(ABC)] @@ -66,6 +57,15 @@ LL - #[inline(ABC)] LL + #[inline] | +error[E0539]: malformed `repr` attribute input + --> $DIR/issue-43988.rs:34:14 + | +LL | let _z = #[repr] 1; + | ^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C)]` + error[E0518]: attribute should be applied to function or closure --> $DIR/issue-43988.rs:5:5 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 5ea623a713e..e1c45e832af 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -1,21 +1,3 @@ -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:74:1 - | -LL | #[inline(never)] - | ^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:73:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -note: the lint level is defined here - --> $DIR/unused-attr-duplicate.rs:12:9 - | -LL | #![deny(unused_attributes)] - | ^^^^^^^^^^^^^^^^^ - error: unused attribute --> $DIR/unused-attr-duplicate.rs:33:1 | @@ -27,6 +9,11 @@ note: attribute also specified here | LL | #[no_link] | ^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/unused-attr-duplicate.rs:12:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ error: unused attribute --> $DIR/unused-attr-duplicate.rs:37:1 @@ -289,5 +276,18 @@ note: attribute also specified here LL | #[macro_export] | ^^^^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:74:1 + | +LL | #[inline(never)] + | ^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:73:1 + | +LL | #[inline(always)] + | ^^^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + error: aborting due to 23 previous errors diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout index 834954d8dc0..7c41225f95e 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout @@ -8,7 +8,7 @@ extern crate std; // issue#97006 macro_rules! m { ($attr_path: path) => { #[$attr_path] fn f() {} } } -#[attr="Inline(Hint)")] +#[attr = Inline(Hint)] fn f() { } fn main() { } diff --git a/tests/ui/repr/repr.stderr b/tests/ui/repr/repr.stderr index e0bec666381..f3b11398eaa 100644 --- a/tests/ui/repr/repr.stderr +++ b/tests/ui/repr/repr.stderr @@ -1,20 +1,30 @@ -error: malformed `repr` attribute input +error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:1:1 | LL | #[repr] - | ^^^^^^^ help: must be of the form: `#[repr(C)]` + | ^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C)]` -error: malformed `repr` attribute input +error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:4:1 | LL | #[repr = "B"] - | ^^^^^^^^^^^^^ help: must be of the form: `#[repr(C)]` + | ^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C)]` -error: malformed `repr` attribute input +error[E0539]: malformed `repr` attribute input --> $DIR/repr.rs:7:1 | LL | #[repr = "C"] - | ^^^^^^^^^^^^^ help: must be of the form: `#[repr(C)]` + | ^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[repr(C)]` error: aborting due to 3 previous errors +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/span/E0535.rs b/tests/ui/span/E0535.rs deleted file mode 100644 index e0c6dbfc591..00000000000 --- a/tests/ui/span/E0535.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[inline(unknown)] //~ ERROR malformed `inline` attribute -pub fn something() {} - -fn main() { - something(); -} diff --git a/tests/ui/span/E0535.stderr b/tests/ui/span/E0535.stderr deleted file mode 100644 index fb0b72c0001..00000000000 --- a/tests/ui/span/E0535.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0539]: malformed `inline` attribute input - --> $DIR/E0535.rs:1:1 - | -LL | #[inline(unknown)] - | ^^^^^^^^^^^^^^^^^^ - | -note: valid arguments are `always` or `never` - --> $DIR/E0535.rs:1:10 - | -LL | #[inline(unknown)] - | ^^^^^^^ -help: try changing it to one of the following valid forms of the attribute - | -LL - #[inline(unknown)] -LL + #[inline(always|never)] - | -LL - #[inline(unknown)] -LL + #[inline] - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/span/E0539.rs b/tests/ui/span/E0539.rs new file mode 100644 index 00000000000..e0c6dbfc591 --- /dev/null +++ b/tests/ui/span/E0539.rs @@ -0,0 +1,6 @@ +#[inline(unknown)] //~ ERROR malformed `inline` attribute +pub fn something() {} + +fn main() { + something(); +} diff --git a/tests/ui/span/E0539.stderr b/tests/ui/span/E0539.stderr new file mode 100644 index 00000000000..01f091a2676 --- /dev/null +++ b/tests/ui/span/E0539.stderr @@ -0,0 +1,20 @@ +error[E0539]: malformed `inline` attribute input + --> $DIR/E0539.rs:1:1 + | +LL | #[inline(unknown)] + | ^^^^^^^^^-------^^ + | | + | valid arguments are `always` or `never` + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[inline(unknown)] +LL + #[inline(always|never)] + | +LL - #[inline(unknown)] +LL + #[inline] + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr index 412af87bad6..5b35a51cad7 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity-2.stderr @@ -2,13 +2,10 @@ error[E0538]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity-2.rs:7:1 | LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` - | -note: found `feature` used as a key more than once - --> $DIR/stability-attribute-sanity-2.rs:7:25 - | -LL | #[stable(feature = "a", feature = "b", since = "1.0.0")] - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^-------^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | found `feature` used as a key more than once + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0541]: unknown meta item 'sinse' --> $DIR/stability-attribute-sanity-2.rs:10:25 diff --git a/tests/ui/stability-attribute/stability-attribute-sanity.stderr b/tests/ui/stability-attribute/stability-attribute-sanity.stderr index 86284b0c1b8..ae948237d7e 100644 --- a/tests/ui/stability-attribute/stability-attribute-sanity.stderr +++ b/tests/ui/stability-attribute/stability-attribute-sanity.stderr @@ -8,49 +8,37 @@ error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:11:5 | LL | #[stable(feature = "a", since)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` - | -note: expected this to be of the form `since = "..."` - --> $DIR/stability-attribute-sanity.rs:11:29 - | -LL | #[stable(feature = "a", since)] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^-----^^ + | | | + | | expected this to be of the form `since = "..."` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:14:5 | LL | #[stable(feature, since = "3.3.3")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` - | -note: expected this to be of the form `feature = "..."` - --> $DIR/stability-attribute-sanity.rs:14:14 - | -LL | #[stable(feature, since = "3.3.3")] - | ^^^^^^^ + | ^^^^^^^^^-------^^^^^^^^^^^^^^^^^^^ + | | | + | | expected this to be of the form `feature = "..."` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:17:5 | LL | #[stable(feature = "a", since(b))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` - | -note: expected this to be of the form `since = "..."` - --> $DIR/stability-attribute-sanity.rs:17:29 - | -LL | #[stable(feature = "a", since(b))] - | ^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^--------^^ + | | | + | | expected this to be of the form `since = "..."` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0539]: malformed `stable` attribute input --> $DIR/stability-attribute-sanity.rs:20:5 | LL | #[stable(feature(b), since = "3.3.3")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[stable(feature = "name", since = "version")]` - | -note: expected this to be of the form `feature = "..."` - --> $DIR/stability-attribute-sanity.rs:20:14 - | -LL | #[stable(feature(b), since = "3.3.3")] - | ^^^^^^^^^^ + | ^^^^^^^^^----------^^^^^^^^^^^^^^^^^^^ + | | | + | | expected this to be of the form `feature = "..."` + | help: must be of the form: `#[stable(feature = "name", since = "version")]` error[E0546]: missing 'feature' --> $DIR/stability-attribute-sanity.rs:25:5 -- cgit 1.4.1-3-g733a5 From 1fdf2b562070ec98c5b32ee67b8c6d8145127a6e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Mon, 9 Jun 2025 20:08:52 +0200 Subject: add `#[align]` attribute Right now it's used for functions with `fn_align`, in the future it will get more uses (statics, struct fields, etc.) --- .../rustc_attr_data_structures/src/attributes.rs | 3 + compiler/rustc_attr_parsing/messages.ftl | 3 + compiler/rustc_attr_parsing/src/attributes/repr.rs | 58 ++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 3 +- .../rustc_attr_parsing/src/session_diagnostics.rs | 8 +++ compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 14 +--- compiler/rustc_feature/src/builtin_attrs.rs | 1 + .../rustc_middle/src/middle/codegen_fn_attrs.rs | 3 +- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/messages.ftl | 15 +++-- compiler/rustc_passes/src/check_attr.rs | 75 +++++++++++++--------- compiler/rustc_passes/src/errors.rs | 30 +++++++-- src/tools/miri/tests/pass/fn_align.rs | 10 +-- tests/assembly/naked-functions/wasm32.rs | 2 +- tests/codegen/align-fn.rs | 22 ++++--- tests/codegen/min-function-alignment.rs | 6 +- tests/codegen/naked-fn/aligned.rs | 2 +- tests/codegen/naked-fn/min-function-alignment.rs | 4 +- tests/ui/asm/naked-with-invalid-repr-attr.rs | 3 +- tests/ui/asm/naked-with-invalid-repr-attr.stderr | 12 ++-- tests/ui/attributes/arg-error-issue-121425.stderr | 12 ++-- tests/ui/attributes/invalid-repr.rs | 2 +- tests/ui/attributes/invalid-repr.stderr | 4 +- tests/ui/attributes/malformed-fn-align.rs | 17 ++++- tests/ui/attributes/malformed-fn-align.stderr | 69 ++++++++++++++++++-- tests/ui/feature-gates/feature-gate-fn_align.rs | 7 +- .../ui/feature-gates/feature-gate-fn_align.stderr | 37 +++++++---- tests/ui/repr/attr-usage-repr.rs | 2 +- tests/ui/repr/attr-usage-repr.stderr | 4 +- tests/ui/repr/malformed-repr-hints.stderr | 24 +++---- 30 files changed, 320 insertions(+), 133 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index d4c43456049..cdc01dc6c91 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -182,6 +182,9 @@ impl Deprecation { #[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute)] pub enum AttributeKind { // tidy-alphabetical-start + /// Represents `#[align(N)]`. + Align { align: Align, span: Span }, + /// Represents `#[rustc_allow_const_fn_unstable]`. AllowConstFnUnstable(ThinVec), diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl index b9b386635f6..0891afc003e 100644 --- a/compiler/rustc_attr_parsing/messages.ftl +++ b/compiler/rustc_attr_parsing/messages.ftl @@ -44,6 +44,9 @@ attr_parsing_incorrect_repr_format_packed_expect_integer = attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all +attr_parsing_invalid_alignment_value = + invalid alignment value: {$error_part} + attr_parsing_invalid_issue_string = `issue` must be a non-zero numeric string or "none" .must_not_be_zero = `issue` must not be "0", use "none" instead diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs index ae9e7871874..c9f9f34bdb7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/repr.rs +++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs @@ -4,7 +4,7 @@ use rustc_attr_data_structures::{AttributeKind, IntType, ReprAttr}; use rustc_feature::{AttributeTemplate, template}; use rustc_span::{DUMMY_SP, Span, Symbol, sym}; -use super::{CombineAttributeParser, ConvertFn}; +use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext}; use crate::context::{AcceptContext, Stage}; use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser}; use crate::session_diagnostics; @@ -203,7 +203,7 @@ fn parse_repr_align( }); } Align => { - cx.dcx().emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg { + cx.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg { span: param_span, }); } @@ -266,3 +266,57 @@ fn parse_alignment(node: &LitKind) -> Result { Err("not an unsuffixed integer") } } + +/// Parse #[align(N)]. +#[derive(Default)] +pub(crate) struct AlignParser(Option<(Align, Span)>); + +impl AlignParser { + const PATH: &'static [Symbol] = &[sym::align]; + const TEMPLATE: AttributeTemplate = template!(Word, List: ""); + + fn parse<'c, S: Stage>( + &mut self, + cx: &'c mut AcceptContext<'_, '_, S>, + args: &'c ArgParser<'_>, + ) { + match args { + ArgParser::NoArgs | ArgParser::NameValue(_) => { + cx.expected_list(cx.attr_span); + } + ArgParser::List(list) => { + let Some(align) = list.single() else { + cx.expected_single_argument(list.span); + return; + }; + + let Some(lit) = align.lit() else { + cx.emit_err(session_diagnostics::IncorrectReprFormatExpectInteger { + span: align.span(), + }); + + return; + }; + + match parse_alignment(&lit.kind) { + Ok(literal) => self.0 = Ord::max(self.0, Some((literal, cx.attr_span))), + Err(message) => { + cx.emit_err(session_diagnostics::InvalidAlignmentValue { + span: lit.span, + error_part: message, + }); + } + } + } + } + } +} + +impl AttributeParser for AlignParser { + const ATTRIBUTES: AcceptMapping = &[(Self::PATH, Self::TEMPLATE, Self::parse)]; + + fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option { + let (align, span) = self.0?; + Some(AttributeKind::Align { align, span }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 51c1760da30..d7570634c1f 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -19,7 +19,7 @@ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::lint_helpers::AsPtrParser; -use crate::attributes::repr::ReprParser; +use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::stability::{ BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, }; @@ -90,6 +90,7 @@ macro_rules! attribute_parsers { attribute_parsers!( pub(crate) static ATTRIBUTE_PARSERS = [ // tidy-alphabetical-start + AlignParser, BodyStabilityParser, ConfusablesParser, ConstStabilityParser, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 57ac92a0ca1..337921a318c 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -450,6 +450,14 @@ pub(crate) struct EmptyConfusables { pub span: Span, } +#[derive(Diagnostic)] +#[diag(attr_parsing_invalid_alignment_value, code = E0589)] +pub(crate) struct InvalidAlignmentValue { + #[primary_span] + pub span: Span, + pub error_part: &'static str, +} + #[derive(Diagnostic)] #[diag(attr_parsing_repr_ident, code = E0565)] pub(crate) struct ReprIdent { diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 188a9a98ce7..98742255063 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -3,7 +3,6 @@ use std::str::FromStr; use rustc_abi::ExternAbi; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; -use rustc_attr_data_structures::ReprAttr::ReprAlign; use rustc_attr_data_structures::{ AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, find_attr, }; @@ -110,17 +109,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } }; - if let hir::Attribute::Parsed(p) = attr { - match p { - AttributeKind::Repr(reprs) => { - codegen_fn_attrs.alignment = reprs - .iter() - .filter_map(|(r, _)| if let ReprAlign(x) = r { Some(*x) } else { None }) - .max(); - } - - _ => {} - } + if let hir::Attribute::Parsed(AttributeKind::Align { align, .. }) = attr { + codegen_fn_attrs.alignment = Some(*align); } let Some(Ident { name, .. }) = attr.ident() else { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index b5218ec267c..5b1f1684d54 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -495,6 +495,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(no_link, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, EncodeCrossCrate::No), + gated!(align, Normal, template!(List: "alignment"), DuplicatesOk, EncodeCrossCrate::No, fn_align, experimental!(align)), ungated!(unsafe(Edition2024) export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), ungated!(unsafe(Edition2024) link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding, EncodeCrossCrate::No), ungated!(unsafe(Edition2024) no_mangle, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No), diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index f21cf5fa45e..2f16d385efb 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -47,8 +47,7 @@ pub struct CodegenFnAttrs { /// be generated against a specific instruction set. Only usable on architectures which allow /// switching between multiple instruction sets. pub instruction_set: Option, - /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be - /// aligned to. + /// The `#[align(...)]` attribute. Determines the alignment of the function body. pub alignment: Option, /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around /// the function entry. diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index b3096e46b09..a12215a44f9 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -289,6 +289,7 @@ fn emit_malformed_attribute( | sym::rustc_force_inline | sym::rustc_confusables | sym::repr + | sym::align | sym::deprecated ) { return; diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index 7c237d708c0..c1a2b3b2973 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -13,6 +13,10 @@ passes_abi_ne = passes_abi_of = fn_abi_of({$fn_name}) = {$fn_abi} +passes_align_should_be_repr_align = + `#[align(...)]` is not supported on {$item} items + .suggestion = use `#[repr(align(...))]` instead + passes_allow_incoherent_impl = `rustc_allow_incoherent_impl` attribute should be applied to impl items .label = the only currently supported targets are inherent methods @@ -29,10 +33,6 @@ passes_attr_application_struct = attribute should be applied to a struct .label = not a struct -passes_attr_application_struct_enum_function_method_union = - attribute should be applied to a struct, enum, function, associated function, or union - .label = not a struct, enum, function, associated function, or union - passes_attr_application_struct_enum_union = attribute should be applied to a struct, enum, or union .label = not a struct, enum, or union @@ -583,13 +583,14 @@ passes_remove_fields = *[other] fields } -passes_repr_align_function = - `repr(align)` attributes on functions are unstable - passes_repr_align_greater_than_target_max = alignment must not be greater than `isize::MAX` bytes .note = `isize::MAX` is {$size} for the current target +passes_repr_align_should_be_align = + `#[repr(align(...))]` is not supported on {$item} items + .help = use `#[align(...)]` instead + passes_repr_conflicting = conflicting representation hints diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5ce803aa1f8..50d6c5d9764 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -146,6 +146,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */ } + Attribute::Parsed(AttributeKind::Align { align, span: repr_span }) => { + self.check_align(span, target, *align, *repr_span) + } + Attribute::Parsed( AttributeKind::BodyStability { .. } | AttributeKind::ConstStabilityIndirect @@ -643,6 +647,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { sym::naked, sym::instruction_set, sym::repr, + sym::align, sym::rustc_std_internal_symbol, // code generation sym::cold, @@ -679,7 +684,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { // this check can be part of the parser and be removed here match other_attr { Attribute::Parsed( - AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. }, + AttributeKind::Deprecation { .. } + | AttributeKind::Repr { .. } + | AttributeKind::Align { .. }, ) => { continue; } @@ -1964,6 +1971,28 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } + /// Checks if the `#[align]` attributes on `item` are valid. + fn check_align(&self, span: Span, target: Target, align: Align, repr_span: Span) { + match target { + Target::Fn | Target::Method(_) => {} + Target::Struct | Target::Union | Target::Enum => { + self.dcx().emit_err(errors::AlignShouldBeReprAlign { + span: repr_span, + item: target.name(), + align_bytes: align.bytes(), + }); + } + _ => { + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: repr_span, + span, + }); + } + } + + self.check_align_value(align, repr_span); + } + /// Checks if the `#[repr]` attributes on `item` are valid. fn check_repr( &self, @@ -2016,23 +2045,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Union | Target::Enum => {} Target::Fn | Target::Method(_) => { - if !self.tcx.features().fn_align() { - feature_err( - &self.tcx.sess, - sym::fn_align, - *repr_span, - fluent::passes_repr_align_function, - ) - .emit(); - } + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.name(), + }); } _ => { - self.dcx().emit_err( - errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: *repr_span, - span, - }, - ); + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); } } @@ -2090,21 +2112,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { match target { Target::Struct | Target::Union | Target::Enum => continue, Target::Fn | Target::Method(_) => { - feature_err( - &self.tcx.sess, - sym::fn_align, - *repr_span, - fluent::passes_repr_align_function, - ) - .emit(); + self.dcx().emit_err(errors::ReprAlignShouldBeAlign { + span: *repr_span, + item: target.name(), + }); } _ => { - self.dcx().emit_err( - errors::AttrApplication::StructEnumFunctionMethodUnion { - hint_span: *repr_span, - span, - }, - ); + self.dcx().emit_err(errors::AttrApplication::StructEnumUnion { + hint_span: *repr_span, + span, + }); } } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index f0d4b610f63..587d9170f06 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -1308,13 +1308,6 @@ pub(crate) enum AttrApplication { #[label] span: Span, }, - #[diag(passes_attr_application_struct_enum_function_method_union, code = E0517)] - StructEnumFunctionMethodUnion { - #[primary_span] - hint_span: Span, - #[label] - span: Span, - }, } #[derive(Diagnostic)] @@ -1816,3 +1809,26 @@ pub(crate) enum UnexportableItem<'a> { field_name: &'a str, }, } + +#[derive(Diagnostic)] +#[diag(passes_repr_align_should_be_align)] +pub(crate) struct ReprAlignShouldBeAlign { + #[primary_span] + #[help] + pub span: Span, + pub item: &'static str, +} + +#[derive(Diagnostic)] +#[diag(passes_align_should_be_repr_align)] +pub(crate) struct AlignShouldBeReprAlign { + #[primary_span] + #[suggestion( + style = "verbose", + applicability = "machine-applicable", + code = "#[repr(align({align_bytes}))]" + )] + pub span: Span, + pub item: &'static str, + pub align_bytes: u64, +} diff --git a/src/tools/miri/tests/pass/fn_align.rs b/src/tools/miri/tests/pass/fn_align.rs index 550bb1cb4d7..28f92995880 100644 --- a/src/tools/miri/tests/pass/fn_align.rs +++ b/src/tools/miri/tests/pass/fn_align.rs @@ -1,21 +1,21 @@ //@compile-flags: -Zmin-function-alignment=8 #![feature(fn_align)] -// When a function uses `repr(align(N))`, the function address should be a multiple of `N`. +// When a function uses `align(N)`, the function address should be a multiple of `N`. -#[repr(align(256))] +#[align(256)] fn foo() {} -#[repr(align(16))] +#[align(16)] fn bar() {} -#[repr(align(4))] +#[align(4)] fn baz() {} fn main() { assert!((foo as usize).is_multiple_of(256)); assert!((bar as usize).is_multiple_of(16)); - // The maximum of `repr(align(N))` and `-Zmin-function-alignment=N` is used. + // The maximum of `align(N)` and `-Zmin-function-alignment=N` is used. assert!((baz as usize).is_multiple_of(8)); } diff --git a/tests/assembly/naked-functions/wasm32.rs b/tests/assembly/naked-functions/wasm32.rs index 984152f2b45..5f114246ad5 100644 --- a/tests/assembly/naked-functions/wasm32.rs +++ b/tests/assembly/naked-functions/wasm32.rs @@ -37,7 +37,7 @@ extern "C" fn nop() { #[unsafe(naked)] #[linkage = "weak"] // wasm functions cannot be aligned, so this has no effect -#[repr(align(32))] +#[align(32)] extern "C" fn weak_aligned_nop() { naked_asm!("nop") } diff --git a/tests/codegen/align-fn.rs b/tests/codegen/align-fn.rs index 660d8cd2bbf..267da060240 100644 --- a/tests/codegen/align-fn.rs +++ b/tests/codegen/align-fn.rs @@ -5,7 +5,7 @@ // CHECK: align 16 #[no_mangle] -#[repr(align(16))] +#[align(16)] pub fn fn_align() {} pub struct A; @@ -13,12 +13,12 @@ pub struct A; impl A { // CHECK: align 16 #[no_mangle] - #[repr(align(16))] + #[align(16)] pub fn method_align(self) {} // CHECK: align 16 #[no_mangle] - #[repr(align(16))] + #[align(16)] pub fn associated_fn() {} } @@ -26,19 +26,19 @@ trait T: Sized { fn trait_fn() {} // CHECK: align 32 - #[repr(align(32))] + #[align(32)] fn trait_method(self) {} } impl T for A { // CHECK: align 16 #[no_mangle] - #[repr(align(16))] + #[align(16)] fn trait_fn() {} // CHECK: align 16 #[no_mangle] - #[repr(align(16))] + #[align(16)] fn trait_method(self) {} } @@ -51,18 +51,20 @@ pub fn foo() { // CHECK-LABEL: align_specified_twice_1 // CHECK-SAME: align 64 #[no_mangle] -#[repr(align(32), align(64))] +#[align(32)] +#[align(64)] pub fn align_specified_twice_1() {} // CHECK-LABEL: align_specified_twice_2 // CHECK-SAME: align 128 #[no_mangle] -#[repr(align(128), align(32))] +#[align(128)] +#[align(32)] pub fn align_specified_twice_2() {} // CHECK-LABEL: align_specified_twice_3 // CHECK-SAME: align 256 #[no_mangle] -#[repr(align(32))] -#[repr(align(256))] +#[align(32)] +#[align(256)] pub fn align_specified_twice_3() {} diff --git a/tests/codegen/min-function-alignment.rs b/tests/codegen/min-function-alignment.rs index 7c0ad12402a..75f845572a4 100644 --- a/tests/codegen/min-function-alignment.rs +++ b/tests/codegen/min-function-alignment.rs @@ -18,16 +18,16 @@ pub fn no_explicit_align() {} // align16: align 16 // align1024: align 1024 #[no_mangle] -#[repr(align(8))] +#[align(8)] pub fn lower_align() {} -// the higher value of min-function-alignment and repr(align) wins out +// the higher value of min-function-alignment and the align attribute wins out // // CHECK-LABEL: @higher_align // align16: align 32 // align1024: align 1024 #[no_mangle] -#[repr(align(32))] +#[align(32)] pub fn higher_align() {} // cold functions follow the same rules as other functions diff --git a/tests/codegen/naked-fn/aligned.rs b/tests/codegen/naked-fn/aligned.rs index 47ef779f1b2..f9fce8e5a5d 100644 --- a/tests/codegen/naked-fn/aligned.rs +++ b/tests/codegen/naked-fn/aligned.rs @@ -8,7 +8,7 @@ use std::arch::naked_asm; // CHECK: .balign 16 // CHECK-LABEL: naked_empty: -#[repr(align(16))] +#[align(16)] #[no_mangle] #[unsafe(naked)] pub extern "C" fn naked_empty() { diff --git a/tests/codegen/naked-fn/min-function-alignment.rs b/tests/codegen/naked-fn/min-function-alignment.rs index 1d778be8c90..59554c1cae5 100644 --- a/tests/codegen/naked-fn/min-function-alignment.rs +++ b/tests/codegen/naked-fn/min-function-alignment.rs @@ -16,7 +16,7 @@ pub extern "C" fn naked_no_explicit_align() { // CHECK: .balign 16 #[no_mangle] -#[repr(align(8))] +#[align(8)] #[unsafe(naked)] pub extern "C" fn naked_lower_align() { core::arch::naked_asm!("ret") @@ -24,7 +24,7 @@ pub extern "C" fn naked_lower_align() { // CHECK: .balign 32 #[no_mangle] -#[repr(align(32))] +#[align(32)] #[unsafe(naked)] pub extern "C" fn naked_higher_align() { core::arch::naked_asm!("ret") diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.rs b/tests/ui/asm/naked-with-invalid-repr-attr.rs index 96eed70dc55..bfbbf29a69e 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.rs +++ b/tests/ui/asm/naked-with-invalid-repr-attr.rs @@ -19,8 +19,9 @@ extern "C" fn example2() { naked_asm!("") } -#[repr(align(16), C)] +#[repr(C)] //~^ ERROR attribute should be applied to a struct, enum, or union [E0517] +#[align(16)] #[unsafe(naked)] extern "C" fn example3() { //~^ NOTE not a struct, enum, or union diff --git a/tests/ui/asm/naked-with-invalid-repr-attr.stderr b/tests/ui/asm/naked-with-invalid-repr-attr.stderr index f173a39e5bf..4eb4a4e5a04 100644 --- a/tests/ui/asm/naked-with-invalid-repr-attr.stderr +++ b/tests/ui/asm/naked-with-invalid-repr-attr.stderr @@ -23,10 +23,10 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:22:19 + --> $DIR/naked-with-invalid-repr-attr.rs:22:8 | -LL | #[repr(align(16), C)] - | ^ +LL | #[repr(C)] + | ^ ... LL | / extern "C" fn example3() { LL | | @@ -35,7 +35,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct, enum, or union - --> $DIR/naked-with-invalid-repr-attr.rs:31:8 + --> $DIR/naked-with-invalid-repr-attr.rs:32:8 | LL | #[repr(C, packed)] | ^ @@ -48,7 +48,7 @@ LL | | } | |_- not a struct, enum, or union error[E0517]: attribute should be applied to a struct or union - --> $DIR/naked-with-invalid-repr-attr.rs:31:11 + --> $DIR/naked-with-invalid-repr-attr.rs:32:11 | LL | #[repr(C, packed)] | ^^^^^^ @@ -61,7 +61,7 @@ LL | | } | |_- not a struct or union error[E0517]: attribute should be applied to an enum - --> $DIR/naked-with-invalid-repr-attr.rs:41:8 + --> $DIR/naked-with-invalid-repr-attr.rs:42:8 | LL | #[repr(u8)] | ^^ diff --git a/tests/ui/attributes/arg-error-issue-121425.stderr b/tests/ui/attributes/arg-error-issue-121425.stderr index 6e71f15fdc8..1beb99b1703 100644 --- a/tests/ui/attributes/arg-error-issue-121425.stderr +++ b/tests/ui/attributes/arg-error-issue-121425.stderr @@ -1,9 +1,3 @@ -error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - --> $DIR/arg-error-issue-121425.rs:16:8 - | -LL | #[repr(align())] - | ^^^^^^^ - error[E0693]: incorrect `repr(align)` attribute format: `align` expects a literal integer as argument --> $DIR/arg-error-issue-121425.rs:4:14 | @@ -22,6 +16,12 @@ error[E0589]: invalid `repr(align)` attribute: not an unsuffixed integer LL | #[repr(align("str"))] | ^^^^^ +error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses + --> $DIR/arg-error-issue-121425.rs:16:8 + | +LL | #[repr(align())] + | ^^^^^^^ + error[E0552]: incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument --> $DIR/arg-error-issue-121425.rs:21:15 | diff --git a/tests/ui/attributes/invalid-repr.rs b/tests/ui/attributes/invalid-repr.rs index 10a487c127e..d7933533405 100644 --- a/tests/ui/attributes/invalid-repr.rs +++ b/tests/ui/attributes/invalid-repr.rs @@ -1,5 +1,5 @@ #[repr(align(16))] -//~^ ERROR attribute should be applied to a struct, enum, function, associated function, or union +//~^ ERROR attribute should be applied to a struct, enum, or union pub type Foo = i32; fn main() {} diff --git a/tests/ui/attributes/invalid-repr.stderr b/tests/ui/attributes/invalid-repr.stderr index 681460ad081..3f5a305c6b7 100644 --- a/tests/ui/attributes/invalid-repr.stderr +++ b/tests/ui/attributes/invalid-repr.stderr @@ -1,11 +1,11 @@ -error[E0517]: attribute should be applied to a struct, enum, function, associated function, or union +error[E0517]: attribute should be applied to a struct, enum, or union --> $DIR/invalid-repr.rs:1:8 | LL | #[repr(align(16))] | ^^^^^^^^^ LL | LL | pub type Foo = i32; - | ------------------- not a struct, enum, function, associated function, or union + | ------------------- not a struct, enum, or union error: aborting due to 1 previous error diff --git a/tests/ui/attributes/malformed-fn-align.rs b/tests/ui/attributes/malformed-fn-align.rs index 4aaad01b723..35ffd6d8acc 100644 --- a/tests/ui/attributes/malformed-fn-align.rs +++ b/tests/ui/attributes/malformed-fn-align.rs @@ -2,6 +2,21 @@ #![crate_type = "lib"] trait MyTrait { - #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument + #[align] //~ ERROR malformed `align` attribute input fn myfun(); } + +#[align = 16] //~ ERROR malformed `align` attribute input +fn f1() {} + +#[align("hello")] //~ ERROR invalid alignment value: not an unsuffixed integer +fn f2() {} + +#[align(0)] //~ ERROR invalid alignment value: not a power of two +fn f3() {} + +#[repr(align(16))] //~ ERROR `#[repr(align(...))]` is not supported on function items +fn f4() {} + +#[align(16)] //~ ERROR `#[align(...)]` is not supported on struct items +struct S1; diff --git a/tests/ui/attributes/malformed-fn-align.stderr b/tests/ui/attributes/malformed-fn-align.stderr index 57913c48ef7..765255c2c3a 100644 --- a/tests/ui/attributes/malformed-fn-align.stderr +++ b/tests/ui/attributes/malformed-fn-align.stderr @@ -1,9 +1,66 @@ -error[E0589]: invalid `repr(align)` attribute: `align` needs an argument - --> $DIR/malformed-fn-align.rs:5:12 +error[E0539]: malformed `align` attribute input + --> $DIR/malformed-fn-align.rs:5:5 + | +LL | #[align] + | ^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[align()] + | ++++++++++++++++++++++ + +error[E0539]: malformed `align` attribute input + --> $DIR/malformed-fn-align.rs:9:1 + | +LL | #[align = 16] + | ^^^^^^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL - #[align = 16] +LL + #[align()] + | +LL - #[align = 16] +LL + #[align] + | + +error[E0589]: invalid alignment value: not an unsuffixed integer + --> $DIR/malformed-fn-align.rs:12:9 + | +LL | #[align("hello")] + | ^^^^^^^ + +error[E0589]: invalid alignment value: not a power of two + --> $DIR/malformed-fn-align.rs:15:9 + | +LL | #[align(0)] + | ^ + +error: `#[repr(align(...))]` is not supported on function items + --> $DIR/malformed-fn-align.rs:18:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + | +help: use `#[align(...)]` instead + --> $DIR/malformed-fn-align.rs:18:8 + | +LL | #[repr(align(16))] + | ^^^^^^^^^ + +error: `#[align(...)]` is not supported on struct items + --> $DIR/malformed-fn-align.rs:21:1 + | +LL | #[align(16)] + | ^^^^^^^^^^^^ + | +help: use `#[repr(align(...))]` instead + | +LL - #[align(16)] +LL + #[repr(align(16))] | -LL | #[repr(align)] - | ^^^^^ help: supply an argument here: `align(...)` -error: aborting due to 1 previous error +error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0589`. +Some errors have detailed explanations: E0539, E0589. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/feature-gates/feature-gate-fn_align.rs b/tests/ui/feature-gates/feature-gate-fn_align.rs index 744877704dd..b6c300e5cbe 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.rs +++ b/tests/ui/feature-gates/feature-gate-fn_align.rs @@ -1,9 +1,12 @@ #![crate_type = "lib"] -#[repr(align(16))] //~ ERROR `repr(align)` attributes on functions are unstable +#[align(16)] +//~^ ERROR the `#[align]` attribute is an experimental feature fn requires_alignment() {} trait MyTrait { - #[repr(align)] //~ ERROR invalid `repr(align)` attribute: `align` needs an argument + #[align] + //~^ ERROR the `#[align]` attribute is an experimental feature + //~| ERROR malformed `align` attribute input fn myfun(); } diff --git a/tests/ui/feature-gates/feature-gate-fn_align.stderr b/tests/ui/feature-gates/feature-gate-fn_align.stderr index ff17c29fe02..93ef136dc73 100644 --- a/tests/ui/feature-gates/feature-gate-fn_align.stderr +++ b/tests/ui/feature-gates/feature-gate-fn_align.stderr @@ -1,20 +1,35 @@ -error[E0589]: invalid `repr(align)` attribute: `align` needs an argument - --> $DIR/feature-gate-fn_align.rs:7:12 +error[E0658]: the `#[align]` attribute is an experimental feature + --> $DIR/feature-gate-fn_align.rs:3:1 | -LL | #[repr(align)] - | ^^^^^ help: supply an argument here: `align(...)` +LL | #[align(16)] + | ^^^^^^^^^^^^ + | + = note: see issue #82232 for more information + = help: add `#![feature(fn_align)]` 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]: `repr(align)` attributes on functions are unstable - --> $DIR/feature-gate-fn_align.rs:3:8 +error[E0658]: the `#[align]` attribute is an experimental feature + --> $DIR/feature-gate-fn_align.rs:8:5 | -LL | #[repr(align(16))] - | ^^^^^^^^^ +LL | #[align] + | ^^^^^^^^ | = note: see issue #82232 for more information = help: add `#![feature(fn_align)]` 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 2 previous errors +error[E0539]: malformed `align` attribute input + --> $DIR/feature-gate-fn_align.rs:8:5 + | +LL | #[align] + | ^^^^^^^^ expected this to be a list + | +help: try changing it to one of the following valid forms of the attribute + | +LL | #[align()] + | ++++++++++++++++++++++ + +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0589, E0658. -For more information about an error, try `rustc --explain E0589`. +Some errors have detailed explanations: E0539, E0658. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/repr/attr-usage-repr.rs b/tests/ui/repr/attr-usage-repr.rs index cbf99f16e03..ca63ac564fc 100644 --- a/tests/ui/repr/attr-usage-repr.rs +++ b/tests/ui/repr/attr-usage-repr.rs @@ -45,7 +45,7 @@ enum EInt { B, } -#[repr()] //~ ERROR attribute should be applied to a struct, enum, function, associated function, or union [E0517] +#[repr()] //~ ERROR attribute should be applied to a struct, enum, or union [E0517] type SirThisIsAType = i32; #[repr()] diff --git a/tests/ui/repr/attr-usage-repr.stderr b/tests/ui/repr/attr-usage-repr.stderr index a25e68c483f..a62992c597a 100644 --- a/tests/ui/repr/attr-usage-repr.stderr +++ b/tests/ui/repr/attr-usage-repr.stderr @@ -36,13 +36,13 @@ LL | | B, LL | | } | |_- not a struct -error[E0517]: attribute should be applied to a struct, enum, function, associated function, or union +error[E0517]: attribute should be applied to a struct, enum, or union --> $DIR/attr-usage-repr.rs:48:1 | LL | #[repr()] | ^^^^^^^^^ LL | type SirThisIsAType = i32; - | -------------------------- not a struct, enum, function, associated function, or union + | -------------------------- not a struct, enum, or union error: aborting due to 5 previous errors diff --git a/tests/ui/repr/malformed-repr-hints.stderr b/tests/ui/repr/malformed-repr-hints.stderr index 7a6e9ccc73e..6fb92755761 100644 --- a/tests/ui/repr/malformed-repr-hints.stderr +++ b/tests/ui/repr/malformed-repr-hints.stderr @@ -1,15 +1,3 @@ -error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - --> $DIR/malformed-repr-hints.rs:14:8 - | -LL | #[repr(align(2, 4))] - | ^^^^^^^^^^^ - -error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - --> $DIR/malformed-repr-hints.rs:18:8 - | -LL | #[repr(align())] - | ^^^^^^^ - error[E0552]: incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all --> $DIR/malformed-repr-hints.rs:6:8 | @@ -22,6 +10,18 @@ error[E0589]: invalid `repr(align)` attribute: `align` needs an argument LL | #[repr(align)] | ^^^^^ help: supply an argument here: `align(...)` +error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses + --> $DIR/malformed-repr-hints.rs:14:8 + | +LL | #[repr(align(2, 4))] + | ^^^^^^^^^^^ + +error[E0693]: incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses + --> $DIR/malformed-repr-hints.rs:18:8 + | +LL | #[repr(align())] + | ^^^^^^^ + error[E0552]: invalid representation hint: `Rust` does not take a parenthesized argument list --> $DIR/malformed-repr-hints.rs:23:8 | -- cgit 1.4.1-3-g733a5 From b64fd13a04e03bd260b550430daa6fbdf1fb27c9 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Sun, 9 Mar 2025 23:46:47 +0100 Subject: convert the `optimize` attribute to a new parser --- .../rustc_attr_data_structures/src/attributes.rs | 6 ++-- .../src/attributes/codegen_attrs.rs | 40 ++++++++++++++++++++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + compiler/rustc_attr_parsing/src/context.rs | 2 ++ compiler/rustc_codegen_ssa/messages.ftl | 5 --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 29 ++-------------- compiler/rustc_codegen_ssa/src/errors.rs | 14 -------- .../rustc_error_codes/src/error_codes/E0722.md | 8 ++++- compiler/rustc_error_codes/src/lib.rs | 1 + compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 8 +++-- .../feature-gate-optimize_attribute.rs | 2 +- .../feature-gate-optimize_attribute.stderr | 13 ++++--- 13 files changed, 72 insertions(+), 58 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index d4c43456049..e0b0015f365 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -38,7 +38,8 @@ pub enum InstructionSetAttr { ArmT32, } -#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq, HashStable_Generic, Default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Default, PrintAttribute)] +#[derive(Encodable, Decodable, HashStable_Generic)] pub enum OptimizeAttr { /// No `#[optimize(..)]` attribute #[default] @@ -226,7 +227,8 @@ pub enum AttributeKind { /// Represents `#[rustc_macro_transparency]`. MacroTransparency(Transparency), - + /// Represents `#[optimize(size|speed)]` + Optimize(OptimizeAttr, Span), /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr(ThinVec<(ReprAttr, Span)>), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs new file mode 100644 index 00000000000..ddcf82cbf7c --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -0,0 +1,40 @@ +use rustc_attr_data_structures::{AttributeKind, OptimizeAttr}; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::sym; + +use super::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct OptimizeParser; + +impl SingleAttributeParser for OptimizeParser { + const PATH: &[rustc_span::Symbol] = &[sym::optimize]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(List: "size|speed|none"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + + let Some(single) = list.single() else { + cx.expected_single_argument(list.span); + return None; + }; + + let res = match single.meta_item().and_then(|i| i.path().word().map(|i| i.name)) { + Some(sym::size) => OptimizeAttr::Size, + Some(sym::speed) => OptimizeAttr::Speed, + Some(sym::none) => OptimizeAttr::DoNotOptimize, + _ => { + cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]); + OptimizeAttr::Default + } + }; + + Some(AttributeKind::Optimize(res, cx.attr_span)) + } +} diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index fa2a6087506..3bb4c163d32 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -28,6 +28,7 @@ use crate::session_diagnostics::UnusedMultiple; pub(crate) mod allow_unstable; pub(crate) mod cfg; +pub(crate) mod codegen_attrs; pub(crate) mod confusables; pub(crate) mod deprecation; pub(crate) mod inline; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 51c1760da30..18ed98d4a25 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -15,6 +15,7 @@ use rustc_session::Session; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym}; use crate::attributes::allow_unstable::{AllowConstFnUnstableParser, AllowInternalUnstableParser}; +use crate::attributes::codegen_attrs::OptimizeParser; use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; @@ -107,6 +108,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, // tidy-alphabetical-end diff --git a/compiler/rustc_codegen_ssa/messages.ftl b/compiler/rustc_codegen_ssa/messages.ftl index 5322fe58cf3..e4734b18093 100644 --- a/compiler/rustc_codegen_ssa/messages.ftl +++ b/compiler/rustc_codegen_ssa/messages.ftl @@ -48,8 +48,6 @@ codegen_ssa_error_writing_def_file = codegen_ssa_expected_name_value_pair = expected name value pair -codegen_ssa_expected_one_argument = expected one argument - codegen_ssa_expected_used_symbol = expected `used`, `used(compiler)` or `used(linker)` codegen_ssa_extern_funcs_not_found = some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified @@ -86,9 +84,6 @@ codegen_ssa_incorrect_cgu_reuse_type = codegen_ssa_insufficient_vs_code_product = VS Code is a different product, and is not sufficient. -codegen_ssa_invalid_argument = invalid argument - .help = valid inline arguments are `always` and `never` - codegen_ssa_invalid_instruction_set = invalid instruction set specified codegen_ssa_invalid_link_ordinal_nargs = incorrect number of arguments to `#[link_ordinal]` diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 188a9a98ce7..33c457ead30 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -465,33 +465,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::Default, |ia, attr| { - if !attr.has_name(sym::optimize) { - return ia; - } - if attr.is_word() { - tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); - return ia; - } - let Some(ref items) = attr.meta_item_list() else { - return OptimizeAttr::Default; - }; - - let [item] = &items[..] else { - tcx.dcx().emit_err(errors::ExpectedOneArgumentOptimize { span: attr.span() }); - return OptimizeAttr::Default; - }; - if item.has_name(sym::size) { - OptimizeAttr::Size - } else if item.has_name(sym::speed) { - OptimizeAttr::Speed - } else if item.has_name(sym::none) { - OptimizeAttr::DoNotOptimize - } else { - tcx.dcx().emit_err(errors::InvalidArgumentOptimize { span: item.span() }); - OptimizeAttr::Default - } - }); + codegen_fn_attrs.optimize = + find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default); // #73631: closures inherit `#[target_feature]` annotations // diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index 5387b2a7f81..c60661a1410 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -208,20 +208,6 @@ pub(crate) struct OutOfRangeInteger { pub span: Span, } -#[derive(Diagnostic)] -#[diag(codegen_ssa_expected_one_argument, code = E0722)] -pub(crate) struct ExpectedOneArgumentOptimize { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(codegen_ssa_invalid_argument, code = E0722)] -pub(crate) struct InvalidArgumentOptimize { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag(codegen_ssa_copy_path_buf)] pub(crate) struct CopyPathBuf { diff --git a/compiler/rustc_error_codes/src/error_codes/E0722.md b/compiler/rustc_error_codes/src/error_codes/E0722.md index 570717a92bd..1799458d46c 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0722.md +++ b/compiler/rustc_error_codes/src/error_codes/E0722.md @@ -1,8 +1,14 @@ +#### Note: this error code is no longer emitted by the compiler + +This is because it was too specific to the `optimize` attribute. +Similar diagnostics occur for other attributes too. +The example here will now emit `E0539` + The `optimize` attribute was malformed. Erroneous code example: -```compile_fail,E0722 +```compile_fail,E0539 #![feature(optimize_attribute)] #[optimize(something)] // error: invalid argument diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs index 6f5e4829802..22cc1e894da 100644 --- a/compiler/rustc_error_codes/src/lib.rs +++ b/compiler/rustc_error_codes/src/lib.rs @@ -686,6 +686,7 @@ E0805: 0805, // E0707, // multiple elided lifetimes used in arguments of `async fn` // E0709, // multiple different lifetimes used in arguments of `async fn` // E0721, // `await` keyword +// E0722, // replaced with a generic attribute input check // E0723, // unstable feature in `const` context // E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`. // E0744, // merged into E0728 diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index b3096e46b09..1fbc970e2a7 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -290,6 +290,7 @@ fn emit_malformed_attribute( | sym::rustc_confusables | sym::repr | sym::deprecated + | sym::optimize ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5ce803aa1f8..98e8b47127b 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -128,6 +128,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::Inline(kind, attr_span)) => { self.check_inline(hir_id, *attr_span, span, kind, target) } + Attribute::Parsed(AttributeKind::Optimize(_, attr_span)) => { + self.check_optimize(hir_id, *attr_span, span, target) + } Attribute::Parsed(AttributeKind::AllowInternalUnstable(syms)) => self .check_allow_internal_unstable( hir_id, @@ -163,7 +166,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_diagnostic_on_unimplemented(attr.span(), hir_id, target) } [sym::coverage, ..] => self.check_coverage(attr, span, target), - [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target), [sym::no_sanitize, ..] => { self.check_no_sanitize(attr, span, target) } @@ -525,7 +527,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { /// Checks that `#[optimize(..)]` is applied to a function/closure/method, /// or to an impl block or module. - fn check_optimize(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + fn check_optimize(&self, hir_id: HirId, attr_span: Span, span: Span, target: Target) { let is_valid = matches!( target, Target::Fn @@ -534,7 +536,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ); if !is_valid { self.dcx().emit_err(errors::OptimizeInvalidTarget { - attr_span: attr.span(), + attr_span, defn_span: span, on_crate: hir_id == CRATE_HIR_ID, }); diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs index 77cc307c9f4..ed5a11270f8 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.rs +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.rs @@ -11,5 +11,5 @@ fn none() {} #[optimize(banana)] //~^ ERROR the `#[optimize]` attribute is an experimental feature -//~| ERROR E0722 +//~| ERROR malformed `optimize` attribute input [E0539] fn not_known() {} diff --git a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr index 4e6e4ac2703..e7e62b4f989 100644 --- a/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr +++ b/tests/ui/feature-gates/feature-gate-optimize_attribute.stderr @@ -38,13 +38,16 @@ LL | #[optimize(banana)] = help: add `#![feature(optimize_attribute)]` 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[E0722]: invalid argument - --> $DIR/feature-gate-optimize_attribute.rs:12:12 +error[E0539]: malformed `optimize` attribute input + --> $DIR/feature-gate-optimize_attribute.rs:12:1 | LL | #[optimize(banana)] - | ^^^^^^ + | ^^^^^^^^^^^------^^ + | | | + | | valid arguments are `size`, `speed` or `none` + | help: must be of the form: `#[optimize(size|speed|none)]` error: aborting due to 5 previous errors -Some errors have detailed explanations: E0658, E0722. -For more information about an error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0539, E0658. +For more information about an error, try `rustc --explain E0539`. -- cgit 1.4.1-3-g733a5 From 07b9bb1855596ac84a80d898b40c4b403f1dcc3f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 19 Jun 2025 13:02:04 -0500 Subject: Extract Translator struct --- compiler/rustc_codegen_ssa/src/back/write.rs | 20 +++---- compiler/rustc_driver_impl/src/lib.rs | 10 +++- compiler/rustc_error_messages/src/lib.rs | 10 ++-- .../src/annotate_snippet_emitter_writer.rs | 36 ++++-------- compiler/rustc_errors/src/emitter.rs | 67 +++++++++++----------- compiler/rustc_errors/src/json.rs | 40 +++++-------- compiler/rustc_errors/src/json/tests.rs | 6 +- compiler/rustc_errors/src/lib.rs | 15 +++-- compiler/rustc_errors/src/tests.rs | 43 ++++++-------- compiler/rustc_errors/src/translation.rs | 43 +++++++++----- compiler/rustc_parse/src/parser/tests.rs | 6 +- compiler/rustc_session/src/parse.rs | 12 ++-- compiler/rustc_session/src/session.rs | 44 +++++++------- src/librustdoc/core.rs | 9 +-- src/librustdoc/doctest/make.rs | 9 +-- .../passes/lint/check_code_block_syntax.rs | 28 ++++----- .../clippy_lints/src/doc/needless_doctest_main.rs | 5 +- src/tools/rustfmt/src/parse/session.rs | 37 ++++-------- 18 files changed, 190 insertions(+), 250 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index bbf9cceef2a..c3bfe4c13cd 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -14,10 +14,10 @@ use rustc_data_structures::jobserver::{self, Acquired}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::{SelfProfilerRef, VerboseTimingGuard}; use rustc_errors::emitter::Emitter; -use rustc_errors::translation::Translate; +use rustc_errors::translation::Translator; use rustc_errors::{ - Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, FluentBundle, Level, MultiSpan, - Style, Suggestions, + Diag, DiagArgMap, DiagCtxt, DiagMessage, ErrCode, FatalError, Level, MultiSpan, Style, + Suggestions, }; use rustc_fs_util::link_or_copy; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -1889,16 +1889,6 @@ impl SharedEmitter { } } -impl Translate for SharedEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - None - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - panic!("shared emitter attempted to translate a diagnostic"); - } -} - impl Emitter for SharedEmitter { fn emit_diagnostic( &mut self, @@ -1932,6 +1922,10 @@ impl Emitter for SharedEmitter { fn source_map(&self) -> Option<&SourceMap> { None } + + fn translator(&self) -> &Translator { + panic!("shared emitter attempted to translate a diagnostic"); + } } impl SharedEmitterMain { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index d53126d0414..daeca43169d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -38,6 +38,7 @@ use rustc_data_structures::profiling::{ }; use rustc_errors::emitter::stderr_destination; use rustc_errors::registry::Registry; +use rustc_errors::translation::Translator; use rustc_errors::{ColorConfig, DiagCtxt, ErrCode, FatalError, PResult, markdown}; use rustc_feature::find_gated_cfg; // This avoids a false positive with `-Wunused_crate_dependencies`. @@ -109,6 +110,10 @@ use crate::session_diagnostics::{ rustc_fluent_macro::fluent_messages! { "../messages.ftl" } +pub fn default_translator() -> Translator { + Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) +} + pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ // tidy-alphabetical-start crate::DEFAULT_LOCALE_RESOURCE, @@ -1413,11 +1418,10 @@ fn report_ice( extra_info: fn(&DiagCtxt), using_internal_features: &AtomicBool, ) { - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false); + let translator = default_translator(); let emitter = Box::new(rustc_errors::emitter::HumanEmitter::new( stderr_destination(rustc_errors::ColorConfig::Auto), - fallback_bundle, + translator, )); let dcx = rustc_errors::DiagCtxt::new(emitter); let dcx = dcx.handle(); diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 1d3b5b20751..194fc2450ba 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -18,7 +18,7 @@ pub use fluent_bundle::{self, FluentArgs, FluentError, FluentValue}; use fluent_syntax::parser::ParserError; use icu_provider_adapters::fallback::{LocaleFallbackProvider, LocaleFallbacker}; use intl_memoizer::concurrent::IntlLangMemoizer; -use rustc_data_structures::sync::IntoDynSyncSend; +use rustc_data_structures::sync::{DynSend, IntoDynSyncSend}; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use smallvec::SmallVec; @@ -204,16 +204,16 @@ fn register_functions(bundle: &mut FluentBundle) { /// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily /// evaluated fluent bundle. -pub type LazyFallbackBundle = Arc FluentBundle>>; +pub type LazyFallbackBundle = + Arc FluentBundle + DynSend>>>; /// Return the default `FluentBundle` with standard "en-US" diagnostic messages. #[instrument(level = "trace", skip(resources))] -#[define_opaque(LazyFallbackBundle)] pub fn fallback_fluent_bundle( resources: Vec<&'static str>, with_directionality_markers: bool, ) -> LazyFallbackBundle { - Arc::new(LazyLock::new(move || { + Arc::new(LazyLock::new(Box::new(move || { let mut fallback_bundle = new_bundle(vec![langid!("en-US")]); register_functions(&mut fallback_bundle); @@ -228,7 +228,7 @@ pub fn fallback_fluent_bundle( } fallback_bundle - })) + }))) } /// Identifier for the Fluent message/attribute corresponding to a diagnostic message. diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index f3aeb8d224b..2eb3c23259f 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -15,17 +15,15 @@ use rustc_span::source_map::SourceMap; use crate::emitter::FileWithAnnotatedLines; use crate::registry::Registry; use crate::snippet::Line; -use crate::translation::{Translate, to_fluent_args}; +use crate::translation::{Translator, to_fluent_args}; use crate::{ - CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, FluentBundle, LazyFallbackBundle, - Level, MultiSpan, Style, Subdiag, + CodeSuggestion, DiagInner, DiagMessage, Emitter, ErrCode, Level, MultiSpan, Style, Subdiag, }; /// Generates diagnostics using annotate-snippet pub struct AnnotateSnippetEmitter { source_map: Option>, - fluent_bundle: Option>, - fallback_bundle: LazyFallbackBundle, + translator: Translator, /// If true, hides the longer explanation text short_message: bool, @@ -35,16 +33,6 @@ pub struct AnnotateSnippetEmitter { macro_backtrace: bool, } -impl Translate for AnnotateSnippetEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - self.fluent_bundle.as_deref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &self.fallback_bundle - } -} - impl Emitter for AnnotateSnippetEmitter { /// The entry point for the diagnostics generation fn emit_diagnostic(&mut self, mut diag: DiagInner, _registry: &Registry) { @@ -78,6 +66,10 @@ impl Emitter for AnnotateSnippetEmitter { fn should_show_explain(&self) -> bool { !self.short_message } + + fn translator(&self) -> &Translator { + &self.translator + } } /// Provides the source string for the given `line` of `file` @@ -104,19 +96,11 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level { impl AnnotateSnippetEmitter { pub fn new( source_map: Option>, - fluent_bundle: Option>, - fallback_bundle: LazyFallbackBundle, + translator: Translator, short_message: bool, macro_backtrace: bool, ) -> Self { - Self { - source_map, - fluent_bundle, - fallback_bundle, - short_message, - ui_testing: false, - macro_backtrace, - } + Self { source_map, translator, short_message, ui_testing: false, macro_backtrace } } /// Allows to modify `Self` to enable or disable the `ui_testing` flag. @@ -137,7 +121,7 @@ impl AnnotateSnippetEmitter { _children: &[Subdiag], _suggestions: &[CodeSuggestion], ) { - let message = self.translate_messages(messages, args); + let message = self.translator.translate_messages(messages, args); if let Some(source_map) = &self.source_map { // Make sure our primary file comes first let primary_lo = if let Some(primary_span) = msp.primary_span().as_ref() { diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 6ab6f96079e..494f15cc639 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -35,10 +35,10 @@ use crate::snippet::{ }; use crate::styled_buffer::StyledBuffer; use crate::timings::TimingRecord; -use crate::translation::{Translate, to_fluent_args}; +use crate::translation::{Translator, to_fluent_args}; use crate::{ - CodeSuggestion, DiagInner, DiagMessage, ErrCode, FluentBundle, LazyFallbackBundle, Level, - MultiSpan, Subdiag, SubstitutionHighlight, SuggestionStyle, TerminalUrl, + CodeSuggestion, DiagInner, DiagMessage, ErrCode, Level, MultiSpan, Subdiag, + SubstitutionHighlight, SuggestionStyle, TerminalUrl, }; /// Default column width, used in tests and when terminal dimensions cannot be determined. @@ -175,7 +175,7 @@ const ANONYMIZED_LINE_NUM: &str = "LL"; pub type DynEmitter = dyn Emitter + DynSend; /// Emitter trait for emitting errors and other structured information. -pub trait Emitter: Translate { +pub trait Emitter { /// Emit a structured diagnostic. fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry); @@ -212,6 +212,8 @@ pub trait Emitter: Translate { fn source_map(&self) -> Option<&SourceMap>; + fn translator(&self) -> &Translator; + /// Formats the substitutions of the primary_span /// /// There are a lot of conditions to this method, but in short: @@ -224,13 +226,17 @@ pub trait Emitter: Translate { /// * If the current `DiagInner` has multiple suggestions, /// we leave `primary_span` and the suggestions untouched. fn primary_span_formatted( - &mut self, + &self, primary_span: &mut MultiSpan, suggestions: &mut Vec, fluent_args: &FluentArgs<'_>, ) { if let Some((sugg, rest)) = suggestions.split_first() { - let msg = self.translate_message(&sugg.msg, fluent_args).map_err(Report::new).unwrap(); + let msg = self + .translator() + .translate_message(&sugg.msg, fluent_args) + .map_err(Report::new) + .unwrap(); if rest.is_empty() // ^ if there is only one suggestion // don't display multi-suggestions as labels @@ -491,16 +497,6 @@ pub trait Emitter: Translate { } } -impl Translate for HumanEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - self.fluent_bundle.as_deref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &self.fallback_bundle - } -} - impl Emitter for HumanEmitter { fn source_map(&self) -> Option<&SourceMap> { self.sm.as_deref() @@ -538,6 +534,10 @@ impl Emitter for HumanEmitter { fn supports_color(&self) -> bool { self.dst.supports_color() } + + fn translator(&self) -> &Translator { + &self.translator + } } /// An emitter that does nothing when emitting a non-fatal diagnostic. @@ -549,16 +549,6 @@ pub struct SilentEmitter { pub emit_fatal_diagnostic: bool, } -impl Translate for SilentEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - None - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - self.fatal_emitter.fallback_fluent_bundle() - } -} - impl Emitter for SilentEmitter { fn source_map(&self) -> Option<&SourceMap> { None @@ -572,6 +562,10 @@ impl Emitter for SilentEmitter { self.fatal_emitter.emit_diagnostic(diag, registry); } } + + fn translator(&self) -> &Translator { + self.fatal_emitter.translator() + } } /// Maximum number of suggestions to be shown @@ -615,9 +609,8 @@ pub struct HumanEmitter { #[setters(skip)] dst: IntoDynSyncSend, sm: Option>, - fluent_bundle: Option>, #[setters(skip)] - fallback_bundle: LazyFallbackBundle, + translator: Translator, short_message: bool, ui_testing: bool, ignored_directories_in_source_blocks: Vec, @@ -637,12 +630,11 @@ pub(crate) struct FileWithAnnotatedLines { } impl HumanEmitter { - pub fn new(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter { + pub fn new(dst: Destination, translator: Translator) -> HumanEmitter { HumanEmitter { dst: IntoDynSyncSend(dst), sm: None, - fluent_bundle: None, - fallback_bundle, + translator, short_message: false, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), @@ -1433,7 +1425,7 @@ impl HumanEmitter { // very *weird* formats // see? for (text, style) in msgs.iter() { - let text = self.translate_message(text, args).map_err(Report::new).unwrap(); + let text = self.translator.translate_message(text, args).map_err(Report::new).unwrap(); let text = &normalize_whitespace(&text); let lines = text.split('\n').collect::>(); if lines.len() > 1 { @@ -1528,7 +1520,8 @@ impl HumanEmitter { } let mut line = 0; for (text, style) in msgs.iter() { - let text = self.translate_message(text, args).map_err(Report::new).unwrap(); + let text = + self.translator.translate_message(text, args).map_err(Report::new).unwrap(); // Account for newlines to align output to its label. for text in normalize_whitespace(&text).lines() { buffer.append( @@ -1560,7 +1553,7 @@ impl HumanEmitter { .into_iter() .filter_map(|label| match label.label { Some(msg) if label.is_primary => { - let text = self.translate_message(&msg, args).ok()?; + let text = self.translator.translate_message(&msg, args).ok()?; if !text.trim().is_empty() { Some(text.to_string()) } else { None } } _ => None, @@ -3104,7 +3097,11 @@ impl FileWithAnnotatedLines { let label = label.as_ref().map(|m| { normalize_whitespace( - &emitter.translate_message(m, args).map_err(Report::new).unwrap(), + &emitter + .translator() + .translate_message(m, args) + .map_err(Report::new) + .unwrap(), ) }); diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index d67e2ba2d60..6d600f896a0 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -32,11 +32,8 @@ use crate::emitter::{ }; use crate::registry::Registry; use crate::timings::{TimingRecord, TimingSection}; -use crate::translation::{Translate, to_fluent_args}; -use crate::{ - CodeSuggestion, FluentBundle, LazyFallbackBundle, MultiSpan, SpanLabel, Subdiag, Suggestions, - TerminalUrl, -}; +use crate::translation::{Translator, to_fluent_args}; +use crate::{CodeSuggestion, MultiSpan, SpanLabel, Subdiag, Suggestions, TerminalUrl}; #[cfg(test)] mod tests; @@ -47,9 +44,8 @@ pub struct JsonEmitter { dst: IntoDynSyncSend>, #[setters(skip)] sm: Option>, - fluent_bundle: Option>, #[setters(skip)] - fallback_bundle: LazyFallbackBundle, + translator: Translator, #[setters(skip)] pretty: bool, ui_testing: bool, @@ -67,7 +63,7 @@ impl JsonEmitter { pub fn new( dst: Box, sm: Option>, - fallback_bundle: LazyFallbackBundle, + translator: Translator, pretty: bool, json_rendered: HumanReadableErrorType, color_config: ColorConfig, @@ -75,8 +71,7 @@ impl JsonEmitter { JsonEmitter { dst: IntoDynSyncSend(dst), sm, - fluent_bundle: None, - fallback_bundle, + translator, pretty, ui_testing: false, ignored_directories_in_source_blocks: Vec::new(), @@ -110,16 +105,6 @@ enum EmitTyped<'a> { UnusedExtern(UnusedExterns<'a>), } -impl Translate for JsonEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - self.fluent_bundle.as_deref() - } - - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &self.fallback_bundle - } -} - impl Emitter for JsonEmitter { fn emit_diagnostic(&mut self, diag: crate::DiagInner, registry: &Registry) { let data = Diagnostic::from_errors_diagnostic(diag, self, registry); @@ -194,6 +179,10 @@ impl Emitter for JsonEmitter { fn should_show_explain(&self) -> bool { !self.json_rendered.short() } + + fn translator(&self) -> &Translator { + &self.translator + } } // The following data types are provided just for serialisation. @@ -324,7 +313,7 @@ impl Diagnostic { let args = to_fluent_args(diag.args.iter()); let sugg_to_diag = |sugg: &CodeSuggestion| { let translated_message = - je.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); + je.translator.translate_message(&sugg.msg, &args).map_err(Report::new).unwrap(); Diagnostic { message: translated_message.to_string(), code: None, @@ -368,7 +357,7 @@ impl Diagnostic { } } - let translated_message = je.translate_messages(&diag.messages, &args); + let translated_message = je.translator.translate_messages(&diag.messages, &args); let code = if let Some(code) = diag.code { Some(DiagnosticCode { @@ -396,10 +385,9 @@ impl Diagnostic { ColorConfig::Always | ColorConfig::Auto => dst = Box::new(termcolor::Ansi::new(dst)), ColorConfig::Never => {} } - HumanEmitter::new(dst, Arc::clone(&je.fallback_bundle)) + HumanEmitter::new(dst, je.translator.clone()) .short_message(short) .sm(je.sm.clone()) - .fluent_bundle(je.fluent_bundle.clone()) .diagnostic_width(je.diagnostic_width) .macro_backtrace(je.macro_backtrace) .track_diagnostics(je.track_diagnostics) @@ -430,7 +418,7 @@ impl Diagnostic { args: &FluentArgs<'_>, je: &JsonEmitter, ) -> Diagnostic { - let translated_message = je.translate_messages(&subdiag.messages, args); + let translated_message = je.translator.translate_messages(&subdiag.messages, args); Diagnostic { message: translated_message.to_string(), code: None, @@ -454,7 +442,7 @@ impl DiagnosticSpan { span.is_primary, span.label .as_ref() - .map(|m| je.translate_message(m, args).unwrap()) + .map(|m| je.translator.translate_message(m, args).unwrap()) .map(|m| m.to_string()), suggestion, je, diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs index 40973e8e5d8..8cf81f467d8 100644 --- a/compiler/rustc_errors/src/json/tests.rs +++ b/compiler/rustc_errors/src/json/tests.rs @@ -41,14 +41,14 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) { rustc_span::create_default_session_globals_then(|| { let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); sm.new_source_file(Path::new("test.rs").to_owned().into(), code.to_owned()); - let fallback_bundle = - crate::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); + let translator = + Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); let output = Arc::new(Mutex::new(Vec::new())); let je = JsonEmitter::new( Box::new(Shared { data: output.clone() }), Some(sm), - fallback_bundle, + translator, true, // pretty HumanReadableErrorType::Short, ColorConfig::Never, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0bd259366de..70f294b696a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -761,14 +761,8 @@ impl DiagCtxt { fn source_map(&self) -> Option<&SourceMap> { unimplemented!("false emitter must only used during `make_silent`") } - } - - impl translation::Translate for FalseEmitter { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - unimplemented!("false emitter must only used during `make_silent`") - } - fn fallback_fluent_bundle(&self) -> &FluentBundle { + fn translator(&self) -> &translation::Translator { unimplemented!("false emitter must only used during `make_silent`") } } @@ -1771,7 +1765,12 @@ impl DiagCtxtInner { args: impl Iterator>, ) -> String { let args = crate::translation::to_fluent_args(args); - self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string() + self.emitter + .translator() + .translate_message(&message, &args) + .map_err(Report::new) + .unwrap() + .to_string() } fn eagerly_translate_for_subdiag( diff --git a/compiler/rustc_errors/src/tests.rs b/compiler/rustc_errors/src/tests.rs index 376fd24d57b..34ebac0fde1 100644 --- a/compiler/rustc_errors/src/tests.rs +++ b/compiler/rustc_errors/src/tests.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, LazyLock}; + use rustc_data_structures::sync::IntoDynSyncSend; use rustc_error_messages::fluent_bundle::resolver::errors::{ReferenceKind, ResolverError}; use rustc_error_messages::{DiagMessage, langid}; @@ -5,23 +7,9 @@ use rustc_error_messages::{DiagMessage, langid}; use crate::FluentBundle; use crate::error::{TranslateError, TranslateErrorKind}; use crate::fluent_bundle::*; -use crate::translation::Translate; - -struct Dummy { - bundle: FluentBundle, -} - -impl Translate for Dummy { - fn fluent_bundle(&self) -> Option<&FluentBundle> { - None - } +use crate::translation::Translator; - fn fallback_fluent_bundle(&self) -> &FluentBundle { - &self.bundle - } -} - -fn make_dummy(ftl: &'static str) -> Dummy { +fn make_translator(ftl: &'static str) -> Translator { let resource = FluentResource::try_new(ftl.into()).expect("Failed to parse an FTL string."); let langid_en = langid!("en-US"); @@ -33,12 +21,15 @@ fn make_dummy(ftl: &'static str) -> Dummy { bundle.add_resource(resource).expect("Failed to add FTL resources to the bundle."); - Dummy { bundle } + Translator { + fluent_bundle: None, + fallback_fluent_bundle: Arc::new(LazyLock::new(Box::new(|| bundle))), + } } #[test] fn wellformed_fluent() { - let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value + let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value .label = value moved into `{$name}` here .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait .value_borrowed_label = value borrowed here after move @@ -54,7 +45,7 @@ fn wellformed_fluent() { ); assert_eq!( - dummy.translate_message(&message, &args).unwrap(), + translator.translate_message(&message, &args).unwrap(), "borrow this binding in the pattern to avoid moving the value" ); } @@ -66,7 +57,7 @@ fn wellformed_fluent() { ); assert_eq!( - dummy.translate_message(&message, &args).unwrap(), + translator.translate_message(&message, &args).unwrap(), "value borrowed here after move" ); } @@ -78,7 +69,7 @@ fn wellformed_fluent() { ); assert_eq!( - dummy.translate_message(&message, &args).unwrap(), + translator.translate_message(&message, &args).unwrap(), "move occurs because `\u{2068}Foo\u{2069}` has type `\u{2068}std::string::String\u{2069}` which does not implement the `Copy` trait" ); @@ -89,7 +80,7 @@ fn wellformed_fluent() { ); assert_eq!( - dummy.translate_message(&message, &args).unwrap(), + translator.translate_message(&message, &args).unwrap(), "value moved into `\u{2068}Foo\u{2069}` here" ); } @@ -98,7 +89,7 @@ fn wellformed_fluent() { #[test] fn misformed_fluent() { - let dummy = make_dummy("mir_build_borrow_of_moved_value = borrow of moved value + let translator = make_translator("mir_build_borrow_of_moved_value = borrow of moved value .label = value moved into `{name}` here .occurs_because_label = move occurs because `{$oops}` has type `{$ty}` which does not implement the `Copy` trait .suggestion = borrow this binding in the pattern to avoid moving the value"); @@ -112,7 +103,7 @@ fn misformed_fluent() { Some("value_borrowed_label".into()), ); - let err = dummy.translate_message(&message, &args).unwrap_err(); + let err = translator.translate_message(&message, &args).unwrap_err(); assert!( matches!( &err, @@ -141,7 +132,7 @@ fn misformed_fluent() { Some("label".into()), ); - let err = dummy.translate_message(&message, &args).unwrap_err(); + let err = translator.translate_message(&message, &args).unwrap_err(); if let TranslateError::Two { primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, @@ -168,7 +159,7 @@ fn misformed_fluent() { Some("occurs_because_label".into()), ); - let err = dummy.translate_message(&message, &args).unwrap_err(); + let err = translator.translate_message(&message, &args).unwrap_err(); if let TranslateError::Two { primary: box TranslateError::One { kind: TranslateErrorKind::PrimaryBundleMissing, .. }, fallback: box TranslateError::One { kind: TranslateErrorKind::Fluent { errs }, .. }, diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 156f5e5d26e..c0bcec093c7 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -1,8 +1,9 @@ use std::borrow::Cow; use std::env; use std::error::Report; +use std::sync::Arc; -pub use rustc_error_messages::FluentArgs; +pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; use tracing::{debug, trace}; use crate::error::{TranslateError, TranslateErrorKind}; @@ -28,19 +29,33 @@ pub fn to_fluent_args<'iter>(iter: impl Iterator>) -> Flue args } -pub trait Translate { - /// Return `FluentBundle` with localized diagnostics for the locale requested by the user. If no - /// language was requested by the user then this will be `None` and `fallback_fluent_bundle` - /// should be used. - fn fluent_bundle(&self) -> Option<&FluentBundle>; - +#[derive(Clone)] +pub struct Translator { + /// Localized diagnostics for the locale requested by the user. If no language was requested by + /// the user then this will be `None` and `fallback_fluent_bundle` should be used. + pub fluent_bundle: Option>, /// Return `FluentBundle` with localized diagnostics for the default locale of the compiler. /// Used when the user has not requested a specific language or when a localized diagnostic is /// unavailable for the requested locale. - fn fallback_fluent_bundle(&self) -> &FluentBundle; + pub fallback_fluent_bundle: LazyFallbackBundle, +} + +impl Translator { + pub fn with_fallback_bundle( + resources: Vec<&'static str>, + with_directionality_markers: bool, + ) -> Translator { + Translator { + fluent_bundle: None, + fallback_fluent_bundle: crate::fallback_fluent_bundle( + resources, + with_directionality_markers, + ), + } + } /// Convert `DiagMessage`s to a string, performing translation if necessary. - fn translate_messages( + pub fn translate_messages( &self, messages: &[(DiagMessage, Style)], args: &FluentArgs<'_>, @@ -54,7 +69,7 @@ pub trait Translate { } /// Convert a `DiagMessage` to a string, performing translation if necessary. - fn translate_message<'a>( + pub fn translate_message<'a>( &'a self, message: &'a DiagMessage, args: &'a FluentArgs<'_>, @@ -91,7 +106,7 @@ pub trait Translate { }; try { - match self.fluent_bundle().map(|b| translate_with_bundle(b)) { + match self.fluent_bundle.as_ref().map(|b| translate_with_bundle(b)) { // The primary bundle was present and translation succeeded Some(Ok(t)) => t, @@ -102,7 +117,7 @@ pub trait Translate { primary @ TranslateError::One { kind: TranslateErrorKind::MessageMissing, .. }, - )) => translate_with_bundle(self.fallback_fluent_bundle()) + )) => translate_with_bundle(&self.fallback_fluent_bundle) .map_err(|fallback| primary.and(fallback))?, // Always yeet out for errors on debug (unless @@ -118,11 +133,11 @@ pub trait Translate { // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so // just hide it and try with the fallback bundle. - Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle()) + Some(Err(primary)) => translate_with_bundle(&self.fallback_fluent_bundle) .map_err(|fallback| primary.and(fallback))?, // The primary bundle is missing, proceed to the fallback bundle - None => translate_with_bundle(self.fallback_fluent_bundle()) + None => translate_with_bundle(&self.fallback_fluent_bundle) .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, } } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 2a44c90abc1..15679d23bc5 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -14,6 +14,7 @@ use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, Toke use rustc_ast::{self as ast, PatKind, visit}; use rustc_ast_pretty::pprust::item_to_string; use rustc_errors::emitter::{HumanEmitter, OutputTheme}; +use rustc_errors::translation::Translator; use rustc_errors::{DiagCtxt, MultiSpan, PResult}; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -41,9 +42,8 @@ fn string_to_parser(psess: &ParseSess, source_str: String) -> Parser<'_> { fn create_test_handler(theme: OutputTheme) -> (DiagCtxt, Arc, Arc>>) { let output = Arc::new(Mutex::new(Vec::new())); let source_map = Arc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); - let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) + let translator = Translator::with_fallback_bundle(vec![crate::DEFAULT_LOCALE_RESOURCE], false); + let mut emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), translator) .sm(Some(source_map.clone())) .diagnostic_width(Some(140)); emitter = emitter.theme(theme); diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 87c848cf857..30a61cb411b 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -9,9 +9,10 @@ use rustc_ast::node_id::NodeId; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::sync::{AppendOnlyVec, Lock}; use rustc_errors::emitter::{HumanEmitter, SilentEmitter, stderr_destination}; +use rustc_errors::translation::Translator; use rustc_errors::{ ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan, - StashKey, fallback_fluent_bundle, + StashKey, }; use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue}; use rustc_span::edition::Edition; @@ -242,10 +243,10 @@ pub struct ParseSess { impl ParseSess { /// Used for testing. pub fn new(locale_resources: Vec<&'static str>) -> Self { - let fallback_bundle = fallback_fluent_bundle(locale_resources, false); + let translator = Translator::with_fallback_bundle(locale_resources, false); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new( - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle) + HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator) .sm(Some(Arc::clone(&sm))), ); let dcx = DiagCtxt::new(emitter); @@ -277,12 +278,13 @@ impl ParseSess { pub fn with_silent_emitter( locale_resources: Vec<&'static str>, fatal_note: String, + emit_fatal_diagnostic: bool, ) -> Self { - let fallback_bundle = fallback_fluent_bundle(locale_resources, false); + let translator = Translator::with_fallback_bundle(locale_resources, false); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let fatal_emitter = - Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle)); + Box::new(HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator)); let dcx = DiagCtxt::new(Box::new(SilentEmitter { fatal_emitter, fatal_note: Some(fatal_note), diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ca42c5a4256..ad58c3c8f7d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -19,9 +19,10 @@ use rustc_errors::emitter::{ }; use rustc_errors::json::JsonEmitter; use rustc_errors::timings::TimingSectionHandler; +use rustc_errors::translation::Translator; use rustc_errors::{ Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort, - FluentBundle, LazyFallbackBundle, TerminalUrl, fallback_fluent_bundle, + TerminalUrl, fallback_fluent_bundle, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -948,8 +949,7 @@ impl Session { fn default_emitter( sopts: &config::Options, source_map: Arc, - bundle: Option>, - fallback_bundle: LazyFallbackBundle, + translator: Translator, ) -> Box { let macro_backtrace = sopts.unstable_opts.macro_backtrace; let track_diagnostics = sopts.unstable_opts.track_diagnostics; @@ -974,17 +974,11 @@ fn default_emitter( let short = kind.short(); if let HumanReadableErrorType::AnnotateSnippet = kind { - let emitter = AnnotateSnippetEmitter::new( - source_map, - bundle, - fallback_bundle, - short, - macro_backtrace, - ); + let emitter = + AnnotateSnippetEmitter::new(source_map, translator, short, macro_backtrace); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } else { - let emitter = HumanEmitter::new(stderr_destination(color_config), fallback_bundle) - .fluent_bundle(bundle) + let emitter = HumanEmitter::new(stderr_destination(color_config), translator) .sm(source_map) .short_message(short) .diagnostic_width(sopts.diagnostic_width) @@ -1006,12 +1000,11 @@ fn default_emitter( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), source_map, - fallback_bundle, + translator, pretty, json_rendered, color_config, ) - .fluent_bundle(bundle) .ui_testing(sopts.unstable_opts.ui_testing) .ignored_directories_in_source_blocks( sopts.unstable_opts.ignore_directory_in_diagnostics_source_blocks.clone(), @@ -1030,7 +1023,7 @@ fn default_emitter( pub fn build_session( sopts: config::Options, io: CompilerIO, - bundle: Option>, + fluent_bundle: Option>, registry: rustc_errors::registry::Registry, fluent_resources: Vec<&'static str>, driver_lint_caps: FxHashMap, @@ -1052,12 +1045,15 @@ pub fn build_session( let cap_lints_allow = sopts.lint_cap.is_some_and(|cap| cap == lint::Allow); let can_emit_warnings = !(warnings_allow || cap_lints_allow); - let fallback_bundle = fallback_fluent_bundle( - fluent_resources, - sopts.unstable_opts.translate_directionality_markers, - ); + let translator = Translator { + fluent_bundle, + fallback_fluent_bundle: fallback_fluent_bundle( + fluent_resources, + sopts.unstable_opts.translate_directionality_markers, + ), + }; let source_map = rustc_span::source_map::get_source_map().unwrap(); - let emitter = default_emitter(&sopts, Arc::clone(&source_map), bundle, fallback_bundle); + let emitter = default_emitter(&sopts, Arc::clone(&source_map), translator); let mut dcx = DiagCtxt::new(emitter) .with_flags(sopts.unstable_opts.dcx_flags(can_emit_warnings)) @@ -1500,13 +1496,13 @@ impl EarlyDiagCtxt { fn mk_emitter(output: ErrorOutputType) -> Box { // FIXME(#100717): early errors aren't translated at the moment, so this is fine, but it will // need to reference every crate that might emit an early error for translation to work. - let fallback_bundle = - fallback_fluent_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); + let translator = + Translator::with_fallback_bundle(vec![rustc_errors::DEFAULT_LOCALE_RESOURCE], false); let emitter: Box = match output { config::ErrorOutputType::HumanReadable { kind, color_config } => { let short = kind.short(); Box::new( - HumanEmitter::new(stderr_destination(color_config), fallback_bundle) + HumanEmitter::new(stderr_destination(color_config), translator) .theme(if let HumanReadableErrorType::Unicode = kind { OutputTheme::Unicode } else { @@ -1519,7 +1515,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box { Box::new(JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), Some(Arc::new(SourceMap::new(FilePathMapping::empty()))), - fallback_bundle, + translator, pretty, json_rendered, color_config, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 204f8decffc..3b1d89bbecd 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -149,15 +149,12 @@ pub(crate) fn new_dcx( diagnostic_width: Option, unstable_opts: &UnstableOptions, ) -> rustc_errors::DiagCtxt { - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); + let translator = rustc_driver::default_translator(); let emitter: Box = match error_format { ErrorOutputType::HumanReadable { kind, color_config } => { let short = kind.short(); Box::new( - HumanEmitter::new(stderr_destination(color_config), fallback_bundle) + HumanEmitter::new(stderr_destination(color_config), translator) .sm(source_map.map(|sm| sm as _)) .short_message(short) .diagnostic_width(diagnostic_width) @@ -178,7 +175,7 @@ pub(crate) fn new_dcx( JsonEmitter::new( Box::new(io::BufWriter::new(io::stderr())), Some(source_map), - fallback_bundle, + translator, pretty, json_rendered, color_config, diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index 3ff6828e52f..f229f77c978 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -456,16 +456,13 @@ fn parse_source( let filename = FileName::anon_source_code(&wrapped_source); let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); + let translator = rustc_driver::default_translator(); info.supports_color = - HumanEmitter::new(stderr_destination(ColorConfig::Auto), fallback_bundle.clone()) + HumanEmitter::new(stderr_destination(ColorConfig::Auto), translator.clone()) .supports_color(); // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that the parser emits directly into a `Sink` instead of stderr. - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + let emitter = HumanEmitter::new(Box::new(io::sink()), translator); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs index 9662dd85d67..91cddbe5a5b 100644 --- a/src/librustdoc/passes/lint/check_code_block_syntax.rs +++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use rustc_data_structures::sync::Lock; use rustc_errors::emitter::Emitter; use rustc_errors::registry::Registry; -use rustc_errors::translation::{Translate, to_fluent_args}; -use rustc_errors::{Applicability, DiagCtxt, DiagInner, LazyFallbackBundle}; +use rustc_errors::translation::{Translator, to_fluent_args}; +use rustc_errors::{Applicability, DiagCtxt, DiagInner}; use rustc_parse::{source_str_to_stream, unwrap_or_emit_fatal}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use rustc_session::parse::ParseSess; @@ -36,11 +36,8 @@ fn check_rust_syntax( code_block: RustCodeBlock, ) { let buffer = Arc::new(Lock::new(Buffer::default())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); - let emitter = BufferEmitter { buffer: Arc::clone(&buffer), fallback_bundle }; + let translator = rustc_driver::default_translator(); + let emitter = BufferEmitter { buffer: Arc::clone(&buffer), translator }; let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); @@ -149,17 +146,7 @@ struct Buffer { struct BufferEmitter { buffer: Arc>, - fallback_bundle: LazyFallbackBundle, -} - -impl Translate for BufferEmitter { - fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> { - None - } - - fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { - &self.fallback_bundle - } + translator: Translator, } impl Emitter for BufferEmitter { @@ -168,6 +155,7 @@ impl Emitter for BufferEmitter { let fluent_args = to_fluent_args(diag.args.iter()); let translated_main_message = self + .translator .translate_message(&diag.messages[0].0, &fluent_args) .unwrap_or_else(|e| panic!("{e}")); @@ -180,4 +168,8 @@ impl Emitter for BufferEmitter { fn source_map(&self) -> Option<&SourceMap> { None } + + fn translator(&self) -> &Translator { + &self.translator + } } diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs index ec4538039a9..7ba11c20f45 100644 --- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs +++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs @@ -42,9 +42,8 @@ pub fn check( let mut test_attr_spans = vec![]; let filename = FileName::anon_source_code(&code); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); - let emitter = HumanEmitter::new(Box::new(io::sink()), fallback_bundle); + let translator = rustc_driver::default_translator(); + let emitter = HumanEmitter::new(Box::new(io::sink()), translator); let dcx = DiagCtxt::new(Box::new(emitter)).disable_warnings(); #[expect(clippy::arc_with_non_send_sync)] // `Arc` is expected by with_dcx let sm = Arc::new(SourceMap::new(FilePathMapping::empty())); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index afd847f9515..73a89072f14 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -5,7 +5,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::IntoDynSyncSend; use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination}; use rustc_errors::registry::Registry; -use rustc_errors::translation::Translate; +use rustc_errors::translation::Translator; use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ @@ -47,16 +47,6 @@ impl SilentOnIgnoredFilesEmitter { } } -impl Translate for SilentOnIgnoredFilesEmitter { - fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> { - self.emitter.fluent_bundle() - } - - fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { - self.emitter.fallback_fluent_bundle() - } -} - impl Emitter for SilentOnIgnoredFilesEmitter { fn source_map(&self) -> Option<&SourceMap> { None @@ -84,6 +74,10 @@ impl Emitter for SilentOnIgnoredFilesEmitter { } self.handle_non_ignoreable_error(diag, registry); } + + fn translator(&self) -> &Translator { + self.emitter.translator() + } } impl From for ColorConfig { @@ -110,12 +104,9 @@ fn default_dcx( ColorConfig::Never }; - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), - false, - ); + let translator = rustc_driver::default_translator(); let emitter = Box::new( - HumanEmitter::new(stderr_destination(emit_color), fallback_bundle) + HumanEmitter::new(stderr_destination(emit_color), translator) .sm(Some(source_map.clone())), ); @@ -335,16 +326,6 @@ mod tests { num_emitted_errors: Arc, } - impl Translate for TestEmitter { - fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> { - None - } - - fn fallback_fluent_bundle(&self) -> &rustc_errors::FluentBundle { - panic!("test emitter attempted to translate a diagnostic"); - } - } - impl Emitter for TestEmitter { fn source_map(&self) -> Option<&SourceMap> { None @@ -353,6 +334,10 @@ mod tests { fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) { self.num_emitted_errors.fetch_add(1, Ordering::Release); } + + fn translator(&self) -> &Translator { + panic!("test emitter attempted to translate a diagnostic"); + } } fn build_diagnostic(level: DiagnosticLevel, span: Option) -> DiagInner { -- cgit 1.4.1-3-g733a5 From b9107a83a1ace3f292641977df70e9610e0e4482 Mon Sep 17 00:00:00 2001 From: Jana Dönszelmann Date: Thu, 12 Jun 2025 14:24:40 +0200 Subject: expected word diagnostic test --- compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs | 2 +- compiler/rustc_attr_parsing/src/context.rs | 4 ++-- compiler/rustc_parse/src/validate_attr.rs | 1 + src/rustdoc-json-types/lib.rs | 4 ++-- tests/ui/attributes/expected-word.rs | 3 +++ tests/ui/attributes/expected-word.stderr | 12 ++++++++++++ 6 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 tests/ui/attributes/expected-word.rs create mode 100644 tests/ui/attributes/expected-word.stderr (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index 1509531ca98..1b03525a5ce 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -49,7 +49,7 @@ impl SingleAttributeParser for ColdParser { fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { if !args.no_args() { - cx.expected_no_args(cx.attr_span); + cx.expected_no_args(args.span().unwrap_or(cx.attr_span)); return None; }; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index e8bcf57e109..648bd431898 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -235,9 +235,9 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { }) } - pub(crate) fn expected_no_args(&self, span: Span) -> ErrorGuaranteed { + pub(crate) fn expected_no_args(&self, args_span: Span) -> ErrorGuaranteed { self.emit_err(AttributeParseError { - span, + span: args_span, attr_span: self.attr_span, template: self.template.clone(), attribute: self.attr_path.clone(), diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index 646e996ff30..ed1737bee33 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -292,6 +292,7 @@ fn emit_malformed_attribute( | sym::align | sym::deprecated | sym::optimize + | sym::cold ) { return; } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index d5de43feb58..4d25124f9f2 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -37,8 +37,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: Pretty printing of optimize attributes changed -pub const FORMAT_VERSION: u32 = 49; +// Latest feature: Pretty printing of cold attributes changed +pub const FORMAT_VERSION: u32 = 50; /// The root of the emitted JSON blob. /// diff --git a/tests/ui/attributes/expected-word.rs b/tests/ui/attributes/expected-word.rs new file mode 100644 index 00000000000..246aa78db82 --- /dev/null +++ b/tests/ui/attributes/expected-word.rs @@ -0,0 +1,3 @@ +#[cold = true] +//~^ ERROR malformed `cold` attribute input [E0565] +fn main() {} diff --git a/tests/ui/attributes/expected-word.stderr b/tests/ui/attributes/expected-word.stderr new file mode 100644 index 00000000000..dcb10e7aee8 --- /dev/null +++ b/tests/ui/attributes/expected-word.stderr @@ -0,0 +1,12 @@ +error[E0565]: malformed `cold` attribute input + --> $DIR/expected-word.rs:1:1 + | +LL | #[cold = true] + | ^^^^^^^------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[cold]` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0565`. -- cgit 1.4.1-3-g733a5 From 6809ec16482ec72ba46566fe815b2bc89210612f Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 20 Jun 2025 12:45:26 -0500 Subject: Factor out seen_comma variable --- compiler/rustc_parse/src/parser/item.rs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 658ed4bd41c..28da287fcc2 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1916,11 +1916,7 @@ impl<'a> Parser<'a> { 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, safety, attrs)?; - if self.token == token::Comma { - seen_comma = true; - } if self.eat(exp!(Semi)) { let sp = self.prev_token.span; let mut err = @@ -1945,19 +1941,11 @@ impl<'a> Parser<'a> { missing_comma: None, }; self.bump(); // consume the doc comment - let comma_after_doc_seen = self.eat(exp!(Comma)); - // `seen_comma` is always false, because we are inside doc block - // condition is here to make code more readable - if !seen_comma && comma_after_doc_seen { - seen_comma = true; - } - if comma_after_doc_seen || self.token == token::CloseBrace { + if self.eat(exp!(Comma)) || self.token == token::CloseBrace { self.dcx().emit_err(err); } else { - if !seen_comma { - let sp = previous_span.shrink_to_hi(); - err.missing_comma = Some(sp); - } + let sp = previous_span.shrink_to_hi(); + err.missing_comma = Some(sp); return Err(self.dcx().create_err(err)); } } -- cgit 1.4.1-3-g733a5 From 26a6b557172a7186c140c7aa42bf3873802a39fd Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Fri, 20 Jun 2025 12:18:37 -0500 Subject: Recover from semicolon field separator --- compiler/rustc_parse/src/parser/item.rs | 33 ++++++++++++----------- tests/ui/parser/recover/recover-field-semi.rs | 6 +++-- tests/ui/parser/recover/recover-field-semi.stderr | 12 ++++----- 3 files changed, 27 insertions(+), 24 deletions(-) (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 28da287fcc2..5088caa80f8 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1781,7 +1781,7 @@ impl<'a> Parser<'a> { let mut recovered = Recovered::No; if self.eat(exp!(OpenBrace)) { while self.token != token::CloseBrace { - match self.parse_field_def(adt_ty) { + match self.parse_field_def(adt_ty, ident_span) { Ok(field) => { fields.push(field); } @@ -1894,7 +1894,7 @@ impl<'a> Parser<'a> { } /// Parses an element of a struct declaration. - fn parse_field_def(&mut self, adt_ty: &str) -> PResult<'a, FieldDef> { + fn parse_field_def(&mut self, adt_ty: &str, ident_span: Span) -> PResult<'a, FieldDef> { self.recover_vcs_conflict_marker(); let attrs = self.parse_outer_attributes()?; self.recover_vcs_conflict_marker(); @@ -1902,7 +1902,7 @@ impl<'a> Parser<'a> { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; let safety = this.parse_unsafe_field(); - this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs) + this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs, ident_span) .map(|field| (field, Trailing::No, UsePreAttrPos::No)) }) } @@ -1915,24 +1915,27 @@ impl<'a> Parser<'a> { vis: Visibility, safety: Safety, attrs: AttrVec, + ident_span: Span, ) -> PResult<'a, FieldDef> { let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?; - if self.eat(exp!(Semi)) { - let sp = self.prev_token.span; - let mut err = - self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`")); - err.span_suggestion_short( - sp, - "replace `;` with `,`", - ",", - Applicability::MachineApplicable, - ); - return Err(err); - } match self.token.kind { token::Comma => { self.bump(); } + token::Semi => { + self.bump(); + let sp = self.prev_token.span; + let mut err = + self.dcx().struct_span_err(sp, format!("{adt_ty} fields are separated by `,`")); + err.span_suggestion_short( + sp, + "replace `;` with `,`", + ",", + Applicability::MachineApplicable, + ); + err.span_label(ident_span, format!("while parsing this {adt_ty}")); + err.emit(); + } token::CloseBrace => {} token::DocComment(..) => { let previous_span = self.prev_token.span; diff --git a/tests/ui/parser/recover/recover-field-semi.rs b/tests/ui/parser/recover/recover-field-semi.rs index b703578860e..b6f235f8ad1 100644 --- a/tests/ui/parser/recover/recover-field-semi.rs +++ b/tests/ui/parser/recover/recover-field-semi.rs @@ -3,7 +3,7 @@ struct Foo { //~^ ERROR struct fields are separated by `,` } -union Bar { //~ ERROR +union Bar { foo: i32; //~^ ERROR union fields are separated by `,` } @@ -13,4 +13,6 @@ enum Baz { //~^ ERROR struct fields are separated by `,` } -fn main() {} +fn main() { + let _ = Foo { foo: "" }; //~ ERROR mismatched types +} diff --git a/tests/ui/parser/recover/recover-field-semi.stderr b/tests/ui/parser/recover/recover-field-semi.stderr index 3cf4847488c..9b1a34e134b 100644 --- a/tests/ui/parser/recover/recover-field-semi.stderr +++ b/tests/ui/parser/recover/recover-field-semi.stderr @@ -22,14 +22,12 @@ LL | Qux { foo: i32; } | | | while parsing this struct -error: unions cannot have zero fields - --> $DIR/recover-field-semi.rs:6:1 +error[E0308]: mismatched types + --> $DIR/recover-field-semi.rs:17:24 | -LL | / union Bar { -LL | | foo: i32; -LL | | -LL | | } - | |_^ +LL | let _ = Foo { foo: "" }; + | ^^ expected `i32`, found `&str` error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From b24df424888d9db3a22d6d52f3f516e29d5be21a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 22 Jun 2025 12:09:14 +0200 Subject: Port `#[must_use]` to new attribute parsing infrastructure Signed-off-by: Jonathan Brouwer --- .../rustc_attr_data_structures/src/attributes.rs | 7 ++++ compiler/rustc_attr_parsing/src/attributes/mod.rs | 1 + .../rustc_attr_parsing/src/attributes/must_use.rs | 40 ++++++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 ++ .../rustc_attr_parsing/src/session_diagnostics.rs | 9 +++++ compiler/rustc_lint/src/unused.rs | 9 +++-- compiler/rustc_parse/src/validate_attr.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 11 +++--- src/rustdoc-json-types/lib.rs | 4 +-- .../clippy/clippy_lints/src/functions/must_use.rs | 30 +++++++++------- .../clippy_lints/src/return_self_not_must_use.rs | 9 +++-- src/tools/clippy/clippy_utils/src/lib.rs | 5 ++- src/tools/clippy/clippy_utils/src/ty/mod.rs | 16 ++++++--- tests/rustdoc-json/attrs/must_use.rs | 4 +-- tests/ui/attributes/malformed-must_use.rs | 4 +++ tests/ui/attributes/malformed-must_use.stderr | 8 +++++ .../issue-43106-gating-of-builtin-attrs.stderr | 12 +++---- tests/ui/lint/unused/unused-attr-duplicate.stderr | 26 +++++++------- tests/ui/parser/bad-lit-suffixes.rs | 1 - tests/ui/parser/bad-lit-suffixes.stderr | 21 ++---------- 20 files changed, 151 insertions(+), 69 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/must_use.rs create mode 100644 tests/ui/attributes/malformed-must_use.rs create mode 100644 tests/ui/attributes/malformed-must_use.stderr (limited to 'compiler/rustc_parse/src') diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index f0f5cc4db07..ce1d8080262 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -237,6 +237,13 @@ pub enum AttributeKind { /// Represents [`#[may_dangle]`](https://std-dev-guide.rust-lang.org/tricky/may-dangle.html). MayDangle(Span), + /// Represents `#[must_use]`. + MustUse { + span: Span, + /// must_use can optionally have a reason: `#[must_use = "reason this must be used"]` + reason: Option, + }, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 1bb5edba326..3162c1fc727 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -33,6 +33,7 @@ pub(crate) mod confusables; pub(crate) mod deprecation; pub(crate) mod inline; pub(crate) mod lint_helpers; +pub(crate) mod must_use; pub(crate) mod repr; pub(crate) mod semantics; pub(crate) mod stability; diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs new file mode 100644 index 00000000000..a672d956127 --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs @@ -0,0 +1,40 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_errors::DiagArgValue; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Symbol, sym}; + +use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; +use crate::session_diagnostics; + +pub(crate) struct MustUseParser; + +impl SingleAttributeParser for MustUseParser { + const PATH: &[Symbol] = &[sym::must_use]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepLast; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::WarnButFutureError; + const TEMPLATE: AttributeTemplate = template!(Word, NameValueStr: "reason"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + Some(AttributeKind::MustUse { + span: cx.attr_span, + reason: match args { + ArgParser::NoArgs => None, + ArgParser::NameValue(name_value) => name_value.value_as_str(), + ArgParser::List(_) => { + let suggestions = + >::TEMPLATE.suggestions(false, "must_use"); + cx.emit_err(session_diagnostics::MustUseIllFormedAttributeInput { + num_suggestions: suggestions.len(), + suggestions: DiagArgValue::StrListSepByAnd( + suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(), + ), + span: cx.attr_span, + }); + return None; + } + }, + }) + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index b95ea598e72..fbe874d606c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -20,6 +20,7 @@ use crate::attributes::confusables::ConfusablesParser; use crate::attributes::deprecation::DeprecationParser; use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; use crate::attributes::lint_helpers::{AsPtrParser, PubTransparentParser}; +use crate::attributes::must_use::MustUseParser; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -112,6 +113,7 @@ attribute_parsers!( Single, Single, Single, + Single, Single, Single, Single, diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 29f2e44a98a..2a020770e5d 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -436,6 +436,15 @@ pub(crate) struct IllFormedAttributeInput { pub suggestions: DiagArgValue, } +#[derive(Diagnostic)] +#[diag(attr_parsing_ill_formed_attribute_input)] +pub(crate) struct MustUseIllFormedAttributeInput { + #[primary_span] + pub span: Span, + pub num_suggestions: usize, + pub suggestions: DiagArgValue, +} + #[derive(Diagnostic)] #[diag(attr_parsing_stability_outside_std, code = E0734)] pub(crate) struct StabilityOutsideStd { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1620f425794..a868c887493 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -2,6 +2,7 @@ use std::iter; use rustc_ast::util::{classify, parser}; use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_errors::{MultiSpan, pluralize}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; @@ -368,10 +369,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option { - if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { + if let Some(reason) = find_attr!( + cx.tcx.get_all_attrs(def_id), + AttributeKind::MustUse { reason, .. } => reason + ) { // check for #[must_use = "..."] - let reason = attr.value_str(); - Some(MustUsePath::Def(span, def_id, reason)) + Some(MustUsePath::Def(span, def_id, *reason)) } else { None } diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index ed1737bee33..8e6442353c3 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -293,6 +293,7 @@ fn emit_malformed_attribute( | sym::deprecated | sym::optimize | sym::cold + | sym::must_use ) { return; } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index d802bf4df19..e11ec2ed47a 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -171,6 +171,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> { Attribute::Parsed(AttributeKind::MayDangle(attr_span)) => { self.check_may_dangle(hir_id, *attr_span) } + Attribute::Parsed(AttributeKind::MustUse { span, .. }) => { + self.check_must_use(hir_id, *span, target) + } Attribute::Unparsed(attr_item) => { style = Some(attr_item.style); match attr.path().as_slice() { @@ -245,7 +248,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | [sym::const_trait, ..] => self.check_must_be_applied_to_trait(attr, span, target), [sym::collapse_debuginfo, ..] => self.check_collapse_debuginfo(attr, span, target), [sym::must_not_suspend, ..] => self.check_must_not_suspend(attr, span, target), - [sym::must_use, ..] => self.check_must_use(hir_id, attr, target), [sym::rustc_pass_by_value, ..] => self.check_pass_by_value(attr, span, target), [sym::rustc_allow_incoherent_impl, ..] => { self.check_allow_incoherent_impl(attr, span, target) @@ -696,7 +698,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::Deprecation { .. } | AttributeKind::Repr { .. } | AttributeKind::Align { .. } - | AttributeKind::Cold(..), + | AttributeKind::Cold(..) + | AttributeKind::MustUse { .. }, ) => { continue; } @@ -1576,7 +1579,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } /// Warns against some misuses of `#[must_use]` - fn check_must_use(&self, hir_id: HirId, attr: &Attribute, target: Target) { + fn check_must_use(&self, hir_id: HirId, attr_span: Span, target: Target) { if matches!( target, Target::Fn @@ -1616,7 +1619,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.tcx.emit_node_span_lint( UNUSED_ATTRIBUTES, hir_id, - attr.span(), + attr_span, errors::MustUseNoEffect { article, target }, ); } diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index c9b4da183a3..e5c246cb69c 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -37,8 +37,8 @@ pub type FxHashMap = HashMap; // re-export for use in src/librustdoc // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line // are deliberately not in a doc comment, because they need not be in public docs.) // -// Latest feature: improve handling of generic args -pub const FORMAT_VERSION: u32 = 51; +// Latest feature: Pretty printing of must_use attributes changed +pub const FORMAT_VERSION: u32 = 52; /// The root of the emitted JSON blob. /// diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 70655838b6a..ea9ed4ddade 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -15,6 +15,9 @@ use clippy_utils::ty::is_must_use_ty; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{return_ty, trait_ref_of_method}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_attr_data_structures::AttributeKind; +use rustc_span::Symbol; +use rustc_attr_data_structures::find_attr; use core::ops::ControlFlow; @@ -22,7 +25,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir_attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); + let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); if let hir::ItemKind::Fn { ref sig, body: ref body_id, @@ -31,8 +34,8 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_> { let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); - if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); + if let Some((attr_span, reason)) = attr { + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); } else if is_public && !is_proc_macro(attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) { check_must_use_candidate( cx, @@ -52,9 +55,9 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); - if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); + let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); + if let Some((attr_span, reason)) = attr { + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); } else if is_public && !is_proc_macro(attrs) && trait_ref_of_method(cx, item.owner_id).is_none() { check_must_use_candidate( cx, @@ -75,9 +78,9 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir_attrs(item.hir_id()); - let attr = cx.tcx.get_attr(item.owner_id, sym::must_use); - if let Some(attr) = attr { - check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr, attrs, sig); + let attr = find_attr!(cx.tcx.hir_attrs(item.hir_id()), AttributeKind::MustUse { span, reason } => (span, reason)); + if let Some((attr_span, reason)) = attr { + check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, *attr_span, *reason, attrs, sig); } else if let hir::TraitFn::Provided(eid) = *eid { let body = cx.tcx.hir_body(eid); if attr.is_none() && is_public && !is_proc_macro(attrs) { @@ -103,7 +106,8 @@ fn check_needless_must_use( item_id: hir::OwnerId, item_span: Span, fn_header_span: Span, - attr: &Attribute, + attr_span: Span, + reason: Option, attrs: &[Attribute], sig: &FnSig<'_>, ) { @@ -119,7 +123,7 @@ fn check_needless_must_use( "this unit-returning function has a `#[must_use]` attribute", |diag| { diag.span_suggestion( - attr.span(), + attr_span, "remove the attribute", "", Applicability::MachineApplicable, @@ -137,11 +141,11 @@ fn check_needless_must_use( MUST_USE_UNIT, fn_header_span, "this unit-returning function has a `#[must_use]` attribute", - Some(attr.span()), + Some(attr_span), "remove `must_use`", ); } - } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { + } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type if sig.header.is_async() { let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode()); diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 07ae92fa984..1b304dc5768 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -6,7 +6,9 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::{Span, sym}; +use rustc_span::{Span}; +use rustc_attr_data_structures::AttributeKind; +use rustc_attr_data_structures::find_attr; declare_clippy_lint! { /// ### What it does @@ -74,7 +76,10 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa // We only show this warning for public exported methods. && cx.effective_visibilities.is_exported(fn_def) // We don't want to emit this lint if the `#[must_use]` attribute is already there. - && !cx.tcx.hir_attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use)) + && !find_attr!( + cx.tcx.hir_attrs(owner_id.into()), + AttributeKind::MustUse { .. } + ) && cx.tcx.visibility(fn_def.to_def_id()).is_public() && let ret_ty = return_ty(cx, owner_id) && let self_arg = nth_arg(cx, owner_id, 0) diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c7a2375c8df..913589319fc 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -1886,7 +1886,10 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use)) + did.is_some_and(|did| find_attr!( + cx.tcx.get_all_attrs(did), + AttributeKind::MustUse { ..} + )) } /// Checks if a function's body represents the identity function. Looks for bodies of the form: diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 32a992ccc2d..782b079ce09 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -31,6 +31,8 @@ use rustc_trait_selection::traits::{Obligation, ObligationCause}; use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; +use rustc_attr_data_structures::find_attr; +use rustc_attr_data_structures::AttributeKind; use crate::path_res; use crate::paths::{PathNS, lookup_path_str}; @@ -326,8 +328,14 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use), - ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use), + ty::Adt(adt, _) => find_attr!( + cx.tcx.get_all_attrs(adt.did()), + AttributeKind::MustUse { ..} + ), + ty::Foreign(did) => find_attr!( + cx.tcx.get_all_attrs(*did), + AttributeKind::MustUse { ..} + ), ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays @@ -337,7 +345,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() - && cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) + && find_attr!(cx.tcx.get_all_attrs(trait_predicate.trait_ref.def_id), AttributeKind::MustUse { ..}) { return true; } @@ -347,7 +355,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Dynamic(binder, _, _) => { for predicate in *binder { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() - && cx.tcx.has_attr(trait_ref.def_id, sym::must_use) + && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { ..}) { return true; } diff --git a/tests/rustdoc-json/attrs/must_use.rs b/tests/rustdoc-json/attrs/must_use.rs index 64df8e5f509..3ca6f5a75a5 100644 --- a/tests/rustdoc-json/attrs/must_use.rs +++ b/tests/rustdoc-json/attrs/must_use.rs @@ -1,9 +1,9 @@ #![no_std] -//@ is "$.index[?(@.name=='example')].attrs" '["#[must_use]"]' +//@ is "$.index[?(@.name=='example')].attrs" '["#[attr = MustUse]"]' #[must_use] pub fn example() -> impl Iterator {} -//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[must_use = \"does nothing if you do not use it\"]"]' +//@ is "$.index[?(@.name=='explicit_message')].attrs" '["#[attr = MustUse {reason: \"does nothing if you do not use it\"}]"]' #[must_use = "does nothing if you do not use it"] pub fn explicit_message() -> impl Iterator {} diff --git a/tests/ui/attributes/malformed-must_use.rs b/tests/ui/attributes/malformed-must_use.rs new file mode 100644 index 00000000000..4b98affa8ab --- /dev/null +++ b/tests/ui/attributes/malformed-must_use.rs @@ -0,0 +1,4 @@ +#[must_use()] //~ ERROR valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` +struct Test; + +fn main() {} diff --git a/tests/ui/attributes/malformed-must_use.stderr b/tests/ui/attributes/malformed-must_use.stderr new file mode 100644 index 00000000000..c948ba67744 --- /dev/null +++ b/tests/ui/attributes/malformed-must_use.stderr @@ -0,0 +1,8 @@ +error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]` + --> $DIR/malformed-must_use.rs:1:1 + | +LL | #[must_use()] + | ^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 9280dfdf92e..d2b1d71ab87 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -403,12 +403,6 @@ LL | #![link_section = "1800"] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -warning: `#[must_use]` has no effect when applied to a module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1 - | -LL | #![must_use] - | ^^^^^^^^^^^^ - warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | @@ -417,6 +411,12 @@ LL | #![cold] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +warning: `#[must_use]` has no effect when applied to a module + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1 + | +LL | #![must_use] + | ^^^^^^^^^^^^ + warning: `#[macro_use]` only has an effect on `extern crate` and modules --> $DIR/issue-43106-gating-of-builtin-attrs.rs:176:5 | diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr index 03ce9757014..6fdd0adf4cf 100644 --- a/tests/ui/lint/unused/unused-attr-duplicate.stderr +++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr @@ -65,19 +65,6 @@ LL | #[should_panic] | ^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:60:1 - | -LL | #[must_use = "some message"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:59:1 - | -LL | #[must_use] - | ^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - error: unused attribute --> $DIR/unused-attr-duplicate.rs:66:1 | @@ -264,6 +251,19 @@ note: attribute also specified here LL | #[macro_export] | ^^^^^^^^^^^^^^^ +error: unused attribute + --> $DIR/unused-attr-duplicate.rs:60:1 + | +LL | #[must_use = "some message"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/unused-attr-duplicate.rs:59:1 + | +LL | #[must_use] + | ^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + error: unused attribute --> $DIR/unused-attr-duplicate.rs:74:1 | diff --git a/tests/ui/parser/bad-lit-suffixes.rs b/tests/ui/parser/bad-lit-suffixes.rs index f29dc53d322..4e8edf4d46e 100644 --- a/tests/ui/parser/bad-lit-suffixes.rs +++ b/tests/ui/parser/bad-lit-suffixes.rs @@ -33,7 +33,6 @@ fn f() {} #[must_use = "string"suffix] //~^ ERROR suffixes on string literals are invalid -//~| ERROR malformed `must_use` attribute input fn g() {} #[link(name = "string"suffix)] diff --git a/tests/ui/parser/bad-lit-suffixes.stderr b/tests/ui/parser/bad-lit-suffixes.stderr index 86ef35bf783..416143e496a 100644 --- a/tests/ui/parser/bad-lit-suffixes.stderr +++ b/tests/ui/parser/bad-lit-suffixes.stderr @@ -22,29 +22,14 @@ error: suffixes on string literals are invalid LL | #[must_use = "string"suffix] | ^^^^^^^^^^^^^^ invalid suffix `suffix` -error: malformed `must_use` attribute input - --> $DIR/bad-lit-suffixes.rs:34:1 - | -LL | #[must_use = "string"suffix] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the following are the possible correct uses - | -LL - #[must_use = "string"suffix] -LL + #[must_use = "reason"] - | -LL - #[must_use = "string"suffix] -LL + #[must_use] - | - error: suffixes on string literals are invalid - --> $DIR/bad-lit-suffixes.rs:39:15 + --> $DIR/bad-lit-suffixes.rs:38:15 | LL | #[link(name = "string"suffix)] | ^^^^^^^^^^^^^^ invalid suffix `suffix` error: invalid suffix `suffix` for number literal - --> $DIR/bad-lit-suffixes.rs:43:41 + --> $DIR/bad-lit-suffixes.rs:42:41 | LL | #[rustc_layout_scalar_valid_range_start(0suffix)] | ^^^^^^^ invalid suffix `suffix` @@ -165,5 +150,5 @@ LL | 1.0e10suffix; | = help: valid suffixes are `f32` and `f64` -error: aborting due to 21 previous errors; 2 warnings emitted +error: aborting due to 20 previous errors; 2 warnings emitted -- cgit 1.4.1-3-g733a5