diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index ca4e556dcdb80..eed729a1777fc 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -24,8 +24,9 @@ pub(crate) struct CrateNameInvalid<'a> { pub struct FerrisIdentifier { #[primary_span] pub spans: Vec, - #[suggestion(code = "ferris", applicability = "maybe-incorrect")] + #[suggestion(code = "{ferris_fix}", applicability = "maybe-incorrect")] pub first_span: Span, + pub ferris_fix: &'static str, } #[derive(Diagnostic)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d70d9d344b94c..524b0ab235031 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -302,7 +302,57 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { spans.sort(); if ident == sym::ferris { let first_span = spans[0]; - sess.dcx().emit_err(errors::FerrisIdentifier { spans, first_span }); + + enum FerrisFix { + SnakeCase, + ScreamingSnakeCase, + PascalCase, + } + + impl FerrisFix { + const fn as_str(self) -> &'static str { + match self { + FerrisFix::SnakeCase => "ferris", + FerrisFix::ScreamingSnakeCase => "FERRIS", + FerrisFix::PascalCase => "Ferris", + } + } + } + + // Obtain the symbol that is before Ferris. + let symbols = &sess.psess.symbol_gallery.symbols.lock(); + let mut symbols: Vec<_> = symbols.iter().collect(); + + // sort by spans + symbols.sort_unstable_by_key(|k| k.1); + let ferris_fix = match symbols.binary_search_by(|(_, span)| span.cmp(&&first_span)) + { + Ok(index) | Err(index) if index > 0 => { + let keyword = symbols[index - 1].0.as_str(); + // if we have e.g. `static mut` + if keyword == "mut" { + let keyword_before = + symbols.get(index - 2).map(|s| s.0.as_str()).unwrap_or_default(); + if keyword_before == "static" { + FerrisFix::ScreamingSnakeCase + } else { + FerrisFix::SnakeCase + } + } else { + match keyword { + "const" | "static" => FerrisFix::ScreamingSnakeCase, + "struct" | "trait" | "mod" | "union" | "type" | "enum" => { + FerrisFix::PascalCase + } + _ => FerrisFix::SnakeCase, + } + } + } + _ => FerrisFix::SnakeCase, + } + .as_str(); + + sess.dcx().emit_err(errors::FerrisIdentifier { spans, first_span, ferris_fix }); } else { sess.dcx().emit_err(errors::EmojiIdentifier { spans, ident }); } diff --git a/test.rs b/test.rs new file mode 100644 index 0000000000000..66afef3d14b4b --- /dev/null +++ b/test.rs @@ -0,0 +1,5 @@ +struct 🦀 {} + +fn main() { + let a = 4; +} diff --git a/tests/ui/parser/ferris-static-mut.rs b/tests/ui/parser/ferris-static-mut.rs new file mode 100644 index 0000000000000..67186982691e0 --- /dev/null +++ b/tests/ui/parser/ferris-static-mut.rs @@ -0,0 +1,3 @@ +static mut 🦀: &str = "ferris!";//~ ERROR Ferris cannot be used as an identifier + +fn main() {} diff --git a/tests/ui/parser/ferris-static-mut.stderr b/tests/ui/parser/ferris-static-mut.stderr new file mode 100644 index 0000000000000..6af85bbaf7dad --- /dev/null +++ b/tests/ui/parser/ferris-static-mut.stderr @@ -0,0 +1,8 @@ +error: Ferris cannot be used as an identifier + --> $DIR/ferris-static-mut.rs:1:12 + | +LL | static mut 🦀: &str = "ferris!"; + | ^^ help: try using their name instead: `FERRIS` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/ferris-struct.rs b/tests/ui/parser/ferris-struct.rs new file mode 100644 index 0000000000000..581957fa7757e --- /dev/null +++ b/tests/ui/parser/ferris-struct.rs @@ -0,0 +1,3 @@ +struct 🦀 {}//~ ERROR Ferris cannot be used as an identifier + +fn main() {} diff --git a/tests/ui/parser/ferris-struct.stderr b/tests/ui/parser/ferris-struct.stderr new file mode 100644 index 0000000000000..34fe28c62975c --- /dev/null +++ b/tests/ui/parser/ferris-struct.stderr @@ -0,0 +1,8 @@ +error: Ferris cannot be used as an identifier + --> $DIR/ferris-struct.rs:1:8 + | +LL | struct 🦀 {} + | ^^ help: try using their name instead: `Ferris` + +error: aborting due to 1 previous error +