Refactor diagnostics in handle_errors function

This commit is contained in:
Hampus Lidin 2022-08-21 08:46:05 +02:00
parent e0dc8d7801
commit 0005f628f0
5 changed files with 254 additions and 31 deletions

View File

@ -14,6 +14,8 @@ use rustc_span::hygiene::Transparency;
use rustc_span::{symbol::sym, symbol::Symbol, Span};
use std::num::NonZeroU32;
use crate::session_diagnostics;
pub fn is_builtin_attr(attr: &Attribute) -> bool {
attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
}
@ -25,46 +27,38 @@ enum AttrError {
NonIdentFeature,
MissingFeature,
MultipleStabilityLevels,
UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
UnsupportedLiteral(UnsupportedLiteralReason, /* is_bytestr */ bool),
}
pub(crate) enum UnsupportedLiteralReason {
Generic,
CfgString,
DeprecatedString,
DeprecatedKvPair,
}
fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
let diag = &sess.span_diagnostic;
match error {
AttrError::MultipleItem(item) => {
struct_span_err!(diag, span, E0538, "multiple '{}' items", item).emit();
sess.emit_err(session_diagnostics::MultipleItem { span, item });
}
AttrError::UnknownMetaItem(item, expected) => {
let expected = expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
.span_label(span, format!("expected one of {}", expected.join(", ")))
.emit();
sess.emit_err(session_diagnostics::UnknownMetaItem { span, item, expected });
}
AttrError::MissingSince => {
struct_span_err!(diag, span, E0542, "missing 'since'").emit();
sess.emit_err(session_diagnostics::MissingSince { span });
}
AttrError::NonIdentFeature => {
struct_span_err!(diag, span, E0546, "'feature' is not an identifier").emit();
sess.emit_err(session_diagnostics::NonIdentFeature { span });
}
AttrError::MissingFeature => {
struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
sess.emit_err(session_diagnostics::MissingFeature { span });
}
AttrError::MultipleStabilityLevels => {
struct_span_err!(diag, span, E0544, "multiple stability levels").emit();
sess.emit_err(session_diagnostics::MultipleStabilityLevels { span });
}
AttrError::UnsupportedLiteral(msg, is_bytestr) => {
let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
if is_bytestr {
if let Ok(lint_str) = sess.source_map().span_to_snippet(span) {
err.span_suggestion(
span,
"consider removing the prefix",
&lint_str[1..],
Applicability::MaybeIncorrect,
);
}
}
err.emit();
AttrError::UnsupportedLiteral(reason, is_bytestr) => {
sess.emit_err(session_diagnostics::UnsupportedLiteral { span, reason, is_bytestr });
}
}
}
@ -326,7 +320,7 @@ where
handle_errors(
&sess.parse_sess,
meta.span(),
AttrError::UnsupportedLiteral("unsupported literal", false),
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
);
continue 'outer;
};
@ -494,7 +488,10 @@ where
handle_errors(
&sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral("unsupported literal", false),
AttrError::UnsupportedLiteral(
UnsupportedLiteralReason::Generic,
false,
),
);
continue 'outer;
}
@ -711,7 +708,7 @@ pub fn eval_condition(
handle_errors(
sess,
mi.span(),
AttrError::UnsupportedLiteral("unsupported literal", false),
AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
);
return false;
}
@ -790,7 +787,7 @@ pub fn eval_condition(
sess,
lit.span,
AttrError::UnsupportedLiteral(
"literal in `cfg` predicate value must be a string",
UnsupportedLiteralReason::CfgString,
lit.kind.is_bytestr(),
),
);
@ -870,8 +867,7 @@ where
&sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral(
"literal in `deprecated` \
value must be a string",
UnsupportedLiteralReason::DeprecatedString,
lit.kind.is_bytestr(),
),
);
@ -934,7 +930,7 @@ where
&sess.parse_sess,
lit.span,
AttrError::UnsupportedLiteral(
"item in `deprecated` must be a key/value pair",
UnsupportedLiteralReason::DeprecatedKvPair,
false,
),
);

View File

@ -10,6 +10,7 @@
extern crate rustc_macros;
mod builtin;
mod session_diagnostics;
pub use builtin::*;
pub use IntType::*;

View File

@ -0,0 +1,196 @@
use std::num::IntErrorKind;
use rustc_errors::{error_code, fluent, Applicability, DiagnosticBuilder, ErrorGuaranteed};
use rustc_macros::SessionDiagnostic;
use rustc_session::{parse::ParseSess, SessionDiagnostic};
use rustc_span::Span;
use crate::UnsupportedLiteralReason;
#[derive(SessionDiagnostic)]
#[error(attr::multiple_item, code = "E0538")]
pub(crate) struct MultipleItem {
#[primary_span]
pub span: Span,
pub item: String,
}
#[derive(SessionDiagnostic)]
#[error(attr::missing_since, code = "E0542")]
pub(crate) struct MissingSince {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::non_ident_feature, code = "E0546")]
pub(crate) struct NonIdentFeature {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::missing_feature, code = "E0546")]
pub(crate) struct MissingFeature {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::multiple_stability_levels, code = "E0544")]
pub(crate) struct MultipleStabilityLevels {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::invalid_meta_item, code = "E0539")]
pub(crate) struct InvalidMetaItem {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::missing_issue, code = "E0547")]
pub(crate) struct MissingIssue {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::rustc_promotable_pairing, code = "E0717")]
pub(crate) struct RustcPromotablePairing {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::rustc_allowed_unstable_pairing, code = "E0789")]
pub(crate) struct RustcAllowedUnstablePairing {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::soft_no_args)]
pub(crate) struct SoftNoArgs {
#[primary_span]
pub span: Span,
}
#[derive(SessionDiagnostic)]
#[error(attr::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(SessionSubdiagnostic)]
pub(crate) enum InvalidIssueStringCause {
#[label(attr::must_not_be_zero)]
MustNotBeZero {
#[primary_span]
span: Span,
},
#[label(attr::empty)]
Empty {
#[primary_span]
span: Span,
},
#[label(attr::invalid_digit)]
InvalidDigit {
#[primary_span]
span: Span,
},
#[label(attr::pos_overflow)]
PosOverflow {
#[primary_span]
span: Span,
},
#[label(attr::neg_overflow)]
NegOverflow {
#[primary_span]
span: Span,
},
}
impl InvalidIssueStringCause {
pub 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,
}
}
}
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> SessionDiagnostic<'a> for UnknownMetaItem<'_> {
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let expected = self.expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
let mut diag = sess.span_diagnostic.struct_span_err_with_code(
self.span,
fluent::attr::unknown_meta_item,
error_code!(E0541),
);
diag.set_arg("item", self.item);
diag.set_arg("expected", expected.join(", "));
diag.span_label(self.span, fluent::attr::label);
diag
}
}
pub(crate) struct UnsupportedLiteral {
pub span: Span,
pub reason: UnsupportedLiteralReason,
pub is_bytestr: bool,
}
impl<'a> SessionDiagnostic<'a> for UnsupportedLiteral {
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut diag = sess.span_diagnostic.struct_span_err_with_code(
self.span,
match self.reason {
UnsupportedLiteralReason::Generic => fluent::attr::unsupported_literal_generic,
UnsupportedLiteralReason::CfgString => fluent::attr::unsupported_literal_cfg_string,
UnsupportedLiteralReason::DeprecatedString => {
fluent::attr::unsupported_literal_deprecated_string
}
UnsupportedLiteralReason::DeprecatedKvPair => {
fluent::attr::unsupported_literal_deprecated_kv_pair
}
},
error_code!(E0565),
);
if self.is_bytestr {
if let Ok(lint_str) = sess.source_map().span_to_snippet(self.span) {
diag.span_suggestion(
self.span,
fluent::attr::unsupported_literal_suggestion,
&lint_str[1..],
Applicability::MaybeIncorrect,
);
}
}
diag
}
}

View File

@ -0,0 +1,29 @@
attr_multiple_item =
multiple '{$item}' items
attr_unknown_meta_item =
unknown meta item '{$item}'
.label = expected one of {$expected}
attr_missing_since =
missing 'since'
attr_non_ident_feature =
'feature' is not an identifier
attr_missing_feature =
missing 'feature'
attr_multiple_stability_levels =
multiple stability levels
attr_unsupported_literal_generic =
unsupported literal
attr_unsupported_literal_cfg_string =
literal in `cfg` predicate value must be a string
attr_unsupported_literal_deprecated_string =
literal in `deprecated` value must be a string
attr_unsupported_literal_deprecated_kv_pair =
item in `deprecated` must be a key/value pair
attr_unsupported_literal_suggestion =
consider removing the prefix

View File

@ -33,6 +33,7 @@ pub use unic_langid::{langid, LanguageIdentifier};
// Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
fluent_messages! {
ast_passes => "../locales/en-US/ast_passes.ftl",
attr => "../locales/en-US/attr.ftl",
borrowck => "../locales/en-US/borrowck.ftl",
builtin_macros => "../locales/en-US/builtin_macros.ftl",
const_eval => "../locales/en-US/const_eval.ftl",