Skip to content

Commit 731c4ea

Browse files
committed
add libstd struct misspelling type check diagnostics
add tests broaden libstd support add tests fmt eliminate match attempts on names < 2 chars addresses CI errors during match attempts against generic type names like "N". These will not be the correct suggestions. remove *Debug structs, overlaps with debug macro typo refactor match approach filter on the type namespace and filter out tool mod types update tests
1 parent cbc73dc commit 731c4ea

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2806
-0
lines changed

src/librustc_resolve/late/diagnostics.rs

+242
Original file line numberDiff line numberDiff line change
@@ -353,10 +353,252 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
353353
}
354354
_ => {}
355355
}
356+
if ns == TypeNS && path.len() == 1 {
357+
// Case-insensitive test against most libstd struct names
358+
// as another fallback
359+
match res {
360+
Some(Res::ToolMod) => {}
361+
_ => {
362+
let structs = self.get_case_insensitive_libstd_structs_matches(
363+
&ident.name.to_ident_string(),
364+
);
365+
if structs.len() == 1 {
366+
err.span_suggestion_verbose(
367+
ident_span,
368+
&format!("did you mean `{}`?", structs[0]),
369+
format!("{}", structs[0]),
370+
Applicability::MaybeIncorrect,
371+
);
372+
} else if structs.len() > 1 {
373+
let mut struct_suggestions = Vec::new();
374+
let message = "did you mean one of these?:";
375+
for a_struct in structs.iter() {
376+
struct_suggestions.push(format!("{}", a_struct));
377+
}
378+
err.span_suggestions(
379+
ident_span,
380+
message,
381+
struct_suggestions.into_iter(),
382+
Applicability::MaybeIncorrect,
383+
);
384+
}
385+
}
386+
}
387+
}
356388
}
389+
357390
(err, candidates)
358391
}
359392

393+
/// Get a case-insensitive match with standard library
394+
/// structs that are *not imported* in the prelude.
395+
/// This is used for type checking diagnostics in cases when
396+
/// the type is not in scope and the name includes case
397+
/// misspelling (e.g., `Hashmap`, not `HashMap`).
398+
fn get_case_insensitive_libstd_structs_matches(&self, needle: &str) -> Vec<String> {
399+
// Excludes error types
400+
// Excludes nightly only types
401+
// Excludes types with case-sensitive macro names (e.g., `File` -> `file`)
402+
// Excludes deprecated types (e.g., `std::str::LinesAny`)
403+
let libstd_structs = [
404+
"std::alloc::Layout",
405+
"std::alloc::System",
406+
"std::any::TypeId",
407+
"std::ascii::EscapeDefault",
408+
"std::cell::Cell",
409+
"std::char::DecodeUtf16",
410+
"std::char::EscapeDefault",
411+
"std::char::EscapeUnicode",
412+
"std::char::ToLowercase",
413+
"std::char::ToUppercase",
414+
"std::cmp::Reverse",
415+
"std::collections::BTreeMap",
416+
"std::collections::BTreeSet",
417+
"std::collections::BinaryHeap",
418+
"std::collections::HashMap",
419+
"std::collections::HashSet",
420+
"std::collections::LinkedList",
421+
"std::collections::VecDeque",
422+
"std::env::Args",
423+
"std::env::ArgsOs",
424+
"std::env::SplitPaths",
425+
"std::env::Vars",
426+
"std::env::VarsOs",
427+
"std::ffi::CStr",
428+
"std::ffi::CString",
429+
"std::ffi::OsStr",
430+
"std::ffi::OsString",
431+
"std::fmt::DebugList",
432+
"std::fmt::DebugMap",
433+
"std::fmt::DebugSet",
434+
"std::fmt::DebugStruct",
435+
"std::fmt::DebugTuple",
436+
"std::fmt::Formatter",
437+
"std::fs::DirBuilder",
438+
"std::fs::DirEntry",
439+
"std::fs::FileType",
440+
"std::fs::Metadata",
441+
"std::fs::OpenOptions",
442+
"std::fs::Permissions",
443+
"std::fs::ReadDir",
444+
"std::hash::BuildHasherDefault",
445+
"std::io::BufReader",
446+
"std::io::BufWriter",
447+
"std::io::Bytes",
448+
"std::io::Chain",
449+
"std::io::Cursor",
450+
"std::io::Empty",
451+
"std::io::IoSlice",
452+
"std::io::IoSliceMut",
453+
"std::io::LineWriter",
454+
"std::io::Lines",
455+
"std::io::Repeat",
456+
"std::io::Sink",
457+
"std::io::Split",
458+
"std::io::Stderr",
459+
"std::io::StderrLock",
460+
"std::io::Stdin",
461+
"std::io::StdinLock",
462+
"std::io::Stdout",
463+
"std::io::StdoutLock",
464+
"std::io::Take",
465+
"std::iter::Chain",
466+
"std::iter::Cloned",
467+
"std::iter::Copied",
468+
"std::iter::Cycle",
469+
"std::iter::Empty",
470+
"std::iter::Enumerate",
471+
"std::iter::Filter",
472+
"std::iter::FilterMap",
473+
"std::iter::Flatten",
474+
"std::iter::FromFn",
475+
"std::iter::Fuse",
476+
"std::iter::Inspect",
477+
"std::iter::Map",
478+
"std::iter::Once",
479+
"std::iter::OnceWith",
480+
"std::iter::Peekable",
481+
"std::iter::Repeat",
482+
"std::iter::RepeatWith",
483+
"std::iter::Rev",
484+
"std::iter::Scan",
485+
"std::iter::Skip",
486+
"std::iter::SkipWhile",
487+
"std::iter::StepBy",
488+
"std::iter::Successors",
489+
"std::iter::Take",
490+
"std::iter::TakeWhile",
491+
"std::iter::Zip",
492+
"std::marker::PhantomData",
493+
"std::marker::PhantomPinned",
494+
"std::mem::Discriminant",
495+
"std::mem::ManuallyDrop",
496+
"std::net::Incoming",
497+
"std::net::Ipv4Addr",
498+
"std::net::Ipv6Addr",
499+
"std::net::SocketAddrV4",
500+
"std::net::SocketAddrV6",
501+
"std::net::TcpListener",
502+
"std::net::TcpStream",
503+
"std::net::UdpSocket",
504+
"std::num::NonZeroI8",
505+
"std::num::NonZeroI16",
506+
"std::num::NonZeroI32",
507+
"std::num::NonZeroI64",
508+
"std::num::NonZeroI128",
509+
"std::num::NonZeroU8",
510+
"std::num::NonZeroU16",
511+
"std::num::NonZeroU32",
512+
"std::num::NonZeroU64",
513+
"std::num::NonZeroU128",
514+
"std::num::NonZeroUsize",
515+
"std::num::Wrapping",
516+
"std::ops::Range",
517+
"std::ops::RangeFrom",
518+
"std::ops::RangeFull",
519+
"std::ops::RangeInclusive",
520+
"std::ops::RangeTo",
521+
"std::ops::RangeToInclusive",
522+
"std::panic::AssertUnwindSafe",
523+
"std::panic::Location",
524+
"std::panic::PanicInfo",
525+
"std::path::Ancestors",
526+
"std::path::Components",
527+
"std::path::PathBuf",
528+
"std::path::PrefixComponent",
529+
"std::pin::Pin",
530+
"std::process::Child",
531+
"std::process::ChildStderr",
532+
"std::process::ChildStdin",
533+
"std::process::ChildStdout",
534+
"std::process::Command",
535+
"std::process::ExitStatus",
536+
"std::process::Output",
537+
"std::process::Stdio",
538+
"std::ptr::NonNull",
539+
"std::rc::Rc",
540+
"std::rc::Weak",
541+
"std::str::Bytes",
542+
"std::str::CharIndices",
543+
"std::str::Chars",
544+
"std::str::EncodeUtf16",
545+
"std::str::EscapeDefault",
546+
"std::str::EscapeUnicode",
547+
"std::str::Lines",
548+
"std::str::MatchIndices",
549+
"std::str::RMatchIndices",
550+
"std::str::RMatches",
551+
"std::str::RSplit",
552+
"std::str::RSplitN",
553+
"std::str::RSplitTerminator",
554+
"std::str::Split",
555+
"std::str::SplitAsciiWhitespace",
556+
"std::str::SplitN",
557+
"std::str::SplitTerminator",
558+
"std::str::SplitWhitespace",
559+
"std::string::Drain",
560+
"std::sync::Arc",
561+
"std::sync::Barrier",
562+
"std::sync::BarrierWaitResult",
563+
"std::sync::Condvar",
564+
"std::sync::Mutex",
565+
"std::sync::MutexGuard",
566+
"std::sync::Once",
567+
"std::sync::RwLock",
568+
"std::sync::RwLockReadGuard",
569+
"std::sync::RwLockWriteGuard",
570+
"std::sync::WaitTimeoutResult",
571+
"std::sync::Weak",
572+
"std::task::Context",
573+
"std::task::RawWaker",
574+
"std::task::RawWakerVTable",
575+
"std::task::Waker",
576+
"std::thread::Builder",
577+
"std::thread::JoinHandle",
578+
"std::thread::LocalKey",
579+
"std::thread::Thread",
580+
"std::thread::ThreadId",
581+
"std::time::Duration",
582+
"std::time::Instant",
583+
"std::time::SystemTime",
584+
];
585+
586+
let mut structs = Vec::new();
587+
// abort for single character type names
588+
if needle.len() < 2 {
589+
return structs;
590+
}
591+
for item in libstd_structs.iter() {
592+
// check the struct name in the module path
593+
let struct_path: Vec<&str> = item.split("::").collect();
594+
// case-insensitive comparison of names
595+
if needle.to_lowercase() == struct_path.last().unwrap().to_lowercase() {
596+
structs.push(item.to_string());
597+
}
598+
}
599+
structs
600+
}
601+
360602
/// Check if the source is call expression and the first argument is `self`. If true,
361603
/// return the span of whole call and the span for all arguments expect the first one (`self`).
362604
fn call_has_self_arg(&self, source: PathSource<'_>) -> Option<(Span, Option<Span>)> {

src/test/ui/libstd-case-typo/alloc.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// checks case typos with libstd::alloc structs
2+
fn main(){}
3+
4+
fn test_layout(_x: LayOut){}
5+
//~^ ERROR: cannot find type `LayOut` in this scope
6+
fn test_system(_x: system){}
7+
//~^ ERROR: cannot find type `system` in this scope
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0412]: cannot find type `LayOut` in this scope
2+
--> $DIR/alloc.rs:4:20
3+
|
4+
LL | fn test_layout(_x: LayOut){}
5+
| ^^^^^^ not found in this scope
6+
|
7+
help: did you mean `std::alloc::Layout`?
8+
|
9+
LL | fn test_layout(_x: std::alloc::Layout){}
10+
| ^^^^^^^^^^^^^^^^^^
11+
12+
error[E0412]: cannot find type `system` in this scope
13+
--> $DIR/alloc.rs:6:20
14+
|
15+
LL | fn test_system(_x: system){}
16+
| ^^^^^^ not found in this scope
17+
|
18+
help: did you mean `std::alloc::System`?
19+
|
20+
LL | fn test_system(_x: std::alloc::System){}
21+
| ^^^^^^^^^^^^^^^^^^
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0412`.

src/test/ui/libstd-case-typo/any.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// checks case typos with libstd::any structs
2+
fn main(){}
3+
4+
fn test_typeid(_x: Typeid){}
5+
//~^ ERROR: cannot find type `Typeid` in this scope
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0412]: cannot find type `Typeid` in this scope
2+
--> $DIR/any.rs:4:20
3+
|
4+
LL | fn test_typeid(_x: Typeid){}
5+
| ^^^^^^ not found in this scope
6+
|
7+
help: did you mean `std::any::TypeId`?
8+
|
9+
LL | fn test_typeid(_x: std::any::TypeId){}
10+
| ^^^^^^^^^^^^^^^^
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0412`.

src/test/ui/libstd-case-typo/ascii.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// checks case typos with libstd::ascii structs
2+
fn main(){}
3+
4+
fn test_escdef(_x: Escapedefault){}
5+
//~^ ERROR: cannot find type `Escapedefault` in this scope
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0412]: cannot find type `Escapedefault` in this scope
2+
--> $DIR/ascii.rs:4:20
3+
|
4+
LL | fn test_escdef(_x: Escapedefault){}
5+
| ^^^^^^^^^^^^^ not found in this scope
6+
|
7+
help: did you mean one of these?:
8+
|
9+
LL | fn test_escdef(_x: std::ascii::EscapeDefault){}
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
11+
LL | fn test_escdef(_x: std::char::EscapeDefault){}
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^
13+
LL | fn test_escdef(_x: std::str::EscapeDefault){}
14+
| ^^^^^^^^^^^^^^^^^^^^^^^
15+
16+
error: aborting due to previous error
17+
18+
For more information about this error, try `rustc --explain E0412`.

src/test/ui/libstd-case-typo/cell.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// checks case typos with libstd::cell structs
2+
fn main(){}
3+
4+
fn test_cell(_x: cell<()>){}
5+
//~^ ERROR: cannot find type `cell` in this scope
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0412]: cannot find type `cell` in this scope
2+
--> $DIR/cell.rs:4:18
3+
|
4+
LL | fn test_cell(_x: cell<()>){}
5+
| ^^^^ not found in this scope
6+
|
7+
help: did you mean `std::cell::Cell`?
8+
|
9+
LL | fn test_cell(_x: std::cell::Cell<()>){}
10+
| ^^^^^^^^^^^^^^^
11+
12+
error: aborting due to previous error
13+
14+
For more information about this error, try `rustc --explain E0412`.

src/test/ui/libstd-case-typo/char.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// checks case typos with libstd::char structs
2+
fn main(){}
3+
4+
fn test_du16(_x: DecodeUTF16<()>){}
5+
//~^ ERROR: cannot find type `DecodeUTF16` in this scope
6+
7+
fn test_edflt(_x: Escapedefault){}
8+
//~^ ERROR: cannot find type `Escapedefault` in this scope
9+
10+
fn test_euni(_x: Escapeunicode){}
11+
//~^ ERROR: cannot find type `Escapeunicode` in this scope
12+
13+
fn test_tolow(_x: Tolowercase){}
14+
//~^ ERROR: cannot find type `Tolowercase` in this scope
15+
16+
fn test_toupper(_x: Touppercase){}
17+
//~^ ERROR: cannot find type `Touppercase` in this scope

0 commit comments

Comments
 (0)