From 85e24b0d3625f6ae16f61a7e352384b8419401b5 Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Fri, 28 Nov 2025 19:45:07 +0100 Subject: [PATCH] Make the capitalization explicit on keyword misspell error --- compiler/rustc_errors/src/emitter.rs | 2 ++ compiler/rustc_parse/messages.ftl | 2 +- compiler/rustc_parse/src/errors.rs | 23 ++++++++++++++-- compiler/rustc_parse/src/parser/mod.rs | 15 ++++++++++- tests/ui/parser/item-kw-case-mismatch.stderr | 28 ++++++++++---------- 5 files changed, 52 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d9132ca123490..d08d5a5a1ea29 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -3544,6 +3544,8 @@ pub fn detect_confusion_type(sm: &SourceMap, suggested: &str, sp: Span) -> Confu let mut has_digit_letter_confusable = false; let mut has_other_diff = false; + // Letters whose lowercase version is very similar to the uppercase + // version. let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z']; let digit_letter_confusables = [('0', 'O'), ('1', 'l'), ('5', 'S'), ('8', 'B'), ('9', 'g')]; diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 7055e60956a9c..bc81acefef743 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -512,7 +512,7 @@ parse_keyword_lifetime = lifetimes cannot use keyword names parse_kw_bad_case = keyword `{$kw}` is written in the wrong case - .suggestion = write it in the correct case + .suggestion = write it in {$case} parse_label_inner_attr_does_not_annotate_this = the inner attribute doesn't annotate this {$item} parse_label_unexpected_token = unexpected token diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 62a333fbf81d7..9344848c76531 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1,14 +1,15 @@ // ignore-tidy-filelength use std::borrow::Cow; +use std::path::PathBuf; use rustc_ast::token::Token; use rustc_ast::util::parser::ExprPrecedence; use rustc_ast::{Path, Visibility}; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic, - SuggestionStyle, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, + Level, Subdiagnostic, SuggestionStyle, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; @@ -3335,6 +3336,24 @@ pub(crate) struct KwBadCase<'a> { #[suggestion(code = "{kw}", style = "verbose", applicability = "machine-applicable")] pub span: Span, pub kw: &'a str, + pub case: Case, +} + +pub(crate) enum Case { + Upper, + Lower, + Mixed, +} + +impl IntoDiagArg for Case { + fn into_diag_arg(self, path: &mut Option) -> DiagArgValue { + match self { + Case::Upper => "uppercase", + Case::Lower => "lowercase", + Case::Mixed => "the correct case", + } + .into_diag_arg(path) + } } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 14a738fb9d247..8577ea40589a8 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -606,7 +606,20 @@ impl<'a> Parser<'a> { // Do an ASCII case-insensitive match, because all keywords are ASCII. && ident.as_str().eq_ignore_ascii_case(exp.kw.as_str()) { - self.dcx().emit_err(errors::KwBadCase { span: ident.span, kw: exp.kw.as_str() }); + let kw = exp.kw.as_str(); + let is_upper = kw.chars().all(char::is_uppercase); + let is_lower = kw.chars().all(char::is_lowercase); + + let case = match (is_upper, is_lower) { + (true, true) => { + unreachable!("keyword that is both fully upper- and fully lowercase") + } + (true, false) => errors::Case::Upper, + (false, true) => errors::Case::Lower, + (false, false) => errors::Case::Mixed, + }; + + self.dcx().emit_err(errors::KwBadCase { span: ident.span, kw, case }); self.bump(); true } else { diff --git a/tests/ui/parser/item-kw-case-mismatch.stderr b/tests/ui/parser/item-kw-case-mismatch.stderr index d2a1eb7f2f521..55cbc6be943cc 100644 --- a/tests/ui/parser/item-kw-case-mismatch.stderr +++ b/tests/ui/parser/item-kw-case-mismatch.stderr @@ -4,7 +4,7 @@ error: keyword `use` is written in the wrong case LL | Use std::ptr::read; | ^^^ | -help: write it in the correct case (notice the capitalization) +help: write it in lowercase (notice the capitalization) | LL - Use std::ptr::read; LL + use std::ptr::read; @@ -16,7 +16,7 @@ error: keyword `use` is written in the wrong case LL | USE std::ptr::write; | ^^^ | -help: write it in the correct case +help: write it in lowercase | LL - USE std::ptr::write; LL + use std::ptr::write; @@ -28,7 +28,7 @@ error: keyword `fn` is written in the wrong case LL | async Fn _a() {} | ^^ | -help: write it in the correct case (notice the capitalization) +help: write it in lowercase (notice the capitalization) | LL - async Fn _a() {} LL + async fn _a() {} @@ -40,7 +40,7 @@ error: keyword `fn` is written in the wrong case LL | Fn _b() {} | ^^ | -help: write it in the correct case (notice the capitalization) +help: write it in lowercase (notice the capitalization) | LL - Fn _b() {} LL + fn _b() {} @@ -52,7 +52,7 @@ error: keyword `async` is written in the wrong case LL | aSYNC fN _c() {} | ^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - aSYNC fN _c() {} LL + async fN _c() {} @@ -64,7 +64,7 @@ error: keyword `fn` is written in the wrong case LL | aSYNC fN _c() {} | ^^ | -help: write it in the correct case +help: write it in lowercase | LL - aSYNC fN _c() {} LL + aSYNC fn _c() {} @@ -76,7 +76,7 @@ error: keyword `async` is written in the wrong case LL | Async fn _d() {} | ^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - Async fn _d() {} LL + async fn _d() {} @@ -88,7 +88,7 @@ error: keyword `const` is written in the wrong case LL | CONST UNSAFE FN _e() {} | ^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - CONST UNSAFE FN _e() {} LL + const UNSAFE FN _e() {} @@ -100,7 +100,7 @@ error: keyword `unsafe` is written in the wrong case LL | CONST UNSAFE FN _e() {} | ^^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - CONST UNSAFE FN _e() {} LL + CONST unsafe FN _e() {} @@ -112,7 +112,7 @@ error: keyword `fn` is written in the wrong case LL | CONST UNSAFE FN _e() {} | ^^ | -help: write it in the correct case +help: write it in lowercase | LL - CONST UNSAFE FN _e() {} LL + CONST UNSAFE fn _e() {} @@ -124,7 +124,7 @@ error: keyword `unsafe` is written in the wrong case LL | unSAFE EXTern "C" fn _f() {} | ^^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - unSAFE EXTern "C" fn _f() {} LL + unsafe EXTern "C" fn _f() {} @@ -136,7 +136,7 @@ error: keyword `extern` is written in the wrong case LL | unSAFE EXTern "C" fn _f() {} | ^^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - unSAFE EXTern "C" fn _f() {} LL + unSAFE extern "C" fn _f() {} @@ -148,7 +148,7 @@ error: keyword `extern` is written in the wrong case LL | EXTERN "C" FN _g() {} | ^^^^^^ | -help: write it in the correct case +help: write it in lowercase | LL - EXTERN "C" FN _g() {} LL + extern "C" FN _g() {} @@ -160,7 +160,7 @@ error: keyword `fn` is written in the wrong case LL | EXTERN "C" FN _g() {} | ^^ | -help: write it in the correct case +help: write it in lowercase | LL - EXTERN "C" FN _g() {} LL + EXTERN "C" fn _g() {}