@@ -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 > ) > {
0 commit comments