| use std::num::IntErrorKind; |
| |
| use rustc_ast as ast; |
| use rustc_errors::codes::*; |
| use rustc_errors::{ |
| Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, |
| }; |
| use rustc_feature::AttributeTemplate; |
| use rustc_hir::AttrPath; |
| use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; |
| use rustc_span::{Span, Symbol}; |
| |
| use crate::fluent_generated as fluent; |
| |
| pub(crate) enum UnsupportedLiteralReason { |
| Generic, |
| CfgString, |
| CfgBoolean, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_expected_one_cfg_pattern, code = E0536)] |
| pub(crate) struct ExpectedOneCfgPattern { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_predicate, code = E0537)] |
| pub(crate) struct InvalidPredicate { |
| #[primary_span] |
| pub span: Span, |
| |
| pub predicate: String, |
| } |
| |
| /// Error code: E0541 |
| pub(crate) struct UnknownMetaItem<'a> { |
| pub span: Span, |
| pub item: String, |
| pub expected: &'a [&'a str], |
| } |
| |
| // Manual implementation to be able to format `expected` items correctly. |
| impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> { |
| fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { |
| let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); |
| Diag::new(dcx, level, fluent::attr_parsing_unknown_meta_item) |
| .with_span(self.span) |
| .with_code(E0541) |
| .with_arg("item", self.item) |
| .with_arg("expected", expected.join(", ")) |
| .with_span_label(self.span, fluent::attr_parsing_label) |
| } |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_missing_since, code = E0542)] |
| pub(crate) struct MissingSince { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_missing_note, code = E0543)] |
| pub(crate) struct MissingNote { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_multiple_stability_levels, code = E0544)] |
| pub(crate) struct MultipleStabilityLevels { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_issue_string, code = E0545)] |
| pub(crate) struct InvalidIssueString { |
| #[primary_span] |
| pub span: Span, |
| |
| #[subdiagnostic] |
| pub cause: Option<InvalidIssueStringCause>, |
| } |
| |
| // The error kinds of `IntErrorKind` are duplicated here in order to allow the messages to be |
| // translatable. |
| #[derive(Subdiagnostic)] |
| pub(crate) enum InvalidIssueStringCause { |
| #[label(attr_parsing_must_not_be_zero)] |
| MustNotBeZero { |
| #[primary_span] |
| span: Span, |
| }, |
| |
| #[label(attr_parsing_empty)] |
| Empty { |
| #[primary_span] |
| span: Span, |
| }, |
| |
| #[label(attr_parsing_invalid_digit)] |
| InvalidDigit { |
| #[primary_span] |
| span: Span, |
| }, |
| |
| #[label(attr_parsing_pos_overflow)] |
| PosOverflow { |
| #[primary_span] |
| span: Span, |
| }, |
| |
| #[label(attr_parsing_neg_overflow)] |
| NegOverflow { |
| #[primary_span] |
| span: Span, |
| }, |
| } |
| |
| impl InvalidIssueStringCause { |
| pub(crate) fn from_int_error_kind(span: Span, kind: &IntErrorKind) -> Option<Self> { |
| match kind { |
| IntErrorKind::Empty => Some(Self::Empty { span }), |
| IntErrorKind::InvalidDigit => Some(Self::InvalidDigit { span }), |
| IntErrorKind::PosOverflow => Some(Self::PosOverflow { span }), |
| IntErrorKind::NegOverflow => Some(Self::NegOverflow { span }), |
| IntErrorKind::Zero => Some(Self::MustNotBeZero { span }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_missing_feature, code = E0546)] |
| pub(crate) struct MissingFeature { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_non_ident_feature, code = E0546)] |
| pub(crate) struct NonIdentFeature { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_missing_issue, code = E0547)] |
| pub(crate) struct MissingIssue { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? |
| // It is more similar to `IncorrectReprFormatGeneric`. |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] |
| pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { |
| #[primary_span] |
| pub span: Span, |
| } |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] |
| pub(crate) struct IncorrectReprFormatPackedExpectInteger { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] |
| pub(crate) struct InvalidReprHintNoParen { |
| #[primary_span] |
| pub span: Span, |
| |
| pub name: Symbol, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] |
| pub(crate) struct InvalidReprHintNoValue { |
| #[primary_span] |
| pub span: Span, |
| |
| pub name: Symbol, |
| } |
| |
| /// Error code: E0565 |
| // FIXME(jdonszelmann): slowly phased out |
| pub(crate) struct UnsupportedLiteral { |
| pub span: Span, |
| pub reason: UnsupportedLiteralReason, |
| pub is_bytestr: bool, |
| pub start_point_span: Span, |
| } |
| |
| impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral { |
| fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { |
| let mut diag = Diag::new( |
| dcx, |
| level, |
| match self.reason { |
| UnsupportedLiteralReason::Generic => { |
| fluent::attr_parsing_unsupported_literal_generic |
| } |
| UnsupportedLiteralReason::CfgString => { |
| fluent::attr_parsing_unsupported_literal_cfg_string |
| } |
| UnsupportedLiteralReason::CfgBoolean => { |
| fluent::attr_parsing_unsupported_literal_cfg_boolean |
| } |
| }, |
| ); |
| diag.span(self.span); |
| diag.code(E0565); |
| if self.is_bytestr { |
| diag.span_suggestion( |
| self.start_point_span, |
| fluent::attr_parsing_unsupported_literal_suggestion, |
| "", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| diag |
| } |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] |
| pub(crate) struct InvalidReprAlignNeedArg { |
| #[primary_span] |
| #[suggestion(code = "align(...)", applicability = "has-placeholders")] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_repr_generic, code = E0589)] |
| pub(crate) struct InvalidReprGeneric<'a> { |
| #[primary_span] |
| pub span: Span, |
| |
| pub repr_arg: String, |
| pub error_part: &'a str, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] |
| pub(crate) struct IncorrectReprFormatAlignOneArg { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] |
| pub(crate) struct IncorrectReprFormatExpectInteger { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] |
| pub(crate) struct IncorrectReprFormatGeneric { |
| #[primary_span] |
| pub span: Span, |
| |
| pub repr_arg: Symbol, |
| |
| #[subdiagnostic] |
| pub cause: Option<IncorrectReprFormatGenericCause>, |
| } |
| |
| #[derive(Subdiagnostic)] |
| pub(crate) enum IncorrectReprFormatGenericCause { |
| #[suggestion( |
| attr_parsing_suggestion, |
| code = "{name}({value})", |
| applicability = "machine-applicable" |
| )] |
| Int { |
| #[primary_span] |
| span: Span, |
| |
| #[skip_arg] |
| name: Symbol, |
| |
| #[skip_arg] |
| value: u128, |
| }, |
| |
| #[suggestion( |
| attr_parsing_suggestion, |
| code = "{name}({value})", |
| applicability = "machine-applicable" |
| )] |
| Symbol { |
| #[primary_span] |
| span: Span, |
| |
| #[skip_arg] |
| name: Symbol, |
| |
| #[skip_arg] |
| value: Symbol, |
| }, |
| } |
| |
| impl IncorrectReprFormatGenericCause { |
| pub(crate) fn from_lit_kind(span: Span, kind: &ast::LitKind, name: Symbol) -> Option<Self> { |
| match *kind { |
| ast::LitKind::Int(value, ast::LitIntType::Unsuffixed) => { |
| Some(Self::Int { span, name, value: value.get() }) |
| } |
| ast::LitKind::Str(value, _) => Some(Self::Symbol { span, name, value }), |
| _ => None, |
| } |
| } |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] |
| pub(crate) struct RustcPromotablePairing { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] |
| pub(crate) struct RustcAllowedUnstablePairing { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_cfg_predicate_identifier)] |
| pub(crate) struct CfgPredicateIdentifier { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_deprecated_item_suggestion)] |
| pub(crate) struct DeprecatedItemSuggestion { |
| #[primary_span] |
| pub span: Span, |
| |
| #[help] |
| pub is_nightly: bool, |
| |
| #[note] |
| pub details: (), |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_expected_single_version_literal)] |
| pub(crate) struct ExpectedSingleVersionLiteral { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_expected_version_literal)] |
| pub(crate) struct ExpectedVersionLiteral { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_expects_feature_list)] |
| pub(crate) struct ExpectsFeatureList { |
| #[primary_span] |
| pub span: Span, |
| |
| pub name: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_expects_features)] |
| pub(crate) struct ExpectsFeatures { |
| #[primary_span] |
| pub span: Span, |
| |
| pub name: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_invalid_since)] |
| pub(crate) struct InvalidSince { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_soft_no_args)] |
| pub(crate) struct SoftNoArgs { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_unknown_version_literal)] |
| pub(crate) struct UnknownVersionLiteral { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| // FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_unused_multiple)] |
| pub(crate) struct UnusedMultiple { |
| #[primary_span] |
| #[suggestion(code = "", applicability = "machine-applicable")] |
| pub this: Span, |
| #[note] |
| pub other: Span, |
| pub name: Symbol, |
| } |
| |
| #[derive(LintDiagnostic)] |
| #[diag(attr_parsing_unused_duplicate)] |
| pub(crate) struct UnusedDuplicate { |
| #[suggestion(code = "", applicability = "machine-applicable")] |
| pub this: Span, |
| #[note] |
| pub other: Span, |
| #[warning] |
| pub warning: bool, |
| } |
| |
| // FIXME(jdonszelmann): duplicated in rustc_lints, should be moved here completely. |
| #[derive(LintDiagnostic)] |
| #[diag(attr_parsing_ill_formed_attribute_input)] |
| pub(crate) struct IllFormedAttributeInput { |
| pub num_suggestions: usize, |
| pub suggestions: DiagArgValue, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_ill_formed_attribute_input)] |
| pub(crate) struct IllFormedAttributeInputLint { |
| #[primary_span] |
| pub span: Span, |
| pub num_suggestions: usize, |
| pub suggestions: DiagArgValue, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_null_on_export, code = E0648)] |
| pub(crate) struct NullOnExport { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_null_on_link_section, code = E0648)] |
| pub(crate) struct NullOnLinkSection { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_stability_outside_std, code = E0734)] |
| pub(crate) struct StabilityOutsideStd { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_empty_confusables)] |
| pub(crate) struct EmptyConfusables { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(LintDiagnostic)] |
| #[diag(attr_parsing_empty_attribute)] |
| pub(crate) struct EmptyAttributeList { |
| #[suggestion(code = "", applicability = "machine-applicable")] |
| pub attr_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 { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_unrecognized_repr_hint, code = E0552)] |
| #[help] |
| pub(crate) struct UnrecognizedReprHint { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] |
| #[help] |
| pub(crate) struct UnstableFeatureBoundIncompatibleStability { |
| #[primary_span] |
| pub span: Span, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] |
| pub(crate) struct NakedFunctionIncompatibleAttribute { |
| #[primary_span] |
| #[label] |
| pub span: Span, |
| #[label(attr_parsing_naked_attribute)] |
| pub naked_span: Span, |
| pub attr: String, |
| } |
| |
| #[derive(Diagnostic)] |
| #[diag(attr_parsing_link_ordinal_out_of_range)] |
| #[note] |
| pub(crate) struct LinkOrdinalOutOfRange { |
| #[primary_span] |
| pub span: Span, |
| pub ordinal: u128, |
| } |
| |
| pub(crate) enum AttributeParseErrorReason { |
| ExpectedNoArgs, |
| ExpectedStringLiteral { |
| byte_string: Option<Span>, |
| }, |
| ExpectedIntegerLiteral, |
| ExpectedAtLeastOneArgument, |
| ExpectedSingleArgument, |
| ExpectedList, |
| UnexpectedLiteral, |
| ExpectedNameValue(Option<Symbol>), |
| DuplicateKey(Symbol), |
| ExpectedSpecificArgument { |
| possibilities: Vec<&'static str>, |
| strings: bool, |
| /// Should we tell the user to write a list when they didn't? |
| list: bool, |
| }, |
| ExpectedIdentifier, |
| } |
| |
| pub(crate) struct AttributeParseError { |
| pub(crate) span: Span, |
| pub(crate) attr_span: Span, |
| pub(crate) template: AttributeTemplate, |
| pub(crate) attribute: AttrPath, |
| pub(crate) reason: AttributeParseErrorReason, |
| } |
| |
| impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError { |
| fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { |
| let name = self.attribute.to_string(); |
| |
| let mut diag = Diag::new(dcx, level, format!("malformed `{name}` attribute input")); |
| diag.span(self.attr_span); |
| diag.code(E0539); |
| match self.reason { |
| 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::ExpectedIntegerLiteral => { |
| diag.span_label(self.span, "expected an integer literal here"); |
| } |
| AttributeParseErrorReason::ExpectedSingleArgument => { |
| diag.span_label(self.span, "expected a single argument here"); |
| diag.code(E0805); |
| } |
| AttributeParseErrorReason::ExpectedAtLeastOneArgument => { |
| diag.span_label(self.span, "expected at least 1 argument here"); |
| } |
| AttributeParseErrorReason::ExpectedList => { |
| diag.span_label(self.span, "expected this to be a list"); |
| } |
| AttributeParseErrorReason::DuplicateKey(key) => { |
| 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, "didn't expect a literal here"); |
| diag.code(E0565); |
| } |
| AttributeParseErrorReason::ExpectedNoArgs => { |
| diag.span_label(self.span, "didn't expect any arguments here"); |
| diag.code(E0565); |
| } |
| AttributeParseErrorReason::ExpectedNameValue(None) => { |
| // If the span is the entire attribute, the suggestion we add below this match already contains enough information |
| if self.span != self.attr_span { |
| diag.span_label( |
| self.span, |
| format!("expected this to be of the form `... = \"...\"`"), |
| ); |
| } |
| } |
| AttributeParseErrorReason::ExpectedNameValue(Some(name)) => { |
| diag.span_label( |
| self.span, |
| format!("expected this to be of the form `{name} = \"...\"`"), |
| ); |
| } |
| AttributeParseErrorReason::ExpectedSpecificArgument { |
| possibilities, |
| strings, |
| list: false, |
| } => { |
| let quote = if strings { '"' } else { '`' }; |
| match possibilities.as_slice() { |
| &[] => {} |
| &[x] => { |
| diag.span_label( |
| self.span, |
| format!("the only valid argument here is {quote}{x}{quote}"), |
| ); |
| } |
| [first, second] => { |
| 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(); |
| for i in first { |
| res.push_str(&format!("{quote}{i}{quote}, ")); |
| } |
| res.push_str(&format!( |
| "{quote}{second_to_last}{quote} or {quote}{last}{quote}" |
| )); |
| |
| diag.span_label(self.span, format!("valid arguments are {res}")); |
| } |
| } |
| } |
| AttributeParseErrorReason::ExpectedSpecificArgument { |
| possibilities, |
| strings, |
| list: true, |
| } => { |
| let quote = if strings { '"' } else { '`' }; |
| match possibilities.as_slice() { |
| &[] => {} |
| &[x] => { |
| diag.span_label( |
| self.span, |
| format!( |
| "this attribute is only valid with {quote}{x}{quote} as an argument" |
| ), |
| ); |
| } |
| [first, second] => { |
| diag.span_label(self.span, format!("this attribute is only valid with either {quote}{first}{quote} or {quote}{second}{quote} as an argument")); |
| } |
| [first @ .., second_to_last, last] => { |
| let mut res = String::new(); |
| for i in first { |
| res.push_str(&format!("{quote}{i}{quote}, ")); |
| } |
| res.push_str(&format!( |
| "{quote}{second_to_last}{quote} or {quote}{last}{quote}" |
| )); |
| |
| diag.span_label(self.span, format!("this attribute is only valid with one of the following arguments: {res}")); |
| } |
| } |
| } |
| AttributeParseErrorReason::ExpectedIdentifier => { |
| diag.span_label(self.span, "expected a valid identifier here"); |
| } |
| } |
| |
| let suggestions = self.template.suggestions(false, &name); |
| diag.span_suggestions( |
| self.attr_span, |
| if suggestions.len() == 1 { |
| "must be of the form" |
| } else { |
| "try changing it to one of the following valid forms of the attribute" |
| }, |
| suggestions, |
| Applicability::HasPlaceholders, |
| ); |
| |
| diag |
| } |
| } |