diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 8458b53933537..5ad3ff71a6d4a 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -347,5 +347,13 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di "reduce the glob import's visibility or increase visibility of imported items", ); } + BuiltinLintDiag::MaybeTypo { span, name } => { + diag.span_suggestion_verbose( + span, + "an attribute with a similar name exists", + name, + Applicability::MachineApplicable, + ); + } } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ed165188787a5..e06e3e9b805f7 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -663,6 +663,10 @@ pub enum BuiltinLintDiag { span: Span, max_vis: String, }, + MaybeTypo { + span: Span, + name: Symbol, + }, } /// Lints that are buffered up early on in the `Session` before the diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 35bf3f761df39..e3cfe6a6e05a5 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -29,6 +29,7 @@ use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE}; use rustc_session::lint::builtin::{UNUSED_MACROS, UNUSED_MACRO_RULES}; use rustc_session::lint::BuiltinLintDiag; use rustc_session::parse::feature_err; +use rustc_span::edit_distance::edit_distance; use rustc_span::edition::Edition; use rustc_span::hygiene::{self, ExpnData, ExpnKind, LocalExpnId}; use rustc_span::hygiene::{AstPass, MacroKind}; @@ -568,15 +569,24 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) - && path.segments.len() >= 2 - && path.segments[0].ident.name == sym::diagnostic - && path.segments[1].ident.name != sym::on_unimplemented + && let [namespace, attribute, ..] = &*path.segments + && namespace.ident.name == sym::diagnostic + && attribute.ident.name != sym::on_unimplemented { - self.tcx.sess.psess.buffer_lint( + let distance = + edit_distance(attribute.ident.name.as_str(), sym::on_unimplemented.as_str(), 5); + + let help = if distance.is_some() { + BuiltinLintDiag::MaybeTypo { span: attribute.span(), name: sym::on_unimplemented } + } else { + BuiltinLintDiag::Normal + }; + self.tcx.sess.psess.buffer_lint_with_diagnostic( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - path.segments[1].span(), + attribute.span(), node_id, "unknown diagnostic attribute", + help, ); } diff --git a/tests/ui/diagnostic_namespace/suggest_typos.rs b/tests/ui/diagnostic_namespace/suggest_typos.rs new file mode 100644 index 0000000000000..b25f097a8adc1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/suggest_typos.rs @@ -0,0 +1,18 @@ +#![deny(unknown_or_malformed_diagnostic_attributes)] + +#[diagnostic::onunimplemented] +//~^ERROR unknown diagnostic attribute +//~^^HELP an attribute with a similar name exists +trait X{} + +#[diagnostic::un_onimplemented] +//~^ERROR unknown diagnostic attribute +//~^^HELP an attribute with a similar name exists +trait Y{} + +#[diagnostic::on_implemented] +//~^ERROR unknown diagnostic attribute +//~^^HELP an attribute with a similar name exists +trait Z{} + +fn main(){} diff --git a/tests/ui/diagnostic_namespace/suggest_typos.stderr b/tests/ui/diagnostic_namespace/suggest_typos.stderr new file mode 100644 index 0000000000000..307311258f274 --- /dev/null +++ b/tests/ui/diagnostic_namespace/suggest_typos.stderr @@ -0,0 +1,40 @@ +error: unknown diagnostic attribute + --> $DIR/suggest_typos.rs:3:15 + | +LL | #[diagnostic::onunimplemented] + | ^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/suggest_typos.rs:1:9 + | +LL | #![deny(unknown_or_malformed_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: an attribute with a similar name exists + | +LL | #[diagnostic::on_unimplemented] + | ~~~~~~~~~~~~~~~~ + +error: unknown diagnostic attribute + --> $DIR/suggest_typos.rs:8:15 + | +LL | #[diagnostic::un_onimplemented] + | ^^^^^^^^^^^^^^^^ + | +help: an attribute with a similar name exists + | +LL | #[diagnostic::on_unimplemented] + | ~~~~~~~~~~~~~~~~ + +error: unknown diagnostic attribute + --> $DIR/suggest_typos.rs:13:15 + | +LL | #[diagnostic::on_implemented] + | ^^^^^^^^^^^^^^ + | +help: an attribute with a similar name exists + | +LL | #[diagnostic::on_unimplemented] + | ~~~~~~~~~~~~~~~~ + +error: aborting due to 3 previous errors +