@@ -353,10 +353,252 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> {
353
353
}
354
354
_ => { }
355
355
}
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
+ }
356
388
}
389
+
357
390
( err, candidates)
358
391
}
359
392
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
+
360
602
/// Check if the source is call expression and the first argument is `self`. If true,
361
603
/// return the span of whole call and the span for all arguments expect the first one (`self`).
362
604
fn call_has_self_arg ( & self , source : PathSource < ' _ > ) -> Option < ( Span , Option < Span > ) > {
0 commit comments