|
commit c158687905e707104dbb5c0350426165d0d33f7b |
|
Author: Dmytro Sirenko <me@dmytrish.net> |
|
Date: Thu Jul 30 15:00:22 2026 +0200 |
|
|
|
Add lighthearted "fell asleep on keyboard" diagnostic |
|
|
|
When an unresolved identifier, path, method, or field ends with more than |
|
seven repetitions of the same letter (a keyboard mash such as |
|
`prehopjjkpppppppp`), emit a humorous error suggesting the author dozed off, |
|
in place of the usual typo/import/method suggestions. |
|
|
|
- rustc_span: add shared `Symbol::looks_like_keyboard_mash` helper. |
|
- rustc_resolve: override E0425 message/label and short-circuit suggestions. |
|
- rustc_hir_typeck: emit clean E0599 (method) and E0609 (field) variants. |
|
|
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
|
Claude-Session: https://claude.ai/code/session_01NFioFszpMzmNAiWF4AD8K8 |
|
|
|
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs |
|
index f1d9100a1d5..5a822500980 100644 |
|
--- a/compiler/rustc_hir_typeck/src/expr.rs |
|
+++ b/compiler/rustc_hir_typeck/src/expr.rs |
|
@@ -2963,6 +2963,20 @@ fn ban_nonexisting_field( |
|
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", |
|
ident, base, expr, base_ty |
|
); |
|
+ // Easter egg: if the field looks like a keyboard mash (a long trailing run of |
|
+ // the same letter, e.g. `prehopjjkpppppppp`), assume the author dozed off. |
|
+ if ident.name.looks_like_keyboard_mash() { |
|
+ let mut err = struct_span_code_err!( |
|
+ self.dcx(), |
|
+ ident.span, |
|
+ E0609, |
|
+ "it seems like you've fallen asleep and hit your keyboard" |
|
+ ); |
|
+ err.span_label(ident.span, "you fell asleep here"); |
|
+ err.help("try improving your sleep schedule or reducing your hours"); |
|
+ return err.emit(); |
|
+ } |
|
+ |
|
let mut err = self.no_such_field_err(ident, base_ty, expr); |
|
|
|
match *base_ty.peel_refs().kind() { |
|
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs |
|
index c9ec32159f4..e373c59d793 100644 |
|
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs |
|
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs |
|
@@ -524,6 +524,22 @@ fn create_no_assoc_err( |
|
sugg_span: Span, |
|
unsatisfied_predicates: &UnsatisfiedPredicates<'tcx>, |
|
) -> Diag<'_> { |
|
+ // Easter egg: if the method/associated item looks like a keyboard mash (a long |
|
+ // trailing run of the same letter, e.g. `prehopjjkpppppppp`), assume the author |
|
+ // dozed off mid-expression and emit a lighthearted error instead of the usual |
|
+ // (here irrelevant) suggestions. |
|
+ if item_ident.name.looks_like_keyboard_mash() { |
|
+ let mut err = struct_span_code_err!( |
|
+ self.dcx(), |
|
+ item_ident.span, |
|
+ E0599, |
|
+ "it seems like you've fallen asleep and hit your keyboard" |
|
+ ); |
|
+ err.span_label(item_ident.span, "you fell asleep here"); |
|
+ err.help("try improving your sleep schedule or reducing your hours"); |
|
+ return err; |
|
+ } |
|
+ |
|
// Don't show expanded generic arguments when the method can't be found in any |
|
// implementation (#81576). |
|
let mut ty = rcvr_ty; |
|
@@ -1284,6 +1300,14 @@ fn report_no_match_method_error( |
|
unsatisfied_predicates, |
|
) |
|
}; |
|
+ |
|
+ // Easter egg: for a keyboard-mash method/associated item, `create_no_assoc_err` |
|
+ // has already produced the lighthearted error; emit it as-is and skip the usual |
|
+ // labels and suggestions, which would just be noise here. |
|
+ if item_ident.name.looks_like_keyboard_mash() { |
|
+ return err.emit(); |
|
+ } |
|
+ |
|
if let SelfSource::MethodCall(rcvr_expr) = source { |
|
self.err_ctxt().note_field_shadowed_by_private_candidate( |
|
&mut err, |
|
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs |
|
index 873aad38a78..8931d80ec22 100644 |
|
--- a/compiler/rustc_resolve/src/late/diagnostics.rs |
|
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs |
|
@@ -569,8 +569,17 @@ fn make_base_error( |
|
(format!("not found in {mod_str}"), override_suggestion) |
|
}; |
|
|
|
+ let mut msg = format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"); |
|
+ // Easter egg: if the unresolved identifier looks like a keyboard mash |
|
+ // (a long trailing run of the same letter, e.g. `prehopjjkpppppppp`), |
|
+ // assume the author dozed off mid-expression. |
|
+ if item_str.name.looks_like_keyboard_mash() { |
|
+ msg = "it seems like you've fallen asleep and hit your keyboard".to_string(); |
|
+ span_label = Some((item_span, "you fell asleep here")); |
|
+ } |
|
+ |
|
BaseError { |
|
- msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"), |
|
+ msg, |
|
fallback_label, |
|
span: item_span, |
|
span_label, |
|
@@ -645,6 +654,20 @@ pub(crate) fn smart_resolve_report_errors( |
|
let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone()); |
|
err.code(code); |
|
|
|
+ // Easter egg (see `looks_like_keyboard_mash`): when the identifier looks like |
|
+ // someone fell asleep on the keyboard, emit the flavor label and help, and skip |
|
+ // the usual typo/import suggestions (which would just be noise here). |
|
+ if res.is_none() |
|
+ && let Some(seg) = path.last() |
|
+ && seg.ident.name.looks_like_keyboard_mash() |
|
+ { |
|
+ if let Some((span, label)) = base_error.span_label { |
|
+ err.span_label(span, label); |
|
+ } |
|
+ err.help("try improving your sleep schedule or reducing your hours"); |
|
+ return (err, Vec::new()); |
|
+ } |
|
+ |
|
// Try to get the span of the identifier within the path's syntax context |
|
// (if that's different). |
|
if let Some(within_macro_span) = |
|
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs |
|
index 72339efd0a1..a2d55c9c727 100644 |
|
--- a/compiler/rustc_span/src/symbol.rs |
|
+++ b/compiler/rustc_span/src/symbol.rs |
|
@@ -2597,6 +2597,21 @@ pub fn is_empty(self) -> bool { |
|
self == sym::empty |
|
} |
|
|
|
+ /// Returns `true` if the symbol ends with more than seven repetitions of the |
|
+ /// same letter — the tell-tale sign of someone having fallen asleep on their |
|
+ /// keyboard (e.g. `prehopjjkpppppppp`). Used to emit a lighthearted diagnostic |
|
+ /// for unresolved identifiers, paths, methods, and fields. |
|
+ pub fn looks_like_keyboard_mash(self) -> bool { |
|
+ let mut chars = self.as_str().chars().rev(); |
|
+ let Some(last) = chars.next() else { return false }; |
|
+ if !last.is_alphabetic() { |
|
+ return false; |
|
+ } |
|
+ // We've already consumed one occurrence (`last`); count the rest of the run. |
|
+ let run = 1 + chars.take_while(|&c| c == last).count(); |
|
+ run > 7 |
|
+ } |
|
+ |
|
/// This method is supposed to be used in error messages, so it's expected to be |
|
/// identical to printing the original identifier token written in source code |
|
/// (`token_to_string`, `Ident::to_string`), except that symbols don't keep the rawness flag |