From 3595fea4365594588cf7364c1dd36634decb4628 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Wed, 6 May 2026 12:08:35 +0200 Subject: [PATCH 01/10] stdlib test cases Co-authored-by: Chris Simpkins --- tests/ui/README.md | 3 + tests/ui/libstd-case-typo/libstd.rs | 439 ++++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 tests/ui/libstd-case-typo/libstd.rs diff --git a/tests/ui/README.md b/tests/ui/README.md index 6ffef1a1cd699..8892961980143 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -818,6 +818,9 @@ Exercises let-else constructs. Exercises of the lexer. +## `tests/ui/libstd-case-typo/` +Tests for import suggestions with typo tolerance. + ## `tests/ui/lifetimes/` Broad directory on lifetimes, including proper specifiers, lifetimes not living long enough, or undeclared lifetime names. diff --git a/tests/ui/libstd-case-typo/libstd.rs b/tests/ui/libstd-case-typo/libstd.rs new file mode 100644 index 0000000000000..45463a5467cd9 --- /dev/null +++ b/tests/ui/libstd-case-typo/libstd.rs @@ -0,0 +1,439 @@ +// checks case typos with libstd::alloc structs + +fn test_layout(_x: LayOut){} +//~^ ERROR: cannot find type `LayOut` in this scope +fn test_system(_x: system){} +//~^ ERROR: cannot find type `system` in this scope + +// checks case typos with libstd::any structs + +fn test_typeid(_x: Typeid){} +//~^ ERROR: cannot find type `Typeid` in this scope + +// checks case typos with libstd::ascii structs + +fn test_escapedefault(_x: Escapedefault){} +//~^ ERROR: cannot find type `Escapedefault` in this scope + +// checks case typos with libstd::cell structs + +fn test_cell(_x: cell<()>){} +//~^ ERROR: cannot find type `cell` in this scope + +// checks case typos with libstd::char structs + +fn test_decodeutf16(_x: DecodeUTF16<()>){} +//~^ ERROR: cannot find type `DecodeUTF16` in this scope + +fn test_escapeunicode(_x: Escapeunicode){} +//~^ ERROR: cannot find type `Escapeunicode` in this scope + +fn test_tolowercase(_x: Tolowercase){} +//~^ ERROR: cannot find type `Tolowercase` in this scope + +fn test_touppercase(_x: Touppercase){} +//~^ ERROR: cannot find type `Touppercase` in this scope + +// checks case typos with libstd::cmp structs + +fn test_reverse(_x: reverse<()>){} +//~^ ERROR: cannot find type `reverse` in this scope + +// checks case typos with libstd::collections structs + +fn test_btreemap(_x: BtreeMap<(), ()>){} +//~^ ERROR: cannot find type `BtreeMap` in this scope +fn test_btreeset(_x: BtreeSet<()>){} +//~^ ERROR: cannot find type `BtreeSet` in this scope +fn test_binaryheap(_x: Binaryheap<()>){} +//~^ ERROR: cannot find type `Binaryheap` in this scope +fn test_hashmap(_x: Hashmap){} +//~^ ERROR: cannot find type `Hashmap` in this scope +fn test_hashset(_x: Hashset<()>){} +//~^ ERROR: cannot find type `Hashset` in this scope +fn test_linkedlist(_x: Linkedlist<()>){} +//~^ ERROR: cannot find type `Linkedlist` in this scope +fn test_vecdeque(_x: Vecdeque<()>){} +//~^ ERROR: cannot find type `Vecdeque` in this scope + +// checks case typos with libstd::env structs + +fn test_args(_x: args){} +//~^ ERROR: cannot find type `args` in this scope +fn test_argsos(_x: Argsos){} +//~^ ERROR: cannot find type `Argsos` in this scope +fn test_splitpaths(_x: Splitpaths<'_>){} +//~^ ERROR: cannot find type `Splitpaths` in this scope +fn test_vars(_x: vars){} +//~^ ERROR: cannot find type `vars` in this scope +fn test_varsos(_x: Varsos){} +//~^ ERROR: cannot find type `Varsos` in this scope + +// checks case typos with libstd::ffi structs + +fn test_cstr(_x: cStr){} +//~^ ERROR: cannot find type `cStr` in this scope +fn test_osstr(_x: Osstr){} +//~^ ERROR: cannot find type `Osstr` in this scope +fn test_osstring(_x: Osstring){} +//~^ ERROR: cannot find type `Osstring` in this scope + +// checks case typos with libstd::fmt structs + +fn test_debuglist(_x: Debuglist){} +//~^ ERROR: cannot find type `Debuglist` in this scope +fn test_debugmap(_x: Debugmap){} +//~^ ERROR: cannot find type `Debugmap` in this scope +fn test_debugset(_x: Debugset){} +//~^ ERROR: cannot find type `Debugset` in this scope +fn test_debugstruct(_x: Debugstruct){} +//~^ ERROR: cannot find type `Debugstruct` in this scope +fn test_debugtuple(_x: Debugtuple){} +//~^ ERROR: cannot find type `Debugtuple` in this scope +fn test_fmter(mut _x: formatter){} +//~^ ERROR: cannot find type `formatter` in this scope + +// checks case typos with libstd::fs structs + +fn test_dirbuilder(_x: Dirbuilder){} +//~^ ERROR: cannot find type `Dirbuilder` in this scope +fn test_direntry(_x: Direntry){} +//~^ ERROR: cannot find type `Direntry` in this scope +fn test_filetype(_x: Filetype){} +//~^ ERROR: cannot find type `Filetype` in this scope +fn test_metadata(_x: MetaData){} +//~^ ERROR: cannot find type `MetaData` in this scope +fn test_openoptions(_x: Openoptions){} +//~^ ERROR: cannot find type `Openoptions` in this scope +fn test_permissions(_x: permissions){} +//~^ ERROR: cannot find type `permissions` in this scope +fn test_readdir(_x: Readdir){} +//~^ ERROR: cannot find type `Readdir` in this scope + +// checks case typos with libstd::hash structs + +fn test_buildhasherdefault(_x: BuildhasherDefault){} +//~^ ERROR: cannot find type `BuildhasherDefault` in this scope + +// checks case typos with libstd::io structs + +fn test_bufreader(_x: Bufreader<()>){} +//~^ ERROR: cannot find type `Bufreader` in this scope +fn test_bufwriter(_x: Bufwriter<()>){} +//~^ ERROR: cannot find type `Bufwriter` in this scope +fn test_bytes(_x: bytes<()>){} +//~^ ERROR: cannot find type `bytes` in this scope +fn test_chain(_x: chain<(), ()>){} +//~^ ERROR: cannot find type `chain` in this scope +fn test_cursor(_x: cursor<()>){} +//~^ ERROR: cannot find type `cursor` in this scope +fn test_empty(_x: empty){} +//~^ ERROR: cannot find type `empty` in this scope +fn test_ioslice(_x: Ioslice){} +//~^ ERROR: cannot find type `Ioslice` in this scope +fn test_ioslicemut(_x: IosliceMut){} +//~^ ERROR: cannot find type `IosliceMut` in this scope +fn test_linewriter(_x: Linewriter<()>){} +//~^ ERROR: cannot find type `Linewriter` in this scope +fn test_lines(_x: lines<()>){} +//~^ ERROR: cannot find type `lines` in this scope +fn test_repeat(_x: repeat){} +//~^ ERROR: cannot find type `repeat` in this scope +fn test_sink(_x: sink){} +//~^ ERROR: cannot find type `sink` in this scope +fn test_split(_x: split<()>){} +//~^ ERROR: cannot find type `split` in this scope +fn test_stderr(_x: StdErr){} +//~^ ERROR: cannot find type `StdErr` in this scope +fn test_stderrlock(_x: StdErrLock){} +//~^ ERROR: cannot find type `StdErrLock` in this scope +fn test_stdin(_x: StdIn){} +//~^ ERROR: cannot find type `StdIn` in this scope +fn test_stdinlock(_x: StdInLock){} +//~^ ERROR: cannot find type `StdInLock` in this scope +fn test_stdout(_x: StdOut){} +//~^ ERROR: cannot find type `StdOut` in this scope +fn test_stdoutlock(_x: StdOutLock){} +//~^ ERROR: cannot find type `StdOutLock` in this scope +fn test_take(_x: take){} +//~^ ERROR: cannot find type `take` in this scope + +// checks case typos with libstd::iter structs + +fn test_cloned(_x: cloned<(), ()>){} +//~^ ERROR: cannot find type `cloned` in this scope +fn test_copied(_x: copied<(), ()>){} +//~^ ERROR: cannot find type `copied` in this scope +fn test_cycle(_x: cycle<(), ()>){} +//~^ ERROR: cannot find type `cycle` in this scope +fn test_enumerate(_x: enumerate<(), ()>){} +//~^ ERROR: cannot find type `enumerate` in this scope +fn test_filter(_x: filter<(), ()>){} +//~^ ERROR: cannot find type `filter` in this scope +fn test_filtermap(_x: Filtermap<(), ()>){} +//~^ ERROR: cannot find type `Filtermap` in this scope +fn test_flatten(_x: flatten<()>){} +//~^ ERROR: cannot find type `flatten` in this scope +fn test_fromfn(_x: Fromfn<()>){} +//~^ ERROR: cannot find type `Fromfn` in this scope +fn test_fuse(_x: fuse<()>){} +//~^ ERROR: cannot find type `fuse` in this scope +fn test_inspect(_x: inspect<(), ()>){} +//~^ ERROR: cannot find type `inspect` in this scope +fn test_map(_x: map<(), ()>){} +//~^ ERROR: cannot find type `map` in this scope +fn test_once(_x: once<()>){} +//~^ ERROR: cannot find type `once` in this scope +fn test_oncewith(_x: Oncewith<()>){} +//~^ ERROR: cannot find type `Oncewith` in this scope +fn test_peekable(_x: peekable<()>){} +//~^ ERROR: cannot find type `peekable` in this scope +fn test_repeatwith(_x: Repeatwith<()>){} +//~^ ERROR: cannot find type `Repeatwith` in this scope +fn test_rev(_x: rev<()>){} +//~^ ERROR: cannot find type `rev` in this scope +fn test_scan(_x: scan<(), (), ()>){} +//~^ ERROR: cannot find type `scan` in this scope +fn test_skip(_x: skip<()>){} +//~^ ERROR: cannot find type `skip` in this scope +fn test_skipwhile(_x: Skipwhile<(), ()>){} +//~^ ERROR: cannot find type `Skipwhile` in this scope +fn test_stepby(_x: Stepby<()>){} +//~^ ERROR: cannot find type `Stepby` in this scope +fn test_successors(_x: successors<()>){} +//~^ ERROR: cannot find type `successors` in this scope +fn test_takewhile(_x: Takewhile<(), ()>){} +//~^ ERROR: cannot find type `Takewhile` in this scope +fn test_zip(_x: zip<(), ()>){} +//~^ ERROR: cannot find type `zip` in this scope + +// checks case typos with libstd::marker structs + +fn test_phantomdata(_x: Phantomdata){} +//~^ ERROR: cannot find type `Phantomdata` in this scope +fn test_phantompinned(_x: Phantompinned){} +//~^ ERROR: cannot find type `Phantompinned` in this scope + +// checks case typos with libstd::mem structs + +fn test_discriminant(_x: discriminant<()>){} +//~^ ERROR: cannot find type `discriminant` in this scope +fn test_manuallydrop(_x: Manuallydrop<()>){} +//~^ ERROR: cannot find type `Manuallydrop` in this scope + +// checks case typos with libstd::net structs + +fn test_incoming(_x: incoming){} +//~^ ERROR: cannot find type `incoming` in this scope +fn test_ipv4addr(_x: IPv4Addr){} +//~^ ERROR: cannot find type `IPv4Addr` in this scope +fn test_ipv6addr(_x: IPv6Addr){} +//~^ ERROR: cannot find type `IPv6Addr` in this scope +fn test_socketaddrv4(_x: SocketAddrv4){} +//~^ ERROR: cannot find type `SocketAddrv4` in this scope +fn test_socketaddrv6(_x: SocketAddrv6){} +//~^ ERROR: cannot find type `SocketAddrv6` in this scope +fn test_tcplistener(_x: TCPListener){} +//~^ ERROR: cannot find type `TCPListener` in this scope +fn test_tcpstream(_x: TCPStream){} +//~^ ERROR: cannot find type `TCPStream` in this scope +fn test_udpsocket(_x: UDPSocket){} +//~^ ERROR: cannot find type `UDPSocket` in this scope + +// checks case typos with libstd::num structs + +fn test_nonzeroi8(_x: NonZeroi8){} +//~^ ERROR: cannot find type `NonZeroi8` in this scope +fn test_nonzeroi16(_x: NonZeroi16){} +//~^ ERROR: cannot find type `NonZeroi16` in this scope +fn test_nonzeroi32(_x: NonZeroi32){} +//~^ ERROR: cannot find type `NonZeroi32` in this scope +fn test_nonzeroi64(_x: NonZeroi64){} +//~^ ERROR: cannot find type `NonZeroi64` in this scope +fn test_nonzeroi128(_x: NonZeroi128){} +//~^ ERROR: cannot find type `NonZeroi128` in this scope +fn test_nonzerou8(_x: NonZerou8){} +//~^ ERROR: cannot find type `NonZerou8` in this scope +fn test_nonzerou16(_x: NonZerou16){} +//~^ ERROR: cannot find type `NonZerou16` in this scope +fn test_nonzerou32(_x: NonZerou32){} +//~^ ERROR: cannot find type `NonZerou32` in this scope +fn test_nonzerou64(_x: NonZerou64){} +//~^ ERROR: cannot find type `NonZerou64` in this scope +fn test_nonzerou128(_x: NonZerou128){} +//~^ ERROR: cannot find type `NonZerou128` in this scope +fn test_nonzerousize(_x: NonzeroUsize){} +//~^ ERROR: cannot find type `NonzeroUsize` in this scope +fn test_wrapping(_x: wrapping){} +//~^ ERROR: cannot find type `wrapping` in this scope + +// checks case typos with libstd::ops structs + +fn test_range(_x: range<()>){} +//~^ ERROR: cannot find type `range` in this scope +fn test_rangefrom(_x: Rangefrom<()>){} +//~^ ERROR: cannot find type `Rangefrom` in this scope +fn test_rangefull(_x: Rangefull<()>){} +//~^ ERROR: cannot find type `Rangefull` in this scope +fn test_rangeinclusive(_x: Rangeinclusive<()>){} +//~^ ERROR: cannot find type `Rangeinclusive` in this scope +fn test_rangeto(_x: Rangeto<()>){} +//~^ ERROR: cannot find type `Rangeto` in this scope +fn test_rangetoinclusive(_x: RangetoInclusive<()>){} +//~^ ERROR: cannot find type `RangetoInclusive` in this scope + +// checks case typos with libstd::panic structs + +fn test_assertunwindsafe(_x: AssertUnwindsafe<()>){} +//~^ ERROR: cannot find type `AssertUnwindsafe` in this scope +fn test_location(_x: location<()>){} +//~^ ERROR: cannot find type `location` in this scope +fn test_panicinfo(_x: Panicinfo<()>){} +//~^ ERROR: cannot find type `Panicinfo` in this scope + +// checks case typos with libstd::path structs + +fn test_ancestors(_x: ancestors){} +//~^ ERROR: cannot find type `ancestors` in this scope +fn test_components(_x: components){} +//~^ ERROR: cannot find type `components` in this scope +fn test_pathbuf(_x: Pathbuf){} +//~^ ERROR: cannot find type `Pathbuf` in this scope +fn test_prefixcomponent(_x: Prefixcomponent){} +//~^ ERROR: cannot find type `Prefixcomponent` in this scope + +// checks case typos with libstd::pin structs + +fn test_pin(_x: pin<()>){} +//~^ ERROR: cannot find type `pin` in this scope + +// checks case typos with libstd::process structs + +fn test_child(_x: child){} +//~^ ERROR: cannot find type `child` in this scope +fn test_childstderr(_x: ChildStdErr){} +//~^ ERROR: cannot find type `ChildStdErr` in this scope +fn test_childstdin(_x: ChildStdIn){} +//~^ ERROR: cannot find type `ChildStdIn` in this scope +fn test_childstdout(_x: ChildStdOut){} +//~^ ERROR: cannot find type `ChildStdOut` in this scope +fn test_command(_x: command){} +//~^ ERROR: cannot find type `command` in this scope +fn test_exitstatus(_x: Exitstatus){} +//~^ ERROR: cannot find type `Exitstatus` in this scope +fn test_output(_x: output){} +//~^ ERROR: cannot find type `output` in this scope +fn test_stdio(_x: StdIo){} +//~^ ERROR: cannot find type `StdIo` in this scope + +// checks case typos with libstd::ptr structs + +fn test_nonnull(_x: Nonnull<()>){} +//~^ ERROR: cannot find type `Nonnull` in this scope + +// checks case typos with libstd::rc structs + +fn test_rc(_x: rc<()>){} +//~^ ERROR: cannot find type `rc` in this scope +fn test_weak(_x: weak<()>){} +//~^ ERROR: cannot find type `weak` in this scope + +// checks case typos with libstd::string structs + +fn test_drain(_x: drain){} +//~^ ERROR: cannot find type `drain` in this scope + +// checks case typos with libstd::str structs + +fn test_charindices(_x: Charindices){} +//~^ ERROR: cannot find type `Charindices` in this scope +fn test_chars(_x: chars){} +//~^ ERROR: cannot find type `chars` in this scope +fn test_encodeutf16(_x: EncodeUTF16){} +//~^ ERROR: cannot find type `EncodeUTF16` in this scope +fn test_matchindices(_x: Matchindices){} +//~^ ERROR: cannot find type `Matchindices` in this scope +fn test_rmatchindices(_x: RmatchIndices){} +//~^ ERROR: cannot find type `RmatchIndices` in this scope +fn test_rmatches(_x: Rmatches){} +//~^ ERROR: cannot find type `Rmatches` in this scope +fn test_rsplit(_x: Rsplit){} +//~^ ERROR: cannot find type `Rsplit` in this scope +fn test_rsplitn(_x: RSplitn){} +//~^ ERROR: cannot find type `RSplitn` in this scope +fn test_rsplitterminator(_x: RsplitTerminator){} +//~^ ERROR: cannot find type `RsplitTerminator` in this scope +fn test_splitasciiwhitespace(_x: SplitASCIIWhitespace){} +//~^ ERROR: cannot find type `SplitASCIIWhitespace` in this scope +fn test_splitn(_x: Splitn){} +//~^ ERROR: cannot find type `Splitn` in this scope +fn test_splitterminator(_x: Splitterminator){} +//~^ ERROR: cannot find type `Splitterminator` in this scope +fn test_splitwhitespace(_x: Splitwhitespace){} +//~^ ERROR: cannot find type `Splitwhitespace` in this scope + +// checks case typos with libstd::sync structs + +fn test_arc(_x: arc<()>){} +//~^ ERROR: cannot find type `arc` in this scope +fn test_barrier(_x: barrier<()>){} +//~^ ERROR: cannot find type `barrier` in this scope +fn test_barrierwaitresult(_x: BarrierwaitResult<()>){} +//~^ ERROR: cannot find type `BarrierwaitResult` in this scope +fn test_condvar(_x: CondVar<()>){} +//~^ ERROR: cannot find type `CondVar` in this scope +fn test_mutex(_x: mutex<()>){} +//~^ ERROR: cannot find type `mutex` in this scope +fn test_mutexguard(_x: Mutexguard<()>){} +//~^ ERROR: cannot find type `Mutexguard` in this scope +fn test_rwlock(_x: RWlock<()>){} +//~^ ERROR: cannot find type `RWlock` in this scope +fn test_rwlockreadguard(_x: RWlockReadGuard<()>){} +//~^ ERROR: cannot find type `RWlockReadGuard` in this scope +fn test_rwlockwriteguard(_x: RWlockWriteGuard<()>){} +//~^ ERROR: cannot find type `RWlockWriteGuard` in this scope +fn test_waittimeoutresult(_x: WaittimeoutResult<()>){} +//~^ ERROR: cannot find type `WaittimeoutResult` in this scope + +// checks case typos with libstd::task structs + +fn test_context(_x: context){} +//~^ ERROR: cannot find type `context` in this scope +fn test_rawwaker(_x: Rawwaker){} +//~^ ERROR: cannot find type `Rawwaker` in this scope +fn test_rawwakervtable(_x: RawwakerVTable){} +//~^ ERROR: cannot find type `RawwakerVTable` in this scope +fn test_waker(_x: waker){} +//~^ ERROR: cannot find type `waker` in this scope + +// checks case typos with libstd::thread structs + +fn test_builder(_x: builder){} +//~^ ERROR: cannot find type `builder` in this scope +fn test_joinhandle(_x: Joinhandle<()>){} +//~^ ERROR: cannot find type `Joinhandle` in this scope +fn test_localkey(_x: Localkey<()>){} +//~^ ERROR: cannot find type `Localkey` in this scope +fn test_thread(_x: thread){} +//~^ ERROR: cannot find type `thread` in this scope +fn test_threadid(_x: ThreadID){} +//~^ ERROR: cannot find type `ThreadID` in this scope + +// checks case typos with libstd::time structs + +fn test_duration(_x: duration){} +//~^ ERROR: cannot find type `duration` in this scope +fn test_instant(_x: instant){} +//~^ ERROR: cannot find type `instant` in this scope +fn test_systemtime(_x: Systemtime){} +//~^ ERROR: cannot find type `Systemtime` in this scope + +fn test_systemtime2(_x: SystemTime){} +//~^ ERROR: cannot find type `SystemTime` in this scope + +struct SystemTome{} +mod st{ + struct SystemTame{} +} + +fn main(){} From 4ffaa3461a96c081341e7a31c576249e7b2c8954 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:39:13 +0200 Subject: [PATCH 02/10] swap diagnostics.rs and error_helper.rs (undo #157974) --- compiler/rustc_resolve/src/diagnostics.rs | 5620 ++++++++++++++------ compiler/rustc_resolve/src/error_helper.rs | 5616 ++++++------------- 2 files changed, 5618 insertions(+), 5618 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 2baf423e296d6..81c1b6fa8d675 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,1778 +1,4160 @@ +// ignore-tidy-filelength +use std::mem; +use std::ops::ControlFlow; + +use itertools::Itertools as _; +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{ + self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, + join_path_idents, +}; +use rustc_ast_pretty::pprust; +use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; -use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, + pluralize, struct_span_code_err, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::{Ident, Span, Spanned, Symbol}; - -use crate::Res; -use crate::late::PatternSource; - -#[derive(Diagnostic)] -#[diag("can't use {$is_self -> - [true] `Self` - *[false] generic parameters - } from outer item", code = E0401)] -#[note( - "nested items are independent from their parent item for everything except for privacy and name resolution" -)] -pub(crate) struct GenericParamsFromOuterItem { - #[primary_span] - #[label( - "use of {$is_self -> - [true] `Self` - *[false] generic parameter - } from outer item" - )] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) label: Option, - #[subdiagnostic] - pub(crate) refer_to_type_directly: Option, - #[subdiagnostic] - pub(crate) use_let: Option, - #[subdiagnostic] - pub(crate) sugg: Option, - #[subdiagnostic] - pub(crate) static_or_const: Option, - pub(crate) is_self: bool, - #[subdiagnostic] - pub(crate) item: Option, -} - -#[derive(Subdiagnostic)] -#[label( - "{$is_self -> - [true] `Self` - *[false] generic parameter - } used in this inner {$descr}" -)] -pub(crate) struct GenericParamsFromOuterItemInnerItem { - #[primary_span] - pub(crate) span: Span, - pub(crate) descr: String, - pub(crate) is_self: bool, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemStaticOrConst { - #[note("a `static` is a separate item from the item that contains it")] - Static, - #[note("a `const` is a separate item from the item that contains it")] - Const, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemLabel { - #[label("can't use `Self` here")] - SelfTyParam(#[primary_span] Span), - #[label("`Self` type implicitly declared here, by this `impl`")] - SelfTyAlias(#[primary_span] Span), - #[label("type parameter from outer item")] - TyParam(#[primary_span] Span), - #[label("const parameter from outer item")] - ConstParam(#[primary_span] Span), -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try introducing a local generic parameter here", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemSugg { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try using a local `let` binding instead", - code = "let", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemUseLet { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "refer to the type directly here instead", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UseTypeDirectly { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Diagnostic)] -#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] -pub(crate) struct NameAlreadyUsedInParameterList { - #[primary_span] - #[label("already used")] - pub(crate) span: Span, - #[label("first use of `{$name}`")] - pub(crate) first_use_span: Span, - pub(crate) name: Ident, -} - -#[derive(Diagnostic)] -#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] -pub(crate) struct MethodNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) method: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated function with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedFnWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] -pub(crate) struct TypeNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) type_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated type with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedTypeWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] -pub(crate) struct ConstNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) const_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated constant with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedConstWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] -pub(crate) struct VariableBoundWithDifferentMode { - #[primary_span] - #[label("bound in different ways")] - pub(crate) span: Span, - #[label("first binding")] - pub(crate) first_binding_span: Span, - pub(crate) variable_name: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] -pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { - #[primary_span] - #[label("used as parameter more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] -pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { - #[primary_span] - #[label("used in a pattern more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("use of undeclared label `{$name}`", code = E0426)] -pub(crate) struct UndeclaredLabel { - #[primary_span] - #[label("undeclared label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) sub_reachable: Option, - #[subdiagnostic] - pub(crate) sub_reachable_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_unreachable: Option, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); - -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct TryUsingSimilarlyNamedLabel { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is unreachable")] -pub(crate) struct UnreachableLabelWithSimilarNameExists { - #[primary_span] - pub(crate) ident_span: Span, -} - -#[derive(Diagnostic)] -#[diag("can't capture dynamic environment in a fn item", code = E0434)] -#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] -pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant", code = E0435)] -pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) with: Option>, - #[subdiagnostic] - pub(crate) with_label: Option, - #[subdiagnostic] - pub(crate) without: Option>, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider using `{$suggestion}` instead of `{$current}`", - style = "verbose", - applicability = "has-placeholders" -)] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { - // #[primary_span] - #[suggestion_part(code = "{suggestion} ")] - pub(crate) span: Span, - pub(crate) suggestion: &'a str, - #[suggestion_part(code = ": /* Type */")] - pub(crate) type_span: Option, - pub(crate) current: &'a str, -} - -#[derive(Subdiagnostic)] -#[label("non-constant value")] -pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[label("this would need to be a `{$suggestion}`")] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { - #[primary_span] - pub(crate) ident_span: Span, - pub(crate) suggestion: &'a str, -} - -#[derive(Diagnostic)] -#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] -pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { - #[primary_span] - #[label("cannot be named the same as {$article} {$shadowed_binding}")] - pub(crate) span: Span, - pub(crate) shadowing_binding: PatternSource, - pub(crate) shadowed_binding: Res, - pub(crate) article: &'a str, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] - pub(crate) shadowed_binding_span: Span, - pub(crate) participle: &'a str, - pub(crate) name: Symbol, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try specify the pattern arguments", - code = "{name}(..)", - applicability = "unspecified" -)] -pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] -pub(crate) struct ForwardDeclaredGenericParam { - #[primary_span] - #[label("cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} +use rustc_feature::BUILTIN_ATTRIBUTES; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; +use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; +use rustc_hir::def::Namespace::{self, *}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::{Attribute, PrimTy, Stability, StabilityLevel, find_attr}; +use rustc_middle::bug; +use rustc_middle::ty::{TyCtxt, Visibility}; +use rustc_session::Session; +use rustc_session::lint::builtin::{ + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, +}; +use rustc_session::utils::was_invoked_from_cargo; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edition::Edition; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::SourceMap; +use rustc_span::{ + BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, +}; +use thin_vec::{ThinVec, thin_vec}; +use tracing::{debug, instrument}; -#[derive(Diagnostic)] -#[diag("const parameter types cannot reference parameters before they are declared")] -pub(crate) struct ForwardDeclaredGenericInConstParamTy { - #[primary_span] - #[label("const parameter type cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} +use crate::diagnostics::{ + self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, + ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, + MaybeMissingMacroRulesName, +}; +use crate::hygiene::Macros20NormalizedSyntaxContext; +use crate::imports::{Import, ImportKind, UnresolvedImportError, import_path_to_string}; +use crate::late::{DiagMetadata, PatternSource, Rib}; +use crate::{ + AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, + DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, + LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, + PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, + VisResolutionError, path_names_to_string, +}; -#[derive(Diagnostic)] -#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] -pub(crate) struct ParamInTyOfConstParam { - #[primary_span] - #[label("the type must not depend on the parameter `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, -} +/// A vector of spans and replacements, a message and applicability. +pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); -#[derive(Diagnostic)] -#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] -pub(crate) struct SelfInGenericParamDefault { - #[primary_span] - pub(crate) span: Span, -} +/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a +/// similarly named label and whether or not it is reachable. +pub(crate) type LabelSuggestion = (Ident, bool); -#[derive(Diagnostic)] -#[diag("cannot use `Self` in const parameter type")] -pub(crate) struct SelfInConstGenericTy { - #[primary_span] - pub(crate) span: Span, +#[derive(Clone)] +pub(crate) struct StructCtor { + pub res: Res, + pub vis: Visibility, + pub field_visibilities: Vec>, } -#[derive(Diagnostic)] -#[diag( - "{$is_gca -> - [true] generic parameters in const blocks are not allowed; use a named `const` item instead - *[false] generic parameters may not be used in const operations -}" -)] -pub(crate) struct ParamInNonTrivialAnonConst { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInNonTrivialAnonConst, - #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] - pub(crate) help: bool, - pub(crate) is_gca: bool, - #[help( - "consider factoring the expression into a `type const` item and use it as the const argument instead" - )] - pub(crate) help_gca: bool, +impl StructCtor { + pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { + self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) + } } #[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInNonTrivialAnonConst { - #[note("type parameters may not be used in const expressions")] - Type, - #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] - Const { name: Symbol }, - #[note("lifetime parameters may not be used in const expressions")] - Lifetime, -} - -#[derive(Diagnostic)] -#[diag("use of unreachable label `{$name}`", code = E0767)] -#[note("labels are unreachable through functions, closures, async blocks and modules")] -pub(crate) struct UnreachableLabel { - #[primary_span] - #[label("unreachable label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[label("unreachable label defined here")] - pub(crate) definition_span: Span, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_suggestion_label: Option, - #[subdiagnostic] - pub(crate) sub_unreachable_label: Option, +pub(crate) enum SuggestionTarget { + /// The target has a similar name as the name used by the programmer (probably a typo) + SimilarlyNamed, + /// The target is the only valid item that can be used in the corresponding context + SingleItem, } -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct UnreachableLabelSubSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct UnreachableLabelSubLabel { - #[primary_span] - pub(crate) ident_span: Span, +#[derive(Debug)] +pub(crate) struct TypoSuggestion { + pub candidate: Symbol, + /// The source location where the name is defined; None if the name is not defined + /// in source e.g. primitives + pub span: Option, + pub res: Res, + pub target: SuggestionTarget, +} + +impl TypoSuggestion { + pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { + Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } + } } -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is also unreachable")] -pub(crate) struct UnreachableLabelSubLabelUnreachable { - #[primary_span] - pub(crate) ident_span: Span, -} +/// A free importable items suggested in case of resolution failure. +#[derive(Debug, Clone)] +pub(crate) struct ImportSuggestion { + pub did: Option, + pub descr: &'static str, + pub path: Path, + pub accessible: bool, + // false if the path traverses a foreign `#[doc(hidden)]` item. + pub doc_visible: bool, + pub via_import: bool, + /// An extra note that should be issued if this item is suggested + pub note: Option, + pub is_stable: bool, +} + +/// Adjust the impl span so that just the `impl` keyword is taken by removing +/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and +/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). +/// +/// *Attention*: the method used is very fragile since it essentially duplicates the work of the +/// parser. If you need to use this function or something similar, please consider updating the +/// `source_map` functions and this function to something more robust. +fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { + let impl_span = sm.span_until_char(impl_span, '<'); + sm.span_until_whitespace(impl_span) +} + +impl<'ra, 'tcx> Resolver<'ra, 'tcx> { + /// Reports unresolved imports. + /// + /// Multiple unresolved import errors within the same use tree are combined into a single + /// diagnostic. + pub(crate) fn throw_unresolved_import_error( + &mut self, + mut errors: Vec<(Import<'_>, UnresolvedImportError)>, + glob_error: bool, + ) { + errors.retain(|(_import, err)| match err.module { + // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. + Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, + // If we've encountered something like `use _;`, we've already emitted an error stating + // that `_` is not a valid identifier, so we ignore that resolve error. + _ => err.segment.map(|s| s.name) != Some(kw::Underscore), + }); + if errors.is_empty() { + self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); + return; + } -#[derive(Diagnostic)] -#[diag("invalid `sym` operand")] -#[help("`sym` operands must refer to either a function or a static")] -pub(crate) struct InvalidAsmSym { - #[primary_span] - #[label("is a local variable")] - pub(crate) span: Span, -} + let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); + + let paths = errors + .iter() + .map(|(import, err)| { + let path = import_path_to_string( + &import.module_path.iter().map(|seg| seg.ident).collect::>(), + &import.kind, + err.span, + ); + format!("`{path}`") + }) + .collect::>(); + let default_message = + format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); + + // Process `import` use of the `#[diagnostic::on_unknown]` attribute. + // + // We don't need to check feature gates here; that happens on initialization of the + // `on_unknown_attr` fields. + let (mut message, label, mut notes) = + if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) { + let this = errors + .iter() + .map(|(_import, err)| { + // Is this unwrap_or reachable? + err.segment.map(|s| s.name).unwrap_or(kw::Underscore) + }) + .join(", "); + + let args = FormatArgs { unresolved: this.clone(), this, .. }; + + let CustomDiagnostic { message, label, notes, parent_label: _dead } = + directive.eval(None, &args); + + (message, label, notes) + } else { + (None, None, Vec::new()) + }; + + // `module` use of the `#[diagnostic::on_unknown]` attribute. + // We assume that someone who put the attribute on the import has more information than + // the person who put it on the module, so we choose to prioritize the import attribute. + let mut mod_diagnostics: Vec = errors + .iter() + .map(|(import, import_error)| { + if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() + && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind + { + let Some(directive) = self.on_unknown_data(def_id) else { + return CustomDiagnostic::default(); + }; + + let this = if let Some(name) = name { + name.to_string() + } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { + crate_name.to_string() + } else { + "".to_string() + }; + let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); + let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; + + directive.eval(None, &args) + } else { + CustomDiagnostic::default() + } + }) + .collect(); + + // If there is no import attribute with a message, + // but all mod messages are the same, use that. + let mod_message = + mod_diagnostics.iter_mut().flat_map(|d| d.message.take()).all_equal_value(); + if message.is_none() + && let Ok(mod_msg) = mod_message + { + message = Some(mod_msg); + } -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant")] -pub(crate) struct LowercaseSelf { - #[primary_span] - #[suggestion( - "try using `Self`", - code = "Self", - applicability = "maybe-incorrect", - style = "short" - )] - pub(crate) span: Span, -} + let mut diag = if let Some(message) = message { + struct_span_code_err!(self.dcx(), span, E0432, "{message}").with_note(default_message) + } else { + struct_span_code_err!(self.dcx(), span, E0432, "{default_message}") + }; + + for mod_diag in mod_diagnostics.iter_mut() { + for mod_note in mod_diag.notes.drain(..) { + if !notes.contains(&mod_note) { + notes.push(mod_note); + } + } + } -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("never patterns cannot contain variable bindings")] -pub(crate) struct BindingInNeverPattern { - #[primary_span] - #[suggestion( - "use a wildcard `_` instead", - code = "_", - applicability = "machine-applicable", - style = "short" - )] - pub(crate) span: Span, -} + if !notes.is_empty() { + for note in notes { + diag.note(note); + } + } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = + errors.iter().last() + { + diag.note(note.clone()); + } -#[derive(Diagnostic)] -#[diag("duplicate definitions with name `{$name}`:", code = E0201)] -pub(crate) struct TraitImplDuplicate { - #[primary_span] - #[label("duplicate definition")] - pub(crate) span: Span, - #[label("previous definition here")] - pub(crate) old_span: Span, - #[label("item in trait")] - pub(crate) trait_item_span: Span, - pub(crate) name: Ident, -} + /// Upper limit on the number of `span_label` messages. + const MAX_LABEL_COUNT: usize = 10; + let mod_labels = mod_diagnostics.into_iter().map(|cd| cd.label); + + for ((import, err), mod_label) in errors.into_iter().zip(mod_labels).take(MAX_LABEL_COUNT) { + let label_span = match err.segment { + Some(segment) => segment.span, + None => err.span, + }; + if let Some(label) = &label { + diag.span_label(label_span, label.clone()); + } else if let Some(label) = mod_label { + diag.span_label(label_span, label); + } else if let Some(label) = &err.label { + diag.span_label(label_span, label.clone()); + } -#[derive(Diagnostic)] -#[diag("relative paths are not supported in visibilities in 2018 edition or later")] -pub(crate) struct Relative2018 { - #[primary_span] - pub(crate) span: Span, - #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] - pub(crate) path_span: Span, - pub(crate) path_str: String, -} + if let Some((suggestions, msg, applicability)) = err.suggestion { + if suggestions.is_empty() { + diag.help(msg); + continue; + } + diag.multipart_suggestion(msg, suggestions, applicability); + } -#[derive(Diagnostic)] -#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] -pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] -pub(crate) struct ExpectedModuleFound { - #[primary_span] - #[label("not a module")] - pub(crate) span: Span, - pub(crate) res: Res, - pub(crate) path_str: String, -} + if let Some(candidates) = &err.candidates { + match &import.kind { + ImportKind::Single { nested: false, source, target, .. } => import_candidates( + self.tcx, + &mut diag, + Some(err.span), + candidates, + DiagMode::Import { append: false, unresolved_import: true }, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ), + ImportKind::Single { nested: true, source, target, .. } => { + import_candidates( + self.tcx, + &mut diag, + None, + candidates, + DiagMode::Normal, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ); + } + _ => {} + } + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the visibility", code = E0578)] -pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("trait implementation can only be restricted to ancestor modules")] -pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("cannot use a tool module through an import")] -pub(crate) struct ToolModuleImported { - #[primary_span] - pub(crate) span: Span, - #[note("the tool module imported here")] - pub(crate) import: Span, -} + if matches!(import.kind, ImportKind::Single { .. }) + && let Some(segment) = err.segment + && let Some(module) = err.module + { + self.find_cfg_stripped(&mut diag, &segment.name, module) + } + } -#[derive(Diagnostic)] -#[diag("visibility must resolve to a module")] -pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected {$expected}, found {$found} `{$macro_path}`")] -pub(crate) struct MacroExpectedFound<'a> { - #[primary_span] - #[label("not {$article} {$expected}")] - pub(crate) span: Span, - pub(crate) found: &'a str, - pub(crate) article: &'static str, - pub(crate) expected: &'a str, - pub(crate) macro_path: &'a str, - #[subdiagnostic] - pub(crate) remove_surrounding_derive: Option, - #[subdiagnostic] - pub(crate) add_as_non_derive: Option>, -} + let guar = diag.emit(); + if glob_error { + self.glob_error = Some(guar); + } + } -#[derive(Subdiagnostic)] -#[help("remove from the surrounding `derive()`")] -pub(crate) struct RemoveSurroundingDerive { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.tcx.dcx() + } -#[derive(Subdiagnostic)] -#[help( - " - add as non-Derive macro - `#[{$macro_path}]`" -)] -pub(crate) struct AddAsNonDerive<'a> { - pub(crate) macro_path: &'a str, -} + pub(crate) fn report_errors(&mut self, krate: &Crate) { + self.report_delayed_vis_resolution_errors(); + self.report_with_use_injections(krate); + + for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { + self.lint_buffer.buffer_lint( + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + CRATE_NODE_ID, + span_use, + diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths { + definition: span_def, + }, + ); + } -#[derive(Diagnostic)] -#[diag("can't use a procedural macro from the same crate that defines it")] -pub(crate) struct ProcMacroSameCrate { - #[primary_span] - pub(crate) span: Span, - #[help("you can define integration tests in a directory named `tests`")] - pub(crate) is_test: bool, -} + for ambiguity_error in &self.ambiguity_errors { + let mut diag = self.ambiguity_diagnostic(ambiguity_error); + + if let Some(ambiguity_warning) = ambiguity_error.warning { + let node_id = match ambiguity_error.b1.0.kind { + DeclKind::Import { import, .. } => import.root_id, + DeclKind::Def(_) => CRATE_NODE_ID, + }; + + let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, + AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, + AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, + }; + + self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); + } else { + diag.is_error = true; + self.dcx().emit_err(diag); + } + } -#[derive(Diagnostic)] -#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label("names from parent modules are not accessible without an explicit import")] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Symbol, -} + let mut reported_spans = FxHashSet::default(); + for error in mem::take(&mut self.privacy_errors) { + if reported_spans.insert(error.dedup_span) { + self.report_privacy_error(&error); + } + } + } -#[derive(Diagnostic)] -#[diag( - "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" -)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note("the macro is defined here")] - pub definition: Span, -} + fn report_delayed_vis_resolution_errors(&mut self) { + for DelayedVisResolutionError { vis, parent_scope, error } in + mem::take(&mut self.delayed_vis_resolution_errors) + { + match self.try_resolve_visibility(&parent_scope, &vis, true) { + Ok(_) => self.report_vis_error(error), + Err(error) => self.report_vis_error(error), + }; + } + } -#[derive(Diagnostic)] -#[diag("`#[macro_use]` is not supported on `extern crate self`")] -pub(crate) struct MacroUseExternCrateSelf { - #[primary_span] - pub(crate) span: Span, -} + fn report_with_use_injections(&mut self, krate: &Crate) { + for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in + mem::take(&mut self.use_injections) + { + let (span, found_use) = if node_id != DUMMY_NODE_ID { + UsePlacementFinder::check(krate, node_id) + } else { + (None, FoundUse::No) + }; + + if !candidates.is_empty() { + show_candidates( + self.tcx, + &mut err, + span, + &candidates, + if instead { Instead::Yes } else { Instead::No }, + found_use, + DiagMode::Normal, + path, + "", + ); + err.emit(); + } else if let Some((span, msg, sugg, appl)) = suggestion { + err.span_suggestion_verbose(span, msg, sugg, appl); + err.emit(); + } else if let [segment] = path.as_slice() + && is_call + { + err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); + } else { + err.emit(); + } + } + } -#[derive(Diagnostic)] -#[diag("not sure whether the path is accessible or not")] -#[note("the type may have associated items, but we are currently not checking them")] -pub(crate) struct CfgAccessibleUnsure { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn report_conflict( + &mut self, + ident: IdentKey, + ns: Namespace, + old_binding: Decl<'ra>, + new_binding: Decl<'ra>, + ) { + // Error on the second of two conflicting names + if old_binding.span.lo() > new_binding.span.lo() { + return self.report_conflict(ident, ns, new_binding, old_binding); + } -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("generic parameters may not be used in enum discriminant values")] -pub(crate) struct ParamInEnumDiscriminant { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInEnumDiscriminant, -} + let container = match old_binding.parent_module.unwrap().expect_local().kind { + // Avoid using TyCtxt::def_kind_descr in the resolver, because it + // indirectly *calls* the resolver, and would cause a query cycle. + ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), + ModuleKind::Block => "block", + }; -#[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInEnumDiscriminant { - #[note("type parameters may not be used in enum discriminant values")] - Type, - #[note("const parameters may not be used in enum discriminant values")] - Const, - #[note("lifetime parameters may not be used in enum discriminant values")] - Lifetime, -} + let (name, span) = + (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); -#[derive(Subdiagnostic)] -#[label("you can use `as` to change the binding name of the import")] -pub(crate) struct ChangeImportBinding { - #[primary_span] - pub(crate) span: Span, -} + if self.name_already_seen.get(&name) == Some(&span) { + return; + } -#[derive(Subdiagnostic)] -#[suggestion( - "you can use `as` to change the binding name of the import", - code = "{suggestion}", - applicability = "maybe-incorrect" -)] -pub(crate) struct ChangeImportBindingSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} + let old_kind = match (ns, old_binding.res()) { + (ValueNS, _) => "value", + (MacroNS, _) => "macro", + (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", + (TypeNS, Res::Def(DefKind::Mod, _)) => "module", + (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", + (TypeNS, _) => "type", + }; + + let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { + (true, true) => E0259, + (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { + true => E0254, + false => E0260, + }, + _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { + (false, false) => E0428, + (true, true) => E0252, + _ => E0255, + }, + }; + + let label = match new_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name }, + false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name }, + }; + + let old_binding_label = + (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { + let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); + match old_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import { + span, + old_kind, + name, + }, + false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition { + span, + old_kind, + name, + }, + } + }); + + let mut err = self + .dcx() + .create_err(diagnostics::NameDefinedMultipleTime { + span, + name, + descr: ns.descr(), + container, + label, + old_binding_label, + }) + .with_code(code); + + // See https://github.com/rust-lang/rust/issues/32354 + use DeclKind::Import; + let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { + !binding.span.is_dummy() + && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) + }; + let import = match (&new_binding.kind, &old_binding.kind) { + // If there are two imports where one or both have attributes then prefer removing the + // import without attributes. + (Import { import: new, .. }, Import { import: old, .. }) + if { + (new.has_attributes || old.has_attributes) + && can_suggest(old_binding, *old) + && can_suggest(new_binding, *new) + } => + { + if old.has_attributes { + Some((*new, new_binding.span, true)) + } else { + Some((*old, old_binding.span, true)) + } + } + // Otherwise prioritize the new binding. + (Import { import, .. }, other) if can_suggest(new_binding, *import) => { + Some((*import, new_binding.span, other.is_import())) + } + (other, Import { import, .. }) if can_suggest(old_binding, *import) => { + Some((*import, old_binding.span, other.is_import())) + } + _ => None, + }; + + // Check if the target of the use for both bindings is the same. + let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); + let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); + let from_item = + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); + // Only suggest removing an import if both bindings are to the same def, if both spans + // aren't dummy spans. Further, if both bindings are imports, then the ident must have + // been introduced by an item. + let should_remove_import = duplicate + && !has_dummy_span + && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); + + match import { + Some((import, span, true)) if should_remove_import && import.is_nested() => { + self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); + } + Some((import, _, true)) if should_remove_import && !import.is_glob() => { + // Simple case - remove the entire import. Due to the above match arm, this can + // only be a single use so just remove it entirely. + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } + Some((import, span, _)) => { + self.add_suggestion_for_rename_of_use(&mut err, name, import, span); + } + _ => {} + } -#[derive(Diagnostic)] -#[diag("imports cannot refer to {$what}")] -pub(crate) struct ImportsCannotReferTo<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) what: &'a str, -} + err.emit(); + self.name_already_seen.insert(name, span); + } -#[derive(Diagnostic)] -#[diag("cannot find {$expected} `{$ident}` in this scope")] -pub(crate) struct CannotFindIdentInThisScope<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) expected: &'a str, - pub(crate) ident: Ident, -} + /// This function adds a suggestion to change the binding name of a new import that conflicts + /// with an existing import. + /// + /// ```text,ignore (diagnostic) + /// help: you can use `as` to change the binding name of the import + /// | + /// LL | use foo::bar as other_bar; + /// | ^^^^^^^^^^^^^^^^^^^^^ + /// ``` + fn add_suggestion_for_rename_of_use( + &self, + err: &mut Diag<'_>, + name: Symbol, + import: Import<'_>, + binding_span: Span, + ) { + let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { + format!("Other{name}") + } else { + format!("other_{name}") + }; + + let mut suggestion = None; + let mut span = binding_span; + match import.kind { + ImportKind::Single { source, .. } => { + if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) + && pos as usize <= snippet.len() + { + span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( + binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), + ); + suggestion = Some(format!(" as {suggested_name}")); + } + } + ImportKind::ExternCrate { source, target, .. } => { + suggestion = Some(format!( + "extern crate {} as {};", + source.unwrap_or(target.name), + suggested_name, + )) + } + _ => unreachable!(), + } -#[derive(Subdiagnostic)] -#[note("unsafe traits like `{$ident}` should be implemented explicitly")] -pub(crate) struct ExplicitUnsafeTraits { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + if let Some(suggestion) = suggestion { + err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); + } else { + err.subdiagnostic(ChangeImportBinding { span }); + } + } -#[derive(Subdiagnostic)] -#[note("a macro with the same name exists, but it appears later")] -pub(crate) struct MacroDefinedLater { - #[primary_span] - pub(crate) span: Span, -} + /// This function adds a suggestion to remove an unnecessary binding from an import that is + /// nested. In the following example, this function will be invoked to remove the `a` binding + /// in the second use statement: + /// + /// ```ignore (diagnostic) + /// use issue_52891::a; + /// use issue_52891::{d, a, e}; + /// ``` + /// + /// The following suggestion will be added: + /// + /// ```ignore (diagnostic) + /// use issue_52891::{d, a, e}; + /// ^-- help: remove unnecessary import + /// ``` + /// + /// If the nested use contains only one import then the suggestion will remove the entire + /// line. + /// + /// It is expected that the provided import is nested - this isn't checked by the + /// function. If this invariant is not upheld, this function's behaviour will be unexpected + /// as characters expected by span manipulations won't be present. + fn add_suggestion_for_duplicate_nested_use( + &self, + err: &mut Diag<'_>, + import: Import<'_>, + binding_span: Span, + ) { + assert!(import.is_nested()); + + // Two examples will be used to illustrate the span manipulations we're doing: + // + // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, a, e};`. + // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, e, a};`. + + let (found_closing_brace, span) = + find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); + + // If there was a closing brace then identify the span to remove any trailing commas from + // previous imports. + if found_closing_brace { + if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span }); + } else { + // Remove the entire line if we cannot extend the span back, this indicates an + // `issue_52891::{self}` case. + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } -#[derive(Subdiagnostic)] -#[label("consider moving the definition of `{$ident}` before this call")] -pub(crate) struct MacroSuggMovePosition { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + return; + } -#[derive(Subdiagnostic)] -pub(crate) enum MacroRulesNot { - #[label("`{$ident}` exists, but has no rules for function-like invocation")] - Func { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `attr` rules")] - Attr { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `derive` rules")] - Derive { - #[primary_span] - span: Span, - ident: Ident, - }, -} + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span }); + } -#[derive(Subdiagnostic)] -#[note("maybe you have forgotten to define a name for this `macro_rules!`")] -pub(crate) struct MaybeMissingMacroRulesName { - #[primary_span] - pub(crate) spans: MultiSpan, -} + pub(crate) fn lint_if_path_starts_with_module( + &mut self, + finalize: Finalize, + path: &[Segment], + second_binding: Option>, + ) { + let Finalize { node_id, root_span, .. } = finalize; + + let first_name = match path.get(0) { + // In the 2018 edition this lint is a hard error, so nothing to do + Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { + seg.ident.name + } + _ => return, + }; -#[derive(Subdiagnostic)] -#[help("have you added the `#[macro_use]` on the module/import?")] -pub(crate) struct AddedMacroUse; + // We're only interested in `use` paths which should start with + // `{{root}}` currently. + if first_name != kw::PathRoot { + return; + } -#[derive(Subdiagnostic)] -#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] -pub(crate) struct ConsiderAddingADerive { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} + match path.get(1) { + // If this import looks like `crate::...` it's already good + Some(Segment { ident, .. }) if ident.name == kw::Crate => return, + // Otherwise go below to see if it's an extern crate + Some(_) => {} + // If the path has length one (and it's `PathRoot` most likely) + // then we don't know whether we're gonna be importing a crate or an + // item in our crate. Defer this lint to elsewhere + None => return, + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the import")] -pub(crate) struct CannotDetermineImportResolution { - #[primary_span] - pub(crate) span: Span, -} + // If the first element of our path was actually resolved to an + // `ExternCrate` (also used for `crate::...`) then no need to issue a + // warning, this looks all good! + if let Some(binding) = second_binding + && let DeclKind::Import { import, .. } = binding.kind + // Careful: we still want to rewrite paths from renamed extern crates. + && let ImportKind::ExternCrate { source: None, .. } = import.kind + { + return; + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the {$kind} `{$path}`")] -#[note("import resolution is stuck, try simplifying macro imports")] -pub(crate) struct CannotDetermineMacroResolution { - #[primary_span] - pub(crate) span: Span, - pub(crate) kind: &'static str, - pub(crate) path: String, -} + self.lint_buffer.dyn_buffer_lint_any( + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + node_id, + root_span, + move |dcx, level, sess| { + let (replacement, applicability) = match sess + .downcast_ref::() + .expect("expected a `Session`") + .source_map() + .span_to_snippet(root_span) + { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + diagnostics::AbsPathWithModule { + sugg: diagnostics::AbsPathWithModuleSugg { + span: root_span, + applicability, + replacement, + }, + } + .into_diag(dcx, level) + }, + ); + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] -pub(crate) struct CannotBeReexportedPrivate { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn add_module_candidates( + &self, + module: Module<'ra>, + names: &mut Vec, + filter_fn: &impl Fn(Res) -> bool, + ctxt: Option, + ) { + module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { + let res = binding.res(); + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { + names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); + } + }); + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] -pub(crate) struct CannotBeReexportedCratePublic { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + /// Combines an error with provided span and emits it. + /// + /// This takes the error provided, combines it with the span and any additional spans inside the + /// error and emits it. + pub(crate) fn report_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> ErrorGuaranteed { + self.into_struct_error(span, resolution_error).emit() + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedPrivateNS { - #[primary_span] - #[label("re-export of private `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn into_struct_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> Diag<'_> { + match resolution_error { + ResolutionError::GenericParamsFromOuterItem { + outer_res, + has_generic_params, + def_kind, + inner_item, + current_self_ty, + } => { + use diagnostics::GenericParamsFromOuterItemLabel as Label; + let static_or_const = match def_kind { + DefKind::Static { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static) + } + DefKind::Const { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const) + } + _ => None, + }; + let is_self = + matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); + let mut err = diagnostics::GenericParamsFromOuterItem { + span, + label: None, + refer_to_type_directly: None, + use_let: None, + sugg: None, + static_or_const, + is_self, + item: inner_item.as_ref().map(|(label_span, _, kind)| { + diagnostics::GenericParamsFromOuterItemInnerItem { + span: *label_span, + descr: kind.descr().to_string(), + is_self, + } + }), + }; + + let sm = self.tcx.sess.source_map(); + // Note: do not early return for missing def_id here, + // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. + let def_id = match outer_res { + Res::SelfTyParam { .. } => { + err.label = Some(Label::SelfTyParam(span)); + None + } + Res::SelfTyAlias { alias_to: def_id, .. } => { + err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( + sm, + self.def_span(def_id), + ))); + err.refer_to_type_directly = current_self_ty + .map(|snippet| diagnostics::UseTypeDirectly { span, snippet }); + None + } + Res::Def(DefKind::TyParam, def_id) => { + err.label = Some(Label::TyParam(self.def_span(def_id))); + Some(def_id) + } + Res::Def(DefKind::ConstParam, def_id) => { + err.label = Some(Label::ConstParam(self.def_span(def_id))); + Some(def_id) + } + _ => { + bug!( + "GenericParamsFromOuterItem should only be used with \ + Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ + DefKind::ConstParam" + ); + } + }; + + if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { + err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet { + span: sm.span_until_whitespace(*item_span), + }); + } + + if let Some(def_id) = def_id + && let HasGenericParams::Yes(span) = has_generic_params + && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) + { + let name = self.tcx.item_name(def_id); + let (span, snippet) = if span.is_empty() { + let snippet = format!("<{name}>"); + (span, snippet) + } else { + let span = sm.span_through_char(span, '<').shrink_to_hi(); + let snippet = format!("{name}, "); + (span, snippet) + }; + err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet }); + } + + self.dcx().create_err(err) + } + ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { + self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList { + span, + first_use_span, + name, + }) + } + ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { + self.dcx().create_err(diagnostics::MethodNotMemberOfTrait { + span, + method, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists { + span: method.span, + candidate: c, + }), + }) + } + ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { + self.dcx().create_err(diagnostics::TypeNotMemberOfTrait { + span, + type_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists { + span: type_.span, + candidate: c, + }), + }) + } + ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { + self.dcx().create_err(diagnostics::ConstNotMemberOfTrait { + span, + const_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists { + span: const_.span, + candidate: c, + }), + }) + } + ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { + let BindingError { name, target, origin, could_be_path } = binding_error; + + let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); + origin_sp.sort(); + origin_sp.dedup(); + + let msp = MultiSpan::from_spans(target_sp.clone()); + let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns { + multispan: msp, + name, + }); + for sp in target_sp { + err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name }); + } + for sp in &origin_sp { + err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp }); + } + let mut suggested_typo = false; + if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) + && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) + { + // The check above is so that when we encounter `match foo { (a | b) => {} }`, + // we don't suggest `(a | a) => {}`, which would never be what the user wants. + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(diagnostics::PatternBindingTypo { + spans: origin_sp, + typo, + }); + suggested_typo = true; + } + } + if could_be_path { + let import_suggestions = self.lookup_import_candidates( + name, + Namespace::ValueNS, + &parent_scope, + &|res: Res| { + matches!( + res, + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const) + | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) + | DefKind::Const { .. } + | DefKind::AssocConst { .. }, + _, + ) + ) + }, + ); + + if import_suggestions.is_empty() && !suggested_typo { + let kind_matches: [fn(DefKind) -> bool; 4] = [ + |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), + |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), + |kind| matches!(kind, DefKind::Const { .. }), + |kind| matches!(kind, DefKind::AssocConst { .. }), + ]; + let mut local_names = vec![]; + self.add_module_candidates( + parent_scope.module, + &mut local_names, + &|res| matches!(res, Res::Def(_, _)), + None, + ); + let local_names: FxHashSet<_> = local_names + .into_iter() + .filter_map(|s| match s.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + }) + .collect(); + + let mut local_suggestions = vec![]; + let mut suggestions = vec![]; + for matches_kind in kind_matches { + if let Some(suggestion) = self.early_lookup_typo_candidate( + ScopeSet::All(Namespace::ValueNS), + &parent_scope, + name, + &|res: Res| match res { + Res::Def(k, _) => matches_kind(k), + _ => false, + }, + ) && let Res::Def(kind, mut def_id) = suggestion.res + { + if let DefKind::Ctor(_, _) = kind { + def_id = self.tcx.parent(def_id); + } + let kind = kind.descr(def_id); + if local_names.contains(&def_id) { + // The item is available in the current scope. Very likely to + // be a typo. Don't use the full path. + local_suggestions.push(( + suggestion.candidate, + suggestion.candidate.to_string(), + kind, + )); + } else { + suggestions.push(( + suggestion.candidate, + self.def_path_str(def_id), + kind, + )); + } + } + } + let suggestions = if !local_suggestions.is_empty() { + // There is at least one item available in the current scope that is a + // likely typo. We only show those. + local_suggestions + } else { + suggestions + }; + for (name, sugg, kind) in suggestions { + err.span_suggestion_verbose( + span, + format!( + "you might have meant to use the similarly named {kind} `{name}`", + ), + sugg, + Applicability::MaybeIncorrect, + ); + suggested_typo = true; + } + } + if import_suggestions.is_empty() && !suggested_typo { + let help_msg = format!( + "if you meant to match on a unit struct, unit variant or a `const` \ + item, consider making the path in the pattern qualified: \ + `path::to::ModOrType::{name}`", + ); + err.span_help(span, help_msg); + } + show_candidates( + self.tcx, + &mut err, + Some(span), + &import_suggestions, + Instead::No, + FoundUse::Yes, + DiagMode::Pattern, + vec![], + "", + ); + } + err + } + ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { + self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode { + span, + first_binding_span, + variable_name, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList { + span, + identifier, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern { + span, + identifier, + }) + } + ResolutionError::UndeclaredLabel { name, suggestion } => { + let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion + { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::LabelWithSimilarNameReachable(ident.span)), + Some(diagnostics::TryUsingSimilarlyNamedLabel { + span, + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelWithSimilarNameExists { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UndeclaredLabel { + span, + name, + sub_reachable, + sub_reachable_suggestion, + sub_unreachable, + }) + } + ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { + let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); + err.span_label(span, label); + + if let Some((suggestions, msg, applicability)) = suggestion { + if suggestions.is_empty() { + err.help(msg); + return err; + } + err.multipart_suggestion(msg, suggestions, applicability); + } + + let module = match module { + Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, + _ => CRATE_DEF_ID.to_def_id(), + }; + self.find_cfg_stripped(&mut err, &segment, module); + + err + } + ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { + self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span }) + } + ResolutionError::AttemptToUseNonConstantValueInConstant { + ident, + suggestion, + current, + type_span, + } => { + // let foo =... + // ^^^ given this Span + // ------- get this Span to have an applicable suggestion + + // edit: + // only do this if the const and usage of the non-constant value are on the same line + // the further the two are apart, the higher the chance of the suggestion being wrong + + let sp = self + .tcx + .sess + .source_map() + .span_extend_to_prev_str(ident.span, current, true, false); + + let (with, with_label, without) = match sp { + Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { + let sp = sp + .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) + .until(ident.span); + + // Only suggest replacing the binding keyword if this is a simple + // binding. + // + // Note: this approach still incorrectly suggests for irrefutable + // patterns like `if let x = 1 { const { x } }`, since the text + // between `let` and the identifier is just whitespace. + // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs + let is_simple_binding = + self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { + let after_keyword = snippet[current.len()..].trim(); + after_keyword.is_empty() || after_keyword == "mut" + }); + + if is_simple_binding { + ( + Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion { + span: sp, + suggestion, + current, + type_span, + }), + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } else { + ( + None, + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } + } + _ => ( + None, + None, + Some( + diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion { + ident_span: ident.span, + suggestion, + }, + ), + ), + }; + + self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant { + span, + with, + with_label, + without, + }) + } + ResolutionError::BindingShadowsSomethingUnacceptable { + shadowing_binding, + name, + participle, + article, + shadowed_binding, + shadowed_binding_span, + } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable { + span, + shadowing_binding, + shadowed_binding, + article, + sub_suggestion: match (shadowing_binding, shadowed_binding) { + ( + PatternSource::Match, + Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), + ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion { + span, + name, + }), + _ => None, + }, + shadowed_binding_span, + participle, + name, + }), + ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span }) + } + ForwardGenericParamBanReason::ConstParamTy => self + .dcx() + .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }), + }, + ResolutionError::ParamInTyOfConstParam { name } => { + self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) + } + ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { + span, + name, + param_kind: is_type, + help: self.tcx.sess.is_nightly_build(), + is_gca, + help_gca: is_gca, + }) + } + ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { + span, + name, + param_kind: is_type, + }) + } + ResolutionError::ForwardDeclaredSelf(reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) + } + ForwardGenericParamBanReason::ConstParamTy => { + self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) + } + }, + ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { + let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = + match suggestion { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::UnreachableLabelSubLabel { + ident_span: ident.span, + }), + Some(diagnostics::UnreachableLabelSubSuggestion { + span, + // intentionally taking 'ident.name' instead of 'ident' itself, as this + // could be used in suggestion context + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelSubLabelUnreachable { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UnreachableLabel { + span, + name, + definition_span, + sub_suggestion, + sub_suggestion_label, + sub_unreachable_label, + }) + } + ResolutionError::TraitImplMismatch { + name, + kind, + code, + trait_item_span, + trait_path, + } => self + .dcx() + .create_err(diagnostics::TraitImplMismatch { + span, + name, + kind, + trait_path, + trait_item_span, + }) + .with_code(code), + ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { + self.dcx().create_err(diagnostics::TraitImplDuplicate { + span, + name, + trait_item_span, + old_span, + }) + } + ResolutionError::InvalidAsmSym => { + self.dcx().create_err(diagnostics::InvalidAsmSym { span }) + } + ResolutionError::LowercaseSelf => { + self.dcx().create_err(diagnostics::LowercaseSelf { span }) + } + ResolutionError::BindingInNeverPattern => { + self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) + } + } + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedCratePublicNS { - #[primary_span] - #[label("re-export of crate public `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn report_vis_error( + &mut self, + vis_resolution_error: VisResolutionError, + ) -> ErrorGuaranteed { + match vis_resolution_error { + VisResolutionError::Relative2018(span, path) => { + self.dcx().create_err(diagnostics::Relative2018 { + span, + path_span: path.span, + // intentionally converting to String, as the text would also be used as + // in suggestion context + path_str: pprust::path_to_string(&path), + }) + } + VisResolutionError::AncestorOnly(span) => { + self.dcx().create_err(diagnostics::AncestorOnly(span)) + } + VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + segment, + label, + suggestion, + module: None, + message, + }, + ), + VisResolutionError::ExpectedFound(span, path_str, res) => { + self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str }) + } + VisResolutionError::Indeterminate(span) => { + self.dcx().create_err(diagnostics::Indeterminate(span)) + } + VisResolutionError::ModuleOnly(span) => { + self.dcx().create_err(diagnostics::ModuleOnly(span)) + } + } + .emit() + } -#[derive(Diagnostic)] -#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] -pub(crate) struct PrivateExternCrateReexport { - pub ident: Ident, - #[suggestion( - "consider making the `extern crate` item publicly accessible", - code = "pub ", - style = "verbose", - applicability = "maybe-incorrect" - )] - pub sugg: Span, -} + pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + path.push(def_id); + if def_id.is_top_level_module() { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + path.into_iter() + .rev() + .map(|def_id| { + self.tcx + .opt_item_name(def_id) + .map(|name| { + match ( + def_id.is_top_level_module(), + def_id.is_local(), + self.tcx.sess.edition(), + ) { + (true, true, Edition::Edition2015) => String::new(), + (true, true, _) => kw::Crate.to_string(), + (true, false, _) | (false, _, _) => name.to_string(), + } + }) + .unwrap_or_else(|| "_".to_string()) + }) + .collect::>() + .join("::") + } -#[derive(Subdiagnostic)] -#[help("consider adding a `#[macro_export]` to the macro in the imported module")] -pub(crate) struct ConsiderAddingMacroExport { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn add_scope_set_candidates( + &mut self, + suggestions: &mut Vec, + scope_set: ScopeSet<'ra>, + ps: &ParentScope<'ra>, + sp: Span, + filter_fn: &impl Fn(Res) -> bool, + ) { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { + match scope { + Scope::DeriveHelpers(expn_id) => { + let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); + if filter_fn(res) { + suggestions.extend( + this.helper_attrs.get(&expn_id).into_iter().flatten().map( + |&(ident, orig_ident_span, _)| { + TypoSuggestion::new(ident.name, orig_ident_span, res) + }, + ), + ); + } + } + Scope::DeriveHelpersCompat => { + // Never recommend deprecated helper attributes. + } + Scope::MacroRules(macro_rules_scope) => { + if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { + let res = macro_rules_def.decl.res(); + if filter_fn(res) { + suggestions.push(TypoSuggestion::new( + macro_rules_def.ident.name, + macro_rules_def.orig_ident_span, + res, + )) + } + } + } + Scope::ModuleNonGlobs(module, _) => { + this.add_module_candidates(module, suggestions, filter_fn, None); + } + Scope::ModuleGlobs(..) => { + // Already handled in `ModuleNonGlobs`. + } + Scope::MacroUsePrelude => { + suggestions.extend(this.macro_use_prelude.iter().filter_map( + |(name, binding)| { + let res = binding.res(); + filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) + }, + )); + } + Scope::BuiltinAttrs => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); + if filter_fn(res) { + suggestions.extend( + BUILTIN_ATTRIBUTES + .iter() + // These trace attributes are compiler-generated and have + // deliberately invalid names. + .filter(|attr| { + !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) + }) + .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), + ); + } + } + Scope::ExternPreludeItems => { + // Add idents from both item and flag scopes. + suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { + let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); + filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) + })); + } + Scope::ExternPreludeFlags => {} + Scope::ToolPrelude => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); + suggestions.extend( + this.registered_tools + .iter() + .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), + ); + } + Scope::StdLibPrelude => { + if let Some(prelude) = this.prelude { + let mut tmp_suggestions = Vec::new(); + this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); + suggestions.extend( + tmp_suggestions + .into_iter() + .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), + ); + } + } + Scope::BuiltinTypes => { + suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { + let res = Res::PrimTy(*prim_ty); + filter_fn(res) + .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) + })) + } + } -#[derive(Subdiagnostic)] -#[suggestion( - "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", - code = "pub(crate)", - applicability = "maybe-incorrect" -)] -pub(crate) struct ConsiderMarkingAsPubCrate { - #[primary_span] - pub(crate) vis_span: Span, -} + ControlFlow::<()>::Continue(()) + }); + } -#[derive(Subdiagnostic)] -#[note("consider marking `{$ident}` as `pub` in the imported module")] -pub(crate) struct ConsiderMarkingAsPub { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + /// Lookup typo candidate in scope for a macro or import. + fn early_lookup_typo_candidate( + &mut self, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + ident: Ident, + filter_fn: &impl Fn(Res) -> bool, + ) -> Option { + let mut suggestions = Vec::new(); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); + + // Make sure error reporting is deterministic. + suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); + + match find_best_match_for_name( + &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), + ident.name, + None, + ) { + Some(found) if found != ident.name => { + suggestions.into_iter().find(|suggestion| suggestion.candidate == found) + } + _ => None, + } + } -#[derive(Diagnostic)] -#[diag("cannot glob-import all possible crates")] -pub(crate) struct CannotGlobImportAllCrates { - #[primary_span] - pub(crate) span: Span, -} + fn lookup_import_candidates_from_module( + &self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + start_module: Module<'ra>, + crate_path: ThinVec, + filter_fn: FilterFn, + ) -> Vec + where + FilterFn: Fn(Res) -> bool, + { + let mut candidates = Vec::new(); + let mut seen_modules = FxHashSet::default(); + let start_did = start_module.def_id(); + let mut worklist = vec![( + start_module, + ThinVec::::new(), + true, + start_did.is_local() || !self.tcx.is_doc_hidden(start_did), + true, + )]; + let mut worklist_via_import = vec![]; + + while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = + match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } + { + let in_module_is_extern = !in_module.def_id().is_local(); + in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { + // Avoid non-importable candidates. + if name_binding.is_assoc_item() + && !this.tcx.features().import_trait_associated_functions() + { + return; + } + + if ident.name == kw::Underscore { + return; + } + + let child_accessible = + accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); + + // do not venture inside inaccessible items of other crates + if in_module_is_extern && !child_accessible { + return; + } + + let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); + + // There is an assumption elsewhere that paths of variants are in the enum's + // declaration and not imported. With this assumption, the variant component is + // chopped and the rest of the path is assumed to be the enum's own path. For + // errors where a variant is used as the type instead of the enum, this causes + // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. + if via_import && name_binding.is_possibly_imported_variant() { + return; + } + + // #90113: Do not count an inaccessible reexported item as a candidate. + if let DeclKind::Import { source_decl, .. } = name_binding.kind + && this.is_accessible_from(source_decl.vis(), parent_scope.module) + && !this.is_accessible_from(name_binding.vis(), parent_scope.module) + { + return; + } + + let res = name_binding.res(); + let did = match res { + Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), + _ => res.opt_def_id(), + }; + let child_doc_visible = doc_visible + && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); + + // collect results based on the filter function + // avoid suggesting anything from the same module in which we are resolving + // avoid suggesting anything with a hygienic name + if ident.name == lookup_ident.name + && ns == namespace + && in_module != parent_scope.module + && ident.ctxt.is_root() + && filter_fn(res) + { + // create the path + let mut segms = if lookup_ident.span.at_least_rust_2018() { + // crate-local absolute paths start with `crate::` in edition 2018 + // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) + crate_path.clone() + } else { + ThinVec::new() + }; + segms.append(&mut path_segments.clone()); + + segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + let path = Path { span: name_binding.span, segments: segms, tokens: None }; + + if child_accessible + // Remove invisible match if exists + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.accessible) + { + candidates.remove(idx); + } + + let is_stable = if is_stable + && let Some(did) = did + && this.is_stable(did, path.span) + { + true + } else { + false + }; + + // Rreplace unstable suggestions if we meet a new stable one, + // and do nothing if any other situation. For example, if we + // meet `std::ops::Range` after `std::range::legacy::Range`, + // we will remove the latter and then insert the former. + if is_stable + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) + { + candidates.remove(idx); + } + + if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + // See if we're recommending TryFrom, TryInto, or FromIterator and add + // a note about editions + let note = if let Some(did) = did { + let requires_note = !did.is_local() + && find_attr!( + this.tcx, + did, + RustcDiagnosticItem( + sym::TryInto | sym::TryFrom | sym::FromIterator + ) + ); + requires_note.then(|| { + format!( + "'{}' is included in the prelude starting in Edition 2021", + path_names_to_string(&path) + ) + }) + } else { + None + }; + + candidates.push(ImportSuggestion { + did, + descr: res.descr(), + path, + accessible: child_accessible, + doc_visible: child_doc_visible, + note, + via_import, + is_stable, + }); + } + } + + // collect submodules to explore + if let Some(def_id) = name_binding.res().module_like_def_id() { + // form the path + let mut path_segments = path_segments.clone(); + path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + + let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind + && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind + && import.parent_scope.expansion == parent_scope.expansion + { + true + } else { + false + }; + + let is_extern_crate_that_also_appears_in_prelude = + name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); + + if !is_extern_crate_that_also_appears_in_prelude || alias_import { + // add the module to the lookup + if seen_modules.insert(def_id) { + if via_import { &mut worklist_via_import } else { &mut worklist }.push( + ( + this.expect_module(def_id), + path_segments, + child_accessible, + child_doc_visible, + is_stable && this.is_stable(def_id, name_binding.span), + ), + ); + } + } + } + }) + } -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to write a const parameter here", - code = "const ", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyToConstParamSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, -} + candidates + } -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", - code = "{snippet}", - applicability = "machine-applicable", - style = "verbose" -)] -pub(crate) struct UnexpectedMissingConstParameter { - #[primary_span] - pub span: Span, - pub snippet: String, - pub item_name: String, - pub item_location: String, -} + fn is_stable(&self, did: DefId, span: Span) -> bool { + if did.is_local() { + return true; + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to write a const parameter here", - applicability = "has-placeholders", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { - #[suggestion_part(code = "const ")] - pub before: Span, - #[suggestion_part(code = ": /* Type */")] - pub after: Span, -} + match self.tcx.lookup_stability(did) { + Some(Stability { + level: StabilityLevel::Unstable { implied_by, .. }, feature, .. + }) => { + if span.allows_unstable(feature) { + true + } else if self.tcx.features().enabled(feature) { + true + } else if let Some(implied_by) = implied_by + && self.tcx.features().enabled(implied_by) + { + true + } else { + false + } + } + Some(_) => true, + None => false, + } + } -#[derive(Subdiagnostic)] -#[suggestion( - "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { - #[primary_span] - pub span: Span, - pub ident: Ident, - pub snippet: String, -} + /// When name resolution fails, this method can be used to look up candidate + /// entities with the expected name. It allows filtering them using the + /// supplied predicate (which should be used to only accept the types of + /// definitions expected, e.g., traits). The lookup spans across all crates. + /// + /// N.B., the method does not look into imports, but this is not a problem, + /// since we report the definitions (thus, the de-aliased imports). + pub(crate) fn lookup_import_candidates( + &mut self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + filter_fn: FilterFn, + ) -> Vec + where + FilterFn: Fn(Res) -> bool, + { + let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; + let mut suggestions = self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + self.graph_root.to_module(), + crate_path, + &filter_fn, + ); + + if lookup_ident.span.at_least_rust_2018() { + for (ident, entry) in &self.extern_prelude { + if entry.span().from_expansion() { + // Idents are adjusted to the root context before being + // resolved in the extern prelude, so reporting this to the + // user is no help. This skips the injected + // `extern crate std` in the 2018 edition, which would + // otherwise cause duplicate suggestions. + continue; + } + let Some(crate_id) = + self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) + else { + continue; + }; + + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == *ident + && let Some(decl) = name_resolution.borrow().best_decl() + { + match decl.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); + } + crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); + + suggestions.extend(self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + crate_root, + crate_path, + &filter_fn, + )); + } + } -#[derive(Diagnostic)] -#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] -pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { - #[primary_span] - pub(crate) span: Span, -} + suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); + suggestions + } -#[derive(Diagnostic)] -#[diag("`extern crate self;` requires renaming")] -pub(crate) struct ExternCrateSelfRequiresRenaming { - #[primary_span] - #[suggestion( - "rename the `self` crate to be able to import it", - code = "extern crate self as name;", - applicability = "has-placeholders" - )] - pub(crate) span: Span, -} + pub(crate) fn unresolved_macro_suggestions( + &mut self, + err: &mut Diag<'_>, + macro_kind: MacroKind, + parent_scope: &ParentScope<'ra>, + ident: Ident, + krate: &Crate, + sugg_span: Option, + ) { + // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for + // suggestions. + self.register_macros_for_all_crates(); + + let is_expected = + &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); + let suggestion = self.early_lookup_typo_candidate( + ScopeSet::Macro(macro_kind), + parent_scope, + ident, + is_expected, + ); + if !self.add_typo_suggestion(err, suggestion, ident.span) { + self.detect_derive_attribute(err, ident, parent_scope, sugg_span); + } -#[derive(Diagnostic)] -#[diag("`{$name}` is already in scope")] -#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] -pub(crate) struct MacroUseNameAlreadyInUse { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} + let import_suggestions = + self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); + let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { + DUMMY_NODE_ID => (None, FoundUse::No), + node_id => UsePlacementFinder::check(krate, node_id), + }; + show_candidates( + self.tcx, + err, + span, + &import_suggestions, + Instead::No, + found_use, + DiagMode::Normal, + vec![], + "", + ); + + if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { + let label_span = ident.span.shrink_to_hi(); + let mut spans = MultiSpan::from_span(label_span); + spans.push_span_label(label_span, "put a macro name here"); + err.subdiagnostic(MaybeMissingMacroRulesName { spans }); + return; + } -#[derive(Diagnostic)] -#[diag("imported macro not found", code = E0469)] -pub(crate) struct ImportedMacroNotFound { - #[primary_span] - pub(crate) span: Span, -} + if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { + err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); + return; + } -#[derive(Diagnostic)] -#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] -pub(crate) struct MacroExternDeprecated { - #[primary_span] - pub(crate) span: Span, - #[help("try an outer attribute: `#[macro_use]`")] - pub inner_attribute: bool, -} + let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { + if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } + }); + + if let Some((def_id, unused_ident)) = unused_macro { + let scope = self.local_macro_def_scopes[&def_id]; + let parent_nearest = parent_scope.module.nearest_parent_mod(); + let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); + if !unused_macro_kinds.contains(macro_kind.into()) { + match macro_kind { + MacroKind::Bang => { + err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); + } + MacroKind::Attr => { + err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); + } + MacroKind::Derive => { + err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); + } + } + return; + } + if Some(parent_nearest) == scope.opt_def_id() { + err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); + err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); + return; + } + } -#[derive(Diagnostic)] -#[diag("arguments to `macro_use` are not allowed here")] -pub(crate) struct ArgumentsMacroUseNotAllowed { - #[primary_span] - pub(crate) span: Span, -} + if ident.name == kw::Default + && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind + { + let span = self.def_span(def_id); + let source_map = self.tcx.sess.source_map(); + let head_span = source_map.guess_head_span(span); + err.subdiagnostic(ConsiderAddingADerive { + span: head_span.shrink_to_lo(), + suggestion: "#[derive(Default)]\n".to_string(), + }); + } + for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { + let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns), + parent_scope, + None, + None, + None, + ) else { + continue; + }; + + let desc = match binding.res() { + Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { + "a function-like macro".to_string() + } + Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { + format!("an attribute: `#[{ident}]`") + } + Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { + format!("a derive macro: `#[derive({ident})]`") + } + Res::Def(DefKind::Macro(kinds), _) => { + format!("{} {}", kinds.article(), kinds.descr()) + } + Res::ToolMod | Res::OpenMod(..) => { + // Don't confuse the user with tool modules or open modules. + continue; + } + Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { + "only a trait, without a derive macro".to_string() + } + res => format!( + "{} {}, not {} {}", + res.article(), + res.descr(), + macro_kind.article(), + macro_kind.descr_expected(), + ), + }; + if let crate::DeclKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + let note = diagnostics::IdentImporterHereButItIsDesc { + span: import.span, + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + // Silence the 'unused import' warning we might get, + // since this diagnostic already covers that import. + self.record_use(ident, binding, Used::Other); + return; + } + let note = diagnostics::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + return; + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "try renaming it with a name", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnnamedImportSugg { - #[suggestion_part(code = "{ident} as name")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + if self.macro_names.contains(&IdentKey::new(ident)) { + err.subdiagnostic(AddedMacroUse); + return; + } + } -#[derive(Diagnostic)] -#[diag("imports need to be explicitly named")] -pub(crate) struct UnnamedImport { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) sugg: UnnamedImportSugg, -} + /// Given an attribute macro that failed to be resolved, look for `derive` macros that could + /// provide it, either as-is or with small typos. + fn detect_derive_attribute( + &self, + err: &mut Diag<'_>, + ident: Ident, + parent_scope: &ParentScope<'ra>, + sugg_span: Option, + ) { + // Find all of the `derive`s in scope and collect their corresponding declared + // attributes. + // FIXME: this only works if the crate that owns the macro that has the helper_attr + // has already been imported. + let mut derives = vec![]; + let mut all_attrs: UnordMap> = UnordMap::default(); + // We're collecting these in a hashmap, and handle ordering the output further down. + #[allow(rustc::potential_query_instability)] + for (def_id, ext) in self + .local_macro_map + .iter() + .map(|(local_id, ext)| (local_id.to_def_id(), ext)) + .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) + { + for helper_attr in &ext.helper_attrs { + let item_name = self.tcx.item_name(def_id); + all_attrs.entry(*helper_attr).or_default().push(item_name); + if helper_attr == &ident.name { + derives.push(item_name); + } + } + } + let kind = MacroKind::Derive.descr(); + if !derives.is_empty() { + // We found an exact match for the missing attribute in a `derive` macro. Suggest it. + let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); + derives.sort(); + derives.dedup(); + let msg = match &derives[..] { + [derive] => format!(" `{derive}`"), + [start @ .., last] => format!( + "s {} and `{last}`", + start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") + ), + [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), + }; + let msg = format!( + "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ + missing a `derive` attribute", + ident.name, + ); + let sugg_span = + if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { + let span = self.def_span(id); + if span.from_expansion() { + None + } else { + // For enum variants sugg_span is empty but we can get the enum's Span. + Some(span.shrink_to_lo()) + } + } else { + // For items this `Span` will be populated, everything else it'll be None. + sugg_span + }; + match sugg_span { + Some(span) => { + err.span_suggestion_verbose( + span, + msg, + format!("#[derive({})]\n", derives.join(", ")), + Applicability::MaybeIncorrect, + ); + } + None => { + err.note(msg); + } + } + } else { + // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. + let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); + if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) + && let Some(macros) = all_attrs.get(&best_match) + { + let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); + macros.sort(); + macros.dedup(); + let msg = match ¯os[..] { + [] => return, + [name] => format!(" `{name}` accepts"), + [start @ .., end] => format!( + "s {} and `{end}` accept", + start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), + ), + }; + let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); + err.span_suggestion_verbose( + ident.span, + msg, + best_match, + Applicability::MaybeIncorrect, + ); + } + } + } -#[derive(Diagnostic)] -#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] -pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn add_typo_suggestion( + &self, + err: &mut Diag<'_>, + suggestion: Option, + span: Span, + ) -> bool { + let suggestion = match suggestion { + None => return false, + // We shouldn't suggest underscore. + Some(suggestion) if suggestion.candidate == kw::Underscore => return false, + Some(suggestion) => suggestion, + }; + + let mut did_label_def_span = false; + + if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { + if span.overlaps(def_span) { + // Don't suggest typo suggestion for itself like in the following: + // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` + // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 + // | + // LL | struct X {} + // | ----------- `X` defined here + // LL | + // LL | const Y: X = X("ö"); + // | -------------^^^^^^- similarly named constant `Y` defined here + // | + // help: use struct literal syntax instead + // | + // LL | const Y: X = X {}; + // | ^^^^ + // help: a constant with a similar name exists + // | + // LL | const Y: X = Y("ö"); + // | ^ + return false; + } + let span = self.tcx.sess.source_map().guess_head_span(def_span); + let candidate_descr = suggestion.res.descr(); + let candidate = suggestion.candidate; + let label = match suggestion.target { + SuggestionTarget::SimilarlyNamed => { + diagnostics::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } + } + SuggestionTarget::SingleItem => { + diagnostics::DefinedHere::SingleItem { span, candidate_descr, candidate } + } + }; + did_label_def_span = true; + err.subdiagnostic(label); + } -#[derive(Diagnostic)] -#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] -pub(crate) struct ElidedAnonymousLifetimeReportError { - #[primary_span] - #[label("explicit lifetime name needed here")] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) suggestion: Option, -} + let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(span) = suggestion.span + && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') + && snippet == candidate + { + let candidate = suggestion.candidate; + // When the suggested binding change would be from `x` to `_x`, suggest changing the + // original binding definition instead. (#60164) + let msg = format!( + "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" + ); + if !did_label_def_span { + err.span_label(span, format!("`{candidate}` defined here")); + } + (span, msg, snippet) + } else { + let msg = match suggestion.target { + SuggestionTarget::SimilarlyNamed => format!( + "{} {} with a similar name exists", + suggestion.res.article(), + suggestion.res.descr() + ), + SuggestionTarget::SingleItem => { + format!("maybe you meant this {}", suggestion.res.descr()) + } + }; + (span, msg, suggestion.candidate.to_ident_string()) + }; + err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); + true + } -#[derive(Diagnostic)] -#[diag( - "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" -)] -pub(crate) struct LendingIteratorReportError { - #[primary_span] - pub(crate) lifetime: Span, - #[note( - "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" - )] - pub(crate) ty: Span, -} + fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { + let res = b.res(); + if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { + let (built_in, from) = match scope { + Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), + Scope::ExternPreludeFlags + if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() + || matches!(res, Res::OpenMod(..)) => + { + ("", " passed with `--extern`") + } + _ => { + if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { + // These already contain the "built-in" prefix or look bad with it. + ("", "") + } else { + (" built-in", "") + } + } + }; + + let a = if built_in.is_empty() { res.article() } else { "a" }; + format!("{a}{built_in} {thing}{from}", thing = res.descr()) + } else { + let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; + format!("the {thing} {introduced} here", thing = res.descr()) + } + } -#[derive(Diagnostic)] -#[diag("missing lifetime in associated type")] -pub(crate) struct AnonymousLifetimeNonGatReportError { - #[primary_span] - #[label("this lifetime must come from the implemented type")] - pub(crate) lifetime: Span, - #[note( - "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" - )] - pub(crate) decl: MultiSpan, -} + fn ambiguity_diagnostic( + &self, + ambiguity_error: &AmbiguityError<'ra>, + ) -> diagnostics::Ambiguity { + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; + let extern_prelude_ambiguity = || { + // Note: b1 may come from a module scope, as an extern crate item in module. + matches!(scope2, Scope::ExternPreludeFlags) + && self + .extern_prelude + .get(&IdentKey::new(ident)) + .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) + }; + let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { + // We have to print the span-less alternative first, otherwise formatting looks bad. + (b2, b1, scope2, scope1, true) + } else { + (b1, b2, scope1, scope2, false) + }; + + let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { + let what = self.decl_description(b, ident, scope); + let note_msg = format!("`{ident}` could{also} refer to {what}"); + + let thing = b.res().descr(); + let mut help_msgs = Vec::new(); + if b.is_glob_import() + && (kind == AmbiguityKind::GlobVsGlob + || kind == AmbiguityKind::GlobVsExpanded + || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) + { + help_msgs.push(format!( + "consider adding an explicit import of `{ident}` to disambiguate" + )) + } + if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() + { + help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider introducing a higher-ranked lifetime here", - applicability = "machine-applicable" -)] -pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { - #[suggestion_part(code = "for<'a> ")] - pub(crate) lo: Span, - #[suggestion_part(code = "'a ")] - pub(crate) hi: Span, -} + if kind != AmbiguityKind::GlobVsGlob { + if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { + if module == self.graph_root.to_module() { + help_msgs.push(format!( + "use `crate::{ident}` to refer to this {thing} unambiguously" + )); + } else if module.is_normal() { + help_msgs.push(format!( + "use `self::{ident}` to refer to this {thing} unambiguously" + )); + } + } + } -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -pub(crate) struct ExplicitAnonymousLifetimeReportError { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} + ( + Spanned { node: note_msg, span: b.span }, + help_msgs + .iter() + .enumerate() + .map(|(i, help_msg)| { + let or = if i == 0 { "" } else { "or " }; + format!("{or}{help_msg}") + }) + .collect::>(), + ) + }; + let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); + let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); + let help = if kind == AmbiguityKind::GlobVsGlob + && b1 + .parent_module + .and_then(|m| m.opt_def_id()) + .map(|d| !d.is_local()) + .unwrap_or_default() + { + Some(&[ + "consider updating this dependency to resolve this error", + "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", + ] as &[_]) + } else { + None + }; + + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + + diagnostics::Ambiguity { + ident, + help, + ambig_vis, + kind: kind.descr(), + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error: false, + } + } -#[derive(Diagnostic)] -#[diag("implicit elided lifetime not allowed here", code = E0726)] -pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) subdiag: ElidedLifetimeInPathSubdiag, -} + /// If the binding refers to a tuple struct constructor with fields, + /// returns the span of its fields. + fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { + let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = + decl.kind + else { + return None; + }; + + let def_id = self.tcx.parent(ctor_def_id); + self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` + } -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -#[help("use another lifetime specifier")] -pub(crate) struct UnderscoreLifetimeIsReserved { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} + /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. + /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. + /// Returns `None` for block modules that don't have a `DefId`. + fn module_path_names(&self, module: Module<'ra>) -> Option> { + let mut path = Vec::new(); + let mut def_id = module.opt_def_id()?; + while let Some(parent) = self.tcx.opt_parent(def_id) { + if let Some(name) = self.tcx.opt_item_name(def_id) { + path.push(name); + } + if parent.is_top_level_module() { + break; + } + def_id = parent; + } + path.reverse(); + path.insert(0, kw::Crate); + Some(path) + } -#[derive(Diagnostic)] -#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] -pub(crate) struct StaticLifetimeIsReserved { - #[primary_span] - #[label("'static is a reserved lifetime name")] - pub(crate) span: Span, - pub(crate) lifetime: Ident, -} + /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) + /// relative to the current scope, if possible. Only applies to crate-local items and + /// only when the resulting path is actually shorter than the original. + fn shorten_candidate_path( + &self, + suggestion: &mut ImportSuggestion, + current_module: Module<'ra>, + ) { + const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; + + // Only shorten local items. + if suggestion.did.is_none_or(|did| !did.is_local()) { + return; + } -#[derive(Diagnostic)] -#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] -pub(crate) struct VariableIsNotBoundInAllPatterns { - #[primary_span] - pub(crate) multispan: MultiSpan, - pub(crate) name: Ident, -} + // Build current module path: [Crate, foo, bar, ...]. + let Some(current_mod_path) = self.module_path_names(current_module) else { + return; + }; + + // Normalise candidate path: filter out `PathRoot` (`::`), and if the path + // doesn't start with `Crate`, prepend it (edition 2015 paths are relative + // to the crate root without an explicit `crate::` prefix). + let candidate_names = { + let filtered_segments: Vec<_> = suggestion + .path + .segments + .iter() + .filter(|segment| segment.ident.name != kw::PathRoot) + .collect(); + + let mut candidate_names: Vec = + filtered_segments.iter().map(|segment| segment.ident.name).collect(); + if candidate_names.first() != Some(&kw::Crate) { + candidate_names.insert(0, kw::Crate); + } + if candidate_names.len() < 2 { + return; + } + candidate_names + }; + + // The candidate's module path is everything except the last segment (the item name). + let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; + + // Find the longest common prefix between the current module and candidate module paths. + let common_prefix_length = current_mod_path + .iter() + .zip(candidate_mod_names.iter()) + .take_while(|(current, candidate)| current == candidate) + .count(); + + // Non-crate-local item; keep the full absolute path. + if common_prefix_length == 0 { + return; + } -#[derive(Subdiagnostic, Debug, Clone)] -#[label("pattern doesn't bind `{$name}`")] -pub(crate) struct PatternDoesntBindName { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Ident, -} + let super_count = current_mod_path.len() - common_prefix_length; -#[derive(Subdiagnostic, Debug, Clone)] -#[label("variable not in all patterns")] -pub(crate) struct VariableNotInAllPatterns { - #[primary_span] - pub(crate) span: Span, -} + // At the crate root, `use` paths resolve from the crate root anyway, so we can + // drop the `crate::` prefix entirely instead of replacing it with `self::`. + let at_crate_root = current_mod_path.len() == 1; -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to use the similarly named previously used binding `{$typo}`", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct PatternBindingTypo { - #[suggestion_part(code = "{typo}")] - pub(crate) spans: Vec, - pub(crate) typo: Symbol, -} + let mut new_segments = if super_count == 0 && at_crate_root { + ThinVec::new() + } else { + let prefix_keyword = match super_count { + 0 => kw::SelfLower, + 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, + _ => return, // Too many `super` levels; keep the full absolute path. + }; + thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] + }; + for &name in &candidate_names[common_prefix_length..] { + new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); + } -#[derive(Diagnostic)] -#[diag("the name `{$name}` is defined multiple times")] -#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] -pub(crate) struct NameDefinedMultipleTime { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, - pub(crate) descr: &'static str, - pub(crate) container: &'static str, - #[subdiagnostic] - pub(crate) label: NameDefinedMultipleTimeLabel, - #[subdiagnostic] - pub(crate) old_binding_label: Option, -} + // Only apply if the result is strictly shorter than the original path. + if new_segments.len() >= suggestion.path.segments.len() { + return; + } -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeLabel { - #[label("`{$name}` reimported here")] - Reimported { - #[primary_span] - span: Span, - name: Symbol, - }, - #[label("`{$name}` redefined here")] - Redefined { - #[primary_span] - span: Span, - name: Symbol, - }, -} + suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; + } -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { - #[label("previous import of the {$old_kind} `{$name}` here")] - Import { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, - #[label("previous definition of the {$old_kind} `{$name}` here")] - Definition { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, -} + fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { + let PrivacyError { + ident, + decl, + outermost_res, + parent_scope, + single_nested, + dedup_span, + ref source, + } = *privacy_error; + + let res = decl.res(); + let ctor_fields_span = self.ctor_fields_span(decl); + let plain_descr = res.descr().to_string(); + let nonimport_descr = + if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; + let import_descr = nonimport_descr.clone() + " import"; + let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; + + // Print the primary message. + let ident_descr = get_descr(decl); + let mut err = + self.dcx().create_err(diagnostics::IsPrivate { span: ident.span, ident_descr, ident }); + + self.mention_default_field_values(source, ident, &mut err); + + let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { + let mut import_suggestions = self.lookup_import_candidates( + outer_ident, + this_res.ns().unwrap_or(Namespace::TypeNS), + &parent_scope, + &|res: Res| res == this_res, + ); + // Shorten candidate paths using `super::` or `self::` when possible. + for suggestion in &mut import_suggestions { + self.shorten_candidate_path(suggestion, parent_scope.module); + } + let point_to_def = !show_candidates( + self.tcx, + &mut err, + Some(dedup_span.until(outer_ident.span.shrink_to_hi())), + &import_suggestions, + Instead::Yes, + FoundUse::Yes, + DiagMode::Import { append: single_nested, unresolved_import: false }, + vec![], + "", + ); + // If we suggest importing a public re-export, don't point at the definition. + if point_to_def && ident.span != outer_ident.span { + let label = diagnostics::OuterIdentIsNotPubliclyReexported { + span: outer_ident.span, + outer_ident_descr: this_res.descr(), + outer_ident, + }; + err.subdiagnostic(label); + } + !point_to_def + } else { + false + }; + + let mut non_exhaustive = None; + // If an ADT is foreign and marked as `non_exhaustive`, then that's + // probably why we have the privacy error. + // Otherwise, point out if the struct has any private fields. + if let Some(def_id) = res.opt_def_id() + && !def_id.is_local() + && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) + { + non_exhaustive = Some(attr_span); + } else if let Some(span) = ctor_fields_span { + let label = diagnostics::ConstructorPrivateIfAnyFieldPrivate { span }; + err.subdiagnostic(label); + if let Res::Def(_, d) = res + && let Some(fields) = self.field_visibility_spans.get(&d) + { + let spans = fields.iter().map(|span| *span).collect(); + let sugg = diagnostics::ConsiderMakingTheFieldPublic { + spans, + number_of_fields: fields.len(), + }; + err.subdiagnostic(sugg); + } + } -#[derive(Diagnostic)] -#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] -pub(crate) struct IsPrivate<'a> { - #[primary_span] - #[label("private {$ident_descr}")] - pub(crate) span: Span, - pub(crate) ident_descr: &'a str, - pub(crate) ident: Ident, -} + let mut sugg_paths: Vec<(Vec, bool)> = vec![]; + if let Some(mut def_id) = res.opt_def_id() { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + if !def_id.is_top_level_module() { + path.push(def_id); + } else { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + let path_names: Option> = path + .iter() + .rev() + .map(|def_id| { + self.tcx.opt_item_name(*def_id).map(|name| { + Ident::with_dummy_span(if def_id.is_top_level_module() { + kw::Crate + } else { + name + }) + }) + }) + .collect(); + if let Some(&def_id) = path.get(0) + && let Some(path) = path_names + { + if let Some(def_id) = def_id.as_local() { + if self.effective_visibilities.is_directly_public(def_id) { + sugg_paths.push((path, false)); + } + } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) + { + sugg_paths.push((path, false)); + } + } + } -#[derive(Diagnostic)] -#[diag("generic arguments in macro path")] -pub(crate) struct GenericArgumentsInMacroPath { - #[primary_span] - pub(crate) span: Span, -} + // Print the whole import chain to make it easier to see what happens. + let first_binding = decl; + let mut next_binding = Some(decl); + let mut next_ident = ident; + while let Some(binding) = next_binding { + let name = next_ident; + next_binding = match binding.kind { + _ if res == Res::Err => None, + DeclKind::Import { source_decl, import, .. } => match import.kind { + _ if source_decl.span.is_dummy() => None, + ImportKind::Single { source, .. } => { + next_ident = source; + Some(source_decl) + } + ImportKind::Glob { .. } + | ImportKind::MacroUse { .. } + | ImportKind::MacroExport => Some(source_decl), + ImportKind::ExternCrate { .. } => None, + }, + _ => None, + }; + + match binding.kind { + DeclKind::Import { source_decl, import, .. } => { + // Don't include `{{root}}` in suggestions - it's an internal symbol + // that should never be shown to users. + let path = import + .module_path + .iter() + .filter(|seg| seg.ident.name != kw::PathRoot) + .map(|seg| seg.ident.clone()) + .chain(std::iter::once(ident)) + .collect::>(); + let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); + sugg_paths.push((path, through_reexport)); + } + DeclKind::Def(_) => {} + } + let first = binding == first_binding; + let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); + let mut note_span = MultiSpan::from_span(def_span); + if !first && binding.vis().is_public() { + let desc = match binding.kind { + DeclKind::Import { .. } => "re-export", + _ => "directly", + }; + note_span.push_span_label(def_span, format!("you could import this {desc}")); + } + // Final step in the import chain, point out if the ADT is `non_exhaustive` + // which is probably why this privacy violation occurred. + if next_binding.is_none() + && let Some(span) = non_exhaustive + { + note_span.push_span_label( + span, + "cannot be constructed because it is `#[non_exhaustive]`", + ); + } + let note = diagnostics::NoteAndRefersToTheItemDefinedHere { + span: note_span, + binding_descr: get_descr(binding), + binding_name: name, + first, + dots: next_binding.is_some(), + }; + err.subdiagnostic(note); + } + // The suggestion replaces `dedup_span` with a path reaching the failing ident. + // That's valid only when + // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments + // get dropped, and + // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. + // + // See issue #156060. + let can_replace_use = !shown_candidates + && !single_nested + && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); + if can_replace_use { + // We prioritize shorter paths, non-core imports and direct imports over the + // alternatives. + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); + for (sugg, reexport) in sugg_paths { + if sugg.len() <= 1 { + // A single path segment suggestion is wrong. This happens on circular + // imports. `tests/ui/imports/issue-55884-2.rs` + continue; + } + let path = join_path_idents(sugg); + let sugg = if reexport { + diagnostics::ImportIdent::ThroughReExport { span: dedup_span, ident, path } + } else { + diagnostics::ImportIdent::Directly { span: dedup_span, ident, path } + }; + err.subdiagnostic(sugg); + break; + } + } -#[derive(Diagnostic)] -#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] -pub(crate) struct AttributesStartingWithRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} + err.emit(); + } -#[derive(Diagnostic)] -#[diag( - "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" -)] -pub(crate) struct AttributesContainingRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(segment) = struct_expr.path.segments.last() else { return }; + let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; + let Some(Res::Def(_, def_id)) = partial_res.full_res() else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } -#[derive(Diagnostic)] -#[diag("cannot use {$article} {$descr} through an import")] -pub(crate) struct CannotUseThroughAnImport { - #[primary_span] - pub(crate) span: Span, - pub(crate) article: &'static str, - pub(crate) descr: &'static str, - #[note("the {$descr} imported here")] - pub(crate) binding_span: Option, -} + pub(crate) fn find_similarly_named_module_or_crate( + &self, + ident: Symbol, + current_module: Module<'ra>, + ) -> Option { + let mut candidates = self + .extern_prelude + .keys() + .map(|ident| ident.name) + .chain( + self.local_module_map + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .chain( + self.extern_module_map + .borrow() + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .filter(|c| !c.to_string().is_empty()) + .collect::>(); + candidates.sort(); + candidates.dedup(); + find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) + } -#[derive(Diagnostic)] -#[diag("name `{$ident}` is reserved in attribute namespace")] -pub(crate) struct NameReservedInAttributeNamespace { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Symbol, -} + pub(crate) fn report_path_resolution_error( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'ra>, + ribs: Option<&PerNS>>>, + ignore_decl: Option>, + ignore_import: Option>, + module: Option>, + failed_segment_idx: usize, + ident: Ident, + diag_metadata: Option<&DiagMetadata<'_>>, + ) -> (String, String, Option) { + let is_last = failed_segment_idx == path.len() - 1; + let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let module_def_id = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), + _ => None, + }; + let scope = match &path[..failed_segment_idx] { + [.., prev] => { + if prev.ident.name == kw::PathRoot { + format!("the crate root") + } else { + format!("`{}`", prev.ident) + } + } + _ => format!("this scope"), + }; + let message = format!("cannot find `{ident}` in {scope}"); + + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates + .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); + if let Some(candidate) = candidates.get(0) { + let path = { + // remove the possible common prefix of the path + let len = candidate.path.segments.len(); + let start_index = (0..=failed_segment_idx.min(len - 1)) + .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) + .unwrap_or_default(); + let segments = + (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); + Path { segments, span: Span::default(), tokens: None } + }; + ( + message, + String::from("unresolved import"), + Some(( + vec![(ident.span, pprust::path_to_string(&path))], + String::from("a similar path exists"), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == sym::core { + ( + message, + format!("you might be missing crate `{ident}`"), + Some(( + vec![(ident.span, "std".to_string())], + "try using `std` instead of `core`".to_string(), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == kw::Underscore { + ( + "invalid crate or module name `_`".to_string(), + "`_` is not a valid crate or module name".to_string(), + None, + ) + } else if self.tcx.sess.is_rust_2015() { + ( + format!("cannot find module or crate `{ident}` in {scope}"), + format!("use of unresolved module or unlinked crate `{ident}`"), + Some(( + vec![( + self.current_crate_outer_attr_insert_span, + format!("extern crate {ident};\n"), + )], + if was_invoked_from_cargo() { + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add \ + {ident}` to add it to your `Cargo.toml` and import it in your \ + code", + ) + } else { + format!( + "you might be missing a crate named `{ident}`, add it to your \ + project and import it in your code", + ) + }, + Applicability::MaybeIncorrect, + )), + ) + } else { + (message, format!("could not find `{ident}` in the crate root"), None) + } + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => format!("`{parent}`"), + }; + + let mut msg = format!("could not find `{ident}` in {parent}"); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + let binding = if let Some(module) = module { + self.cm() + .resolve_ident_in_module( + module, + ident, + ns_to_try, + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + } else if let Some(ribs) = ribs + && let Some(TypeNS | ValueNS) = opt_ns + { + assert!(ignore_import.is_none()); + match self.resolve_ident_in_lexical_scope( + ident, + ns_to_try, + parent_scope, + None, + &ribs[ns_to_try], + ignore_decl, + diag_metadata, + ) { + // we found a locally-imported or available item/module + Some(LateDecl::Decl(binding)) => Some(binding), + _ => None, + } + } else { + self.cm() + .resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns_to_try), + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + }; + if let Some(binding) = binding { + msg = format!( + "expected {}, found {} `{ident}` in {parent}", + ns.descr(), + binding.res().descr(), + ); + }; + } + (message, msg, None) + } else if ident.name == kw::SelfUpper { + // As mentioned above, `opt_ns` being `None` indicates a module path in import. + // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an + // impl + if opt_ns.is_none() { + (message, "`Self` cannot be used in imports".to_string(), None) + } else { + ( + message, + "`Self` is only available in impls, traits, and type definitions".to_string(), + None, + ) + } + } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { + // Check whether the name refers to an item in the value namespace. + let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); + self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + None, + &ribs[ValueNS], + ignore_decl, + diag_metadata, + ) + } else { + None + }; + let match_span = match binding { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LateDecl::RibDef(Res::Local(id))) => { + Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) + } + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub const Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LateDecl::Decl(name_binding)) => Some(( + name_binding.span, + name_binding.res().article(), + name_binding.res().descr(), + )), + _ => None, + }; + + let message = format!("cannot find type `{ident}` in {scope}"); + let label = if let Some((span, article, descr)) = match_span { + format!( + "`{ident}` is declared as {article} {descr} at `{}`, not a type", + self.tcx + .sess + .source_map() + .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) + ) + } else { + format!("use of undeclared type `{ident}`") + }; + (message, label, None) + } else { + let mut suggestion = None; + if ident.name == sym::alloc { + suggestion = Some(( + vec![], + String::from("add `extern crate alloc` to use the `alloc` crate"), + Applicability::MaybeIncorrect, + )) + } -#[derive(Diagnostic)] -#[diag("cannot find a built-in macro with name `{$ident}`")] -pub(crate) struct CannotFindBuiltinMacroWithName { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + suggestion = suggestion.or_else(|| { + self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( + |sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + ) + }, + ) + }); + if let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ValueNS), + parent_scope, + None, + ignore_decl, + ignore_import, + ) { + let descr = binding.res().descr(); + let message = format!("cannot find module or crate `{ident}` in {scope}"); + (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) + } else { + let suggestion = if suggestion.is_some() { + suggestion + } else if let Some(m) = self.undeclared_module_exists(ident) { + self.undeclared_module_suggest_declare(ident, m) + } else if was_invoked_from_cargo() { + Some(( + vec![], + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml`", + ), + Applicability::MaybeIncorrect, + )) + } else { + Some(( + vec![], + format!("you might be missing a crate named `{ident}`",), + Applicability::MaybeIncorrect, + )) + }; + let message = format!("cannot find module or crate `{ident}` in {scope}"); + ( + message, + format!("use of unresolved module or unlinked crate `{ident}`"), + suggestion, + ) + } + } + } -#[derive(Diagnostic)] -#[diag("tool `{$tool}` was already registered")] -pub(crate) struct ToolWasAlreadyRegistered { - #[primary_span] - pub(crate) span: Span, - pub(crate) tool: Ident, - #[label("already registered here")] - pub(crate) old_ident_span: Span, -} + fn undeclared_module_suggest_declare( + &self, + ident: Ident, + path: std::path::PathBuf, + ) -> Option<(Vec<(Span, String)>, String, Applicability)> { + Some(( + vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], + format!( + "to make use of source file {}, use `mod {ident}` \ + in this file to declare the module", + path.display() + ), + Applicability::MaybeIncorrect, + )) + } -#[derive(Subdiagnostic)] -pub(crate) enum DefinedHere { - #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] - SimilarlyNamed { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, - #[label("{$candidate_descr} `{$candidate}` defined here")] - SingleItem { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, -} + fn undeclared_module_exists(&self, ident: Ident) -> Option { + let map = self.tcx.sess.source_map(); + + let src = map.span_to_filename(ident.span).into_local_path()?; + let i = ident.as_str(); + // FIXME: add case where non parent using undeclared module (hard?) + let dir = src.parent()?; + let src = src.file_stem()?.to_str()?; + for file in [ + // …/x.rs + dir.join(i).with_extension("rs"), + // …/x/mod.rs + dir.join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + if !matches!(src, "main" | "lib" | "mod") { + for file in [ + // …/x/y.rs + dir.join(src).join(i).with_extension("rs"), + // …/x/y/mod.rs + dir.join(src).join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + } + None + } -#[derive(Subdiagnostic)] -#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] -pub(crate) struct OuterIdentIsNotPubliclyReexported { - #[primary_span] - pub(crate) span: Span, - pub(crate) outer_ident_descr: &'static str, - pub(crate) outer_ident: Ident, -} + /// Adds suggestions for a path that cannot be resolved. + #[instrument(level = "debug", skip(self, parent_scope))] + pub(crate) fn make_path_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + match path[..] { + // `{{root}}::ident::...` on both editions. + // On 2015 `{{root}}` is usually added implicitly. + [first, second, ..] + if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} + // `ident::...` on 2018. + [first, ..] + if first.ident.span.at_least_rust_2018() + && !first.ident.is_path_segment_keyword() => + { + // Insert a placeholder that's later replaced by `self`/`super`/etc. + path.insert(0, Segment::from_ident(Ident::dummy())); + } + _ => return None, + } -#[derive(Subdiagnostic)] -#[label("a constructor is private if any of the fields is private")] -pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { - #[primary_span] - pub(crate) span: Span, -} + self.make_missing_self_suggestion(path.clone(), parent_scope) + .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "{ $number_of_fields -> - [one] consider making the field publicly accessible - *[other] consider making the fields publicly accessible - }", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct ConsiderMakingTheFieldPublic { - #[suggestion_part(code = "pub ")] - pub(crate) spans: Vec, - pub(crate) number_of_fields: usize, -} + /// Suggest a missing `self::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `self::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_self_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `self` and check if that is valid. + path[0].ident.name = kw::SelfLower; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } -#[derive(Subdiagnostic)] -pub(crate) enum ImportIdent { - #[suggestion( - "import `{$ident}` through the re-export", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - ThroughReExport { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, - #[suggestion( - "import `{$ident}` directly", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - Directly { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, -} + /// Suggests a missing `crate::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `crate::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Crate; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { + Some(( + path, + Some( + "`use` statements changed in Rust 2018; read more at \ + " + .to_string(), + ), + )) + } else { + None + } + } -#[derive(Subdiagnostic)] -#[note( - "{$first -> - [true] {$dots -> - [true] the {$binding_descr} `{$binding_name}` is defined here... - *[false] the {$binding_descr} `{$binding_name}` is defined here - } - *[false] {$dots -> - [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... - *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here - } - }" -)] -pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { - #[primary_span] - pub(crate) span: MultiSpan, - pub(crate) binding_descr: &'a str, - pub(crate) binding_name: Ident, - pub(crate) first: bool, - pub(crate) dots: bool, -} + /// Suggests a missing `super::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `super::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_super_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Super; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } -#[derive(Subdiagnostic)] -#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] -pub(crate) struct RemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} + /// Suggests a missing external crate name if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foobar::Baz; + /// | ^^^^^^ did you mean `baz::foobar`? + /// ``` + /// + /// Used when importing a submodule of an external crate but missing that crate's + /// name as the first part of path. + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_external_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + if path[1].ident.span.is_rust_2015() { + return None; + } -#[derive(Subdiagnostic)] -#[suggestion( - "remove unnecessary import", - code = "", - applicability = "maybe-incorrect", - style = "tool-only" -)] -pub(crate) struct ToolOnlyRemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} + // Sort extern crate names in *reverse* order to get + // 1) some consistent ordering for emitted diagnostics, and + // 2) `std` suggestions before `core` suggestions. + let mut extern_crate_names = + self.extern_prelude.keys().map(|ident| ident.name).collect::>(); + extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); + + for name in extern_crate_names.into_iter() { + // Replace first ident with a crate name and check if that is valid. + path[0].ident.name = name; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?name, ?result); + if let PathResult::Module(..) = result { + return Some((path, None)); + } + } -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] -pub(crate) struct IdentImporterHereButItIsDesc<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} + None + } -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] -pub(crate) struct IdentInScopeButItIsDesc<'a> { - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} + /// Suggests importing a macro from the root of the crate rather than a module within + /// the crate. + /// + /// ```text + /// help: a macro with this name exists at the root of the crate + /// | + /// LL | use issue_59764::makro; + /// | ^^^^^^^^^^^^^^^^^^ + /// | + /// = note: this could be because a macro annotated with `#[macro_export]` will be exported + /// at the root of the crate instead of the module where it is defined + /// ``` + pub(crate) fn check_for_module_export_macro( + &mut self, + import: Import<'ra>, + module: ModuleOrUniformRoot<'ra>, + ident: Ident, + ) -> Option<(Option, Option)> { + let ModuleOrUniformRoot::Module(mut crate_module) = module else { + return None; + }; -pub(crate) struct FoundItemConfigureOut { - pub(crate) span: Span, - pub(crate) item_was: ItemWas, -} + while let Some(parent) = crate_module.parent { + crate_module = parent; + } -pub(crate) enum ItemWas { - BehindFeature { feature: Symbol, span: Span }, - CfgOut { span: Span }, -} + if module == ModuleOrUniformRoot::Module(crate_module) { + // Don't make a suggestion if the import was already from the root of the crate. + return None; + } -impl Subdiagnostic for FoundItemConfigureOut { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - let mut multispan: MultiSpan = self.span.into(); - match self.item_was { - ItemWas::BehindFeature { feature, span } => { - let value = feature.into_diag_arg(&mut None); - let msg = msg!("the item is gated behind the `{$feature}` feature") - .arg("feature", value) - .format(); - multispan.push_span_label(span, msg); + let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); + let binding = self.resolution(crate_module, binding_key)?.best_decl()?; + let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { + return None; + }; + if !kinds.contains(MacroKinds::BANG) { + return None; + } + let module_name = crate_module.name().unwrap_or(kw::Crate); + let import_snippet = match import.kind { + ImportKind::Single { source, target, .. } if source != target => { + format!("{source} as {target}") + } + _ => format!("{ident}"), + }; + + let mut corrections: Vec<(Span, String)> = Vec::new(); + if !import.is_nested() { + // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove + // intermediate segments. + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); + } else { + // Find the binding span (and any trailing commas and spaces). + // i.e. `use a::b::{c, d, e};` + // ^^^ + let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( + self.tcx.sess, + import.span, + import.use_span, + ); + debug!(found_closing_brace, ?binding_span); + + let mut removal_span = binding_span; + + // If the binding span ended with a closing brace, as in the below example: + // i.e. `use a::b::{c, d};` + // ^ + // Then expand the span of characters to remove to include the previous + // binding's trailing comma. + // i.e. `use a::b::{c, d};` + // ^^^ + if found_closing_brace + && let Some(previous_span) = + extend_span_to_previous_binding(self.tcx.sess, binding_span) + { + debug!(?previous_span); + removal_span = removal_span.with_lo(previous_span.lo()); } - ItemWas::CfgOut { span } => { - multispan.push_span_label(span, msg!("the item is gated here")); + debug!(?removal_span); + + // Remove the `removal_span`. + corrections.push((removal_span, "".to_string())); + + // Find the span after the crate name and if it has nested imports immediately + // after the crate name already. + // i.e. `use a::b::{c, d};` + // ^^^^^^^^^ + // or `use a::{b, c, d}};` + // ^^^^^^^^^^^ + let (has_nested, after_crate_name) = + find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); + debug!(has_nested, ?after_crate_name); + + let source_map = self.tcx.sess.source_map(); + + // Make sure this is actually crate-relative. + let is_definitely_crate = import + .module_path + .first() + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + + // Add the import to the start, with a `{` if required. + let start_point = source_map.start_point(after_crate_name); + if is_definitely_crate + && let Ok(start_snippet) = source_map.span_to_snippet(start_point) + { + corrections.push(( + start_point, + if has_nested { + // In this case, `start_snippet` must equal '{'. + format!("{start_snippet}{import_snippet}, ") + } else { + // In this case, add a `{`, then the moved import, then whatever + // was there before. + format!("{{{import_snippet}, {start_snippet}") + }, + )); + + // Add a `};` to the end if nested, matching the `{` added at the start. + if !has_nested { + corrections.push((source_map.end_point(after_crate_name), "};".to_string())); + } + } else { + // If the root import is module-relative, add the import separately + corrections.push(( + import.use_span.shrink_to_lo(), + format!("use {module_name}::{import_snippet};\n"), + )); } } - diag.span_note(multispan, msg!("found an item that was configured out")); - } -} -#[derive(Diagnostic)] -#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] -pub(crate) struct TraitImplMismatch { - #[primary_span] - #[label("does not match trait")] - pub(crate) span: Span, - pub(crate) name: Ident, - pub(crate) kind: &'static str, - pub(crate) trait_path: String, - #[label("item in trait")] - pub(crate) trait_item_span: Span, -} + let suggestion = Some(( + corrections, + String::from("a macro with this name exists at the root of the crate"), + Applicability::MaybeIncorrect, + )); + Some(( + suggestion, + Some( + "this could be because a macro annotated with `#[macro_export]` will be exported \ + at the root of the crate instead of the module where it is defined" + .to_string(), + ), + )) + } -#[derive(Diagnostic)] -#[diag("derive helper attribute is used before it is introduced")] -pub(crate) struct LegacyDeriveHelpers { - #[label("the attribute is introduced here")] - pub span: Span, -} + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { + ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { + Some(def_id) + } + _ => None, + })?; + Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; -#[derive(Diagnostic)] -#[diag("unused extern crate")] -pub(crate) struct UnusedExternCrate { - #[label("unused")] - pub span: Span, - #[suggestion( - "remove the unused `extern crate`", - code = "", - applicability = "machine-applicable", - style = "verbose" - )] - pub removal_span: Span, -} + for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { + if ident.name != *segment { + continue; + } -#[derive(Diagnostic)] -#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] -pub(crate) struct ReexportPrivateDependency { - pub name: Symbol, - pub kind: &'static str, - pub krate: Symbol, -} + let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); + + fn comes_from_same_module_for_glob( + r: &Resolver<'_, '_>, + parent_module: DefId, + module: DefId, + visited: &mut FxHashMap, + ) -> bool { + if let Some(&cached) = visited.get(&parent_module) { + // this branch is prevent from being called recursively infinity, + // because there has some cycles in globs imports, + // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` + return cached; + } + visited.insert(parent_module, false); + let mut res = false; + let m = r.expect_module(parent_module); + if m.is_local() { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + res = true; + break; + } + } + } + } + visited.insert(parent_module, res); + res + } -#[derive(Diagnostic)] -#[diag("unused label")] -pub(crate) struct UnusedLabel; + let comes_from_same_module = parent_module == module + || comes_from_same_module_for_glob( + self, + parent_module, + module, + &mut Default::default(), + ); + if !comes_from_same_module { + continue; + } -#[derive(Diagnostic)] -#[diag("unused `#[macro_use]` import")] -pub(crate) struct UnusedMacroUse; + let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { + diagnostics::ItemWas::BehindFeature { feature, span: cfg.1 } + } else { + diagnostics::ItemWas::CfgOut { span: cfg.1 } + }; + let note = diagnostics::FoundItemConfigureOut { span: ident.span, item_was }; + err.subdiagnostic(note); + } + } -#[derive(Diagnostic)] -#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] -#[help("remove it and import macros at use sites with a `use` item instead")] -pub(crate) struct MacroUseDeprecated; + pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { + match def_id.as_local() { + Some(def_id) => self.struct_ctors.get(&def_id).cloned(), + None => { + self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { + let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); + let vis = self.tcx.visibility(ctor_def_id); + let field_visibilities = self + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&field_id| self.tcx.visibility(field_id)) + .collect(); + StructCtor { res, vis, field_visibilities } + }) + } + } + } -#[derive(Diagnostic)] -#[diag("macro `{$ident}` is private")] -pub(crate) struct MacroIsPrivate { - pub ident: Ident, + /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. + fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { + match def_id.as_local() { + Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), + None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), + } + } } -#[derive(Diagnostic)] -#[diag("unused macro definition: `{$name}`")] -pub(crate) struct UnusedMacroDefinition { - pub name: Symbol, -} +/// Given a `binding_span` of a binding within a use statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^ +/// ``` +/// +/// then return the span until the next binding or the end of the statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^^^ +/// ``` +fn find_span_of_binding_until_next_binding( + sess: &Session, + binding_span: Span, + use_span: Span, +) -> (bool, Span) { + let source_map = sess.source_map(); + + // Find the span of everything after the binding. + // i.e. `a, e};` or `a};` + let binding_until_end = binding_span.with_hi(use_span.hi()); + + // Find everything after the binding but not including the binding. + // i.e. `, e};` or `};` + let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); + + // Keep characters in the span until we encounter something that isn't a comma or + // whitespace. + // i.e. `, ` or ``. + // + // Also note whether a closing brace character was encountered. If there + // was, then later go backwards to remove any trailing commas that are left. + let mut found_closing_brace = false; + let after_binding_until_next_binding = + source_map.span_take_while(after_binding_until_end, |&ch| { + if ch == '}' { + found_closing_brace = true; + } + ch == ' ' || ch == ',' + }); + + // Combine the two spans. + // i.e. `a, ` or `a`. + // + // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` + let span = binding_span.with_hi(after_binding_until_next_binding.hi()); + + (found_closing_brace, span) +} + +/// Given a `binding_span`, return the span through to the comma or opening brace of the previous +/// binding. +/// +/// ```ignore (illustrative) +/// use foo::a::{a, b, c}; +/// // ^^--- binding span +/// // | +/// // returned span +/// +/// use foo::{a, b, c}; +/// // --- binding span +/// ``` +fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { + let source_map = sess.source_map(); + + // `prev_source` will contain all of the source that came before the span. + // Then split based on a command and take the first (i.e. closest to our span) + // snippet. In the example, this is a space. + let prev_source = source_map.span_to_prev_source(binding_span).ok()?; + + let prev_comma = prev_source.rsplit(',').collect::>(); + let prev_starting_brace = prev_source.rsplit('{').collect::>(); + if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { + return None; + } -#[derive(Diagnostic)] -#[diag("rule #{$n} of macro `{$name}` is never used")] -pub(crate) struct MacroRuleNeverUsed { - pub n: usize, - pub name: Symbol, -} + let prev_comma = prev_comma.first().unwrap(); + let prev_starting_brace = prev_starting_brace.first().unwrap(); -#[derive(Diagnostic)] -#[diag("`extern crate` is not idiomatic in the new edition")] -pub(crate) struct ExternCrateNotIdiomatic { - #[suggestion( - "convert it to a `use`", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - pub code: &'static str, -} + // If the amount of source code before the comma is greater than + // the amount of source code before the starting brace then we've only + // got one item in the nested item (eg. `issue_52891::{self}`). + if prev_comma.len() > prev_starting_brace.len() { + return None; + } -#[derive(Diagnostic)] -#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] -#[help("import `macro_rules` with `use` to make it callable above its definition")] -pub(crate) struct OutOfScopeMacroCalls { - #[label("not found from {$location}")] - pub span: Span, - pub path: String, - pub location: String, -} + Some(binding_span.with_lo(BytePos( + // Take away the number of bytes for the characters we've found and an + // extra for the comma. + binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, + ))) +} + +/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if +/// it is a nested use tree. +/// +/// ```ignore (illustrative) +/// use foo::a::{b, c}; +/// // ^^^^^^^^^^ -- false +/// +/// use foo::{a, b, c}; +/// // ^^^^^^^^^^ -- true +/// +/// use foo::{a, b::{c, d}}; +/// // ^^^^^^^^^^^^^^^ -- true +/// ``` +#[instrument(level = "debug", skip(sess))] +fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { + let source_map = sess.source_map(); + + // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. + let mut num_colons = 0; + // Find second colon.. `use issue_59764:` + let until_second_colon = source_map.span_take_while(use_span, |c| { + if *c == ':' { + num_colons += 1; + } + !matches!(c, ':' if num_colons == 2) + }); + // Find everything after the second colon.. `foo::{baz, makro};` + let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); + + let mut found_a_non_whitespace_character = false; + // Find the first non-whitespace character in `from_second_colon`.. `f` + let after_second_colon = source_map.span_take_while(from_second_colon, |c| { + if found_a_non_whitespace_character { + return false; + } + if !c.is_whitespace() { + found_a_non_whitespace_character = true; + } + true + }); -#[derive(Diagnostic)] -#[diag( - "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" -)] -pub(crate) struct RedundantImportVisibility { - #[note("the most public imported item is `{$max_vis}`")] - pub span: Span, - #[help("reduce the glob import's visibility or increase visibility of imported items")] - pub help: (), - pub import_vis: String, - pub max_vis: String, + // Find the first `{` in from_second_colon.. `foo::{` + let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); + + (next_left_bracket == after_second_colon, from_second_colon) } -#[derive(Diagnostic)] -#[diag("unknown diagnostic attribute")] -pub(crate) struct UnknownDiagnosticAttribute { - #[subdiagnostic] - pub help: Option, +/// A suggestion has already been emitted, change the wording slightly to clarify that both are +/// independent options. +enum Instead { + Yes, + No, } -#[derive(Subdiagnostic)] -pub(crate) enum UnknownDiagnosticAttributeHelp { - #[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" - )] - Typo { - #[primary_span] - span: Span, - typo_name: Symbol, - }, - #[help("add `#![feature({$feature})]` to the crate attributes to enable")] - UseFeature { feature: Symbol }, +/// Whether an existing place with an `use` item was found. +enum FoundUse { + Yes, + No, } -// FIXME: Make this properly translatable. -pub(crate) struct Ambiguity { - pub ident: Ident, - pub ambig_vis: Option, - pub kind: &'static str, - pub help: Option<&'static [&'static str]>, - pub b1_note: Spanned, - pub b1_help_msgs: Vec, - pub b2_note: Spanned, - pub b2_help_msgs: Vec, - /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. - pub is_error: bool, +/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. +pub(crate) enum DiagMode { + Normal, + /// The binding is part of a pattern + Pattern, + /// The binding is part of a use statement + Import { + /// `true` means diagnostics is for unresolved import + unresolved_import: bool, + /// `true` mean add the tips afterward for case `use a::{b,c}`, + /// rather than replacing within. + append: bool, + }, } -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let Self { - ident, - ambig_vis, - kind, - help, - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error, - } = self; +pub(crate) fn import_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + mode: DiagMode, + append: &str, +) { + show_candidates( + tcx, + err, + use_placement_span, + candidates, + Instead::Yes, + FoundUse::Yes, + mode, + vec![], + append, + ); +} + +type PathString<'a> = (String, &'a str, Option, &'a Option, bool); + +/// When an entity with a given name is not available in scope, we search for +/// entities with that name in all crates. This method allows outputting the +/// results of this search in a programmer-friendly way. If any entities are +/// found and suggested, returns `true`, otherwise returns `false`. +fn show_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + instead: Instead, + found_use: FoundUse, + mode: DiagMode, + path: Vec, + append: &str, +) -> bool { + if candidates.is_empty() { + return false; + } - let mut diag = Diag::new(dcx, level, "").with_span(ident.span); - if is_error { - diag.code(E0659); - } - if let Some(ambig_vis) = ambig_vis { - diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); - } else { - diag.primary_message(format!("`{}` is ambiguous", ident)); - diag.span_label(ident.span, "ambiguous name"); - } - diag.note(format!("ambiguous because of {}", kind)); - diag.span_note(b1_note.span, b1_note.node); - if let Some(help) = help { - for help in help { - diag.help(*help); + let mut showed = false; + let mut accessible_path_strings: Vec> = Vec::new(); + let mut inaccessible_path_strings: Vec> = Vec::new(); + + candidates.iter().for_each(|c| { + if c.accessible { + // Don't suggest `#[doc(hidden)]` items from other crates + if c.doc_visible { + accessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + )) } + } else { + inaccessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + )) } - for help_msg in b1_help_msgs { - diag.help(help_msg); - } - diag.span_note(b2_note.span, b2_note.node); - for help_msg in b2_help_msgs { - diag.help(help_msg); + }); + + // we want consistent results across executions, but candidates are produced + // by iterating through a hash map, so make sure they are ordered: + for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { + path_strings.sort_by(|a, b| a.0.cmp(&b.0)); + path_strings.dedup_by(|a, b| a.0 == b.0); + let core_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); + let std_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); + let foreign_crate_path_strings = + path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); + + // We list the `crate` local paths first. + // Then we list the `std`/`core` paths. + if std_path_strings.len() == core_path_strings.len() { + // Do not list `core::` paths if we are already listing the `std::` ones. + path_strings.extend(std_path_strings); + } else { + path_strings.extend(std_path_strings); + path_strings.extend(core_path_strings); } - diag + // List all paths from foreign crates last. + path_strings.extend(foreign_crate_path_strings); } -} -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` never used")] -pub(crate) struct UnusedLifetime { - #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] - pub deletion_span: Option, + if !accessible_path_strings.is_empty() { + let (determiner, kind, s, name, through) = + if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { + ( + "this", + *descr, + "", + format!(" `{name}`"), + if *via_import { " through its public re-export" } else { "" }, + ) + } else { + // Get the unique item kinds and if there's only one, we use the right kind name + // instead of the more generic "items". + let kinds = accessible_path_strings + .iter() + .map(|(_, descr, _, _, _)| *descr) + .collect::>(); + let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; + let s = if kind.ends_with('s') { "es" } else { "s" }; + + ("one of these", kind, s, String::new(), "") + }; + + let instead = if let Instead::Yes = instead { " instead" } else { "" }; + let mut msg = if let DiagMode::Pattern = mode { + format!( + "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ + pattern", + ) + } else { + format!("consider importing {determiner} {kind}{s}{through}{instead}") + }; - pub ident: Ident, -} + for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } -#[derive(Diagnostic)] -#[diag("ambiguous glob re-exports")] -pub(crate) struct AmbiguousGlobReexports { - #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] - pub first_reexport: Span, - #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] - pub duplicate_reexport: Span, + let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { + msg.push(':'); - pub name: String, - pub namespace: String, -} + for candidate in accessible_path_strings { + msg.push('\n'); + msg.push_str(&candidate.0); + } + }; + + if let Some(span) = use_placement_span { + let (add_use, trailing) = match mode { + DiagMode::Pattern => { + err.span_suggestions( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + return true; + } + DiagMode::Import { .. } => ("", ""), + DiagMode::Normal => ("use ", ";\n"), + }; + for candidate in &mut accessible_path_strings { + // produce an additional newline to separate the new use statement + // from the directly following item. + let additional_newline = if let FoundUse::No = found_use + && let DiagMode::Normal = mode + { + "\n" + } else { + "" + }; + candidate.0 = + format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); + } -#[derive(Diagnostic)] -#[diag("private item shadows public glob re-export")] -pub(crate) struct HiddenGlobReexports { - #[note( - "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" - )] - pub glob_reexport: Span, - #[note("but the private item here shadows it")] - pub private_item: Span, - - pub name: String, - pub namespace: String, -} + match mode { + DiagMode::Import { append: true, .. } => { + append_candidates(&mut msg, accessible_path_strings); + err.span_help(span, msg); + } + _ => { + err.span_suggestions_with_style( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } -#[derive(Diagnostic)] -#[diag("the item `{$ident}` is imported redundantly")] -pub(crate) struct RedundantImport { - #[subdiagnostic] - pub subs: Vec, - pub ident: Ident, -} + if let [first, .., last] = &path[..] { + let sp = first.ident.span.until(last.ident.span); + // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). + // Can happen for derive-generated spans. + if sp.can_be_used_for_suggestions() && !sp.is_empty() { + err.span_suggestion_verbose( + sp, + format!("if you import `{}`, refer to it directly", last.ident), + "", + Applicability::Unspecified, + ); + } + } + } else { + append_candidates(&mut msg, accessible_path_strings); + err.help(msg); + } + showed = true; + } + if !inaccessible_path_strings.is_empty() + && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) + { + let prefix = + if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; + if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { + let msg = format!( + "{prefix}{descr} `{name}`{} exists but is inaccessible", + if let DiagMode::Pattern = mode { ", which" } else { "" } + ); + + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + let mut multi_span = MultiSpan::from_span(span); + multi_span.push_span_label(span, "not accessible"); + err.span_note(multi_span, msg); + } else { + err.note(msg); + } + if let Some(note) = (*note).as_deref() { + err.note(note.to_string()); + } + } else { + let descr = inaccessible_path_strings + .iter() + .map(|&(_, descr, _, _, _)| descr) + .all_equal_value() + .unwrap_or("item"); + let plural_descr = + if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; + + let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); + let mut has_colon = false; + + let mut spans = Vec::new(); + for (name, _, source_span, _, _) in &inaccessible_path_strings { + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + spans.push((name, span)); + } else { + if !has_colon { + msg.push(':'); + has_colon = true; + } + msg.push('\n'); + msg.push_str(name); + } + } -#[derive(Subdiagnostic)] -pub(crate) enum RedundantImportSub { - #[label("the item `{$ident}` is already imported here")] - ImportedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined here")] - DefinedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already imported by the extern prelude")] - ImportedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined by the extern prelude")] - DefinedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, -} + let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); + for (name, span) in spans { + multi_span.push_span_label(span, format!("`{name}`: not accessible")); + } -#[derive(Diagnostic)] -#[diag("unnecessary qualification")] -pub(crate) struct UnusedQualifications { - #[suggestion( - "remove the unnecessary path segments", - style = "verbose", - code = "", - applicability = "machine-applicable" - )] - pub removal_span: Span, -} + for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } -#[derive(Diagnostic)] -#[diag( - "{$elided -> - [true] `&` without an explicit lifetime name cannot be used here - *[false] `'_` cannot be used here - }" -)] -pub(crate) struct AssociatedConstElidedLifetime { - #[suggestion( - "use the `'static` lifetime", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - - pub code: &'static str, - pub elided: bool, - #[note("cannot automatically infer `'static` because of other lifetimes in scope")] - pub lifetimes_in_scope: MultiSpan, + err.span_note(multi_span, msg); + } + showed = true; + } + showed } -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` only used once")] -pub(crate) struct SingleUseLifetime { - #[label("this lifetime...")] - pub param_span: Span, - #[label("...is used only here")] - pub use_span: Span, - #[subdiagnostic] - pub suggestion: Option, - - pub ident: Ident, +#[derive(Debug)] +struct UsePlacementFinder { + target_module: NodeId, + first_legal_span: Option, + first_use_span: Option, +} + +impl UsePlacementFinder { + fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { + let mut finder = + UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; + finder.visit_crate(krate); + if let Some(use_span) = finder.first_use_span { + (Some(use_span), FoundUse::Yes) + } else { + (finder.first_legal_span, FoundUse::No) + } + } } -#[derive(Subdiagnostic)] -#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] -pub(crate) struct SingleUseLifetimeSugg { - #[suggestion_part(code = "")] - pub deletion_span: Option, - #[suggestion_part(code = "{replace_lt}")] - pub use_span: Span, +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { + fn visit_crate(&mut self, c: &Crate) { + if self.target_module == CRATE_NODE_ID { + let inject = c.spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(&c.items); + } else { + visit::walk_crate(self, c); + } + } + + fn visit_item(&mut self, item: &'tcx ast::Item) { + if self.target_module == item.id { + if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + let inject = mod_spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(items); + } + } else { + visit::walk_item(self, item); + } + } +} - pub replace_lt: String, +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec, + spans: FxHashMap>, } -#[derive(Diagnostic)] -#[diag( - "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" -)] -pub(crate) struct AbsPathWithModule { - #[subdiagnostic] - pub sugg: AbsPathWithModuleSugg, +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } } -#[derive(Subdiagnostic)] -#[suggestion("use `crate`", code = "{replacement}")] -pub(crate) struct AbsPathWithModuleSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, - pub replacement: String, +fn search_for_any_use_in_items(items: &[Box]) -> Option { + for item in items { + if let ItemKind::Use(..) = item.kind + && is_span_suitable_for_use_injection(item.span) + { + let mut lo = item.span.lo(); + for attr in &item.attrs { + if attr.span.eq_ctxt(item.span) { + lo = std::cmp::min(lo, attr.span.lo()); + } + } + return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); + } + } + None } -#[derive(Diagnostic)] -#[diag("hidden lifetime parameters in types are deprecated")] -pub(crate) struct ElidedLifetimesInPaths { - #[subdiagnostic] - pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, +fn is_span_suitable_for_use_injection(s: Span) -> bool { + // don't suggest placing a use before the prelude + // import or other generated ones + !s.from_expansion() } -#[derive(Diagnostic)] -#[diag( - "{$num_snippets -> - [one] unused import: {$span_snippets} - *[other] unused imports: {$span_snippets} - }" -)] -pub(crate) struct UnusedImports { - #[subdiagnostic] - pub sugg: Option, - #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] - pub test_module_span: Option, - - pub span_snippets: DiagArgValue, - pub num_snippets: usize, +#[derive(Debug, Clone, Default)] +pub(crate) struct OnUnknownData { + pub(crate) directive: Box, } -#[derive(Subdiagnostic)] -pub(crate) enum UnusedImportsSugg { - #[suggestion( - "remove the whole `use` item", - applicability = "machine-applicable", - code = "", - style = "tool-only" - )] - RemoveWholeUse { - #[primary_span] - span: Span, - }, - #[multipart_suggestion( - "{$num_to_remove -> - [one] remove the unused import - *[other] remove the unused imports - }", - applicability = "machine-applicable", - style = "tool-only" - )] - RemoveImports { - #[suggestion_part(code = "")] - remove_spans: Vec, - num_to_remove: usize, - }, +impl OnUnknownData { + pub(crate) fn from_attrs<'tcx>( + tcx: TyCtxt<'tcx>, + attrs: &[ast::Attribute], + ) -> Option { + if tcx.features().diagnostic_on_unknown() + && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = + AttributeParser::parse_limited(tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown]) + { + Some(Self { directive: directive? }) + } else { + None + } + } } diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs index 81c1b6fa8d675..2baf423e296d6 100644 --- a/compiler/rustc_resolve/src/error_helper.rs +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -1,4160 +1,1778 @@ -// ignore-tidy-filelength -use std::mem; -use std::ops::ControlFlow; - -use itertools::Itertools as _; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{ - self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, - join_path_idents, -}; -use rustc_ast_pretty::pprust; -use rustc_attr_parsing::AttributeParser; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, - pluralize, struct_span_code_err, -}; -use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; -use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; -use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; -use rustc_hir::{Attribute, PrimTy, Stability, StabilityLevel, find_attr}; -use rustc_middle::bug; -use rustc_middle::ty::{TyCtxt, Visibility}; -use rustc_session::Session; -use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, - AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, -}; -use rustc_session::utils::was_invoked_from_cargo; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::SourceMap; -use rustc_span::{ - BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, }; -use thin_vec::{ThinVec, thin_vec}; -use tracing::{debug, instrument}; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{Ident, Span, Spanned, Symbol}; + +use crate::Res; +use crate::late::PatternSource; + +#[derive(Diagnostic)] +#[diag("can't use {$is_self -> + [true] `Self` + *[false] generic parameters + } from outer item", code = E0401)] +#[note( + "nested items are independent from their parent item for everything except for privacy and name resolution" +)] +pub(crate) struct GenericParamsFromOuterItem { + #[primary_span] + #[label( + "use of {$is_self -> + [true] `Self` + *[false] generic parameter + } from outer item" + )] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) label: Option, + #[subdiagnostic] + pub(crate) refer_to_type_directly: Option, + #[subdiagnostic] + pub(crate) use_let: Option, + #[subdiagnostic] + pub(crate) sugg: Option, + #[subdiagnostic] + pub(crate) static_or_const: Option, + pub(crate) is_self: bool, + #[subdiagnostic] + pub(crate) item: Option, +} -use crate::diagnostics::{ - self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, - ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, - MaybeMissingMacroRulesName, -}; -use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, ImportKind, UnresolvedImportError, import_path_to_string}; -use crate::late::{DiagMetadata, PatternSource, Rib}; -use crate::{ - AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, - DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, - LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, - PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, - VisResolutionError, path_names_to_string, -}; +#[derive(Subdiagnostic)] +#[label( + "{$is_self -> + [true] `Self` + *[false] generic parameter + } used in this inner {$descr}" +)] +pub(crate) struct GenericParamsFromOuterItemInnerItem { + #[primary_span] + pub(crate) span: Span, + pub(crate) descr: String, + pub(crate) is_self: bool, +} -/// A vector of spans and replacements, a message and applicability. -pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemStaticOrConst { + #[note("a `static` is a separate item from the item that contains it")] + Static, + #[note("a `const` is a separate item from the item that contains it")] + Const, +} -/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a -/// similarly named label and whether or not it is reachable. -pub(crate) type LabelSuggestion = (Ident, bool); +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemLabel { + #[label("can't use `Self` here")] + SelfTyParam(#[primary_span] Span), + #[label("`Self` type implicitly declared here, by this `impl`")] + SelfTyAlias(#[primary_span] Span), + #[label("type parameter from outer item")] + TyParam(#[primary_span] Span), + #[label("const parameter from outer item")] + ConstParam(#[primary_span] Span), +} -#[derive(Clone)] -pub(crate) struct StructCtor { - pub res: Res, - pub vis: Visibility, - pub field_visibilities: Vec>, +#[derive(Subdiagnostic)] +#[suggestion( + "try introducing a local generic parameter here", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemSugg { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, } -impl StructCtor { - pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { - self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) - } +#[derive(Subdiagnostic)] +#[suggestion( + "try using a local `let` binding instead", + code = "let", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemUseLet { + #[primary_span] + pub(crate) span: Span, } -#[derive(Debug)] -pub(crate) enum SuggestionTarget { - /// The target has a similar name as the name used by the programmer (probably a typo) - SimilarlyNamed, - /// The target is the only valid item that can be used in the corresponding context - SingleItem, +#[derive(Subdiagnostic)] +#[suggestion( + "refer to the type directly here instead", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UseTypeDirectly { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, } -#[derive(Debug)] -pub(crate) struct TypoSuggestion { - pub candidate: Symbol, - /// The source location where the name is defined; None if the name is not defined - /// in source e.g. primitives - pub span: Option, - pub res: Res, - pub target: SuggestionTarget, -} - -impl TypoSuggestion { - pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { - Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } - } +#[derive(Diagnostic)] +#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] +pub(crate) struct NameAlreadyUsedInParameterList { + #[primary_span] + #[label("already used")] + pub(crate) span: Span, + #[label("first use of `{$name}`")] + pub(crate) first_use_span: Span, + pub(crate) name: Ident, } -/// A free importable items suggested in case of resolution failure. -#[derive(Debug, Clone)] -pub(crate) struct ImportSuggestion { - pub did: Option, - pub descr: &'static str, - pub path: Path, - pub accessible: bool, - // false if the path traverses a foreign `#[doc(hidden)]` item. - pub doc_visible: bool, - pub via_import: bool, - /// An extra note that should be issued if this item is suggested - pub note: Option, - pub is_stable: bool, -} - -/// Adjust the impl span so that just the `impl` keyword is taken by removing -/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and -/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). -/// -/// *Attention*: the method used is very fragile since it essentially duplicates the work of the -/// parser. If you need to use this function or something similar, please consider updating the -/// `source_map` functions and this function to something more robust. -fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { - let impl_span = sm.span_until_char(impl_span, '<'); - sm.span_until_whitespace(impl_span) -} - -impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - /// Reports unresolved imports. - /// - /// Multiple unresolved import errors within the same use tree are combined into a single - /// diagnostic. - pub(crate) fn throw_unresolved_import_error( - &mut self, - mut errors: Vec<(Import<'_>, UnresolvedImportError)>, - glob_error: bool, - ) { - errors.retain(|(_import, err)| match err.module { - // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. - Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, - // If we've encountered something like `use _;`, we've already emitted an error stating - // that `_` is not a valid identifier, so we ignore that resolve error. - _ => err.segment.map(|s| s.name) != Some(kw::Underscore), - }); - if errors.is_empty() { - self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); - return; - } +#[derive(Diagnostic)] +#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] +pub(crate) struct MethodNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) method: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); - - let paths = errors - .iter() - .map(|(import, err)| { - let path = import_path_to_string( - &import.module_path.iter().map(|seg| seg.ident).collect::>(), - &import.kind, - err.span, - ); - format!("`{path}`") - }) - .collect::>(); - let default_message = - format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - - // Process `import` use of the `#[diagnostic::on_unknown]` attribute. - // - // We don't need to check feature gates here; that happens on initialization of the - // `on_unknown_attr` fields. - let (mut message, label, mut notes) = - if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) { - let this = errors - .iter() - .map(|(_import, err)| { - // Is this unwrap_or reachable? - err.segment.map(|s| s.name).unwrap_or(kw::Underscore) - }) - .join(", "); - - let args = FormatArgs { unresolved: this.clone(), this, .. }; - - let CustomDiagnostic { message, label, notes, parent_label: _dead } = - directive.eval(None, &args); - - (message, label, notes) - } else { - (None, None, Vec::new()) - }; - - // `module` use of the `#[diagnostic::on_unknown]` attribute. - // We assume that someone who put the attribute on the import has more information than - // the person who put it on the module, so we choose to prioritize the import attribute. - let mut mod_diagnostics: Vec = errors - .iter() - .map(|(import, import_error)| { - if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() - && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind - { - let Some(directive) = self.on_unknown_data(def_id) else { - return CustomDiagnostic::default(); - }; - - let this = if let Some(name) = name { - name.to_string() - } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { - crate_name.to_string() - } else { - "".to_string() - }; - let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); - let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; - - directive.eval(None, &args) - } else { - CustomDiagnostic::default() - } - }) - .collect(); - - // If there is no import attribute with a message, - // but all mod messages are the same, use that. - let mod_message = - mod_diagnostics.iter_mut().flat_map(|d| d.message.take()).all_equal_value(); - if message.is_none() - && let Ok(mod_msg) = mod_message - { - message = Some(mod_msg); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated function with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedFnWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - let mut diag = if let Some(message) = message { - struct_span_code_err!(self.dcx(), span, E0432, "{message}").with_note(default_message) - } else { - struct_span_code_err!(self.dcx(), span, E0432, "{default_message}") - }; - - for mod_diag in mod_diagnostics.iter_mut() { - for mod_note in mod_diag.notes.drain(..) { - if !notes.contains(&mod_note) { - notes.push(mod_note); - } - } - } +#[derive(Diagnostic)] +#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] +pub(crate) struct TypeNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) type_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - if !notes.is_empty() { - for note in notes { - diag.note(note); - } - } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = - errors.iter().last() - { - diag.note(note.clone()); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated type with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedTypeWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - /// Upper limit on the number of `span_label` messages. - const MAX_LABEL_COUNT: usize = 10; - let mod_labels = mod_diagnostics.into_iter().map(|cd| cd.label); - - for ((import, err), mod_label) in errors.into_iter().zip(mod_labels).take(MAX_LABEL_COUNT) { - let label_span = match err.segment { - Some(segment) => segment.span, - None => err.span, - }; - if let Some(label) = &label { - diag.span_label(label_span, label.clone()); - } else if let Some(label) = mod_label { - diag.span_label(label_span, label); - } else if let Some(label) = &err.label { - diag.span_label(label_span, label.clone()); - } +#[derive(Diagnostic)] +#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] +pub(crate) struct ConstNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) const_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - if let Some((suggestions, msg, applicability)) = err.suggestion { - if suggestions.is_empty() { - diag.help(msg); - continue; - } - diag.multipart_suggestion(msg, suggestions, applicability); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated constant with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedConstWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - if let Some(candidates) = &err.candidates { - match &import.kind { - ImportKind::Single { nested: false, source, target, .. } => import_candidates( - self.tcx, - &mut diag, - Some(err.span), - candidates, - DiagMode::Import { append: false, unresolved_import: true }, - (source != target) - .then(|| format!(" as {target}")) - .as_deref() - .unwrap_or(""), - ), - ImportKind::Single { nested: true, source, target, .. } => { - import_candidates( - self.tcx, - &mut diag, - None, - candidates, - DiagMode::Normal, - (source != target) - .then(|| format!(" as {target}")) - .as_deref() - .unwrap_or(""), - ); - } - _ => {} - } - } +#[derive(Diagnostic)] +#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] +pub(crate) struct VariableBoundWithDifferentMode { + #[primary_span] + #[label("bound in different ways")] + pub(crate) span: Span, + #[label("first binding")] + pub(crate) first_binding_span: Span, + pub(crate) variable_name: Ident, +} - if matches!(import.kind, ImportKind::Single { .. }) - && let Some(segment) = err.segment - && let Some(module) = err.module - { - self.find_cfg_stripped(&mut diag, &segment.name, module) - } - } +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] +pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { + #[primary_span] + #[label("used as parameter more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} - let guar = diag.emit(); - if glob_error { - self.glob_error = Some(guar); - } - } +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] +pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { + #[primary_span] + #[label("used in a pattern more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} - pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { - self.tcx.dcx() - } +#[derive(Diagnostic)] +#[diag("use of undeclared label `{$name}`", code = E0426)] +pub(crate) struct UndeclaredLabel { + #[primary_span] + #[label("undeclared label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) sub_reachable: Option, + #[subdiagnostic] + pub(crate) sub_reachable_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_unreachable: Option, +} - pub(crate) fn report_errors(&mut self, krate: &Crate) { - self.report_delayed_vis_resolution_errors(); - self.report_with_use_injections(krate); - - for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - self.lint_buffer.buffer_lint( - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - CRATE_NODE_ID, - span_use, - diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths { - definition: span_def, - }, - ); - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); + +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct TryUsingSimilarlyNamedLabel { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} - for ambiguity_error in &self.ambiguity_errors { - let mut diag = self.ambiguity_diagnostic(ambiguity_error); - - if let Some(ambiguity_warning) = ambiguity_error.warning { - let node_id = match ambiguity_error.b1.0.kind { - DeclKind::Import { import, .. } => import.root_id, - DeclKind::Def(_) => CRATE_NODE_ID, - }; - - let lint = match ambiguity_warning { - _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, - AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, - AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, - }; - - self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); - } else { - diag.is_error = true; - self.dcx().emit_err(diag); - } - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is unreachable")] +pub(crate) struct UnreachableLabelWithSimilarNameExists { + #[primary_span] + pub(crate) ident_span: Span, +} - let mut reported_spans = FxHashSet::default(); - for error in mem::take(&mut self.privacy_errors) { - if reported_spans.insert(error.dedup_span) { - self.report_privacy_error(&error); - } - } - } +#[derive(Diagnostic)] +#[diag("can't capture dynamic environment in a fn item", code = E0434)] +#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] +pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { + #[primary_span] + pub(crate) span: Span, +} - fn report_delayed_vis_resolution_errors(&mut self) { - for DelayedVisResolutionError { vis, parent_scope, error } in - mem::take(&mut self.delayed_vis_resolution_errors) - { - match self.try_resolve_visibility(&parent_scope, &vis, true) { - Ok(_) => self.report_vis_error(error), - Err(error) => self.report_vis_error(error), - }; - } - } +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant", code = E0435)] +pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) with: Option>, + #[subdiagnostic] + pub(crate) with_label: Option, + #[subdiagnostic] + pub(crate) without: Option>, +} - fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in - mem::take(&mut self.use_injections) - { - let (span, found_use) = if node_id != DUMMY_NODE_ID { - UsePlacementFinder::check(krate, node_id) - } else { - (None, FoundUse::No) - }; - - if !candidates.is_empty() { - show_candidates( - self.tcx, - &mut err, - span, - &candidates, - if instead { Instead::Yes } else { Instead::No }, - found_use, - DiagMode::Normal, - path, - "", - ); - err.emit(); - } else if let Some((span, msg, sugg, appl)) = suggestion { - err.span_suggestion_verbose(span, msg, sugg, appl); - err.emit(); - } else if let [segment] = path.as_slice() - && is_call - { - err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); - } else { - err.emit(); - } - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider using `{$suggestion}` instead of `{$current}`", + style = "verbose", + applicability = "has-placeholders" +)] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { + // #[primary_span] + #[suggestion_part(code = "{suggestion} ")] + pub(crate) span: Span, + pub(crate) suggestion: &'a str, + #[suggestion_part(code = ": /* Type */")] + pub(crate) type_span: Option, + pub(crate) current: &'a str, +} - pub(crate) fn report_conflict( - &mut self, - ident: IdentKey, - ns: Namespace, - old_binding: Decl<'ra>, - new_binding: Decl<'ra>, - ) { - // Error on the second of two conflicting names - if old_binding.span.lo() > new_binding.span.lo() { - return self.report_conflict(ident, ns, new_binding, old_binding); - } +#[derive(Subdiagnostic)] +#[label("non-constant value")] +pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { + #[primary_span] + pub(crate) span: Span, +} - let container = match old_binding.parent_module.unwrap().expect_local().kind { - // Avoid using TyCtxt::def_kind_descr in the resolver, because it - // indirectly *calls* the resolver, and would cause a query cycle. - ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), - ModuleKind::Block => "block", - }; +#[derive(Subdiagnostic)] +#[label("this would need to be a `{$suggestion}`")] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { + #[primary_span] + pub(crate) ident_span: Span, + pub(crate) suggestion: &'a str, +} - let (name, span) = - (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); +#[derive(Diagnostic)] +#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] +pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { + #[primary_span] + #[label("cannot be named the same as {$article} {$shadowed_binding}")] + pub(crate) span: Span, + pub(crate) shadowing_binding: PatternSource, + pub(crate) shadowed_binding: Res, + pub(crate) article: &'a str, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] + pub(crate) shadowed_binding_span: Span, + pub(crate) participle: &'a str, + pub(crate) name: Symbol, +} - if self.name_already_seen.get(&name) == Some(&span) { - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "try specify the pattern arguments", + code = "{name}(..)", + applicability = "unspecified" +)] +pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} - let old_kind = match (ns, old_binding.res()) { - (ValueNS, _) => "value", - (MacroNS, _) => "macro", - (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", - (TypeNS, Res::Def(DefKind::Mod, _)) => "module", - (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", - (TypeNS, _) => "type", - }; - - let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { - (true, true) => E0259, - (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { - true => E0254, - false => E0260, - }, - _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { - (false, false) => E0428, - (true, true) => E0252, - _ => E0255, - }, - }; - - let label = match new_binding.is_import_user_facing() { - true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name }, - false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name }, - }; - - let old_binding_label = - (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { - let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); - match old_binding.is_import_user_facing() { - true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import { - span, - old_kind, - name, - }, - false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition { - span, - old_kind, - name, - }, - } - }); - - let mut err = self - .dcx() - .create_err(diagnostics::NameDefinedMultipleTime { - span, - name, - descr: ns.descr(), - container, - label, - old_binding_label, - }) - .with_code(code); - - // See https://github.com/rust-lang/rust/issues/32354 - use DeclKind::Import; - let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { - !binding.span.is_dummy() - && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) - }; - let import = match (&new_binding.kind, &old_binding.kind) { - // If there are two imports where one or both have attributes then prefer removing the - // import without attributes. - (Import { import: new, .. }, Import { import: old, .. }) - if { - (new.has_attributes || old.has_attributes) - && can_suggest(old_binding, *old) - && can_suggest(new_binding, *new) - } => - { - if old.has_attributes { - Some((*new, new_binding.span, true)) - } else { - Some((*old, old_binding.span, true)) - } - } - // Otherwise prioritize the new binding. - (Import { import, .. }, other) if can_suggest(new_binding, *import) => { - Some((*import, new_binding.span, other.is_import())) - } - (other, Import { import, .. }) if can_suggest(old_binding, *import) => { - Some((*import, old_binding.span, other.is_import())) - } - _ => None, - }; - - // Check if the target of the use for both bindings is the same. - let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); - let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); - let from_item = - self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); - // Only suggest removing an import if both bindings are to the same def, if both spans - // aren't dummy spans. Further, if both bindings are imports, then the ident must have - // been introduced by an item. - let should_remove_import = duplicate - && !has_dummy_span - && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); - - match import { - Some((import, span, true)) if should_remove_import && import.is_nested() => { - self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); - } - Some((import, _, true)) if should_remove_import && !import.is_glob() => { - // Simple case - remove the entire import. Due to the above match arm, this can - // only be a single use so just remove it entirely. - err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } - Some((import, span, _)) => { - self.add_suggestion_for_rename_of_use(&mut err, name, import, span); - } - _ => {} - } +#[derive(Diagnostic)] +#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] +pub(crate) struct ForwardDeclaredGenericParam { + #[primary_span] + #[label("cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} - err.emit(); - self.name_already_seen.insert(name, span); - } +#[derive(Diagnostic)] +#[diag("const parameter types cannot reference parameters before they are declared")] +pub(crate) struct ForwardDeclaredGenericInConstParamTy { + #[primary_span] + #[label("const parameter type cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} - /// This function adds a suggestion to change the binding name of a new import that conflicts - /// with an existing import. - /// - /// ```text,ignore (diagnostic) - /// help: you can use `as` to change the binding name of the import - /// | - /// LL | use foo::bar as other_bar; - /// | ^^^^^^^^^^^^^^^^^^^^^ - /// ``` - fn add_suggestion_for_rename_of_use( - &self, - err: &mut Diag<'_>, - name: Symbol, - import: Import<'_>, - binding_span: Span, - ) { - let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { - format!("Other{name}") - } else { - format!("other_{name}") - }; - - let mut suggestion = None; - let mut span = binding_span; - match import.kind { - ImportKind::Single { source, .. } => { - if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) - && pos as usize <= snippet.len() - { - span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( - binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), - ); - suggestion = Some(format!(" as {suggested_name}")); - } - } - ImportKind::ExternCrate { source, target, .. } => { - suggestion = Some(format!( - "extern crate {} as {};", - source.unwrap_or(target.name), - suggested_name, - )) - } - _ => unreachable!(), - } +#[derive(Diagnostic)] +#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] +pub(crate) struct ParamInTyOfConstParam { + #[primary_span] + #[label("the type must not depend on the parameter `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, +} - if let Some(suggestion) = suggestion { - err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); - } else { - err.subdiagnostic(ChangeImportBinding { span }); - } - } +#[derive(Diagnostic)] +#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] +pub(crate) struct SelfInGenericParamDefault { + #[primary_span] + pub(crate) span: Span, +} - /// This function adds a suggestion to remove an unnecessary binding from an import that is - /// nested. In the following example, this function will be invoked to remove the `a` binding - /// in the second use statement: - /// - /// ```ignore (diagnostic) - /// use issue_52891::a; - /// use issue_52891::{d, a, e}; - /// ``` - /// - /// The following suggestion will be added: - /// - /// ```ignore (diagnostic) - /// use issue_52891::{d, a, e}; - /// ^-- help: remove unnecessary import - /// ``` - /// - /// If the nested use contains only one import then the suggestion will remove the entire - /// line. - /// - /// It is expected that the provided import is nested - this isn't checked by the - /// function. If this invariant is not upheld, this function's behaviour will be unexpected - /// as characters expected by span manipulations won't be present. - fn add_suggestion_for_duplicate_nested_use( - &self, - err: &mut Diag<'_>, - import: Import<'_>, - binding_span: Span, - ) { - assert!(import.is_nested()); - - // Two examples will be used to illustrate the span manipulations we're doing: - // - // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, a, e};`. - // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, e, a};`. - - let (found_closing_brace, span) = - find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); - - // If there was a closing brace then identify the span to remove any trailing commas from - // previous imports. - if found_closing_brace { - if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { - err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span }); - } else { - // Remove the entire line if we cannot extend the span back, this indicates an - // `issue_52891::{self}` case. - err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } +#[derive(Diagnostic)] +#[diag("cannot use `Self` in const parameter type")] +pub(crate) struct SelfInConstGenericTy { + #[primary_span] + pub(crate) span: Span, +} - return; - } +#[derive(Diagnostic)] +#[diag( + "{$is_gca -> + [true] generic parameters in const blocks are not allowed; use a named `const` item instead + *[false] generic parameters may not be used in const operations +}" +)] +pub(crate) struct ParamInNonTrivialAnonConst { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInNonTrivialAnonConst, + #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] + pub(crate) help: bool, + pub(crate) is_gca: bool, + #[help( + "consider factoring the expression into a `type const` item and use it as the const argument instead" + )] + pub(crate) help_gca: bool, +} - err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span }); - } +#[derive(Debug)] +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInNonTrivialAnonConst { + #[note("type parameters may not be used in const expressions")] + Type, + #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] + Const { name: Symbol }, + #[note("lifetime parameters may not be used in const expressions")] + Lifetime, +} - pub(crate) fn lint_if_path_starts_with_module( - &mut self, - finalize: Finalize, - path: &[Segment], - second_binding: Option>, - ) { - let Finalize { node_id, root_span, .. } = finalize; - - let first_name = match path.get(0) { - // In the 2018 edition this lint is a hard error, so nothing to do - Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { - seg.ident.name - } - _ => return, - }; +#[derive(Diagnostic)] +#[diag("use of unreachable label `{$name}`", code = E0767)] +#[note("labels are unreachable through functions, closures, async blocks and modules")] +pub(crate) struct UnreachableLabel { + #[primary_span] + #[label("unreachable label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[label("unreachable label defined here")] + pub(crate) definition_span: Span, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_suggestion_label: Option, + #[subdiagnostic] + pub(crate) sub_unreachable_label: Option, +} - // We're only interested in `use` paths which should start with - // `{{root}}` currently. - if first_name != kw::PathRoot { - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnreachableLabelSubSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} - match path.get(1) { - // If this import looks like `crate::...` it's already good - Some(Segment { ident, .. }) if ident.name == kw::Crate => return, - // Otherwise go below to see if it's an extern crate - Some(_) => {} - // If the path has length one (and it's `PathRoot` most likely) - // then we don't know whether we're gonna be importing a crate or an - // item in our crate. Defer this lint to elsewhere - None => return, - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct UnreachableLabelSubLabel { + #[primary_span] + pub(crate) ident_span: Span, +} - // If the first element of our path was actually resolved to an - // `ExternCrate` (also used for `crate::...`) then no need to issue a - // warning, this looks all good! - if let Some(binding) = second_binding - && let DeclKind::Import { import, .. } = binding.kind - // Careful: we still want to rewrite paths from renamed extern crates. - && let ImportKind::ExternCrate { source: None, .. } = import.kind - { - return; - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is also unreachable")] +pub(crate) struct UnreachableLabelSubLabelUnreachable { + #[primary_span] + pub(crate) ident_span: Span, +} - self.lint_buffer.dyn_buffer_lint_any( - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - node_id, - root_span, - move |dcx, level, sess| { - let (replacement, applicability) = match sess - .downcast_ref::() - .expect("expected a `Session`") - .source_map() - .span_to_snippet(root_span) - { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - diagnostics::AbsPathWithModule { - sugg: diagnostics::AbsPathWithModuleSugg { - span: root_span, - applicability, - replacement, - }, - } - .into_diag(dcx, level) - }, - ); - } +#[derive(Diagnostic)] +#[diag("invalid `sym` operand")] +#[help("`sym` operands must refer to either a function or a static")] +pub(crate) struct InvalidAsmSym { + #[primary_span] + #[label("is a local variable")] + pub(crate) span: Span, +} - pub(crate) fn add_module_candidates( - &self, - module: Module<'ra>, - names: &mut Vec, - filter_fn: &impl Fn(Res) -> bool, - ctxt: Option, - ) { - module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { - let res = binding.res(); - if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { - names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); - } - }); - } +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant")] +pub(crate) struct LowercaseSelf { + #[primary_span] + #[suggestion( + "try using `Self`", + code = "Self", + applicability = "maybe-incorrect", + style = "short" + )] + pub(crate) span: Span, +} - /// Combines an error with provided span and emits it. - /// - /// This takes the error provided, combines it with the span and any additional spans inside the - /// error and emits it. - pub(crate) fn report_error( - &mut self, - span: Span, - resolution_error: ResolutionError<'ra>, - ) -> ErrorGuaranteed { - self.into_struct_error(span, resolution_error).emit() - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("never patterns cannot contain variable bindings")] +pub(crate) struct BindingInNeverPattern { + #[primary_span] + #[suggestion( + "use a wildcard `_` instead", + code = "_", + applicability = "machine-applicable", + style = "short" + )] + pub(crate) span: Span, +} - pub(crate) fn into_struct_error( - &mut self, - span: Span, - resolution_error: ResolutionError<'ra>, - ) -> Diag<'_> { - match resolution_error { - ResolutionError::GenericParamsFromOuterItem { - outer_res, - has_generic_params, - def_kind, - inner_item, - current_self_ty, - } => { - use diagnostics::GenericParamsFromOuterItemLabel as Label; - let static_or_const = match def_kind { - DefKind::Static { .. } => { - Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static) - } - DefKind::Const { .. } => { - Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const) - } - _ => None, - }; - let is_self = - matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); - let mut err = diagnostics::GenericParamsFromOuterItem { - span, - label: None, - refer_to_type_directly: None, - use_let: None, - sugg: None, - static_or_const, - is_self, - item: inner_item.as_ref().map(|(label_span, _, kind)| { - diagnostics::GenericParamsFromOuterItemInnerItem { - span: *label_span, - descr: kind.descr().to_string(), - is_self, - } - }), - }; - - let sm = self.tcx.sess.source_map(); - // Note: do not early return for missing def_id here, - // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. - let def_id = match outer_res { - Res::SelfTyParam { .. } => { - err.label = Some(Label::SelfTyParam(span)); - None - } - Res::SelfTyAlias { alias_to: def_id, .. } => { - err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( - sm, - self.def_span(def_id), - ))); - err.refer_to_type_directly = current_self_ty - .map(|snippet| diagnostics::UseTypeDirectly { span, snippet }); - None - } - Res::Def(DefKind::TyParam, def_id) => { - err.label = Some(Label::TyParam(self.def_span(def_id))); - Some(def_id) - } - Res::Def(DefKind::ConstParam, def_id) => { - err.label = Some(Label::ConstParam(self.def_span(def_id))); - Some(def_id) - } - _ => { - bug!( - "GenericParamsFromOuterItem should only be used with \ - Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ - DefKind::ConstParam" - ); - } - }; - - if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { - err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet { - span: sm.span_until_whitespace(*item_span), - }); - } - - if let Some(def_id) = def_id - && let HasGenericParams::Yes(span) = has_generic_params - && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) - { - let name = self.tcx.item_name(def_id); - let (span, snippet) = if span.is_empty() { - let snippet = format!("<{name}>"); - (span, snippet) - } else { - let span = sm.span_through_char(span, '<').shrink_to_hi(); - let snippet = format!("{name}, "); - (span, snippet) - }; - err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet }); - } - - self.dcx().create_err(err) - } - ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { - self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList { - span, - first_use_span, - name, - }) - } - ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { - self.dcx().create_err(diagnostics::MethodNotMemberOfTrait { - span, - method, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists { - span: method.span, - candidate: c, - }), - }) - } - ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { - self.dcx().create_err(diagnostics::TypeNotMemberOfTrait { - span, - type_, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists { - span: type_.span, - candidate: c, - }), - }) - } - ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { - self.dcx().create_err(diagnostics::ConstNotMemberOfTrait { - span, - const_, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists { - span: const_.span, - candidate: c, - }), - }) - } - ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { - let BindingError { name, target, origin, could_be_path } = binding_error; - - let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); - target_sp.sort(); - target_sp.dedup(); - let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); - origin_sp.sort(); - origin_sp.dedup(); - - let msp = MultiSpan::from_spans(target_sp.clone()); - let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns { - multispan: msp, - name, - }); - for sp in target_sp { - err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name }); - } - for sp in &origin_sp { - err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp }); - } - let mut suggested_typo = false; - if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) - && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) - { - // The check above is so that when we encounter `match foo { (a | b) => {} }`, - // we don't suggest `(a | a) => {}`, which would never be what the user wants. - let mut target_visitor = BindingVisitor::default(); - for pat in &target { - target_visitor.visit_pat(pat); - } - target_visitor.identifiers.sort(); - target_visitor.identifiers.dedup(); - let mut origin_visitor = BindingVisitor::default(); - for (_, pat) in &origin { - origin_visitor.visit_pat(pat); - } - origin_visitor.identifiers.sort(); - origin_visitor.identifiers.dedup(); - // Find if the binding could have been a typo - if let Some(typo) = - find_best_match_for_name(&target_visitor.identifiers, name.name, None) - && !origin_visitor.identifiers.contains(&typo) - { - err.subdiagnostic(diagnostics::PatternBindingTypo { - spans: origin_sp, - typo, - }); - suggested_typo = true; - } - } - if could_be_path { - let import_suggestions = self.lookup_import_candidates( - name, - Namespace::ValueNS, - &parent_scope, - &|res: Res| { - matches!( - res, - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const) - | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) - | DefKind::Const { .. } - | DefKind::AssocConst { .. }, - _, - ) - ) - }, - ); - - if import_suggestions.is_empty() && !suggested_typo { - let kind_matches: [fn(DefKind) -> bool; 4] = [ - |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), - |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), - |kind| matches!(kind, DefKind::Const { .. }), - |kind| matches!(kind, DefKind::AssocConst { .. }), - ]; - let mut local_names = vec![]; - self.add_module_candidates( - parent_scope.module, - &mut local_names, - &|res| matches!(res, Res::Def(_, _)), - None, - ); - let local_names: FxHashSet<_> = local_names - .into_iter() - .filter_map(|s| match s.res { - Res::Def(_, def_id) => Some(def_id), - _ => None, - }) - .collect(); - - let mut local_suggestions = vec![]; - let mut suggestions = vec![]; - for matches_kind in kind_matches { - if let Some(suggestion) = self.early_lookup_typo_candidate( - ScopeSet::All(Namespace::ValueNS), - &parent_scope, - name, - &|res: Res| match res { - Res::Def(k, _) => matches_kind(k), - _ => false, - }, - ) && let Res::Def(kind, mut def_id) = suggestion.res - { - if let DefKind::Ctor(_, _) = kind { - def_id = self.tcx.parent(def_id); - } - let kind = kind.descr(def_id); - if local_names.contains(&def_id) { - // The item is available in the current scope. Very likely to - // be a typo. Don't use the full path. - local_suggestions.push(( - suggestion.candidate, - suggestion.candidate.to_string(), - kind, - )); - } else { - suggestions.push(( - suggestion.candidate, - self.def_path_str(def_id), - kind, - )); - } - } - } - let suggestions = if !local_suggestions.is_empty() { - // There is at least one item available in the current scope that is a - // likely typo. We only show those. - local_suggestions - } else { - suggestions - }; - for (name, sugg, kind) in suggestions { - err.span_suggestion_verbose( - span, - format!( - "you might have meant to use the similarly named {kind} `{name}`", - ), - sugg, - Applicability::MaybeIncorrect, - ); - suggested_typo = true; - } - } - if import_suggestions.is_empty() && !suggested_typo { - let help_msg = format!( - "if you meant to match on a unit struct, unit variant or a `const` \ - item, consider making the path in the pattern qualified: \ - `path::to::ModOrType::{name}`", - ); - err.span_help(span, help_msg); - } - show_candidates( - self.tcx, - &mut err, - Some(span), - &import_suggestions, - Instead::No, - FoundUse::Yes, - DiagMode::Pattern, - vec![], - "", - ); - } - err - } - ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { - self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode { - span, - first_binding_span, - variable_name, - }) - } - ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { - self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList { - span, - identifier, - }) - } - ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { - self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern { - span, - identifier, - }) - } - ResolutionError::UndeclaredLabel { name, suggestion } => { - let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion - { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(diagnostics::LabelWithSimilarNameReachable(ident.span)), - Some(diagnostics::TryUsingSimilarlyNamedLabel { - span, - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(diagnostics::UnreachableLabelWithSimilarNameExists { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(diagnostics::UndeclaredLabel { - span, - name, - sub_reachable, - sub_reachable_suggestion, - sub_unreachable, - }) - } - ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { - let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); - err.span_label(span, label); - - if let Some((suggestions, msg, applicability)) = suggestion { - if suggestions.is_empty() { - err.help(msg); - return err; - } - err.multipart_suggestion(msg, suggestions, applicability); - } - - let module = match module { - Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, - _ => CRATE_DEF_ID.to_def_id(), - }; - self.find_cfg_stripped(&mut err, &segment, module); - - err - } - ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { - self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span }) - } - ResolutionError::AttemptToUseNonConstantValueInConstant { - ident, - suggestion, - current, - type_span, - } => { - // let foo =... - // ^^^ given this Span - // ------- get this Span to have an applicable suggestion - - // edit: - // only do this if the const and usage of the non-constant value are on the same line - // the further the two are apart, the higher the chance of the suggestion being wrong - - let sp = self - .tcx - .sess - .source_map() - .span_extend_to_prev_str(ident.span, current, true, false); - - let (with, with_label, without) = match sp { - Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { - let sp = sp - .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) - .until(ident.span); - - // Only suggest replacing the binding keyword if this is a simple - // binding. - // - // Note: this approach still incorrectly suggests for irrefutable - // patterns like `if let x = 1 { const { x } }`, since the text - // between `let` and the identifier is just whitespace. - // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs - let is_simple_binding = - self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { - let after_keyword = snippet[current.len()..].trim(); - after_keyword.is_empty() || after_keyword == "mut" - }); - - if is_simple_binding { - ( - Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion { - span: sp, - suggestion, - current, - type_span, - }), - Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } else { - ( - None, - Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } - } - _ => ( - None, - None, - Some( - diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion { - ident_span: ident.span, - suggestion, - }, - ), - ), - }; - - self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant { - span, - with, - with_label, - without, - }) - } - ResolutionError::BindingShadowsSomethingUnacceptable { - shadowing_binding, - name, - participle, - article, - shadowed_binding, - shadowed_binding_span, - } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable { - span, - shadowing_binding, - shadowed_binding, - article, - sub_suggestion: match (shadowing_binding, shadowed_binding) { - ( - PatternSource::Match, - Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), - ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion { - span, - name, - }), - _ => None, - }, - shadowed_binding_span, - participle, - name, - }), - ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span }) - } - ForwardGenericParamBanReason::ConstParamTy => self - .dcx() - .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }), - }, - ResolutionError::ParamInTyOfConstParam { name } => { - self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) - } - ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { - self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { - span, - name, - param_kind: is_type, - help: self.tcx.sess.is_nightly_build(), - is_gca, - help_gca: is_gca, - }) - } - ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { - self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { - span, - name, - param_kind: is_type, - }) - } - ResolutionError::ForwardDeclaredSelf(reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) - } - ForwardGenericParamBanReason::ConstParamTy => { - self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) - } - }, - ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { - let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = - match suggestion { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(diagnostics::UnreachableLabelSubLabel { - ident_span: ident.span, - }), - Some(diagnostics::UnreachableLabelSubSuggestion { - span, - // intentionally taking 'ident.name' instead of 'ident' itself, as this - // could be used in suggestion context - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(diagnostics::UnreachableLabelSubLabelUnreachable { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(diagnostics::UnreachableLabel { - span, - name, - definition_span, - sub_suggestion, - sub_suggestion_label, - sub_unreachable_label, - }) - } - ResolutionError::TraitImplMismatch { - name, - kind, - code, - trait_item_span, - trait_path, - } => self - .dcx() - .create_err(diagnostics::TraitImplMismatch { - span, - name, - kind, - trait_path, - trait_item_span, - }) - .with_code(code), - ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { - self.dcx().create_err(diagnostics::TraitImplDuplicate { - span, - name, - trait_item_span, - old_span, - }) - } - ResolutionError::InvalidAsmSym => { - self.dcx().create_err(diagnostics::InvalidAsmSym { span }) - } - ResolutionError::LowercaseSelf => { - self.dcx().create_err(diagnostics::LowercaseSelf { span }) - } - ResolutionError::BindingInNeverPattern => { - self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) - } - } - } +#[derive(Diagnostic)] +#[diag("duplicate definitions with name `{$name}`:", code = E0201)] +pub(crate) struct TraitImplDuplicate { + #[primary_span] + #[label("duplicate definition")] + pub(crate) span: Span, + #[label("previous definition here")] + pub(crate) old_span: Span, + #[label("item in trait")] + pub(crate) trait_item_span: Span, + pub(crate) name: Ident, +} - pub(crate) fn report_vis_error( - &mut self, - vis_resolution_error: VisResolutionError, - ) -> ErrorGuaranteed { - match vis_resolution_error { - VisResolutionError::Relative2018(span, path) => { - self.dcx().create_err(diagnostics::Relative2018 { - span, - path_span: path.span, - // intentionally converting to String, as the text would also be used as - // in suggestion context - path_str: pprust::path_to_string(&path), - }) - } - VisResolutionError::AncestorOnly(span) => { - self.dcx().create_err(diagnostics::AncestorOnly(span)) - } - VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self - .into_struct_error( - span, - ResolutionError::FailedToResolve { - segment, - label, - suggestion, - module: None, - message, - }, - ), - VisResolutionError::ExpectedFound(span, path_str, res) => { - self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str }) - } - VisResolutionError::Indeterminate(span) => { - self.dcx().create_err(diagnostics::Indeterminate(span)) - } - VisResolutionError::ModuleOnly(span) => { - self.dcx().create_err(diagnostics::ModuleOnly(span)) - } - } - .emit() - } +#[derive(Diagnostic)] +#[diag("relative paths are not supported in visibilities in 2018 edition or later")] +pub(crate) struct Relative2018 { + #[primary_span] + pub(crate) span: Span, + #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] + pub(crate) path_span: Span, + pub(crate) path_str: String, +} - pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - path.push(def_id); - if def_id.is_top_level_module() { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - path.into_iter() - .rev() - .map(|def_id| { - self.tcx - .opt_item_name(def_id) - .map(|name| { - match ( - def_id.is_top_level_module(), - def_id.is_local(), - self.tcx.sess.edition(), - ) { - (true, true, Edition::Edition2015) => String::new(), - (true, true, _) => kw::Crate.to_string(), - (true, false, _) | (false, _, _) => name.to_string(), - } - }) - .unwrap_or_else(|| "_".to_string()) - }) - .collect::>() - .join("::") - } +#[derive(Diagnostic)] +#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] +pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] +pub(crate) struct ExpectedModuleFound { + #[primary_span] + #[label("not a module")] + pub(crate) span: Span, + pub(crate) res: Res, + pub(crate) path_str: String, +} - pub(crate) fn add_scope_set_candidates( - &mut self, - suggestions: &mut Vec, - scope_set: ScopeSet<'ra>, - ps: &ParentScope<'ra>, - sp: Span, - filter_fn: &impl Fn(Res) -> bool, - ) { - let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); - self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { - match scope { - Scope::DeriveHelpers(expn_id) => { - let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - if filter_fn(res) { - suggestions.extend( - this.helper_attrs.get(&expn_id).into_iter().flatten().map( - |&(ident, orig_ident_span, _)| { - TypoSuggestion::new(ident.name, orig_ident_span, res) - }, - ), - ); - } - } - Scope::DeriveHelpersCompat => { - // Never recommend deprecated helper attributes. - } - Scope::MacroRules(macro_rules_scope) => { - if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { - let res = macro_rules_def.decl.res(); - if filter_fn(res) { - suggestions.push(TypoSuggestion::new( - macro_rules_def.ident.name, - macro_rules_def.orig_ident_span, - res, - )) - } - } - } - Scope::ModuleNonGlobs(module, _) => { - this.add_module_candidates(module, suggestions, filter_fn, None); - } - Scope::ModuleGlobs(..) => { - // Already handled in `ModuleNonGlobs`. - } - Scope::MacroUsePrelude => { - suggestions.extend(this.macro_use_prelude.iter().filter_map( - |(name, binding)| { - let res = binding.res(); - filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) - }, - )); - } - Scope::BuiltinAttrs => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); - if filter_fn(res) { - suggestions.extend( - BUILTIN_ATTRIBUTES - .iter() - // These trace attributes are compiler-generated and have - // deliberately invalid names. - .filter(|attr| { - !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) - }) - .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), - ); - } - } - Scope::ExternPreludeItems => { - // Add idents from both item and flag scopes. - suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { - let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); - filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) - })); - } - Scope::ExternPreludeFlags => {} - Scope::ToolPrelude => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); - suggestions.extend( - this.registered_tools - .iter() - .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), - ); - } - Scope::StdLibPrelude => { - if let Some(prelude) = this.prelude { - let mut tmp_suggestions = Vec::new(); - this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); - suggestions.extend( - tmp_suggestions - .into_iter() - .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), - ); - } - } - Scope::BuiltinTypes => { - suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { - let res = Res::PrimTy(*prim_ty); - filter_fn(res) - .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) - })) - } - } +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the visibility", code = E0578)] +pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("trait implementation can only be restricted to ancestor modules")] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("cannot use a tool module through an import")] +pub(crate) struct ToolModuleImported { + #[primary_span] + pub(crate) span: Span, + #[note("the tool module imported here")] + pub(crate) import: Span, +} - ControlFlow::<()>::Continue(()) - }); - } +#[derive(Diagnostic)] +#[diag("visibility must resolve to a module")] +pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected {$expected}, found {$found} `{$macro_path}`")] +pub(crate) struct MacroExpectedFound<'a> { + #[primary_span] + #[label("not {$article} {$expected}")] + pub(crate) span: Span, + pub(crate) found: &'a str, + pub(crate) article: &'static str, + pub(crate) expected: &'a str, + pub(crate) macro_path: &'a str, + #[subdiagnostic] + pub(crate) remove_surrounding_derive: Option, + #[subdiagnostic] + pub(crate) add_as_non_derive: Option>, +} - /// Lookup typo candidate in scope for a macro or import. - fn early_lookup_typo_candidate( - &mut self, - scope_set: ScopeSet<'ra>, - parent_scope: &ParentScope<'ra>, - ident: Ident, - filter_fn: &impl Fn(Res) -> bool, - ) -> Option { - let mut suggestions = Vec::new(); - self.add_scope_set_candidates( - &mut suggestions, - scope_set, - parent_scope, - ident.span, - filter_fn, - ); - - // Make sure error reporting is deterministic. - suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); - - match find_best_match_for_name( - &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), - ident.name, - None, - ) { - Some(found) if found != ident.name => { - suggestions.into_iter().find(|suggestion| suggestion.candidate == found) - } - _ => None, - } - } +#[derive(Subdiagnostic)] +#[help("remove from the surrounding `derive()`")] +pub(crate) struct RemoveSurroundingDerive { + #[primary_span] + pub(crate) span: Span, +} - fn lookup_import_candidates_from_module( - &self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - start_module: Module<'ra>, - crate_path: ThinVec, - filter_fn: FilterFn, - ) -> Vec - where - FilterFn: Fn(Res) -> bool, - { - let mut candidates = Vec::new(); - let mut seen_modules = FxHashSet::default(); - let start_did = start_module.def_id(); - let mut worklist = vec![( - start_module, - ThinVec::::new(), - true, - start_did.is_local() || !self.tcx.is_doc_hidden(start_did), - true, - )]; - let mut worklist_via_import = vec![]; - - while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = - match worklist.pop() { - None => worklist_via_import.pop(), - Some(x) => Some(x), - } - { - let in_module_is_extern = !in_module.def_id().is_local(); - in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { - // Avoid non-importable candidates. - if name_binding.is_assoc_item() - && !this.tcx.features().import_trait_associated_functions() - { - return; - } - - if ident.name == kw::Underscore { - return; - } - - let child_accessible = - accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); - - // do not venture inside inaccessible items of other crates - if in_module_is_extern && !child_accessible { - return; - } - - let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); - - // There is an assumption elsewhere that paths of variants are in the enum's - // declaration and not imported. With this assumption, the variant component is - // chopped and the rest of the path is assumed to be the enum's own path. For - // errors where a variant is used as the type instead of the enum, this causes - // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. - if via_import && name_binding.is_possibly_imported_variant() { - return; - } - - // #90113: Do not count an inaccessible reexported item as a candidate. - if let DeclKind::Import { source_decl, .. } = name_binding.kind - && this.is_accessible_from(source_decl.vis(), parent_scope.module) - && !this.is_accessible_from(name_binding.vis(), parent_scope.module) - { - return; - } - - let res = name_binding.res(); - let did = match res { - Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), - _ => res.opt_def_id(), - }; - let child_doc_visible = doc_visible - && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); - - // collect results based on the filter function - // avoid suggesting anything from the same module in which we are resolving - // avoid suggesting anything with a hygienic name - if ident.name == lookup_ident.name - && ns == namespace - && in_module != parent_scope.module - && ident.ctxt.is_root() - && filter_fn(res) - { - // create the path - let mut segms = if lookup_ident.span.at_least_rust_2018() { - // crate-local absolute paths start with `crate::` in edition 2018 - // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) - crate_path.clone() - } else { - ThinVec::new() - }; - segms.append(&mut path_segments.clone()); - - segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - let path = Path { span: name_binding.span, segments: segms, tokens: None }; - - if child_accessible - // Remove invisible match if exists - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.accessible) - { - candidates.remove(idx); - } - - let is_stable = if is_stable - && let Some(did) = did - && this.is_stable(did, path.span) - { - true - } else { - false - }; - - // Rreplace unstable suggestions if we meet a new stable one, - // and do nothing if any other situation. For example, if we - // meet `std::ops::Range` after `std::range::legacy::Range`, - // we will remove the latter and then insert the former. - if is_stable - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) - { - candidates.remove(idx); - } - - if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { - // See if we're recommending TryFrom, TryInto, or FromIterator and add - // a note about editions - let note = if let Some(did) = did { - let requires_note = !did.is_local() - && find_attr!( - this.tcx, - did, - RustcDiagnosticItem( - sym::TryInto | sym::TryFrom | sym::FromIterator - ) - ); - requires_note.then(|| { - format!( - "'{}' is included in the prelude starting in Edition 2021", - path_names_to_string(&path) - ) - }) - } else { - None - }; - - candidates.push(ImportSuggestion { - did, - descr: res.descr(), - path, - accessible: child_accessible, - doc_visible: child_doc_visible, - note, - via_import, - is_stable, - }); - } - } - - // collect submodules to explore - if let Some(def_id) = name_binding.res().module_like_def_id() { - // form the path - let mut path_segments = path_segments.clone(); - path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - - let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind - && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind - && import.parent_scope.expansion == parent_scope.expansion - { - true - } else { - false - }; - - let is_extern_crate_that_also_appears_in_prelude = - name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); - - if !is_extern_crate_that_also_appears_in_prelude || alias_import { - // add the module to the lookup - if seen_modules.insert(def_id) { - if via_import { &mut worklist_via_import } else { &mut worklist }.push( - ( - this.expect_module(def_id), - path_segments, - child_accessible, - child_doc_visible, - is_stable && this.is_stable(def_id, name_binding.span), - ), - ); - } - } - } - }) - } +#[derive(Subdiagnostic)] +#[help( + " + add as non-Derive macro + `#[{$macro_path}]`" +)] +pub(crate) struct AddAsNonDerive<'a> { + pub(crate) macro_path: &'a str, +} - candidates - } +#[derive(Diagnostic)] +#[diag("can't use a procedural macro from the same crate that defines it")] +pub(crate) struct ProcMacroSameCrate { + #[primary_span] + pub(crate) span: Span, + #[help("you can define integration tests in a directory named `tests`")] + pub(crate) is_test: bool, +} - fn is_stable(&self, did: DefId, span: Span) -> bool { - if did.is_local() { - return true; - } +#[derive(Diagnostic)] +#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label("names from parent modules are not accessible without an explicit import")] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Symbol, +} - match self.tcx.lookup_stability(did) { - Some(Stability { - level: StabilityLevel::Unstable { implied_by, .. }, feature, .. - }) => { - if span.allows_unstable(feature) { - true - } else if self.tcx.features().enabled(feature) { - true - } else if let Some(implied_by) = implied_by - && self.tcx.features().enabled(implied_by) - { - true - } else { - false - } - } - Some(_) => true, - None => false, - } - } +#[derive(Diagnostic)] +#[diag( + "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" +)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note("the macro is defined here")] + pub definition: Span, +} - /// When name resolution fails, this method can be used to look up candidate - /// entities with the expected name. It allows filtering them using the - /// supplied predicate (which should be used to only accept the types of - /// definitions expected, e.g., traits). The lookup spans across all crates. - /// - /// N.B., the method does not look into imports, but this is not a problem, - /// since we report the definitions (thus, the de-aliased imports). - pub(crate) fn lookup_import_candidates( - &mut self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - filter_fn: FilterFn, - ) -> Vec - where - FilterFn: Fn(Res) -> bool, - { - let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; - let mut suggestions = self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - self.graph_root.to_module(), - crate_path, - &filter_fn, - ); - - if lookup_ident.span.at_least_rust_2018() { - for (ident, entry) in &self.extern_prelude { - if entry.span().from_expansion() { - // Idents are adjusted to the root context before being - // resolved in the extern prelude, so reporting this to the - // user is no help. This skips the injected - // `extern crate std` in the 2018 edition, which would - // otherwise cause duplicate suggestions. - continue; - } - let Some(crate_id) = - self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) - else { - continue; - }; - - let crate_def_id = crate_id.as_def_id(); - let crate_root = self.expect_module(crate_def_id); - - // Check if there's already an item in scope with the same name as the crate. - // If so, we have to disambiguate the potential import suggestions by making - // the paths *global* (i.e., by prefixing them with `::`). - let needs_disambiguation = - self.resolutions(parent_scope.module).borrow().iter().any( - |(key, name_resolution)| { - if key.ns == TypeNS - && key.ident == *ident - && let Some(decl) = name_resolution.borrow().best_decl() - { - match decl.res() { - // No disambiguation needed if the identically named item we - // found in scope actually refers to the crate in question. - Res::Def(_, def_id) => def_id != crate_def_id, - Res::PrimTy(_) => true, - _ => false, - } - } else { - false - } - }, - ); - let mut crate_path = ThinVec::new(); - if needs_disambiguation { - crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); - } - crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); - - suggestions.extend(self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - crate_root, - crate_path, - &filter_fn, - )); - } - } +#[derive(Diagnostic)] +#[diag("`#[macro_use]` is not supported on `extern crate self`")] +pub(crate) struct MacroUseExternCrateSelf { + #[primary_span] + pub(crate) span: Span, +} - suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); - suggestions - } +#[derive(Diagnostic)] +#[diag("not sure whether the path is accessible or not")] +#[note("the type may have associated items, but we are currently not checking them")] +pub(crate) struct CfgAccessibleUnsure { + #[primary_span] + pub(crate) span: Span, +} - pub(crate) fn unresolved_macro_suggestions( - &mut self, - err: &mut Diag<'_>, - macro_kind: MacroKind, - parent_scope: &ParentScope<'ra>, - ident: Ident, - krate: &Crate, - sugg_span: Option, - ) { - // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for - // suggestions. - self.register_macros_for_all_crates(); - - let is_expected = - &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); - let suggestion = self.early_lookup_typo_candidate( - ScopeSet::Macro(macro_kind), - parent_scope, - ident, - is_expected, - ); - if !self.add_typo_suggestion(err, suggestion, ident.span) { - self.detect_derive_attribute(err, ident, parent_scope, sugg_span); - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("generic parameters may not be used in enum discriminant values")] +pub(crate) struct ParamInEnumDiscriminant { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInEnumDiscriminant, +} - let import_suggestions = - self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); - let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { - DUMMY_NODE_ID => (None, FoundUse::No), - node_id => UsePlacementFinder::check(krate, node_id), - }; - show_candidates( - self.tcx, - err, - span, - &import_suggestions, - Instead::No, - found_use, - DiagMode::Normal, - vec![], - "", - ); - - if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - let label_span = ident.span.shrink_to_hi(); - let mut spans = MultiSpan::from_span(label_span); - spans.push_span_label(label_span, "put a macro name here"); - err.subdiagnostic(MaybeMissingMacroRulesName { spans }); - return; - } +#[derive(Debug)] +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInEnumDiscriminant { + #[note("type parameters may not be used in enum discriminant values")] + Type, + #[note("const parameters may not be used in enum discriminant values")] + Const, + #[note("lifetime parameters may not be used in enum discriminant values")] + Lifetime, +} - if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { - err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); - return; - } +#[derive(Subdiagnostic)] +#[label("you can use `as` to change the binding name of the import")] +pub(crate) struct ChangeImportBinding { + #[primary_span] + pub(crate) span: Span, +} - let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { - if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } - }); - - if let Some((def_id, unused_ident)) = unused_macro { - let scope = self.local_macro_def_scopes[&def_id]; - let parent_nearest = parent_scope.module.nearest_parent_mod(); - let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); - if !unused_macro_kinds.contains(macro_kind.into()) { - match macro_kind { - MacroKind::Bang => { - err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); - } - MacroKind::Attr => { - err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); - } - MacroKind::Derive => { - err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); - } - } - return; - } - if Some(parent_nearest) == scope.opt_def_id() { - err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); - err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); - return; - } - } +#[derive(Subdiagnostic)] +#[suggestion( + "you can use `as` to change the binding name of the import", + code = "{suggestion}", + applicability = "maybe-incorrect" +)] +pub(crate) struct ChangeImportBindingSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} - if ident.name == kw::Default - && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind - { - let span = self.def_span(def_id); - let source_map = self.tcx.sess.source_map(); - let head_span = source_map.guess_head_span(span); - err.subdiagnostic(ConsiderAddingADerive { - span: head_span.shrink_to_lo(), - suggestion: "#[derive(Default)]\n".to_string(), - }); - } - for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { - let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns), - parent_scope, - None, - None, - None, - ) else { - continue; - }; - - let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { - "a function-like macro".to_string() - } - Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{ident}]`") - } - Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { - format!("a derive macro: `#[derive({ident})]`") - } - Res::Def(DefKind::Macro(kinds), _) => { - format!("{} {}", kinds.article(), kinds.descr()) - } - Res::ToolMod | Res::OpenMod(..) => { - // Don't confuse the user with tool modules or open modules. - continue; - } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() - } - res => format!( - "{} {}, not {} {}", - res.article(), - res.descr(), - macro_kind.article(), - macro_kind.descr_expected(), - ), - }; - if let crate::DeclKind::Import { import, .. } = binding.kind - && !import.span.is_dummy() - { - let note = diagnostics::IdentImporterHereButItIsDesc { - span: import.span, - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - // Silence the 'unused import' warning we might get, - // since this diagnostic already covers that import. - self.record_use(ident, binding, Used::Other); - return; - } - let note = diagnostics::IdentInScopeButItIsDesc { - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - return; - } +#[derive(Diagnostic)] +#[diag("imports cannot refer to {$what}")] +pub(crate) struct ImportsCannotReferTo<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) what: &'a str, +} - if self.macro_names.contains(&IdentKey::new(ident)) { - err.subdiagnostic(AddedMacroUse); - return; - } - } +#[derive(Diagnostic)] +#[diag("cannot find {$expected} `{$ident}` in this scope")] +pub(crate) struct CannotFindIdentInThisScope<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) expected: &'a str, + pub(crate) ident: Ident, +} - /// Given an attribute macro that failed to be resolved, look for `derive` macros that could - /// provide it, either as-is or with small typos. - fn detect_derive_attribute( - &self, - err: &mut Diag<'_>, - ident: Ident, - parent_scope: &ParentScope<'ra>, - sugg_span: Option, - ) { - // Find all of the `derive`s in scope and collect their corresponding declared - // attributes. - // FIXME: this only works if the crate that owns the macro that has the helper_attr - // has already been imported. - let mut derives = vec![]; - let mut all_attrs: UnordMap> = UnordMap::default(); - // We're collecting these in a hashmap, and handle ordering the output further down. - #[allow(rustc::potential_query_instability)] - for (def_id, ext) in self - .local_macro_map - .iter() - .map(|(local_id, ext)| (local_id.to_def_id(), ext)) - .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) - { - for helper_attr in &ext.helper_attrs { - let item_name = self.tcx.item_name(def_id); - all_attrs.entry(*helper_attr).or_default().push(item_name); - if helper_attr == &ident.name { - derives.push(item_name); - } - } - } - let kind = MacroKind::Derive.descr(); - if !derives.is_empty() { - // We found an exact match for the missing attribute in a `derive` macro. Suggest it. - let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); - derives.sort(); - derives.dedup(); - let msg = match &derives[..] { - [derive] => format!(" `{derive}`"), - [start @ .., last] => format!( - "s {} and `{last}`", - start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") - ), - [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), - }; - let msg = format!( - "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ - missing a `derive` attribute", - ident.name, - ); - let sugg_span = - if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { - let span = self.def_span(id); - if span.from_expansion() { - None - } else { - // For enum variants sugg_span is empty but we can get the enum's Span. - Some(span.shrink_to_lo()) - } - } else { - // For items this `Span` will be populated, everything else it'll be None. - sugg_span - }; - match sugg_span { - Some(span) => { - err.span_suggestion_verbose( - span, - msg, - format!("#[derive({})]\n", derives.join(", ")), - Applicability::MaybeIncorrect, - ); - } - None => { - err.note(msg); - } - } - } else { - // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. - let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); - if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) - && let Some(macros) = all_attrs.get(&best_match) - { - let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); - macros.sort(); - macros.dedup(); - let msg = match ¯os[..] { - [] => return, - [name] => format!(" `{name}` accepts"), - [start @ .., end] => format!( - "s {} and `{end}` accept", - start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), - ), - }; - let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); - err.span_suggestion_verbose( - ident.span, - msg, - best_match, - Applicability::MaybeIncorrect, - ); - } - } - } +#[derive(Subdiagnostic)] +#[note("unsafe traits like `{$ident}` should be implemented explicitly")] +pub(crate) struct ExplicitUnsafeTraits { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Subdiagnostic)] +#[note("a macro with the same name exists, but it appears later")] +pub(crate) struct MacroDefinedLater { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[label("consider moving the definition of `{$ident}` before this call")] +pub(crate) struct MacroSuggMovePosition { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - pub(crate) fn add_typo_suggestion( - &self, - err: &mut Diag<'_>, - suggestion: Option, +#[derive(Subdiagnostic)] +pub(crate) enum MacroRulesNot { + #[label("`{$ident}` exists, but has no rules for function-like invocation")] + Func { + #[primary_span] span: Span, - ) -> bool { - let suggestion = match suggestion { - None => return false, - // We shouldn't suggest underscore. - Some(suggestion) if suggestion.candidate == kw::Underscore => return false, - Some(suggestion) => suggestion, - }; - - let mut did_label_def_span = false; - - if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { - if span.overlaps(def_span) { - // Don't suggest typo suggestion for itself like in the following: - // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` - // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 - // | - // LL | struct X {} - // | ----------- `X` defined here - // LL | - // LL | const Y: X = X("ö"); - // | -------------^^^^^^- similarly named constant `Y` defined here - // | - // help: use struct literal syntax instead - // | - // LL | const Y: X = X {}; - // | ^^^^ - // help: a constant with a similar name exists - // | - // LL | const Y: X = Y("ö"); - // | ^ - return false; - } - let span = self.tcx.sess.source_map().guess_head_span(def_span); - let candidate_descr = suggestion.res.descr(); - let candidate = suggestion.candidate; - let label = match suggestion.target { - SuggestionTarget::SimilarlyNamed => { - diagnostics::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } - } - SuggestionTarget::SingleItem => { - diagnostics::DefinedHere::SingleItem { span, candidate_descr, candidate } - } - }; - did_label_def_span = true; - err.subdiagnostic(label); - } + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `attr` rules")] + Attr { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `derive` rules")] + Derive { + #[primary_span] + span: Span, + ident: Ident, + }, +} - let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(span) = suggestion.span - && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') - && snippet == candidate - { - let candidate = suggestion.candidate; - // When the suggested binding change would be from `x` to `_x`, suggest changing the - // original binding definition instead. (#60164) - let msg = format!( - "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" - ); - if !did_label_def_span { - err.span_label(span, format!("`{candidate}` defined here")); - } - (span, msg, snippet) - } else { - let msg = match suggestion.target { - SuggestionTarget::SimilarlyNamed => format!( - "{} {} with a similar name exists", - suggestion.res.article(), - suggestion.res.descr() - ), - SuggestionTarget::SingleItem => { - format!("maybe you meant this {}", suggestion.res.descr()) - } - }; - (span, msg, suggestion.candidate.to_ident_string()) - }; - err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); - true - } +#[derive(Subdiagnostic)] +#[note("maybe you have forgotten to define a name for this `macro_rules!`")] +pub(crate) struct MaybeMissingMacroRulesName { + #[primary_span] + pub(crate) spans: MultiSpan, +} - fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { - let res = b.res(); - if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { - let (built_in, from) = match scope { - Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), - Scope::ExternPreludeFlags - if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() - || matches!(res, Res::OpenMod(..)) => - { - ("", " passed with `--extern`") - } - _ => { - if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { - // These already contain the "built-in" prefix or look bad with it. - ("", "") - } else { - (" built-in", "") - } - } - }; - - let a = if built_in.is_empty() { res.article() } else { "a" }; - format!("{a}{built_in} {thing}{from}", thing = res.descr()) - } else { - let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; - format!("the {thing} {introduced} here", thing = res.descr()) - } - } +#[derive(Subdiagnostic)] +#[help("have you added the `#[macro_use]` on the module/import?")] +pub(crate) struct AddedMacroUse; + +#[derive(Subdiagnostic)] +#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] +pub(crate) struct ConsiderAddingADerive { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} + +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the import")] +pub(crate) struct CannotDetermineImportResolution { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the {$kind} `{$path}`")] +#[note("import resolution is stuck, try simplifying macro imports")] +pub(crate) struct CannotDetermineMacroResolution { + #[primary_span] + pub(crate) span: Span, + pub(crate) kind: &'static str, + pub(crate) path: String, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] +pub(crate) struct CannotBeReexportedPrivate { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] +pub(crate) struct CannotBeReexportedCratePublic { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedPrivateNS { + #[primary_span] + #[label("re-export of private `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedCratePublicNS { + #[primary_span] + #[label("re-export of crate public `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] +pub(crate) struct PrivateExternCrateReexport { + pub ident: Ident, + #[suggestion( + "consider making the `extern crate` item publicly accessible", + code = "pub ", + style = "verbose", + applicability = "maybe-incorrect" + )] + pub sugg: Span, +} + +#[derive(Subdiagnostic)] +#[help("consider adding a `#[macro_export]` to the macro in the imported module")] +pub(crate) struct ConsiderAddingMacroExport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", + code = "pub(crate)", + applicability = "maybe-incorrect" +)] +pub(crate) struct ConsiderMarkingAsPubCrate { + #[primary_span] + pub(crate) vis_span: Span, +} + +#[derive(Subdiagnostic)] +#[note("consider marking `{$ident}` as `pub` in the imported module")] +pub(crate) struct ConsiderMarkingAsPub { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("cannot glob-import all possible crates")] +pub(crate) struct CannotGlobImportAllCrates { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to write a const parameter here", + code = "const ", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyToConstParamSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", + code = "{snippet}", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct UnexpectedMissingConstParameter { + #[primary_span] + pub span: Span, + pub snippet: String, + pub item_name: String, + pub item_location: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to write a const parameter here", + applicability = "has-placeholders", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { + #[suggestion_part(code = "const ")] + pub before: Span, + #[suggestion_part(code = ": /* Type */")] + pub after: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { + #[primary_span] + pub span: Span, + pub ident: Ident, + pub snippet: String, +} + +#[derive(Diagnostic)] +#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] +pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`extern crate self;` requires renaming")] +pub(crate) struct ExternCrateSelfRequiresRenaming { + #[primary_span] + #[suggestion( + "rename the `self` crate to be able to import it", + code = "extern crate self as name;", + applicability = "has-placeholders" + )] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`{$name}` is already in scope")] +#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] +pub(crate) struct MacroUseNameAlreadyInUse { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("imported macro not found", code = E0469)] +pub(crate) struct ImportedMacroNotFound { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] +pub(crate) struct MacroExternDeprecated { + #[primary_span] + pub(crate) span: Span, + #[help("try an outer attribute: `#[macro_use]`")] + pub inner_attribute: bool, +} - fn ambiguity_diagnostic( - &self, - ambiguity_error: &AmbiguityError<'ra>, - ) -> diagnostics::Ambiguity { - let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = - *ambiguity_error; - let extern_prelude_ambiguity = || { - // Note: b1 may come from a module scope, as an extern crate item in module. - matches!(scope2, Scope::ExternPreludeFlags) - && self - .extern_prelude - .get(&IdentKey::new(ident)) - .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) - }; - let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { - // We have to print the span-less alternative first, otherwise formatting looks bad. - (b2, b1, scope2, scope1, true) - } else { - (b1, b2, scope1, scope2, false) - }; - - let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { - let what = self.decl_description(b, ident, scope); - let note_msg = format!("`{ident}` could{also} refer to {what}"); - - let thing = b.res().descr(); - let mut help_msgs = Vec::new(); - if b.is_glob_import() - && (kind == AmbiguityKind::GlobVsGlob - || kind == AmbiguityKind::GlobVsExpanded - || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) - { - help_msgs.push(format!( - "consider adding an explicit import of `{ident}` to disambiguate" - )) - } - if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() - { - help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) - } +#[derive(Diagnostic)] +#[diag("arguments to `macro_use` are not allowed here")] +pub(crate) struct ArgumentsMacroUseNotAllowed { + #[primary_span] + pub(crate) span: Span, +} - if kind != AmbiguityKind::GlobVsGlob { - if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { - if module == self.graph_root.to_module() { - help_msgs.push(format!( - "use `crate::{ident}` to refer to this {thing} unambiguously" - )); - } else if module.is_normal() { - help_msgs.push(format!( - "use `self::{ident}` to refer to this {thing} unambiguously" - )); - } - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "try renaming it with a name", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnnamedImportSugg { + #[suggestion_part(code = "{ident} as name")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - ( - Spanned { node: note_msg, span: b.span }, - help_msgs - .iter() - .enumerate() - .map(|(i, help_msg)| { - let or = if i == 0 { "" } else { "or " }; - format!("{or}{help_msg}") - }) - .collect::>(), - ) - }; - let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); - let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); - let help = if kind == AmbiguityKind::GlobVsGlob - && b1 - .parent_module - .and_then(|m| m.opt_def_id()) - .map(|d| !d.is_local()) - .unwrap_or_default() - { - Some(&[ - "consider updating this dependency to resolve this error", - "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", - ] as &[_]) - } else { - None - }; - - let ambig_vis = ambig_vis.map(|(vis1, vis2)| { - format!( - "{} or {}", - vis1.to_string(CRATE_DEF_ID, self.tcx), - vis2.to_string(CRATE_DEF_ID, self.tcx) - ) - }); - - diagnostics::Ambiguity { - ident, - help, - ambig_vis, - kind: kind.descr(), - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error: false, - } - } +#[derive(Diagnostic)] +#[diag("imports need to be explicitly named")] +pub(crate) struct UnnamedImport { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) sugg: UnnamedImportSugg, +} - /// If the binding refers to a tuple struct constructor with fields, - /// returns the span of its fields. - fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { - let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = - decl.kind - else { - return None; - }; - - let def_id = self.tcx.parent(ctor_def_id); - self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` - } +#[derive(Diagnostic)] +#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] +pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { + #[primary_span] + pub(crate) span: Span, +} - /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. - /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. - /// Returns `None` for block modules that don't have a `DefId`. - fn module_path_names(&self, module: Module<'ra>) -> Option> { - let mut path = Vec::new(); - let mut def_id = module.opt_def_id()?; - while let Some(parent) = self.tcx.opt_parent(def_id) { - if let Some(name) = self.tcx.opt_item_name(def_id) { - path.push(name); - } - if parent.is_top_level_module() { - break; - } - def_id = parent; - } - path.reverse(); - path.insert(0, kw::Crate); - Some(path) - } +#[derive(Diagnostic)] +#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] +pub(crate) struct ElidedAnonymousLifetimeReportError { + #[primary_span] + #[label("explicit lifetime name needed here")] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) suggestion: Option, +} - /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) - /// relative to the current scope, if possible. Only applies to crate-local items and - /// only when the resulting path is actually shorter than the original. - fn shorten_candidate_path( - &self, - suggestion: &mut ImportSuggestion, - current_module: Module<'ra>, - ) { - const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; - - // Only shorten local items. - if suggestion.did.is_none_or(|did| !did.is_local()) { - return; - } +#[derive(Diagnostic)] +#[diag( + "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" +)] +pub(crate) struct LendingIteratorReportError { + #[primary_span] + pub(crate) lifetime: Span, + #[note( + "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" + )] + pub(crate) ty: Span, +} - // Build current module path: [Crate, foo, bar, ...]. - let Some(current_mod_path) = self.module_path_names(current_module) else { - return; - }; - - // Normalise candidate path: filter out `PathRoot` (`::`), and if the path - // doesn't start with `Crate`, prepend it (edition 2015 paths are relative - // to the crate root without an explicit `crate::` prefix). - let candidate_names = { - let filtered_segments: Vec<_> = suggestion - .path - .segments - .iter() - .filter(|segment| segment.ident.name != kw::PathRoot) - .collect(); - - let mut candidate_names: Vec = - filtered_segments.iter().map(|segment| segment.ident.name).collect(); - if candidate_names.first() != Some(&kw::Crate) { - candidate_names.insert(0, kw::Crate); - } - if candidate_names.len() < 2 { - return; - } - candidate_names - }; - - // The candidate's module path is everything except the last segment (the item name). - let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; - - // Find the longest common prefix between the current module and candidate module paths. - let common_prefix_length = current_mod_path - .iter() - .zip(candidate_mod_names.iter()) - .take_while(|(current, candidate)| current == candidate) - .count(); - - // Non-crate-local item; keep the full absolute path. - if common_prefix_length == 0 { - return; - } +#[derive(Diagnostic)] +#[diag("missing lifetime in associated type")] +pub(crate) struct AnonymousLifetimeNonGatReportError { + #[primary_span] + #[label("this lifetime must come from the implemented type")] + pub(crate) lifetime: Span, + #[note( + "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" + )] + pub(crate) decl: MultiSpan, +} - let super_count = current_mod_path.len() - common_prefix_length; +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider introducing a higher-ranked lifetime here", + applicability = "machine-applicable" +)] +pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { + #[suggestion_part(code = "for<'a> ")] + pub(crate) lo: Span, + #[suggestion_part(code = "'a ")] + pub(crate) hi: Span, +} - // At the crate root, `use` paths resolve from the crate root anyway, so we can - // drop the `crate::` prefix entirely instead of replacing it with `self::`. - let at_crate_root = current_mod_path.len() == 1; +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +pub(crate) struct ExplicitAnonymousLifetimeReportError { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - let mut new_segments = if super_count == 0 && at_crate_root { - ThinVec::new() - } else { - let prefix_keyword = match super_count { - 0 => kw::SelfLower, - 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, - _ => return, // Too many `super` levels; keep the full absolute path. - }; - thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] - }; - for &name in &candidate_names[common_prefix_length..] { - new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); - } +#[derive(Diagnostic)] +#[diag("implicit elided lifetime not allowed here", code = E0726)] +pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) subdiag: ElidedLifetimeInPathSubdiag, +} - // Only apply if the result is strictly shorter than the original path. - if new_segments.len() >= suggestion.path.segments.len() { - return; - } +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +#[help("use another lifetime specifier")] +pub(crate) struct UnderscoreLifetimeIsReserved { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; - } +#[derive(Diagnostic)] +#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] +pub(crate) struct StaticLifetimeIsReserved { + #[primary_span] + #[label("'static is a reserved lifetime name")] + pub(crate) span: Span, + pub(crate) lifetime: Ident, +} - fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { - let PrivacyError { - ident, - decl, - outermost_res, - parent_scope, - single_nested, - dedup_span, - ref source, - } = *privacy_error; - - let res = decl.res(); - let ctor_fields_span = self.ctor_fields_span(decl); - let plain_descr = res.descr().to_string(); - let nonimport_descr = - if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; - let import_descr = nonimport_descr.clone() + " import"; - let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; - - // Print the primary message. - let ident_descr = get_descr(decl); - let mut err = - self.dcx().create_err(diagnostics::IsPrivate { span: ident.span, ident_descr, ident }); - - self.mention_default_field_values(source, ident, &mut err); - - let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { - let mut import_suggestions = self.lookup_import_candidates( - outer_ident, - this_res.ns().unwrap_or(Namespace::TypeNS), - &parent_scope, - &|res: Res| res == this_res, - ); - // Shorten candidate paths using `super::` or `self::` when possible. - for suggestion in &mut import_suggestions { - self.shorten_candidate_path(suggestion, parent_scope.module); - } - let point_to_def = !show_candidates( - self.tcx, - &mut err, - Some(dedup_span.until(outer_ident.span.shrink_to_hi())), - &import_suggestions, - Instead::Yes, - FoundUse::Yes, - DiagMode::Import { append: single_nested, unresolved_import: false }, - vec![], - "", - ); - // If we suggest importing a public re-export, don't point at the definition. - if point_to_def && ident.span != outer_ident.span { - let label = diagnostics::OuterIdentIsNotPubliclyReexported { - span: outer_ident.span, - outer_ident_descr: this_res.descr(), - outer_ident, - }; - err.subdiagnostic(label); - } - !point_to_def - } else { - false - }; - - let mut non_exhaustive = None; - // If an ADT is foreign and marked as `non_exhaustive`, then that's - // probably why we have the privacy error. - // Otherwise, point out if the struct has any private fields. - if let Some(def_id) = res.opt_def_id() - && !def_id.is_local() - && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) - { - non_exhaustive = Some(attr_span); - } else if let Some(span) = ctor_fields_span { - let label = diagnostics::ConstructorPrivateIfAnyFieldPrivate { span }; - err.subdiagnostic(label); - if let Res::Def(_, d) = res - && let Some(fields) = self.field_visibility_spans.get(&d) - { - let spans = fields.iter().map(|span| *span).collect(); - let sugg = diagnostics::ConsiderMakingTheFieldPublic { - spans, - number_of_fields: fields.len(), - }; - err.subdiagnostic(sugg); - } - } +#[derive(Diagnostic)] +#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] +pub(crate) struct VariableIsNotBoundInAllPatterns { + #[primary_span] + pub(crate) multispan: MultiSpan, + pub(crate) name: Ident, +} - let mut sugg_paths: Vec<(Vec, bool)> = vec![]; - if let Some(mut def_id) = res.opt_def_id() { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - if !def_id.is_top_level_module() { - path.push(def_id); - } else { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - let path_names: Option> = path - .iter() - .rev() - .map(|def_id| { - self.tcx.opt_item_name(*def_id).map(|name| { - Ident::with_dummy_span(if def_id.is_top_level_module() { - kw::Crate - } else { - name - }) - }) - }) - .collect(); - if let Some(&def_id) = path.get(0) - && let Some(path) = path_names - { - if let Some(def_id) = def_id.as_local() { - if self.effective_visibilities.is_directly_public(def_id) { - sugg_paths.push((path, false)); - } - } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) - { - sugg_paths.push((path, false)); - } - } - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("pattern doesn't bind `{$name}`")] +pub(crate) struct PatternDoesntBindName { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Ident, +} - // Print the whole import chain to make it easier to see what happens. - let first_binding = decl; - let mut next_binding = Some(decl); - let mut next_ident = ident; - while let Some(binding) = next_binding { - let name = next_ident; - next_binding = match binding.kind { - _ if res == Res::Err => None, - DeclKind::Import { source_decl, import, .. } => match import.kind { - _ if source_decl.span.is_dummy() => None, - ImportKind::Single { source, .. } => { - next_ident = source; - Some(source_decl) - } - ImportKind::Glob { .. } - | ImportKind::MacroUse { .. } - | ImportKind::MacroExport => Some(source_decl), - ImportKind::ExternCrate { .. } => None, - }, - _ => None, - }; - - match binding.kind { - DeclKind::Import { source_decl, import, .. } => { - // Don't include `{{root}}` in suggestions - it's an internal symbol - // that should never be shown to users. - let path = import - .module_path - .iter() - .filter(|seg| seg.ident.name != kw::PathRoot) - .map(|seg| seg.ident.clone()) - .chain(std::iter::once(ident)) - .collect::>(); - let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); - sugg_paths.push((path, through_reexport)); - } - DeclKind::Def(_) => {} - } - let first = binding == first_binding; - let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); - let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis().is_public() { - let desc = match binding.kind { - DeclKind::Import { .. } => "re-export", - _ => "directly", - }; - note_span.push_span_label(def_span, format!("you could import this {desc}")); - } - // Final step in the import chain, point out if the ADT is `non_exhaustive` - // which is probably why this privacy violation occurred. - if next_binding.is_none() - && let Some(span) = non_exhaustive - { - note_span.push_span_label( - span, - "cannot be constructed because it is `#[non_exhaustive]`", - ); - } - let note = diagnostics::NoteAndRefersToTheItemDefinedHere { - span: note_span, - binding_descr: get_descr(binding), - binding_name: name, - first, - dots: next_binding.is_some(), - }; - err.subdiagnostic(note); - } - // The suggestion replaces `dedup_span` with a path reaching the failing ident. - // That's valid only when - // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments - // get dropped, and - // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. - // - // See issue #156060. - let can_replace_use = !shown_candidates - && !single_nested - && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); - if can_replace_use { - // We prioritize shorter paths, non-core imports and direct imports over the - // alternatives. - sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); - for (sugg, reexport) in sugg_paths { - if sugg.len() <= 1 { - // A single path segment suggestion is wrong. This happens on circular - // imports. `tests/ui/imports/issue-55884-2.rs` - continue; - } - let path = join_path_idents(sugg); - let sugg = if reexport { - diagnostics::ImportIdent::ThroughReExport { span: dedup_span, ident, path } - } else { - diagnostics::ImportIdent::Directly { span: dedup_span, ident, path } - }; - err.subdiagnostic(sugg); - break; - } - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("variable not in all patterns")] +pub(crate) struct VariableNotInAllPatterns { + #[primary_span] + pub(crate) span: Span, +} - err.emit(); - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to use the similarly named previously used binding `{$typo}`", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct PatternBindingTypo { + #[suggestion_part(code = "{typo}")] + pub(crate) spans: Vec, + pub(crate) typo: Symbol, +} - /// When a private field is being set that has a default field value, we suggest using `..` and - /// setting the value of that field implicitly with its default. - /// - /// If we encounter code like - /// ```text - /// struct Priv; - /// pub struct S { - /// pub field: Priv = Priv, - /// } - /// ``` - /// which is used from a place where `Priv` isn't accessible - /// ```text - /// let _ = S { field: m::Priv1 {} }; - /// // ^^^^^ private struct - /// ``` - /// we will suggest instead using the `default_field_values` syntax instead: - /// ```text - /// let _ = S { .. }; - /// ``` - fn mention_default_field_values( - &self, - source: &Option, - ident: Ident, - err: &mut Diag<'_>, - ) { - let Some(expr) = source else { return }; - let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; - // We don't have to handle type-relative paths because they're forbidden in ADT - // expressions, but that would change with `#[feature(more_qualified_paths)]`. - let Some(segment) = struct_expr.path.segments.last() else { return }; - let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; - let Some(Res::Def(_, def_id)) = partial_res.full_res() else { - return; - }; - let Some(default_fields) = self.field_defaults(def_id) else { return }; - if struct_expr.fields.is_empty() { - return; - } - let last_span = struct_expr.fields.iter().last().unwrap().span; - let mut iter = struct_expr.fields.iter().peekable(); - let mut prev: Option = None; - while let Some(field) = iter.next() { - if field.expr.span.overlaps(ident.span) { - err.span_label(field.ident.span, "while setting this field"); - if default_fields.contains(&field.ident.name) { - let sugg = if last_span == field.span { - vec![(field.span, "..".to_string())] - } else { - vec![ - ( - // Account for trailing commas and ensure we remove them. - match (prev, iter.peek()) { - (_, Some(next)) => field.span.with_hi(next.span.lo()), - (Some(prev), _) => field.span.with_lo(prev.hi()), - (None, None) => field.span, - }, - String::new(), - ), - (last_span.shrink_to_hi(), ", ..".to_string()), - ] - }; - err.multipart_suggestion( - format!( - "the type `{ident}` of field `{}` is private, but you can construct \ - the default value defined for it in `{}` using `..` in the struct \ - initializer expression", - field.ident, - self.tcx.item_name(def_id), - ), - sugg, - Applicability::MachineApplicable, - ); - break; - } - } - prev = Some(field.span); - } - } +#[derive(Diagnostic)] +#[diag("the name `{$name}` is defined multiple times")] +#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] +pub(crate) struct NameDefinedMultipleTime { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, + pub(crate) descr: &'static str, + pub(crate) container: &'static str, + #[subdiagnostic] + pub(crate) label: NameDefinedMultipleTimeLabel, + #[subdiagnostic] + pub(crate) old_binding_label: Option, +} - pub(crate) fn find_similarly_named_module_or_crate( - &self, - ident: Symbol, - current_module: Module<'ra>, - ) -> Option { - let mut candidates = self - .extern_prelude - .keys() - .map(|ident| ident.name) - .chain( - self.local_module_map - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .chain( - self.extern_module_map - .borrow() - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .filter(|c| !c.to_string().is_empty()) - .collect::>(); - candidates.sort(); - candidates.dedup(); - find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeLabel { + #[label("`{$name}` reimported here")] + Reimported { + #[primary_span] + span: Span, + name: Symbol, + }, + #[label("`{$name}` redefined here")] + Redefined { + #[primary_span] + span: Span, + name: Symbol, + }, +} - pub(crate) fn report_path_resolution_error( - &mut self, - path: &[Segment], - opt_ns: Option, // `None` indicates a module path in import - parent_scope: &ParentScope<'ra>, - ribs: Option<&PerNS>>>, - ignore_decl: Option>, - ignore_import: Option>, - module: Option>, - failed_segment_idx: usize, - ident: Ident, - diag_metadata: Option<&DiagMetadata<'_>>, - ) -> (String, String, Option) { - let is_last = failed_segment_idx == path.len() - 1; - let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let module_def_id = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), - _ => None, - }; - let scope = match &path[..failed_segment_idx] { - [.., prev] => { - if prev.ident.name == kw::PathRoot { - format!("the crate root") - } else { - format!("`{}`", prev.ident) - } - } - _ => format!("this scope"), - }; - let message = format!("cannot find `{ident}` in {scope}"); - - if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); - candidates - .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); - if let Some(candidate) = candidates.get(0) { - let path = { - // remove the possible common prefix of the path - let len = candidate.path.segments.len(); - let start_index = (0..=failed_segment_idx.min(len - 1)) - .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) - .unwrap_or_default(); - let segments = - (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); - Path { segments, span: Span::default(), tokens: None } - }; - ( - message, - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == sym::core { - ( - message, - format!("you might be missing crate `{ident}`"), - Some(( - vec![(ident.span, "std".to_string())], - "try using `std` instead of `core`".to_string(), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == kw::Underscore { - ( - "invalid crate or module name `_`".to_string(), - "`_` is not a valid crate or module name".to_string(), - None, - ) - } else if self.tcx.sess.is_rust_2015() { - ( - format!("cannot find module or crate `{ident}` in {scope}"), - format!("use of unresolved module or unlinked crate `{ident}`"), - Some(( - vec![( - self.current_crate_outer_attr_insert_span, - format!("extern crate {ident};\n"), - )], - if was_invoked_from_cargo() { - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add \ - {ident}` to add it to your `Cargo.toml` and import it in your \ - code", - ) - } else { - format!( - "you might be missing a crate named `{ident}`, add it to your \ - project and import it in your code", - ) - }, - Applicability::MaybeIncorrect, - )), - ) - } else { - (message, format!("could not find `{ident}` in the crate root"), None) - } - } else if failed_segment_idx > 0 { - let parent = path[failed_segment_idx - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => format!("`{parent}`"), - }; - - let mut msg = format!("could not find `{ident}` in {parent}"); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - let binding = if let Some(module) = module { - self.cm() - .resolve_ident_in_module( - module, - ident, - ns_to_try, - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - } else if let Some(ribs) = ribs - && let Some(TypeNS | ValueNS) = opt_ns - { - assert!(ignore_import.is_none()); - match self.resolve_ident_in_lexical_scope( - ident, - ns_to_try, - parent_scope, - None, - &ribs[ns_to_try], - ignore_decl, - diag_metadata, - ) { - // we found a locally-imported or available item/module - Some(LateDecl::Decl(binding)) => Some(binding), - _ => None, - } - } else { - self.cm() - .resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns_to_try), - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - }; - if let Some(binding) = binding { - msg = format!( - "expected {}, found {} `{ident}` in {parent}", - ns.descr(), - binding.res().descr(), - ); - }; - } - (message, msg, None) - } else if ident.name == kw::SelfUpper { - // As mentioned above, `opt_ns` being `None` indicates a module path in import. - // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an - // impl - if opt_ns.is_none() { - (message, "`Self` cannot be used in imports".to_string(), None) - } else { - ( - message, - "`Self` is only available in impls, traits, and type definitions".to_string(), - None, - ) - } - } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { - // Check whether the name refers to an item in the value namespace. - let binding = if let Some(ribs) = ribs { - assert!(ignore_import.is_none()); - self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - None, - &ribs[ValueNS], - ignore_decl, - diag_metadata, - ) - } else { - None - }; - let match_span = match binding { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LateDecl::RibDef(Res::Local(id))) => { - Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) - } - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub const Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LateDecl::Decl(name_binding)) => Some(( - name_binding.span, - name_binding.res().article(), - name_binding.res().descr(), - )), - _ => None, - }; - - let message = format!("cannot find type `{ident}` in {scope}"); - let label = if let Some((span, article, descr)) = match_span { - format!( - "`{ident}` is declared as {article} {descr} at `{}`, not a type", - self.tcx - .sess - .source_map() - .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) - ) - } else { - format!("use of undeclared type `{ident}`") - }; - (message, label, None) - } else { - let mut suggestion = None; - if ident.name == sym::alloc { - suggestion = Some(( - vec![], - String::from("add `extern crate alloc` to use the `alloc` crate"), - Applicability::MaybeIncorrect, - )) - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { + #[label("previous import of the {$old_kind} `{$name}` here")] + Import { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, + #[label("previous definition of the {$old_kind} `{$name}` here")] + Definition { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, +} - suggestion = suggestion.or_else(|| { - self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( - |sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from("there is a crate or module with a similar name"), - Applicability::MaybeIncorrect, - ) - }, - ) - }); - if let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ValueNS), - parent_scope, - None, - ignore_decl, - ignore_import, - ) { - let descr = binding.res().descr(); - let message = format!("cannot find module or crate `{ident}` in {scope}"); - (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) - } else { - let suggestion = if suggestion.is_some() { - suggestion - } else if let Some(m) = self.undeclared_module_exists(ident) { - self.undeclared_module_suggest_declare(ident, m) - } else if was_invoked_from_cargo() { - Some(( - vec![], - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ - to add it to your `Cargo.toml`", - ), - Applicability::MaybeIncorrect, - )) - } else { - Some(( - vec![], - format!("you might be missing a crate named `{ident}`",), - Applicability::MaybeIncorrect, - )) - }; - let message = format!("cannot find module or crate `{ident}` in {scope}"); - ( - message, - format!("use of unresolved module or unlinked crate `{ident}`"), - suggestion, - ) - } - } - } +#[derive(Diagnostic)] +#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] +pub(crate) struct IsPrivate<'a> { + #[primary_span] + #[label("private {$ident_descr}")] + pub(crate) span: Span, + pub(crate) ident_descr: &'a str, + pub(crate) ident: Ident, +} - fn undeclared_module_suggest_declare( - &self, - ident: Ident, - path: std::path::PathBuf, - ) -> Option<(Vec<(Span, String)>, String, Applicability)> { - Some(( - vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], - format!( - "to make use of source file {}, use `mod {ident}` \ - in this file to declare the module", - path.display() - ), - Applicability::MaybeIncorrect, - )) - } +#[derive(Diagnostic)] +#[diag("generic arguments in macro path")] +pub(crate) struct GenericArgumentsInMacroPath { + #[primary_span] + pub(crate) span: Span, +} - fn undeclared_module_exists(&self, ident: Ident) -> Option { - let map = self.tcx.sess.source_map(); - - let src = map.span_to_filename(ident.span).into_local_path()?; - let i = ident.as_str(); - // FIXME: add case where non parent using undeclared module (hard?) - let dir = src.parent()?; - let src = src.file_stem()?.to_str()?; - for file in [ - // …/x.rs - dir.join(i).with_extension("rs"), - // …/x/mod.rs - dir.join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - if !matches!(src, "main" | "lib" | "mod") { - for file in [ - // …/x/y.rs - dir.join(src).join(i).with_extension("rs"), - // …/x/y/mod.rs - dir.join(src).join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - } - None - } +#[derive(Diagnostic)] +#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] +pub(crate) struct AttributesStartingWithRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - /// Adds suggestions for a path that cannot be resolved. - #[instrument(level = "debug", skip(self, parent_scope))] - pub(crate) fn make_path_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - match path[..] { - // `{{root}}::ident::...` on both editions. - // On 2015 `{{root}}` is usually added implicitly. - [first, second, ..] - if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} - // `ident::...` on 2018. - [first, ..] - if first.ident.span.at_least_rust_2018() - && !first.ident.is_path_segment_keyword() => - { - // Insert a placeholder that's later replaced by `self`/`super`/etc. - path.insert(0, Segment::from_ident(Ident::dummy())); - } - _ => return None, - } +#[derive(Diagnostic)] +#[diag( + "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" +)] +pub(crate) struct AttributesContainingRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - self.make_missing_self_suggestion(path.clone(), parent_scope) - .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) - } +#[derive(Diagnostic)] +#[diag("cannot use {$article} {$descr} through an import")] +pub(crate) struct CannotUseThroughAnImport { + #[primary_span] + pub(crate) span: Span, + pub(crate) article: &'static str, + pub(crate) descr: &'static str, + #[note("the {$descr} imported here")] + pub(crate) binding_span: Option, +} - /// Suggest a missing `self::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `self::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_self_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `self` and check if that is valid. - path[0].ident.name = kw::SelfLower; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag("name `{$ident}` is reserved in attribute namespace")] +pub(crate) struct NameReservedInAttributeNamespace { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Symbol, +} - /// Suggests a missing `crate::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `crate::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Crate; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { - Some(( - path, - Some( - "`use` statements changed in Rust 2018; read more at \ - " - .to_string(), - ), - )) - } else { - None - } - } +#[derive(Diagnostic)] +#[diag("cannot find a built-in macro with name `{$ident}`")] +pub(crate) struct CannotFindBuiltinMacroWithName { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - /// Suggests a missing `super::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `super::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_super_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Super; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag("tool `{$tool}` was already registered")] +pub(crate) struct ToolWasAlreadyRegistered { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + #[label("already registered here")] + pub(crate) old_ident_span: Span, +} - /// Suggests a missing external crate name if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foobar::Baz; - /// | ^^^^^^ did you mean `baz::foobar`? - /// ``` - /// - /// Used when importing a submodule of an external crate but missing that crate's - /// name as the first part of path. - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_external_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - if path[1].ident.span.is_rust_2015() { - return None; - } +#[derive(Subdiagnostic)] +pub(crate) enum DefinedHere { + #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] + SimilarlyNamed { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, + #[label("{$candidate_descr} `{$candidate}` defined here")] + SingleItem { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, +} - // Sort extern crate names in *reverse* order to get - // 1) some consistent ordering for emitted diagnostics, and - // 2) `std` suggestions before `core` suggestions. - let mut extern_crate_names = - self.extern_prelude.keys().map(|ident| ident.name).collect::>(); - extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); - - for name in extern_crate_names.into_iter() { - // Replace first ident with a crate name and check if that is valid. - path[0].ident.name = name; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?name, ?result); - if let PathResult::Module(..) = result { - return Some((path, None)); - } - } +#[derive(Subdiagnostic)] +#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] +pub(crate) struct OuterIdentIsNotPubliclyReexported { + #[primary_span] + pub(crate) span: Span, + pub(crate) outer_ident_descr: &'static str, + pub(crate) outer_ident: Ident, +} - None - } +#[derive(Subdiagnostic)] +#[label("a constructor is private if any of the fields is private")] +pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "{ $number_of_fields -> + [one] consider making the field publicly accessible + *[other] consider making the fields publicly accessible + }", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct ConsiderMakingTheFieldPublic { + #[suggestion_part(code = "pub ")] + pub(crate) spans: Vec, + pub(crate) number_of_fields: usize, +} - /// Suggests importing a macro from the root of the crate rather than a module within - /// the crate. - /// - /// ```text - /// help: a macro with this name exists at the root of the crate - /// | - /// LL | use issue_59764::makro; - /// | ^^^^^^^^^^^^^^^^^^ - /// | - /// = note: this could be because a macro annotated with `#[macro_export]` will be exported - /// at the root of the crate instead of the module where it is defined - /// ``` - pub(crate) fn check_for_module_export_macro( - &mut self, - import: Import<'ra>, - module: ModuleOrUniformRoot<'ra>, +#[derive(Subdiagnostic)] +pub(crate) enum ImportIdent { + #[suggestion( + "import `{$ident}` through the re-export", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + ThroughReExport { + #[primary_span] + span: Span, ident: Ident, - ) -> Option<(Option, Option)> { - let ModuleOrUniformRoot::Module(mut crate_module) = module else { - return None; - }; + path: String, + }, + #[suggestion( + "import `{$ident}` directly", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + Directly { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, +} - while let Some(parent) = crate_module.parent { - crate_module = parent; - } +#[derive(Subdiagnostic)] +#[note( + "{$first -> + [true] {$dots -> + [true] the {$binding_descr} `{$binding_name}` is defined here... + *[false] the {$binding_descr} `{$binding_name}` is defined here + } + *[false] {$dots -> + [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... + *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here + } + }" +)] +pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) binding_descr: &'a str, + pub(crate) binding_name: Ident, + pub(crate) first: bool, + pub(crate) dots: bool, +} - if module == ModuleOrUniformRoot::Module(crate_module) { - // Don't make a suggestion if the import was already from the root of the crate. - return None; - } +#[derive(Subdiagnostic)] +#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] +pub(crate) struct RemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); - let binding = self.resolution(crate_module, binding_key)?.best_decl()?; - let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { - return None; - }; - if !kinds.contains(MacroKinds::BANG) { - return None; - } - let module_name = crate_module.name().unwrap_or(kw::Crate); - let import_snippet = match import.kind { - ImportKind::Single { source, target, .. } if source != target => { - format!("{source} as {target}") - } - _ => format!("{ident}"), - }; - - let mut corrections: Vec<(Span, String)> = Vec::new(); - if !import.is_nested() { - // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove - // intermediate segments. - corrections.push((import.span, format!("{module_name}::{import_snippet}"))); - } else { - // Find the binding span (and any trailing commas and spaces). - // i.e. `use a::b::{c, d, e};` - // ^^^ - let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( - self.tcx.sess, - import.span, - import.use_span, - ); - debug!(found_closing_brace, ?binding_span); - - let mut removal_span = binding_span; - - // If the binding span ended with a closing brace, as in the below example: - // i.e. `use a::b::{c, d};` - // ^ - // Then expand the span of characters to remove to include the previous - // binding's trailing comma. - // i.e. `use a::b::{c, d};` - // ^^^ - if found_closing_brace - && let Some(previous_span) = - extend_span_to_previous_binding(self.tcx.sess, binding_span) - { - debug!(?previous_span); - removal_span = removal_span.with_lo(previous_span.lo()); - } - debug!(?removal_span); - - // Remove the `removal_span`. - corrections.push((removal_span, "".to_string())); - - // Find the span after the crate name and if it has nested imports immediately - // after the crate name already. - // i.e. `use a::b::{c, d};` - // ^^^^^^^^^ - // or `use a::{b, c, d}};` - // ^^^^^^^^^^^ - let (has_nested, after_crate_name) = - find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); - debug!(has_nested, ?after_crate_name); - - let source_map = self.tcx.sess.source_map(); - - // Make sure this is actually crate-relative. - let is_definitely_crate = import - .module_path - .first() - .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); - - // Add the import to the start, with a `{` if required. - let start_point = source_map.start_point(after_crate_name); - if is_definitely_crate - && let Ok(start_snippet) = source_map.span_to_snippet(start_point) - { - corrections.push(( - start_point, - if has_nested { - // In this case, `start_snippet` must equal '{'. - format!("{start_snippet}{import_snippet}, ") - } else { - // In this case, add a `{`, then the moved import, then whatever - // was there before. - format!("{{{import_snippet}, {start_snippet}") - }, - )); - - // Add a `};` to the end if nested, matching the `{` added at the start. - if !has_nested { - corrections.push((source_map.end_point(after_crate_name), "};".to_string())); - } - } else { - // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); - } - } +#[derive(Subdiagnostic)] +#[suggestion( + "remove unnecessary import", + code = "", + applicability = "maybe-incorrect", + style = "tool-only" +)] +pub(crate) struct ToolOnlyRemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - let suggestion = Some(( - corrections, - String::from("a macro with this name exists at the root of the crate"), - Applicability::MaybeIncorrect, - )); - Some(( - suggestion, - Some( - "this could be because a macro annotated with `#[macro_export]` will be exported \ - at the root of the crate instead of the module where it is defined" - .to_string(), - ), - )) - } +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] +pub(crate) struct IdentImporterHereButItIsDesc<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { - let local_items; - let symbols = if module.is_local() { - local_items = self - .stripped_cfg_items - .iter() - .filter_map(|item| { - let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { - ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { - Some(def_id) - } - _ => None, - })?; - Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) - }) - .collect::>(); - local_items.as_slice() - } else { - self.tcx.stripped_cfg_items(module.krate) - }; +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] +pub(crate) struct IdentInScopeButItIsDesc<'a> { + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { - if ident.name != *segment { - continue; - } +pub(crate) struct FoundItemConfigureOut { + pub(crate) span: Span, + pub(crate) item_was: ItemWas, +} - let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); - - fn comes_from_same_module_for_glob( - r: &Resolver<'_, '_>, - parent_module: DefId, - module: DefId, - visited: &mut FxHashMap, - ) -> bool { - if let Some(&cached) = visited.get(&parent_module) { - // this branch is prevent from being called recursively infinity, - // because there has some cycles in globs imports, - // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` - return cached; - } - visited.insert(parent_module, false); - let mut res = false; - let m = r.expect_module(parent_module); - if m.is_local() { - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() - { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) - { - res = true; - break; - } - } - } - } - visited.insert(parent_module, res); - res - } +pub(crate) enum ItemWas { + BehindFeature { feature: Symbol, span: Span }, + CfgOut { span: Span }, +} - let comes_from_same_module = parent_module == module - || comes_from_same_module_for_glob( - self, - parent_module, - module, - &mut Default::default(), - ); - if !comes_from_same_module { - continue; +impl Subdiagnostic for FoundItemConfigureOut { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + let mut multispan: MultiSpan = self.span.into(); + match self.item_was { + ItemWas::BehindFeature { feature, span } => { + let value = feature.into_diag_arg(&mut None); + let msg = msg!("the item is gated behind the `{$feature}` feature") + .arg("feature", value) + .format(); + multispan.push_span_label(span, msg); } - - let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { - diagnostics::ItemWas::BehindFeature { feature, span: cfg.1 } - } else { - diagnostics::ItemWas::CfgOut { span: cfg.1 } - }; - let note = diagnostics::FoundItemConfigureOut { span: ident.span, item_was }; - err.subdiagnostic(note); - } - } - - pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { - match def_id.as_local() { - Some(def_id) => self.struct_ctors.get(&def_id).cloned(), - None => { - self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { - let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); - let vis = self.tcx.visibility(ctor_def_id); - let field_visibilities = self - .tcx - .associated_item_def_ids(def_id) - .iter() - .map(|&field_id| self.tcx.visibility(field_id)) - .collect(); - StructCtor { res, vis, field_visibilities } - }) + ItemWas::CfgOut { span } => { + multispan.push_span_label(span, msg!("the item is gated here")); } } + diag.span_note(multispan, msg!("found an item that was configured out")); } +} - /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. - fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { - match def_id.as_local() { - Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), - None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), - } - } +#[derive(Diagnostic)] +#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] +pub(crate) struct TraitImplMismatch { + #[primary_span] + #[label("does not match trait")] + pub(crate) span: Span, + pub(crate) name: Ident, + pub(crate) kind: &'static str, + pub(crate) trait_path: String, + #[label("item in trait")] + pub(crate) trait_item_span: Span, } -/// Given a `binding_span` of a binding within a use statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^ -/// ``` -/// -/// then return the span until the next binding or the end of the statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^^^ -/// ``` -fn find_span_of_binding_until_next_binding( - sess: &Session, - binding_span: Span, - use_span: Span, -) -> (bool, Span) { - let source_map = sess.source_map(); - - // Find the span of everything after the binding. - // i.e. `a, e};` or `a};` - let binding_until_end = binding_span.with_hi(use_span.hi()); - - // Find everything after the binding but not including the binding. - // i.e. `, e};` or `};` - let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); - - // Keep characters in the span until we encounter something that isn't a comma or - // whitespace. - // i.e. `, ` or ``. - // - // Also note whether a closing brace character was encountered. If there - // was, then later go backwards to remove any trailing commas that are left. - let mut found_closing_brace = false; - let after_binding_until_next_binding = - source_map.span_take_while(after_binding_until_end, |&ch| { - if ch == '}' { - found_closing_brace = true; - } - ch == ' ' || ch == ',' - }); - - // Combine the two spans. - // i.e. `a, ` or `a`. - // - // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` - let span = binding_span.with_hi(after_binding_until_next_binding.hi()); - - (found_closing_brace, span) -} - -/// Given a `binding_span`, return the span through to the comma or opening brace of the previous -/// binding. -/// -/// ```ignore (illustrative) -/// use foo::a::{a, b, c}; -/// // ^^--- binding span -/// // | -/// // returned span -/// -/// use foo::{a, b, c}; -/// // --- binding span -/// ``` -fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { - let source_map = sess.source_map(); - - // `prev_source` will contain all of the source that came before the span. - // Then split based on a command and take the first (i.e. closest to our span) - // snippet. In the example, this is a space. - let prev_source = source_map.span_to_prev_source(binding_span).ok()?; - - let prev_comma = prev_source.rsplit(',').collect::>(); - let prev_starting_brace = prev_source.rsplit('{').collect::>(); - if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { - return None; - } +#[derive(Diagnostic)] +#[diag("derive helper attribute is used before it is introduced")] +pub(crate) struct LegacyDeriveHelpers { + #[label("the attribute is introduced here")] + pub span: Span, +} - let prev_comma = prev_comma.first().unwrap(); - let prev_starting_brace = prev_starting_brace.first().unwrap(); +#[derive(Diagnostic)] +#[diag("unused extern crate")] +pub(crate) struct UnusedExternCrate { + #[label("unused")] + pub span: Span, + #[suggestion( + "remove the unused `extern crate`", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + pub removal_span: Span, +} - // If the amount of source code before the comma is greater than - // the amount of source code before the starting brace then we've only - // got one item in the nested item (eg. `issue_52891::{self}`). - if prev_comma.len() > prev_starting_brace.len() { - return None; - } +#[derive(Diagnostic)] +#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} - Some(binding_span.with_lo(BytePos( - // Take away the number of bytes for the characters we've found and an - // extra for the comma. - binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, - ))) -} - -/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if -/// it is a nested use tree. -/// -/// ```ignore (illustrative) -/// use foo::a::{b, c}; -/// // ^^^^^^^^^^ -- false -/// -/// use foo::{a, b, c}; -/// // ^^^^^^^^^^ -- true -/// -/// use foo::{a, b::{c, d}}; -/// // ^^^^^^^^^^^^^^^ -- true -/// ``` -#[instrument(level = "debug", skip(sess))] -fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { - let source_map = sess.source_map(); - - // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. - let mut num_colons = 0; - // Find second colon.. `use issue_59764:` - let until_second_colon = source_map.span_take_while(use_span, |c| { - if *c == ':' { - num_colons += 1; - } - !matches!(c, ':' if num_colons == 2) - }); - // Find everything after the second colon.. `foo::{baz, makro};` - let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); - - let mut found_a_non_whitespace_character = false; - // Find the first non-whitespace character in `from_second_colon`.. `f` - let after_second_colon = source_map.span_take_while(from_second_colon, |c| { - if found_a_non_whitespace_character { - return false; - } - if !c.is_whitespace() { - found_a_non_whitespace_character = true; - } - true - }); +#[derive(Diagnostic)] +#[diag("unused label")] +pub(crate) struct UnusedLabel; - // Find the first `{` in from_second_colon.. `foo::{` - let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); +#[derive(Diagnostic)] +#[diag("unused `#[macro_use]` import")] +pub(crate) struct UnusedMacroUse; - (next_left_bracket == after_second_colon, from_second_colon) +#[derive(Diagnostic)] +#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] +#[help("remove it and import macros at use sites with a `use` item instead")] +pub(crate) struct MacroUseDeprecated; + +#[derive(Diagnostic)] +#[diag("macro `{$ident}` is private")] +pub(crate) struct MacroIsPrivate { + pub ident: Ident, } -/// A suggestion has already been emitted, change the wording slightly to clarify that both are -/// independent options. -enum Instead { - Yes, - No, +#[derive(Diagnostic)] +#[diag("unused macro definition: `{$name}`")] +pub(crate) struct UnusedMacroDefinition { + pub name: Symbol, } -/// Whether an existing place with an `use` item was found. -enum FoundUse { - Yes, - No, +#[derive(Diagnostic)] +#[diag("rule #{$n} of macro `{$name}` is never used")] +pub(crate) struct MacroRuleNeverUsed { + pub n: usize, + pub name: Symbol, } -/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. -pub(crate) enum DiagMode { - Normal, - /// The binding is part of a pattern - Pattern, - /// The binding is part of a use statement - Import { - /// `true` means diagnostics is for unresolved import - unresolved_import: bool, - /// `true` mean add the tips afterward for case `use a::{b,c}`, - /// rather than replacing within. - append: bool, +#[derive(Diagnostic)] +#[diag("`extern crate` is not idiomatic in the new edition")] +pub(crate) struct ExternCrateNotIdiomatic { + #[suggestion( + "convert it to a `use`", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + pub code: &'static str, +} + +#[derive(Diagnostic)] +#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] +#[help("import `macro_rules` with `use` to make it callable above its definition")] +pub(crate) struct OutOfScopeMacroCalls { + #[label("not found from {$location}")] + pub span: Span, + pub path: String, + pub location: String, +} + +#[derive(Diagnostic)] +#[diag( + "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" +)] +pub(crate) struct RedundantImportVisibility { + #[note("the most public imported item is `{$max_vis}`")] + pub span: Span, + #[help("reduce the glob import's visibility or increase visibility of imported items")] + pub help: (), + pub import_vis: String, + pub max_vis: String, +} + +#[derive(Diagnostic)] +#[diag("unknown diagnostic attribute")] +pub(crate) struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub help: Option, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnknownDiagnosticAttributeHelp { + #[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" + )] + Typo { + #[primary_span] + span: Span, + typo_name: Symbol, }, + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + UseFeature { feature: Symbol }, } -pub(crate) fn import_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - mode: DiagMode, - append: &str, -) { - show_candidates( - tcx, - err, - use_placement_span, - candidates, - Instead::Yes, - FoundUse::Yes, - mode, - vec![], - append, - ); -} - -type PathString<'a> = (String, &'a str, Option, &'a Option, bool); - -/// When an entity with a given name is not available in scope, we search for -/// entities with that name in all crates. This method allows outputting the -/// results of this search in a programmer-friendly way. If any entities are -/// found and suggested, returns `true`, otherwise returns `false`. -fn show_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - instead: Instead, - found_use: FoundUse, - mode: DiagMode, - path: Vec, - append: &str, -) -> bool { - if candidates.is_empty() { - return false; - } +// FIXME: Make this properly translatable. +pub(crate) struct Ambiguity { + pub ident: Ident, + pub ambig_vis: Option, + pub kind: &'static str, + pub help: Option<&'static [&'static str]>, + pub b1_note: Spanned, + pub b1_help_msgs: Vec, + pub b2_note: Spanned, + pub b2_help_msgs: Vec, + /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. + pub is_error: bool, +} - let mut showed = false; - let mut accessible_path_strings: Vec> = Vec::new(); - let mut inaccessible_path_strings: Vec> = Vec::new(); - - candidates.iter().for_each(|c| { - if c.accessible { - // Don't suggest `#[doc(hidden)]` items from other crates - if c.doc_visible { - accessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - )) - } - } else { - inaccessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - )) +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + let Self { + ident, + ambig_vis, + kind, + help, + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error, + } = self; + + let mut diag = Diag::new(dcx, level, "").with_span(ident.span); + if is_error { + diag.code(E0659); } - }); - - // we want consistent results across executions, but candidates are produced - // by iterating through a hash map, so make sure they are ordered: - for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { - path_strings.sort_by(|a, b| a.0.cmp(&b.0)); - path_strings.dedup_by(|a, b| a.0 == b.0); - let core_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); - let std_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); - let foreign_crate_path_strings = - path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); - - // We list the `crate` local paths first. - // Then we list the `std`/`core` paths. - if std_path_strings.len() == core_path_strings.len() { - // Do not list `core::` paths if we are already listing the `std::` ones. - path_strings.extend(std_path_strings); + if let Some(ambig_vis) = ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); } else { - path_strings.extend(std_path_strings); - path_strings.extend(core_path_strings); + diag.primary_message(format!("`{}` is ambiguous", ident)); + diag.span_label(ident.span, "ambiguous name"); } - // List all paths from foreign crates last. - path_strings.extend(foreign_crate_path_strings); - } - - if !accessible_path_strings.is_empty() { - let (determiner, kind, s, name, through) = - if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { - ( - "this", - *descr, - "", - format!(" `{name}`"), - if *via_import { " through its public re-export" } else { "" }, - ) - } else { - // Get the unique item kinds and if there's only one, we use the right kind name - // instead of the more generic "items". - let kinds = accessible_path_strings - .iter() - .map(|(_, descr, _, _, _)| *descr) - .collect::>(); - let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; - let s = if kind.ends_with('s') { "es" } else { "s" }; - - ("one of these", kind, s, String::new(), "") - }; - - let instead = if let Instead::Yes = instead { " instead" } else { "" }; - let mut msg = if let DiagMode::Pattern = mode { - format!( - "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ - pattern", - ) - } else { - format!("consider importing {determiner} {kind}{s}{through}{instead}") - }; - - for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); + diag.note(format!("ambiguous because of {}", kind)); + diag.span_note(b1_note.span, b1_note.node); + if let Some(help) = help { + for help in help { + diag.help(*help); + } } + for help_msg in b1_help_msgs { + diag.help(help_msg); + } + diag.span_note(b2_note.span, b2_note.node); + for help_msg in b2_help_msgs { + diag.help(help_msg); + } + diag + } +} - let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { - msg.push(':'); +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` never used")] +pub(crate) struct UnusedLifetime { + #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] + pub deletion_span: Option, - for candidate in accessible_path_strings { - msg.push('\n'); - msg.push_str(&candidate.0); - } - }; - - if let Some(span) = use_placement_span { - let (add_use, trailing) = match mode { - DiagMode::Pattern => { - err.span_suggestions( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - ); - return true; - } - DiagMode::Import { .. } => ("", ""), - DiagMode::Normal => ("use ", ";\n"), - }; - for candidate in &mut accessible_path_strings { - // produce an additional newline to separate the new use statement - // from the directly following item. - let additional_newline = if let FoundUse::No = found_use - && let DiagMode::Normal = mode - { - "\n" - } else { - "" - }; - candidate.0 = - format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); - } + pub ident: Ident, +} - match mode { - DiagMode::Import { append: true, .. } => { - append_candidates(&mut msg, accessible_path_strings); - err.span_help(span, msg); - } - _ => { - err.span_suggestions_with_style( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); - } - } +#[derive(Diagnostic)] +#[diag("ambiguous glob re-exports")] +pub(crate) struct AmbiguousGlobReexports { + #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] + pub first_reexport: Span, + #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] + pub duplicate_reexport: Span, - if let [first, .., last] = &path[..] { - let sp = first.ident.span.until(last.ident.span); - // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). - // Can happen for derive-generated spans. - if sp.can_be_used_for_suggestions() && !sp.is_empty() { - err.span_suggestion_verbose( - sp, - format!("if you import `{}`, refer to it directly", last.ident), - "", - Applicability::Unspecified, - ); - } - } - } else { - append_candidates(&mut msg, accessible_path_strings); - err.help(msg); - } - showed = true; - } - if !inaccessible_path_strings.is_empty() - && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) - { - let prefix = - if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; - if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { - let msg = format!( - "{prefix}{descr} `{name}`{} exists but is inaccessible", - if let DiagMode::Pattern = mode { ", which" } else { "" } - ); - - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - let mut multi_span = MultiSpan::from_span(span); - multi_span.push_span_label(span, "not accessible"); - err.span_note(multi_span, msg); - } else { - err.note(msg); - } - if let Some(note) = (*note).as_deref() { - err.note(note.to_string()); - } - } else { - let descr = inaccessible_path_strings - .iter() - .map(|&(_, descr, _, _, _)| descr) - .all_equal_value() - .unwrap_or("item"); - let plural_descr = - if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; - - let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); - let mut has_colon = false; - - let mut spans = Vec::new(); - for (name, _, source_span, _, _) in &inaccessible_path_strings { - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - spans.push((name, span)); - } else { - if !has_colon { - msg.push(':'); - has_colon = true; - } - msg.push('\n'); - msg.push_str(name); - } - } + pub name: String, + pub namespace: String, +} - let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); - for (name, span) in spans { - multi_span.push_span_label(span, format!("`{name}`: not accessible")); - } +#[derive(Diagnostic)] +#[diag("private item shadows public glob re-export")] +pub(crate) struct HiddenGlobReexports { + #[note( + "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" + )] + pub glob_reexport: Span, + #[note("but the private item here shadows it")] + pub private_item: Span, + + pub name: String, + pub namespace: String, +} - for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); - } +#[derive(Diagnostic)] +#[diag("the item `{$ident}` is imported redundantly")] +pub(crate) struct RedundantImport { + #[subdiagnostic] + pub subs: Vec, + pub ident: Ident, +} - err.span_note(multi_span, msg); - } - showed = true; - } - showed +#[derive(Subdiagnostic)] +pub(crate) enum RedundantImportSub { + #[label("the item `{$ident}` is already imported here")] + ImportedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined here")] + DefinedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already imported by the extern prelude")] + ImportedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined by the extern prelude")] + DefinedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, } -#[derive(Debug)] -struct UsePlacementFinder { - target_module: NodeId, - first_legal_span: Option, - first_use_span: Option, -} - -impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { - let mut finder = - UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; - finder.visit_crate(krate); - if let Some(use_span) = finder.first_use_span { - (Some(use_span), FoundUse::Yes) - } else { - (finder.first_legal_span, FoundUse::No) - } - } +#[derive(Diagnostic)] +#[diag("unnecessary qualification")] +pub(crate) struct UnusedQualifications { + #[suggestion( + "remove the unnecessary path segments", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] + pub removal_span: Span, } -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { - fn visit_crate(&mut self, c: &Crate) { - if self.target_module == CRATE_NODE_ID { - let inject = c.spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(&c.items); - } else { - visit::walk_crate(self, c); - } - } +#[derive(Diagnostic)] +#[diag( + "{$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + }" +)] +pub(crate) struct AssociatedConstElidedLifetime { + #[suggestion( + "use the `'static` lifetime", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + + pub code: &'static str, + pub elided: bool, + #[note("cannot automatically infer `'static` because of other lifetimes in scope")] + pub lifetimes_in_scope: MultiSpan, +} - fn visit_item(&mut self, item: &'tcx ast::Item) { - if self.target_module == item.id { - if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { - let inject = mod_spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(items); - } - } else { - visit::walk_item(self, item); - } - } +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` only used once")] +pub(crate) struct SingleUseLifetime { + #[label("this lifetime...")] + pub param_span: Span, + #[label("...is used only here")] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, } -#[derive(Default)] -struct BindingVisitor { - identifiers: Vec, - spans: FxHashMap>, +#[derive(Subdiagnostic)] +#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] +pub(crate) struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, + + pub replace_lt: String, } -impl<'tcx> Visitor<'tcx> for BindingVisitor { - fn visit_pat(&mut self, pat: &ast::Pat) { - if let ast::PatKind::Ident(_, ident, _) = pat.kind { - self.identifiers.push(ident.name); - self.spans.entry(ident.name).or_default().push(ident.span); - } - visit::walk_pat(self, pat); - } +#[derive(Diagnostic)] +#[diag( + "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" +)] +pub(crate) struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, } -fn search_for_any_use_in_items(items: &[Box]) -> Option { - for item in items { - if let ItemKind::Use(..) = item.kind - && is_span_suitable_for_use_injection(item.span) - { - let mut lo = item.span.lo(); - for attr in &item.attrs { - if attr.span.eq_ctxt(item.span) { - lo = std::cmp::min(lo, attr.span.lo()); - } - } - return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); - } - } - None +#[derive(Subdiagnostic)] +#[suggestion("use `crate`", code = "{replacement}")] +pub(crate) struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, } -fn is_span_suitable_for_use_injection(s: Span) -> bool { - // don't suggest placing a use before the prelude - // import or other generated ones - !s.from_expansion() +#[derive(Diagnostic)] +#[diag("hidden lifetime parameters in types are deprecated")] +pub(crate) struct ElidedLifetimesInPaths { + #[subdiagnostic] + pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, } -#[derive(Debug, Clone, Default)] -pub(crate) struct OnUnknownData { - pub(crate) directive: Box, +#[derive(Diagnostic)] +#[diag( + "{$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + }" +)] +pub(crate) struct UnusedImports { + #[subdiagnostic] + pub sugg: Option, + #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] + pub test_module_span: Option, + + pub span_snippets: DiagArgValue, + pub num_snippets: usize, } -impl OnUnknownData { - pub(crate) fn from_attrs<'tcx>( - tcx: TyCtxt<'tcx>, - attrs: &[ast::Attribute], - ) -> Option { - if tcx.features().diagnostic_on_unknown() - && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = - AttributeParser::parse_limited(tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown]) - { - Some(Self { directive: directive? }) - } else { - None - } - } +#[derive(Subdiagnostic)] +pub(crate) enum UnusedImportsSugg { + #[suggestion( + "remove the whole `use` item", + applicability = "machine-applicable", + code = "", + style = "tool-only" + )] + RemoveWholeUse { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + "{$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + }", + applicability = "machine-applicable", + style = "tool-only" + )] + RemoveImports { + #[suggestion_part(code = "")] + remove_spans: Vec, + num_to_remove: usize, + }, } From 2e3cc89f260bb6143b601539a9fa3c03181cab55 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Wed, 6 May 2026 12:14:06 +0200 Subject: [PATCH 03/10] suggest imports with different casing adds a is_exact_match field to ImportSuggestion use is_exact_match field to customize help message suggest imports with different casing -only suggest modules if the following segment matches too --- compiler/rustc_resolve/src/diagnostics.rs | 77 ++++++++++-- .../rustc_resolve/src/late/diagnostics.rs | 115 +++++++++++++++++- 2 files changed, 173 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 81c1b6fa8d675..c6cc58a6d3a30 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -121,6 +121,7 @@ pub(crate) struct ImportSuggestion { /// An extra note that should be issued if this item is suggested pub note: Option, pub is_stable: bool, + pub is_exact_match: bool, } /// Adjust the impl span so that just the `impl` keyword is taken by removing @@ -1594,16 +1595,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } } - fn lookup_import_candidates_from_module( + fn lookup_import_candidates_from_module( &self, lookup_ident: Ident, namespace: Namespace, parent_scope: &ParentScope<'ra>, start_module: Module<'ra>, crate_path: ThinVec, + ident_filter_fn: IdentFilterFn, filter_fn: FilterFn, ) -> Vec where + IdentFilterFn: Fn(Ident, Ident) -> bool, FilterFn: Fn(Res) -> bool, { let mut candidates = Vec::new(); @@ -1675,7 +1678,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // collect results based on the filter function // avoid suggesting anything from the same module in which we are resolving // avoid suggesting anything with a hygienic name - if ident.name == lookup_ident.name + if ident_filter_fn(ident.orig(orig_ident_span), lookup_ident) && ns == namespace && in_module != parent_scope.module && ident.ctxt.is_root() @@ -1755,6 +1758,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { note, via_import, is_stable, + is_exact_match: ident.name == lookup_ident.name, }); } } @@ -1831,6 +1835,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { /// /// N.B., the method does not look into imports, but this is not a problem, /// since we report the definitions (thus, the de-aliased imports). + /// + /// The method is implemented in `lookup_import_candidates_impl`. The `_impl` method allows applying a different filter function on the ident than the exact match function used by default here. pub(crate) fn lookup_import_candidates( &mut self, lookup_ident: Ident, @@ -1840,6 +1846,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Vec where FilterFn: Fn(Res) -> bool, + { + self.lookup_import_candidates_impl( + lookup_ident, + namespace, + parent_scope, + |ident: Ident, lookup_ident: Ident| ident.name == lookup_ident.name, + filter_fn, + ) + } + + /// The actual impl of the `lookup_import_candidates function`. + pub(crate) fn lookup_import_candidates_impl( + &mut self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + ident_filter_fn: IdentFilterFn, + filter_fn: FilterFn, + ) -> Vec + where + IdentFilterFn: Fn(Ident, Ident) -> bool, + FilterFn: Fn(Res) -> bool, { let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; let mut suggestions = self.lookup_import_candidates_from_module( @@ -1848,6 +1876,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, self.graph_root.to_module(), crate_path, + &ident_filter_fn, &filter_fn, ); @@ -1904,6 +1933,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { parent_scope, crate_root, crate_path, + &ident_filter_fn, &filter_fn, )); } @@ -3803,7 +3833,7 @@ pub(crate) fn import_candidates( ); } -type PathString<'a> = (String, &'a str, Option, &'a Option, bool); +type PathString<'a> = (String, &'a str, Option, &'a Option, bool, bool); /// When an entity with a given name is not available in scope, we search for /// entities with that name in all crates. This method allows outputting the @@ -3839,6 +3869,7 @@ fn show_candidates( c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), &c.note, c.via_import, + c.is_exact_match, )) } } else { @@ -3848,6 +3879,7 @@ fn show_candidates( c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), &c.note, c.via_import, + c.is_exact_match, )) } }); @@ -3879,9 +3911,10 @@ fn show_candidates( if !accessible_path_strings.is_empty() { let (determiner, kind, s, name, through) = - if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] { + if let [(name, descr, _, _, via_import, is_exact_match)] = &accessible_path_strings[..] + { ( - "this", + if *is_exact_match { "this" } else { "this similarly named" }, *descr, "", format!(" `{name}`"), @@ -3892,12 +3925,24 @@ fn show_candidates( // instead of the more generic "items". let kinds = accessible_path_strings .iter() - .map(|(_, descr, _, _, _)| *descr) + .map(|(_, descr, _, _, _, _)| *descr) .collect::>(); let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; let s = if kind.ends_with('s') { "es" } else { "s" }; + // we should only suggest case insensitive suggestion if no case sensitive match was found, + // so all the suggestion should have the same is_exact_match value. - ("one of these", kind, s, String::new(), "") + ( + if accessible_path_strings[0].5 { + "one of these" + } else { + "one of these similarly named" + }, + kind, + s, + String::new(), + "", + ) }; let instead = if let Instead::Yes = instead { " instead" } else { "" }; @@ -3991,9 +4036,12 @@ fn show_candidates( { let prefix = if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; - if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] { + if let [(name, descr, source_span, note, _, is_exact_match)] = + &inaccessible_path_strings[..] + { let msg = format!( - "{prefix}{descr} `{name}`{} exists but is inaccessible", + "{prefix}{}{descr} `{name}`{} exists but is inaccessible", + if *is_exact_match { "" } else { "similarly named " }, if let DiagMode::Pattern = mode { ", which" } else { "" } ); @@ -4011,17 +4059,20 @@ fn show_candidates( } else { let descr = inaccessible_path_strings .iter() - .map(|&(_, descr, _, _, _)| descr) + .map(|&(_, descr, _, _, _, _)| descr) .all_equal_value() .unwrap_or("item"); let plural_descr = if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; - - let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible"); + let are_exact_matches = inaccessible_path_strings[0].5; + let mut msg = format!( + "{prefix}these {}{plural_descr} exist but are inaccessible", + if are_exact_matches { "" } else { "similarly named " }, + ); let mut has_colon = false; let mut spans = Vec::new(); - for (name, _, source_span, _, _) in &inaccessible_path_strings { + for (name, _, source_span, _, _, _) in &inaccessible_path_strings { if let Some(source_span) = source_span { let span = tcx.sess.source_map().guess_head_span(*source_span); spans.push((name, span)); diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 95fdf8124a225..c3239f0c2fb65 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -15,8 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordItems; use rustc_errors::codes::*; use rustc_errors::{ - Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize, - struct_span_code_err, + Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, Suggestions, + pluralize, struct_span_code_err, }; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; @@ -732,11 +732,25 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { suggested_candidates, ); + self.err_code_special_cases(&mut err, source, path, span); + + let no_suggestion = match &err.suggestions { + Suggestions::Enabled(suggestions) => suggestions.is_empty(), + Suggestions::Sealed(suggestions) => suggestions.is_empty(), + Suggestions::Disabled => false, + }; + if let Some(errcode) = err.code + && errcode.index() == 425 + && candidates.is_empty() + && no_suggestion + { + candidates = self.try_lookup_import_case_insensitive(source, path, following_seg, res); + } + if fallback { // Fallback label. err.span_label(base_error.span, base_error.fallback_label); } - self.err_code_special_cases(&mut err, source, path, span); let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id()); self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module); @@ -879,6 +893,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut suggested_candidates = FxHashSet::default(); // Try to lookup name in more relaxed fashion for better error reporting. let ident = path.last().unwrap().ident; + let ident_filter = &|ident: Ident, ident_lookup: Ident| ident.name == ident_lookup.name; let is_expected = &|res| source.is_expected(res); let ns = source.namespace(); let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _)); @@ -886,7 +901,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let ident_span = path.last().map_or(span, |ident| ident.ident.span); let mut candidates = self .r - .lookup_import_candidates(ident, ns, &self.parent_scope, is_expected) + .lookup_import_candidates_impl(ident, ns, &self.parent_scope, ident_filter, is_expected) .into_iter() .filter(|ImportSuggestion { did, .. }| { match (did, res.and_then(|res| res.opt_def_id())) { @@ -1097,13 +1112,99 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } } - if candidates.is_empty() { candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg); } (false, suggested_candidates, candidates) } + /// Based on a subset of try_lookup_relaxed, this looks for import candidates in a case insensitive manner. + /// We do not suggest alternative capitalizations if only one letter long, too many constants can match and it becomes noisy. + fn try_lookup_import_case_insensitive( + &mut self, + source: PathSource<'_, '_, '_>, + path: &[Segment], + following_seg: Option<&Segment>, + res: Option, + ) -> Vec { + let ident = path.last().unwrap().ident; + // we do not suggest alternative capitalizations if only one letter, too many constants can match and it become noisy. + if ident.as_str().len() < 2 { + return Vec::new(); + } + let is_expected = &|res| source.is_expected(res); + let ns = source.namespace(); + + let case_insensitive_filter = |ident: Ident, lookup_ident: Ident| { + ident.as_str().to_lowercase() == lookup_ident.as_str().to_lowercase() + }; + let filter_function = |res: Res| { + if following_seg.is_none() { + is_expected(res) + } else { + matches!(res, Res::Def(DefKind::Mod, _)) + } + }; + let mut case_agnostic_candidates = self + .r + .lookup_import_candidates_impl( + ident, + ns, + &self.parent_scope, + case_insensitive_filter, + filter_function, + ) + .into_iter() + .filter(|ImportSuggestion { did, .. }| { + // If there's a following segment, only keep modules that contain it + if let Some(following) = following_seg { + let Some(did) = did else { return false }; + let Some(module) = self.r.get_module(*did) else { return false }; + let mut found = false; + module.for_each_child(self.r, |_, ident, _, _, _| { + if ident.name == following.ident.name { + found = true; + } + }); + if !found { + return false; + } + } + + // Filter out items that are in the prelude + if let Some(prelude) = self.r.prelude { + if let Some(suggestion_did) = did { + let mut is_in_prelude = false; + prelude.for_each_child(self.r, |_, _, _, _, decl| { + if decl.res().opt_def_id() == Some(*suggestion_did) { + is_in_prelude = true; + } + }); + if is_in_prelude { + return false; + } + } + } + match (did, res.and_then(|res| res.opt_def_id())) { + (Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did, + _ => true, + } + }) + .collect::>(); + // Try to filter out intrinsics candidates, as long as we have + // some other candidates to suggest. + let intrinsic_candidates: Vec<_> = case_agnostic_candidates + .extract_if(.., |sugg| { + let path = path_names_to_string(&sugg.path); + path.starts_with("core::intrinsics::") || path.starts_with("std::intrinsics::") + }) + .collect(); + if case_agnostic_candidates.is_empty() { + // Put them back if we have no more candidates to suggest... + case_agnostic_candidates = intrinsic_candidates; + } + return case_agnostic_candidates; + } fn lookup_doc_alias_name(&mut self, path: &[Segment], ns: Namespace) -> Option<(DefId, Ident)> { let find_doc_alias_name = |r: &mut Resolver<'ra, '_>, m: Module<'ra>, item_name: Symbol| { @@ -3034,7 +3135,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); let doc_visible = doc_visible && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id)); - if module_def_id == def_id { + let is_exact_match = module_def_id == def_id; + if is_exact_match { let path = Path { span: name_binding.span, segments: path_segments, tokens: None }; result = Some(( @@ -3048,6 +3150,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { note: None, via_import: false, is_stable: true, + is_exact_match, }, )); } else { From 17de2df0f5737b67d057f754467badf6973508a9 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Sun, 31 May 2026 20:33:44 +0200 Subject: [PATCH 04/10] do not suggest typos lacking generic parameters when some required --- compiler/rustc_resolve/src/late.rs | 12 ++++++++++++ compiler/rustc_resolve/src/late/diagnostics.rs | 12 +++++++++++- compiler/rustc_resolve/src/lib.rs | 13 +++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index bf53b891a08f1..f88f7a2329ccd 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5686,6 +5686,18 @@ impl<'ast> Visitor<'ast> for ItemInfoCollector<'_, '_, '_> { .filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime { .. })) .count(); self.r.item_generics_num_lifetimes.insert(def_id, count); + let type_or_const_count = generics + .params + .iter() + .filter(|param| { + matches!( + param.kind, + ast::GenericParamKind::Type { .. } + | ast::GenericParamKind::Const { .. } + ) + }) + .count(); + self.r.item_generics_num_type_or_const_params.insert(def_id, type_or_const_count); } ItemKind::ForeignMod(ForeignMod { items, .. }) => { diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index c3239f0c2fb65..4d5cd80840c41 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1367,7 +1367,17 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut fallback = false; let typo_sugg = typo_sugg .to_opt_suggestion() - .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())); + .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())) + // this filters out suggestions that require parameters when no parameter is present + .filter(|sugg| { + if !path.last().is_some_and(|s| s.has_generic_args) { + return true; + } + let Some(def_id) = sugg.res.opt_def_id() else { + return true; + }; + self.r.item_generics_num_type_or_const_params(def_id) > 0 + }); if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) { fallback = true; match self.diag_metadata.current_let_binding { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 061471ccc97e0..925fb0d05efa2 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1503,6 +1503,8 @@ pub struct Resolver<'ra, 'tcx> { /// Amount of lifetime parameters for each item in the crate. item_generics_num_lifetimes: FxHashMap = default::fx_hash_map(), + /// Amount of type or const parameters for each item in the crate. + item_generics_num_type_or_const_params: FxHashMap = default::fx_hash_map(), /// Generic args to suggest for required params (e.g. `<'_>`, `<_, _>`), if any. item_required_generic_args_suggestions: FxHashMap = default::fx_hash_map(), delegation_fn_sigs: LocalDefIdMap = Default::default(), @@ -1712,6 +1714,17 @@ impl<'tcx> Resolver<'_, 'tcx> { self.tcx.generics_of(def_id).own_counts().lifetimes } } + // Get count of expected parameters + fn item_generics_num_type_or_const_params(&self, def_id: DefId) -> usize { + if let Some(def_id) = def_id.as_local() { + self.item_generics_num_type_or_const_params.get(&def_id).copied().unwrap_or(0) + } else { + let generics = self.tcx.generics_of(def_id); + let counts = generics.own_counts(); + // saturating_sub may not be strictly necessary, better safe than sorry + counts.types.saturating_sub(generics.has_own_self() as usize) + counts.consts + } + } fn item_required_generic_args_suggestion(&self, def_id: DefId) -> String { if let Some(def_id) = def_id.as_local() { From f8dbea897c8e62d34f07fb34b3ff11e94e1fc349 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Sun, 31 May 2026 20:25:17 +0200 Subject: [PATCH 05/10] add suggestion for detect_missing_binding_available_from_pattern When a pattern has `..` and a matching binding, suggest replacing `..` with `binding, ..` --- compiler/rustc_resolve/src/late.rs | 7 ++++++- compiler/rustc_resolve/src/late/diagnostics.rs | 14 +++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index f88f7a2329ccd..fdf5d833bf611 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -286,7 +286,8 @@ impl RibKind<'_> { #[derive(Debug)] pub(crate) struct Rib<'ra, R = Res> { pub bindings: FxIndexMap, - pub patterns_with_skipped_bindings: UnordMap)>>, + pub patterns_with_skipped_bindings: + UnordMap, Result<(), ErrorGuaranteed>)>>, pub kind: RibKind<'ra>, } @@ -4374,6 +4375,10 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .or_default() .push(( pat.span, + match rest { + ast::PatFieldsRest::Rest(span) => Some(*span), + _ => None, + }, match rest { ast::PatFieldsRest::Recovered(guar) => Err(*guar), _ => Ok(()), diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 4d5cd80840c41..9e450d78b28a6 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1657,11 +1657,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { for field in fields { if field.name == segment.ident.name { - if spans.iter().all(|(_, had_error)| had_error.is_err()) { + if spans.iter().all(|(_, _, had_error)| had_error.is_err()) { // This resolution error will likely be fixed by fixing a // syntax error in a pattern, so it is irrelevant to the user. let multispan: MultiSpan = - spans.iter().map(|(s, _)| *s).collect::>().into(); + spans.iter().map(|(s, _, _)| *s).collect::>().into(); err.span_note( multispan, "this pattern had a recovered parse error which likely lost \ @@ -1670,7 +1670,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { err.downgrade_to_delayed_bug(); } let ty = self.r.tcx.item_name(*def_id); - for (span, _) in spans { + for (span, dotdot_span, _) in spans { err.span_label( *span, format!( @@ -1678,6 +1678,14 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { available in `{ty}`", ), ); + if let Some(dotdot_span) = dotdot_span { + err.tool_only_span_suggestion( + *dotdot_span, + format!("include `{field}` in the pattern"), + format!("{field}, .."), + Applicability::MaybeIncorrect, + ); + } } } } From c0cca1c7d73bcdb19c9c211fb8c799275bd114b6 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Sun, 31 May 2026 20:21:48 +0200 Subject: [PATCH 06/10] bless tests --- tests/ui/cast/cast-errors-issue-43825.stderr | 7 + .../unevaluated-const-ice-119731.stderr | 19 +- .../mgca/unused_speculative_def_id.stderr | 11 +- tests/ui/coroutine/layout-error.stderr | 5 + tests/ui/libstd-case-typo/libstd.stderr | 1939 +++++++++++++++++ .../ui/parser/raw/raw-literal-keywords.stderr | 10 + .../export-fully-qualified-2018.stderr | 4 + .../ui/resolve/export-fully-qualified.stderr | 4 + .../resolve/resolve-assoc-suggestions.stderr | 7 + .../resolve-speculative-adjustment.stderr | 5 + .../suggestions/crate-or-module-typo.stderr | 17 +- 11 files changed, 2006 insertions(+), 22 deletions(-) create mode 100644 tests/ui/libstd-case-typo/libstd.stderr diff --git a/tests/ui/cast/cast-errors-issue-43825.stderr b/tests/ui/cast/cast-errors-issue-43825.stderr index 658d5ddbb611c..d0dea3a776371 100644 --- a/tests/ui/cast/cast-errors-issue-43825.stderr +++ b/tests/ui/cast/cast-errors-issue-43825.stderr @@ -3,6 +3,13 @@ error[E0425]: cannot find value `error` in this scope | LL | let error = error; | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named items + | +LL + use std::fmt::Error; + | +LL + use std::fs::TryLockError::Error; + | error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr index 022074181cecd..8969a4001245f 100644 --- a/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unevaluated-const-ice-119731.stderr @@ -10,6 +10,19 @@ LL - pub use v20::{v13, v17}; LL + pub use v20::{v11, v17}; | +error[E0425]: cannot find function `v6` in this scope + --> $DIR/unevaluated-const-ice-119731.rs:13:35 + | +LL | const v0: [[usize; v4]; v4] = v6(v8); + | ^^ not found in this scope + | +help: consider importing one of these similarly named tuple variants + | +LL + use std::net::IpAddr::V6; + | +LL + use std::net::SocketAddr::V6; + | + error[E0425]: cannot find value `v8` in this scope --> $DIR/unevaluated-const-ice-119731.rs:13:38 | @@ -87,12 +100,6 @@ help: add `#![feature(min_adt_const_params)]` to the crate attributes to enable LL + #![feature(min_adt_const_params)] | -error[E0425]: cannot find function `v6` in this scope - --> $DIR/unevaluated-const-ice-119731.rs:13:35 - | -LL | const v0: [[usize; v4]; v4] = v6(v8); - | ^^ not found in this scope - error: maximum number of nodes exceeded in constant v20::v17::::{constant#0} --> $DIR/unevaluated-const-ice-119731.rs:28:37 | diff --git a/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr b/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr index e087e046ec35d..14f80b6b1d71a 100644 --- a/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr +++ b/tests/ui/const-generics/mgca/unused_speculative_def_id.stderr @@ -2,16 +2,7 @@ error[E0425]: cannot find value `ERR` in this scope --> $DIR/unused_speculative_def_id.rs:20:16 | LL | field: A<{ ERR:: }>, - | ^^^ - | - --> $SRC_DIR/core/src/result.rs:LL:COL - | - = note: similarly named tuple variant `Err` defined here -help: a tuple variant with a similar name exists - | -LL - field: A<{ ERR:: }>, -LL + field: A<{ Err:: }>, - | + | ^^^ not found in this scope error: aborting due to 1 previous error diff --git a/tests/ui/coroutine/layout-error.stderr b/tests/ui/coroutine/layout-error.stderr index f3b3843de898c..63ce820f3027c 100644 --- a/tests/ui/coroutine/layout-error.stderr +++ b/tests/ui/coroutine/layout-error.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `Foo` in this scope | LL | let a = Foo; | ^^^ not found in this scope + | +help: consider importing this similarly named function + | +LL + use crate::foo; + | error: aborting due to 1 previous error diff --git a/tests/ui/libstd-case-typo/libstd.stderr b/tests/ui/libstd-case-typo/libstd.stderr new file mode 100644 index 0000000000000..72590e5c78d5e --- /dev/null +++ b/tests/ui/libstd-case-typo/libstd.stderr @@ -0,0 +1,1939 @@ +error[E0425]: cannot find type `LayOut` in this scope + --> $DIR/libstd.rs:3:20 + | +LL | fn test_layout(_x: LayOut){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::alloc::Layout; + | + +error[E0425]: cannot find type `system` in this scope + --> $DIR/libstd.rs:5:20 + | +LL | fn test_system(_x: system){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::alloc::System; + | + +error[E0425]: cannot find type `Typeid` in this scope + --> $DIR/libstd.rs:10:20 + | +LL | fn test_typeid(_x: Typeid){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::any::TypeId; + | + +error[E0425]: cannot find type `Escapedefault` in this scope + --> $DIR/libstd.rs:15:27 + | +LL | fn test_escapedefault(_x: Escapedefault){} + | ^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::ascii::EscapeDefault; + | +LL + use std::char::EscapeDefault; + | +LL + use std::str::EscapeDefault; + | + +error[E0425]: cannot find type `cell` in this scope + --> $DIR/libstd.rs:20:18 + | +LL | fn test_cell(_x: cell<()>){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::cell::Cell; + | + +error[E0425]: cannot find type `DecodeUTF16` in this scope + --> $DIR/libstd.rs:25:25 + | +LL | fn test_decodeutf16(_x: DecodeUTF16<()>){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::char::DecodeUtf16; + | + +error[E0425]: cannot find type `Escapeunicode` in this scope + --> $DIR/libstd.rs:28:27 + | +LL | fn test_escapeunicode(_x: Escapeunicode){} + | ^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::char::EscapeUnicode; + | +LL + use std::str::EscapeUnicode; + | + +error[E0425]: cannot find type `Tolowercase` in this scope + --> $DIR/libstd.rs:31:25 + | +LL | fn test_tolowercase(_x: Tolowercase){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::char::ToLowercase; + | + +error[E0425]: cannot find type `Touppercase` in this scope + --> $DIR/libstd.rs:34:25 + | +LL | fn test_touppercase(_x: Touppercase){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::char::ToUppercase; + | + +error[E0425]: cannot find type `reverse` in this scope + --> $DIR/libstd.rs:39:21 + | +LL | fn test_reverse(_x: reverse<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::cmp::Reverse; + | + +error[E0425]: cannot find type `BtreeMap` in this scope + --> $DIR/libstd.rs:44:22 + | +LL | fn test_btreemap(_x: BtreeMap<(), ()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::BTreeMap; + | + +error[E0425]: cannot find type `BtreeSet` in this scope + --> $DIR/libstd.rs:46:22 + | +LL | fn test_btreeset(_x: BtreeSet<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::BTreeSet; + | + +error[E0425]: cannot find type `Binaryheap` in this scope + --> $DIR/libstd.rs:48:24 + | +LL | fn test_binaryheap(_x: Binaryheap<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::BinaryHeap; + | + +error[E0425]: cannot find type `Hashmap` in this scope + --> $DIR/libstd.rs:50:21 + | +LL | fn test_hashmap(_x: Hashmap){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::HashMap; + | + +error[E0425]: cannot find type `Hashset` in this scope + --> $DIR/libstd.rs:52:21 + | +LL | fn test_hashset(_x: Hashset<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::HashSet; + | + +error[E0425]: cannot find type `Linkedlist` in this scope + --> $DIR/libstd.rs:54:24 + | +LL | fn test_linkedlist(_x: Linkedlist<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::LinkedList; + | + +error[E0425]: cannot find type `Vecdeque` in this scope + --> $DIR/libstd.rs:56:22 + | +LL | fn test_vecdeque(_x: Vecdeque<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::collections::VecDeque; + | + +error[E0425]: cannot find type `args` in this scope + --> $DIR/libstd.rs:61:18 + | +LL | fn test_args(_x: args){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::env::Args; + | + +error[E0425]: cannot find type `Argsos` in this scope + --> $DIR/libstd.rs:63:20 + | +LL | fn test_argsos(_x: Argsos){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::env::ArgsOs; + | + +error[E0425]: cannot find type `Splitpaths` in this scope + --> $DIR/libstd.rs:65:24 + | +LL | fn test_splitpaths(_x: Splitpaths<'_>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::env::SplitPaths; + | + +error[E0425]: cannot find type `vars` in this scope + --> $DIR/libstd.rs:67:18 + | +LL | fn test_vars(_x: vars){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::env::Vars; + | + +error[E0425]: cannot find type `Varsos` in this scope + --> $DIR/libstd.rs:69:20 + | +LL | fn test_varsos(_x: Varsos){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::env::VarsOs; + | + +error[E0425]: cannot find type `cStr` in this scope + --> $DIR/libstd.rs:74:18 + | +LL | fn test_cstr(_x: cStr){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ffi::CStr; + | + +error[E0425]: cannot find type `Osstr` in this scope + --> $DIR/libstd.rs:76:19 + | +LL | fn test_osstr(_x: Osstr){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ffi::OsStr; + | + +error[E0425]: cannot find type `Osstring` in this scope + --> $DIR/libstd.rs:78:22 + | +LL | fn test_osstring(_x: Osstring){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ffi::OsString; + | + +error[E0425]: cannot find type `Debuglist` in this scope + --> $DIR/libstd.rs:83:23 + | +LL | fn test_debuglist(_x: Debuglist){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::DebugList; + | + +error[E0425]: cannot find type `Debugmap` in this scope + --> $DIR/libstd.rs:85:22 + | +LL | fn test_debugmap(_x: Debugmap){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::DebugMap; + | + +error[E0425]: cannot find type `Debugset` in this scope + --> $DIR/libstd.rs:87:22 + | +LL | fn test_debugset(_x: Debugset){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::DebugSet; + | + +error[E0425]: cannot find type `Debugstruct` in this scope + --> $DIR/libstd.rs:89:25 + | +LL | fn test_debugstruct(_x: Debugstruct){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::DebugStruct; + | + +error[E0425]: cannot find type `Debugtuple` in this scope + --> $DIR/libstd.rs:91:24 + | +LL | fn test_debugtuple(_x: Debugtuple){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::DebugTuple; + | + +error[E0425]: cannot find type `formatter` in this scope + --> $DIR/libstd.rs:93:23 + | +LL | fn test_fmter(mut _x: formatter){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fmt::Formatter; + | + +error[E0425]: cannot find type `Dirbuilder` in this scope + --> $DIR/libstd.rs:98:24 + | +LL | fn test_dirbuilder(_x: Dirbuilder){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::DirBuilder; + | + +error[E0425]: cannot find type `Direntry` in this scope + --> $DIR/libstd.rs:100:22 + | +LL | fn test_direntry(_x: Direntry){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::DirEntry; + | + +error[E0425]: cannot find type `Filetype` in this scope + --> $DIR/libstd.rs:102:22 + | +LL | fn test_filetype(_x: Filetype){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::FileType; + | + +error[E0425]: cannot find type `MetaData` in this scope + --> $DIR/libstd.rs:104:22 + | +LL | fn test_metadata(_x: MetaData){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::Metadata; + | + +error[E0425]: cannot find type `Openoptions` in this scope + --> $DIR/libstd.rs:106:25 + | +LL | fn test_openoptions(_x: Openoptions){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::OpenOptions; + | + +error[E0425]: cannot find type `permissions` in this scope + --> $DIR/libstd.rs:108:25 + | +LL | fn test_permissions(_x: permissions){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::Permissions; + | + +error[E0425]: cannot find type `Readdir` in this scope + --> $DIR/libstd.rs:110:21 + | +LL | fn test_readdir(_x: Readdir){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::fs::ReadDir; + | + +error[E0425]: cannot find type `BuildhasherDefault` in this scope + --> $DIR/libstd.rs:115:32 + | +LL | fn test_buildhasherdefault(_x: BuildhasherDefault){} + | ^^^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::hash::BuildHasherDefault; + | + +error[E0425]: cannot find type `Bufreader` in this scope + --> $DIR/libstd.rs:120:23 + | +LL | fn test_bufreader(_x: Bufreader<()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::BufReader; + | + +error[E0425]: cannot find type `Bufwriter` in this scope + --> $DIR/libstd.rs:122:23 + | +LL | fn test_bufwriter(_x: Bufwriter<()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::BufWriter; + | + +error[E0425]: cannot find type `bytes` in this scope + --> $DIR/libstd.rs:124:19 + | +LL | fn test_bytes(_x: bytes<()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Bytes; + | +LL + use std::str::Bytes; + | + +error[E0425]: cannot find type `chain` in this scope + --> $DIR/libstd.rs:126:19 + | +LL | fn test_chain(_x: chain<(), ()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Chain; + | +LL + use std::iter::Chain; + | + +error[E0425]: cannot find type `cursor` in this scope + --> $DIR/libstd.rs:128:20 + | +LL | fn test_cursor(_x: cursor<()>){} + | ^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::collections::btree_map::Cursor; + | +LL + use std::collections::btree_set::Cursor; + | +LL + use std::collections::linked_list::Cursor; + | +LL + use std::io::Cursor; + | + +error[E0425]: cannot find type `empty` in this scope + --> $DIR/libstd.rs:130:19 + | +LL | fn test_empty(_x: empty){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Empty; + | +LL + use std::iter::Empty; + | + +error[E0425]: cannot find type `Ioslice` in this scope + --> $DIR/libstd.rs:132:21 + | +LL | fn test_ioslice(_x: Ioslice){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::IoSlice; + | + +error[E0425]: cannot find type `IosliceMut` in this scope + --> $DIR/libstd.rs:134:24 + | +LL | fn test_ioslicemut(_x: IosliceMut){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::IoSliceMut; + | + +error[E0425]: cannot find type `Linewriter` in this scope + --> $DIR/libstd.rs:136:24 + | +LL | fn test_linewriter(_x: Linewriter<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::LineWriter; + | + +error[E0425]: cannot find type `lines` in this scope + --> $DIR/libstd.rs:138:19 + | +LL | fn test_lines(_x: lines<()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Lines; + | +LL + use std::str::Lines; + | + +error[E0425]: cannot find type `repeat` in this scope + --> $DIR/libstd.rs:140:20 + | +LL | fn test_repeat(_x: repeat){} + | ^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Repeat; + | +LL + use std::iter::Repeat; + | + +error[E0425]: cannot find type `sink` in this scope + --> $DIR/libstd.rs:142:18 + | +LL | fn test_sink(_x: sink){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::Sink; + | + +error[E0425]: cannot find type `split` in this scope + --> $DIR/libstd.rs:144:19 + | +LL | fn test_split(_x: split<()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Split; + | +LL + use std::slice::Split; + | +LL + use std::str::Split; + | + +error[E0425]: cannot find type `StdErr` in this scope + --> $DIR/libstd.rs:146:20 + | +LL | fn test_stderr(_x: StdErr){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::Stderr; + | + +error[E0425]: cannot find type `StdErrLock` in this scope + --> $DIR/libstd.rs:148:24 + | +LL | fn test_stderrlock(_x: StdErrLock){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::StderrLock; + | + +error[E0425]: cannot find type `StdIn` in this scope + --> $DIR/libstd.rs:150:19 + | +LL | fn test_stdin(_x: StdIn){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::Stdin; + | + +error[E0425]: cannot find type `StdInLock` in this scope + --> $DIR/libstd.rs:152:23 + | +LL | fn test_stdinlock(_x: StdInLock){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::StdinLock; + | + +error[E0425]: cannot find type `StdOut` in this scope + --> $DIR/libstd.rs:154:20 + | +LL | fn test_stdout(_x: StdOut){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::Stdout; + | + +error[E0425]: cannot find type `StdOutLock` in this scope + --> $DIR/libstd.rs:156:24 + | +LL | fn test_stdoutlock(_x: StdOutLock){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::io::StdoutLock; + | + +error[E0425]: cannot find type `take` in this scope + --> $DIR/libstd.rs:158:18 + | +LL | fn test_take(_x: take){} + | ^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::io::Take; + | +LL + use std::iter::Take; + | + +error[E0425]: cannot find type `cloned` in this scope + --> $DIR/libstd.rs:163:20 + | +LL | fn test_cloned(_x: cloned<(), ()>){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Cloned; + | + +error[E0425]: cannot find type `copied` in this scope + --> $DIR/libstd.rs:165:20 + | +LL | fn test_copied(_x: copied<(), ()>){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Copied; + | + +error[E0425]: cannot find type `cycle` in this scope + --> $DIR/libstd.rs:167:19 + | +LL | fn test_cycle(_x: cycle<(), ()>){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Cycle; + | + +error[E0425]: cannot find type `enumerate` in this scope + --> $DIR/libstd.rs:169:23 + | +LL | fn test_enumerate(_x: enumerate<(), ()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Enumerate; + | + +error[E0425]: cannot find type `filter` in this scope + --> $DIR/libstd.rs:171:20 + | +LL | fn test_filter(_x: filter<(), ()>){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Filter; + | + +error[E0425]: cannot find type `Filtermap` in this scope + --> $DIR/libstd.rs:173:23 + | +LL | fn test_filtermap(_x: Filtermap<(), ()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::FilterMap; + | + +error[E0425]: cannot find type `flatten` in this scope + --> $DIR/libstd.rs:175:21 + | +LL | fn test_flatten(_x: flatten<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Flatten; + | + +error[E0425]: cannot find type `Fromfn` in this scope + --> $DIR/libstd.rs:177:20 + | +LL | fn test_fromfn(_x: Fromfn<()>){} + | ^^^^^^ + | + --> $SRC_DIR/core/src/convert/mod.rs:LL:COL + | + = note: similarly named trait `From` defined here +help: a trait with a similar name exists + | +LL - fn test_fromfn(_x: Fromfn<()>){} +LL + fn test_fromfn(_x: From<()>){} + | + +error[E0425]: cannot find type `fuse` in this scope + --> $DIR/libstd.rs:179:18 + | +LL | fn test_fuse(_x: fuse<()>){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Fuse; + | + +error[E0425]: cannot find type `inspect` in this scope + --> $DIR/libstd.rs:181:21 + | +LL | fn test_inspect(_x: inspect<(), ()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Inspect; + | + +error[E0425]: cannot find type `map` in this scope + --> $DIR/libstd.rs:183:17 + | +LL | fn test_map(_x: map<(), ()>){} + | ^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Map; + | + +error[E0425]: cannot find type `once` in this scope + --> $DIR/libstd.rs:185:18 + | +LL | fn test_once(_x: once<()>){} + | ^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::iter::Once; + | +LL + use std::sync::Once; + | + +error[E0425]: cannot find type `Oncewith` in this scope + --> $DIR/libstd.rs:187:22 + | +LL | fn test_oncewith(_x: Oncewith<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::OnceWith; + | + +error[E0425]: cannot find type `peekable` in this scope + --> $DIR/libstd.rs:189:22 + | +LL | fn test_peekable(_x: peekable<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Peekable; + | + +error[E0425]: cannot find type `Repeatwith` in this scope + --> $DIR/libstd.rs:191:24 + | +LL | fn test_repeatwith(_x: Repeatwith<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::RepeatWith; + | + +error[E0425]: cannot find type `rev` in this scope + --> $DIR/libstd.rs:193:17 + | +LL | fn test_rev(_x: rev<()>){} + | ^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Rev; + | + +error[E0425]: cannot find type `scan` in this scope + --> $DIR/libstd.rs:195:18 + | +LL | fn test_scan(_x: scan<(), (), ()>){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Scan; + | + +error[E0425]: cannot find type `skip` in this scope + --> $DIR/libstd.rs:197:18 + | +LL | fn test_skip(_x: skip<()>){} + | ^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Skip; + | + +error[E0425]: cannot find type `Skipwhile` in this scope + --> $DIR/libstd.rs:199:23 + | +LL | fn test_skipwhile(_x: Skipwhile<(), ()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::SkipWhile; + | + +error[E0425]: cannot find type `Stepby` in this scope + --> $DIR/libstd.rs:201:20 + | +LL | fn test_stepby(_x: Stepby<()>){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::StepBy; + | + +error[E0425]: cannot find type `successors` in this scope + --> $DIR/libstd.rs:203:24 + | +LL | fn test_successors(_x: successors<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Successors; + | + +error[E0425]: cannot find type `Takewhile` in this scope + --> $DIR/libstd.rs:205:23 + | +LL | fn test_takewhile(_x: Takewhile<(), ()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::TakeWhile; + | + +error[E0425]: cannot find type `zip` in this scope + --> $DIR/libstd.rs:207:17 + | +LL | fn test_zip(_x: zip<(), ()>){} + | ^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::iter::Zip; + | + +error[E0425]: cannot find type `Phantomdata` in this scope + --> $DIR/libstd.rs:212:25 + | +LL | fn test_phantomdata(_x: Phantomdata){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::marker::PhantomData; + | + +error[E0425]: cannot find type `Phantompinned` in this scope + --> $DIR/libstd.rs:214:27 + | +LL | fn test_phantompinned(_x: Phantompinned){} + | ^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::marker::PhantomPinned; + | + +error[E0425]: cannot find type `discriminant` in this scope + --> $DIR/libstd.rs:219:26 + | +LL | fn test_discriminant(_x: discriminant<()>){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::mem::Discriminant; + | + +error[E0425]: cannot find type `Manuallydrop` in this scope + --> $DIR/libstd.rs:221:26 + | +LL | fn test_manuallydrop(_x: Manuallydrop<()>){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::mem::ManuallyDrop; + | + +error[E0425]: cannot find type `incoming` in this scope + --> $DIR/libstd.rs:226:22 + | +LL | fn test_incoming(_x: incoming){} + | ^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::net::Incoming; + | +LL + use std::os::unix::net::Incoming; + | + +error[E0425]: cannot find type `IPv4Addr` in this scope + --> $DIR/libstd.rs:228:22 + | +LL | fn test_ipv4addr(_x: IPv4Addr){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::Ipv4Addr; + | + +error[E0425]: cannot find type `IPv6Addr` in this scope + --> $DIR/libstd.rs:230:22 + | +LL | fn test_ipv6addr(_x: IPv6Addr){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::Ipv6Addr; + | + +error[E0425]: cannot find type `SocketAddrv4` in this scope + --> $DIR/libstd.rs:232:26 + | +LL | fn test_socketaddrv4(_x: SocketAddrv4){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::SocketAddrV4; + | + +error[E0425]: cannot find type `SocketAddrv6` in this scope + --> $DIR/libstd.rs:234:26 + | +LL | fn test_socketaddrv6(_x: SocketAddrv6){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::SocketAddrV6; + | + +error[E0425]: cannot find type `TCPListener` in this scope + --> $DIR/libstd.rs:236:25 + | +LL | fn test_tcplistener(_x: TCPListener){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::TcpListener; + | + +error[E0425]: cannot find type `TCPStream` in this scope + --> $DIR/libstd.rs:238:23 + | +LL | fn test_tcpstream(_x: TCPStream){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::TcpStream; + | + +error[E0425]: cannot find type `UDPSocket` in this scope + --> $DIR/libstd.rs:240:23 + | +LL | fn test_udpsocket(_x: UDPSocket){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::net::UdpSocket; + | + +error[E0425]: cannot find type `NonZeroi8` in this scope + --> $DIR/libstd.rs:245:23 + | +LL | fn test_nonzeroi8(_x: NonZeroi8){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroI8; + | + +error[E0425]: cannot find type `NonZeroi16` in this scope + --> $DIR/libstd.rs:247:24 + | +LL | fn test_nonzeroi16(_x: NonZeroi16){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroI16; + | + +error[E0425]: cannot find type `NonZeroi32` in this scope + --> $DIR/libstd.rs:249:24 + | +LL | fn test_nonzeroi32(_x: NonZeroi32){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroI32; + | + +error[E0425]: cannot find type `NonZeroi64` in this scope + --> $DIR/libstd.rs:251:24 + | +LL | fn test_nonzeroi64(_x: NonZeroi64){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroI64; + | + +error[E0425]: cannot find type `NonZeroi128` in this scope + --> $DIR/libstd.rs:253:25 + | +LL | fn test_nonzeroi128(_x: NonZeroi128){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroI128; + | + +error[E0425]: cannot find type `NonZerou8` in this scope + --> $DIR/libstd.rs:255:23 + | +LL | fn test_nonzerou8(_x: NonZerou8){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroU8; + | + +error[E0425]: cannot find type `NonZerou16` in this scope + --> $DIR/libstd.rs:257:24 + | +LL | fn test_nonzerou16(_x: NonZerou16){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroU16; + | + +error[E0425]: cannot find type `NonZerou32` in this scope + --> $DIR/libstd.rs:259:24 + | +LL | fn test_nonzerou32(_x: NonZerou32){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroU32; + | + +error[E0425]: cannot find type `NonZerou64` in this scope + --> $DIR/libstd.rs:261:24 + | +LL | fn test_nonzerou64(_x: NonZerou64){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroU64; + | + +error[E0425]: cannot find type `NonZerou128` in this scope + --> $DIR/libstd.rs:263:25 + | +LL | fn test_nonzerou128(_x: NonZerou128){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroU128; + | + +error[E0425]: cannot find type `NonzeroUsize` in this scope + --> $DIR/libstd.rs:265:26 + | +LL | fn test_nonzerousize(_x: NonzeroUsize){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::num::NonZeroUsize; + | + +error[E0425]: cannot find type `wrapping` in this scope + --> $DIR/libstd.rs:267:22 + | +LL | fn test_wrapping(_x: wrapping){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::num::Wrapping; + | + +error[E0425]: cannot find type `range` in this scope + --> $DIR/libstd.rs:272:19 + | +LL | fn test_range(_x: range<()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::collections::btree_map::Range; + | +LL + use std::collections::btree_set::Range; + | +LL + use std::ops::Range; + | +LL + use std::range::Range; + | + +error[E0425]: cannot find type `Rangefrom` in this scope + --> $DIR/libstd.rs:274:23 + | +LL | fn test_rangefrom(_x: Rangefrom<()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::ops::RangeFrom; + | +LL + use std::range::RangeFrom; + | + +error[E0425]: cannot find type `Rangefull` in this scope + --> $DIR/libstd.rs:276:23 + | +LL | fn test_rangefull(_x: Rangefull<()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ops::RangeFull; + | + +error[E0425]: cannot find type `Rangeinclusive` in this scope + --> $DIR/libstd.rs:278:28 + | +LL | fn test_rangeinclusive(_x: Rangeinclusive<()>){} + | ^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::ops::RangeInclusive; + | +LL + use std::range::RangeInclusive; + | + +error[E0425]: cannot find type `Rangeto` in this scope + --> $DIR/libstd.rs:280:21 + | +LL | fn test_rangeto(_x: Rangeto<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ops::RangeTo; + | + +error[E0425]: cannot find type `RangetoInclusive` in this scope + --> $DIR/libstd.rs:282:30 + | +LL | fn test_rangetoinclusive(_x: RangetoInclusive<()>){} + | ^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::ops::RangeToInclusive; + | +LL + use std::range::RangeToInclusive; + | + +error[E0425]: cannot find type `AssertUnwindsafe` in this scope + --> $DIR/libstd.rs:287:30 + | +LL | fn test_assertunwindsafe(_x: AssertUnwindsafe<()>){} + | ^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::panic::AssertUnwindSafe; + | + +error[E0425]: cannot find type `location` in this scope + --> $DIR/libstd.rs:289:22 + | +LL | fn test_location(_x: location<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::panic::Location; + | + +error[E0425]: cannot find type `Panicinfo` in this scope + --> $DIR/libstd.rs:291:23 + | +LL | fn test_panicinfo(_x: Panicinfo<()>){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named type alias + | +LL + use std::panic::PanicInfo; + | + +error[E0425]: cannot find type `ancestors` in this scope + --> $DIR/libstd.rs:296:23 + | +LL | fn test_ancestors(_x: ancestors){} + | ^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::path::Ancestors; + | + +error[E0425]: cannot find type `components` in this scope + --> $DIR/libstd.rs:298:24 + | +LL | fn test_components(_x: components){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::path::Components; + | + +error[E0425]: cannot find type `Pathbuf` in this scope + --> $DIR/libstd.rs:300:21 + | +LL | fn test_pathbuf(_x: Pathbuf){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::path::PathBuf; + | + +error[E0425]: cannot find type `Prefixcomponent` in this scope + --> $DIR/libstd.rs:302:29 + | +LL | fn test_prefixcomponent(_x: Prefixcomponent){} + | ^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::path::PrefixComponent; + | + +error[E0425]: cannot find type `pin` in this scope + --> $DIR/libstd.rs:307:17 + | +LL | fn test_pin(_x: pin<()>){} + | ^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::pin::Pin; + | + +error[E0425]: cannot find type `child` in this scope + --> $DIR/libstd.rs:312:19 + | +LL | fn test_child(_x: child){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::Child; + | + +error[E0425]: cannot find type `ChildStdErr` in this scope + --> $DIR/libstd.rs:314:25 + | +LL | fn test_childstderr(_x: ChildStdErr){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::ChildStderr; + | + +error[E0425]: cannot find type `ChildStdIn` in this scope + --> $DIR/libstd.rs:316:24 + | +LL | fn test_childstdin(_x: ChildStdIn){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::ChildStdin; + | + +error[E0425]: cannot find type `ChildStdOut` in this scope + --> $DIR/libstd.rs:318:25 + | +LL | fn test_childstdout(_x: ChildStdOut){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::ChildStdout; + | + +error[E0425]: cannot find type `command` in this scope + --> $DIR/libstd.rs:320:21 + | +LL | fn test_command(_x: command){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::Command; + | + +error[E0425]: cannot find type `Exitstatus` in this scope + --> $DIR/libstd.rs:322:24 + | +LL | fn test_exitstatus(_x: Exitstatus){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::ExitStatus; + | + +error[E0425]: cannot find type `output` in this scope + --> $DIR/libstd.rs:324:20 + | +LL | fn test_output(_x: output){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::Output; + | + +error[E0425]: cannot find type `StdIo` in this scope + --> $DIR/libstd.rs:326:19 + | +LL | fn test_stdio(_x: StdIo){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::process::Stdio; + | + +error[E0425]: cannot find type `Nonnull` in this scope + --> $DIR/libstd.rs:331:21 + | +LL | fn test_nonnull(_x: Nonnull<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::ptr::NonNull; + | + +error[E0425]: cannot find type `rc` in this scope + --> $DIR/libstd.rs:336:16 + | +LL | fn test_rc(_x: rc<()>){} + | ^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::rc::Rc; + | + +error[E0425]: cannot find type `weak` in this scope + --> $DIR/libstd.rs:338:18 + | +LL | fn test_weak(_x: weak<()>){} + | ^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::rc::Weak; + | +LL + use std::sync::Weak; + | + +error[E0425]: cannot find type `drain` in this scope + --> $DIR/libstd.rs:343:19 + | +LL | fn test_drain(_x: drain){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::collections::binary_heap::Drain; + | +LL + use std::collections::hash_map::Drain; + | +LL + use std::collections::hash_set::Drain; + | +LL + use std::collections::vec_deque::Drain; + | + = and 2 other candidates + +error[E0425]: cannot find type `Charindices` in this scope + --> $DIR/libstd.rs:348:25 + | +LL | fn test_charindices(_x: Charindices){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::CharIndices; + | + +error[E0425]: cannot find type `chars` in this scope + --> $DIR/libstd.rs:350:19 + | +LL | fn test_chars(_x: chars){} + | ^^^^^ + | +help: a builtin type with a similar name exists + | +LL - fn test_chars(_x: chars){} +LL + fn test_chars(_x: char){} + | + +error[E0425]: cannot find type `EncodeUTF16` in this scope + --> $DIR/libstd.rs:352:25 + | +LL | fn test_encodeutf16(_x: EncodeUTF16){} + | ^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::EncodeUtf16; + | + +error[E0425]: cannot find type `Matchindices` in this scope + --> $DIR/libstd.rs:354:26 + | +LL | fn test_matchindices(_x: Matchindices){} + | ^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::MatchIndices; + | + +error[E0425]: cannot find type `RmatchIndices` in this scope + --> $DIR/libstd.rs:356:27 + | +LL | fn test_rmatchindices(_x: RmatchIndices){} + | ^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::RMatchIndices; + | + +error[E0425]: cannot find type `Rmatches` in this scope + --> $DIR/libstd.rs:358:22 + | +LL | fn test_rmatches(_x: Rmatches){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::RMatches; + | + +error[E0425]: cannot find type `Rsplit` in this scope + --> $DIR/libstd.rs:360:20 + | +LL | fn test_rsplit(_x: Rsplit){} + | ^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::slice::RSplit; + | +LL + use std::str::RSplit; + | + +error[E0425]: cannot find type `RSplitn` in this scope + --> $DIR/libstd.rs:362:21 + | +LL | fn test_rsplitn(_x: RSplitn){} + | ^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::slice::RSplitN; + | +LL + use std::str::RSplitN; + | + +error[E0425]: cannot find type `RsplitTerminator` in this scope + --> $DIR/libstd.rs:364:30 + | +LL | fn test_rsplitterminator(_x: RsplitTerminator){} + | ^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::RSplitTerminator; + | + +error[E0425]: cannot find type `SplitASCIIWhitespace` in this scope + --> $DIR/libstd.rs:366:34 + | +LL | fn test_splitasciiwhitespace(_x: SplitASCIIWhitespace){} + | ^^^^^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::SplitAsciiWhitespace; + | + +error[E0425]: cannot find type `Splitn` in this scope + --> $DIR/libstd.rs:368:20 + | +LL | fn test_splitn(_x: Splitn){} + | ^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::slice::SplitN; + | +LL + use std::str::SplitN; + | + +error[E0425]: cannot find type `Splitterminator` in this scope + --> $DIR/libstd.rs:370:29 + | +LL | fn test_splitterminator(_x: Splitterminator){} + | ^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::SplitTerminator; + | + +error[E0425]: cannot find type `Splitwhitespace` in this scope + --> $DIR/libstd.rs:372:29 + | +LL | fn test_splitwhitespace(_x: Splitwhitespace){} + | ^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::str::SplitWhitespace; + | + +error[E0425]: cannot find type `arc` in this scope + --> $DIR/libstd.rs:377:17 + | +LL | fn test_arc(_x: arc<()>){} + | ^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::sync::Arc; + | + +error[E0425]: cannot find type `barrier` in this scope + --> $DIR/libstd.rs:379:21 + | +LL | fn test_barrier(_x: barrier<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::sync::Barrier; + | + +error[E0425]: cannot find type `BarrierwaitResult` in this scope + --> $DIR/libstd.rs:381:31 + | +LL | fn test_barrierwaitresult(_x: BarrierwaitResult<()>){} + | ^^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::sync::BarrierWaitResult; + | + +error[E0425]: cannot find type `CondVar` in this scope + --> $DIR/libstd.rs:383:21 + | +LL | fn test_condvar(_x: CondVar<()>){} + | ^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::Condvar; + | +LL + use std::sync::nonpoison::Condvar; + | + +error[E0425]: cannot find type `mutex` in this scope + --> $DIR/libstd.rs:385:19 + | +LL | fn test_mutex(_x: mutex<()>){} + | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::Mutex; + | +LL + use std::sync::nonpoison::Mutex; + | + +error[E0425]: cannot find type `Mutexguard` in this scope + --> $DIR/libstd.rs:387:24 + | +LL | fn test_mutexguard(_x: Mutexguard<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::MutexGuard; + | +LL + use std::sync::nonpoison::MutexGuard; + | + +error[E0425]: cannot find type `RWlock` in this scope + --> $DIR/libstd.rs:389:20 + | +LL | fn test_rwlock(_x: RWlock<()>){} + | ^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::RwLock; + | +LL + use std::sync::nonpoison::RwLock; + | + +error[E0425]: cannot find type `RWlockReadGuard` in this scope + --> $DIR/libstd.rs:391:29 + | +LL | fn test_rwlockreadguard(_x: RWlockReadGuard<()>){} + | ^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::RwLockReadGuard; + | +LL + use std::sync::nonpoison::RwLockReadGuard; + | + +error[E0425]: cannot find type `RWlockWriteGuard` in this scope + --> $DIR/libstd.rs:393:30 + | +LL | fn test_rwlockwriteguard(_x: RWlockWriteGuard<()>){} + | ^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing one of these similarly named structs + | +LL + use std::sync::RwLockWriteGuard; + | +LL + use std::sync::nonpoison::RwLockWriteGuard; + | + +error[E0425]: cannot find type `WaittimeoutResult` in this scope + --> $DIR/libstd.rs:395:31 + | +LL | fn test_waittimeoutresult(_x: WaittimeoutResult<()>){} + | ^^^^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::sync::WaitTimeoutResult; + | + +error[E0425]: cannot find type `context` in this scope + --> $DIR/libstd.rs:400:21 + | +LL | fn test_context(_x: context){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::task::Context; + | + +error[E0425]: cannot find type `Rawwaker` in this scope + --> $DIR/libstd.rs:402:22 + | +LL | fn test_rawwaker(_x: Rawwaker){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::task::RawWaker; + | + +error[E0425]: cannot find type `RawwakerVTable` in this scope + --> $DIR/libstd.rs:404:28 + | +LL | fn test_rawwakervtable(_x: RawwakerVTable){} + | ^^^^^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::task::RawWakerVTable; + | + +error[E0425]: cannot find type `waker` in this scope + --> $DIR/libstd.rs:406:19 + | +LL | fn test_waker(_x: waker){} + | ^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::task::Waker; + | + +error[E0425]: cannot find type `builder` in this scope + --> $DIR/libstd.rs:411:21 + | +LL | fn test_builder(_x: builder){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::thread::Builder; + | + +error[E0425]: cannot find type `Joinhandle` in this scope + --> $DIR/libstd.rs:413:24 + | +LL | fn test_joinhandle(_x: Joinhandle<()>){} + | ^^^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::thread::JoinHandle; + | + +error[E0425]: cannot find type `Localkey` in this scope + --> $DIR/libstd.rs:415:22 + | +LL | fn test_localkey(_x: Localkey<()>){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::thread::LocalKey; + | + +error[E0425]: cannot find type `thread` in this scope + --> $DIR/libstd.rs:417:20 + | +LL | fn test_thread(_x: thread){} + | ^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::thread::Thread; + | + +error[E0425]: cannot find type `ThreadID` in this scope + --> $DIR/libstd.rs:419:22 + | +LL | fn test_threadid(_x: ThreadID){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::thread::ThreadId; + | + +error[E0425]: cannot find type `duration` in this scope + --> $DIR/libstd.rs:424:22 + | +LL | fn test_duration(_x: duration){} + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::time::Duration; + | + +error[E0425]: cannot find type `instant` in this scope + --> $DIR/libstd.rs:426:21 + | +LL | fn test_instant(_x: instant){} + | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named struct + | +LL + use std::time::Instant; + | + +error[E0425]: cannot find type `Systemtime` in this scope + --> $DIR/libstd.rs:428:24 + | +LL | fn test_systemtime(_x: Systemtime){} + | ^^^^^^^^^^ +... +LL | struct SystemTome{} + | ----------------- similarly named struct `SystemTome` defined here + | +help: a struct with a similar name exists + | +LL - fn test_systemtime(_x: Systemtime){} +LL + fn test_systemtime(_x: SystemTome){} + | + +error[E0425]: cannot find type `SystemTime` in this scope + --> $DIR/libstd.rs:431:25 + | +LL | fn test_systemtime2(_x: SystemTime){} + | ^^^^^^^^^^ +... +LL | struct SystemTome{} + | ----------------- similarly named struct `SystemTome` defined here + | +help: a struct with a similar name exists + | +LL - fn test_systemtime2(_x: SystemTime){} +LL + fn test_systemtime2(_x: SystemTome){} + | +help: consider importing this struct + | +LL + use std::time::SystemTime; + | + +error: aborting due to 168 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/parser/raw/raw-literal-keywords.stderr b/tests/ui/parser/raw/raw-literal-keywords.stderr index f7b6c894a90fe..7c7322d12d338 100644 --- a/tests/ui/parser/raw/raw-literal-keywords.stderr +++ b/tests/ui/parser/raw/raw-literal-keywords.stderr @@ -27,12 +27,22 @@ error[E0425]: cannot find value `r#struct` in this scope | LL | let _ = r#struct; | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named tuple variant + | +LL + use std::mem::type_info::TypeKind::Struct; + | error[E0425]: cannot find value `union` in this scope --> $DIR/raw-literal-keywords.rs:22:13 | LL | let _ = r#union; | ^^^^^^^ not found in this scope + | +help: consider importing this similarly named tuple variant + | +LL + use std::mem::type_info::TypeKind::Union; + | error: aborting due to 6 previous errors diff --git a/tests/ui/resolve/export-fully-qualified-2018.stderr b/tests/ui/resolve/export-fully-qualified-2018.stderr index 25c9e6fbdab96..1246d51a7fca1 100644 --- a/tests/ui/resolve/export-fully-qualified-2018.stderr +++ b/tests/ui/resolve/export-fully-qualified-2018.stderr @@ -5,6 +5,10 @@ LL | pub fn bar() { foo::baz(); } | ^^^ use of unresolved module or unlinked crate `foo` | = help: you might be missing a crate named `foo` +help: consider importing this module + | +LL + use crate::foo; + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/export-fully-qualified.stderr b/tests/ui/resolve/export-fully-qualified.stderr index f8433dcfb8924..c9514d34b8d0f 100644 --- a/tests/ui/resolve/export-fully-qualified.stderr +++ b/tests/ui/resolve/export-fully-qualified.stderr @@ -5,6 +5,10 @@ LL | pub fn bar() { foo::baz(); } | ^^^ use of unresolved module or unlinked crate `foo` | = help: you might be missing a crate named `foo` +help: consider importing this module + | +LL + use foo; + | error: aborting due to 1 previous error diff --git a/tests/ui/resolve/resolve-assoc-suggestions.stderr b/tests/ui/resolve/resolve-assoc-suggestions.stderr index e6311962884f1..37fccacec69e9 100644 --- a/tests/ui/resolve/resolve-assoc-suggestions.stderr +++ b/tests/ui/resolve/resolve-assoc-suggestions.stderr @@ -3,6 +3,13 @@ error[E0425]: cannot find type `field` in this scope | LL | let _: field; | ^^^^^ not found in this scope + | +help: consider importing one of these similarly named items + | +LL + use std::field::Field; + | +LL + use std::mem::type_info::Field; + | error[E0531]: cannot find tuple struct or tuple variant `field` in this scope --> $DIR/resolve-assoc-suggestions.rs:18:13 diff --git a/tests/ui/resolve/resolve-speculative-adjustment.stderr b/tests/ui/resolve/resolve-speculative-adjustment.stderr index fb15472bdae8d..e03d0cb2088ab 100644 --- a/tests/ui/resolve/resolve-speculative-adjustment.stderr +++ b/tests/ui/resolve/resolve-speculative-adjustment.stderr @@ -3,6 +3,11 @@ error[E0425]: cannot find value `field` in this scope | LL | field; | ^^^^^ not found in this scope + | +help: consider importing this similarly named function + | +LL + use std::intrinsics::mir::Field; + | error[E0425]: cannot find value `field` in this scope --> $DIR/resolve-speculative-adjustment.rs:23:9 diff --git a/tests/ui/suggestions/crate-or-module-typo.stderr b/tests/ui/suggestions/crate-or-module-typo.stderr index 49ac1c89ab52a..59d6c64b1b934 100644 --- a/tests/ui/suggestions/crate-or-module-typo.stderr +++ b/tests/ui/suggestions/crate-or-module-typo.stderr @@ -21,6 +21,17 @@ LL - use bas::bar; LL + use bar::bar; | +error[E0433]: cannot find module or crate `bar` in this scope + --> $DIR/crate-or-module-typo.rs:6:20 + | +LL | pub fn bar() { bar::baz(); } + | ^^^ function `bar` is not a crate or module + | +help: consider importing this module + | +LL + use crate::bar; + | + error[E0433]: cannot find module or crate `st` in this scope --> $DIR/crate-or-module-typo.rs:14:10 | @@ -41,12 +52,6 @@ LL - bar: st::cell::Cell LL + bar: cell::Cell | -error[E0433]: cannot find module or crate `bar` in this scope - --> $DIR/crate-or-module-typo.rs:6:20 - | -LL | pub fn bar() { bar::baz(); } - | ^^^ function `bar` is not a crate or module - error: aborting due to 4 previous errors Some errors have detailed explanations: E0432, E0433. From 3bd3f497eeaa530a82342b91c28e24d5de4eeca9 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:55:00 +0200 Subject: [PATCH 07/10] additional insensitive import test --- .../ui/suggestions/case-insensitive-import.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/suggestions/case-insensitive-import.rs diff --git a/tests/ui/suggestions/case-insensitive-import.rs b/tests/ui/suggestions/case-insensitive-import.rs new file mode 100644 index 0000000000000..eee380f2f07dc --- /dev/null +++ b/tests/ui/suggestions/case-insensitive-import.rs @@ -0,0 +1,30 @@ +mod a{ + enum EnumAB{ + A, + B + } +} +mod b{ + fn enumab_not_in_scope()->enumab{ //~ ERROR + unimplemented!() + } + fn shutdown_not_in_scope()->Shutdown{ //~ ERROR + unimplemented!() + } + fn both_not_in_scope_and_capitalization()->both{ //~ ERROR + unimplemented!() + } + fn shutdown_both_not_in_scope_and_capitalization()->shutdown{ //~ ERROR + both //~ ERROR + } + fn both_missing_std()->std::net::Shutdown{ + net::shutdown::Both //~ ERROR + } + fn shutdown_capitalization()->std::net::Shutdown{ + std::net::shutdown::Both //~ ERROR + } + fn shutdown_and_both_missing_capitalization()->std::net::Shutdown{ + std::net::shutdown::both //~ ERROR + } +} +fn main(){} From 4a04f09c89235d9aaf326149ea62aa884a74cad9 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Tue, 2 Jun 2026 00:56:34 +0200 Subject: [PATCH 08/10] bless additional test --- .../case-insensitive-import.stderr | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/ui/suggestions/case-insensitive-import.stderr diff --git a/tests/ui/suggestions/case-insensitive-import.stderr b/tests/ui/suggestions/case-insensitive-import.stderr new file mode 100644 index 0000000000000..2f30802babc5d --- /dev/null +++ b/tests/ui/suggestions/case-insensitive-import.stderr @@ -0,0 +1,87 @@ +error[E0433]: cannot find module or crate `net` in this scope + --> $DIR/case-insensitive-import.rs:21:9 + | +LL | net::shutdown::Both + | ^^^ use of unresolved module or unlinked crate `net` + | + = help: you might be missing a crate named `net` + +error[E0433]: cannot find `shutdown` in `net` + --> $DIR/case-insensitive-import.rs:24:19 + | +LL | std::net::shutdown::Both + | ^^^^^^^^ could not find `shutdown` in `net` + | +help: an enum with a similar name exists (notice the capitalization) + | +LL - std::net::shutdown::Both +LL + std::net::Shutdown::Both + | + +error[E0433]: cannot find `shutdown` in `net` + --> $DIR/case-insensitive-import.rs:27:19 + | +LL | std::net::shutdown::both + | ^^^^^^^^ could not find `shutdown` in `net` + | +help: an enum with a similar name exists (notice the capitalization) + | +LL - std::net::shutdown::both +LL + std::net::Shutdown::both + | + +error[E0425]: cannot find type `enumab` in this scope + --> $DIR/case-insensitive-import.rs:8:31 + | +LL | fn enumab_not_in_scope()->enumab{ + | ^^^^^^ not found in this scope + | +note: similarly named enum `a::EnumAB` exists but is inaccessible + --> $DIR/case-insensitive-import.rs:2:5 + | +LL | enum EnumAB{ + | ^^^^^^^^^^^ not accessible + +error[E0425]: cannot find type `Shutdown` in this scope + --> $DIR/case-insensitive-import.rs:11:33 + | +LL | fn shutdown_not_in_scope()->Shutdown{ + | ^^^^^^^^ not found in this scope + | +help: consider importing this enum + | +LL + use std::net::Shutdown; + | + +error[E0425]: cannot find type `both` in this scope + --> $DIR/case-insensitive-import.rs:14:48 + | +LL | fn both_not_in_scope_and_capitalization()->both{ + | ^^^^ not found in this scope + +error[E0425]: cannot find type `shutdown` in this scope + --> $DIR/case-insensitive-import.rs:17:57 + | +LL | fn shutdown_both_not_in_scope_and_capitalization()->shutdown{ + | ^^^^^^^^ not found in this scope + | +help: consider importing this similarly named enum + | +LL + use std::net::Shutdown; + | + +error[E0425]: cannot find value `both` in this scope + --> $DIR/case-insensitive-import.rs:18:9 + | +LL | both + | ^^^^ not found in this scope + | +help: consider importing this similarly named unit variant + | +LL + use std::net::Shutdown::Both; + | + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0425, E0433. +For more information about an error, try `rustc --explain E0425`. From 981c290f3fe22e0785268925b789a8a3e9287908 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:40:55 +0200 Subject: [PATCH 09/10] swap diagnostics.rs and error_helper.rs (redo #157974) --- compiler/rustc_resolve/src/diagnostics.rs | 5665 ++++++------------- compiler/rustc_resolve/src/error_helper.rs | 5669 ++++++++++++++------ 2 files changed, 5667 insertions(+), 5667 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index c6cc58a6d3a30..2baf423e296d6 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,4211 +1,1778 @@ -// ignore-tidy-filelength -use std::mem; -use std::ops::ControlFlow; - -use itertools::Itertools as _; -use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{ - self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, - join_path_idents, -}; -use rustc_ast_pretty::pprust; -use rustc_attr_parsing::AttributeParser; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, - pluralize, struct_span_code_err, -}; -use rustc_feature::BUILTIN_ATTRIBUTES; -use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; -use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; -use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; -use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; -use rustc_hir::{Attribute, PrimTy, Stability, StabilityLevel, find_attr}; -use rustc_middle::bug; -use rustc_middle::ty::{TyCtxt, Visibility}; -use rustc_session::Session; -use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, - AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, -}; -use rustc_session::utils::was_invoked_from_cargo; -use rustc_span::edit_distance::find_best_match_for_name; -use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; -use rustc_span::source_map::SourceMap; -use rustc_span::{ - BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, + Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, + EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, }; -use thin_vec::{ThinVec, thin_vec}; -use tracing::{debug, instrument}; +use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_span::{Ident, Span, Spanned, Symbol}; + +use crate::Res; +use crate::late::PatternSource; + +#[derive(Diagnostic)] +#[diag("can't use {$is_self -> + [true] `Self` + *[false] generic parameters + } from outer item", code = E0401)] +#[note( + "nested items are independent from their parent item for everything except for privacy and name resolution" +)] +pub(crate) struct GenericParamsFromOuterItem { + #[primary_span] + #[label( + "use of {$is_self -> + [true] `Self` + *[false] generic parameter + } from outer item" + )] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) label: Option, + #[subdiagnostic] + pub(crate) refer_to_type_directly: Option, + #[subdiagnostic] + pub(crate) use_let: Option, + #[subdiagnostic] + pub(crate) sugg: Option, + #[subdiagnostic] + pub(crate) static_or_const: Option, + pub(crate) is_self: bool, + #[subdiagnostic] + pub(crate) item: Option, +} -use crate::diagnostics::{ - self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, - ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, - MaybeMissingMacroRulesName, -}; -use crate::hygiene::Macros20NormalizedSyntaxContext; -use crate::imports::{Import, ImportKind, UnresolvedImportError, import_path_to_string}; -use crate::late::{DiagMetadata, PatternSource, Rib}; -use crate::{ - AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, - DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, - LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, - PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, - VisResolutionError, path_names_to_string, -}; +#[derive(Subdiagnostic)] +#[label( + "{$is_self -> + [true] `Self` + *[false] generic parameter + } used in this inner {$descr}" +)] +pub(crate) struct GenericParamsFromOuterItemInnerItem { + #[primary_span] + pub(crate) span: Span, + pub(crate) descr: String, + pub(crate) is_self: bool, +} -/// A vector of spans and replacements, a message and applicability. -pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemStaticOrConst { + #[note("a `static` is a separate item from the item that contains it")] + Static, + #[note("a `const` is a separate item from the item that contains it")] + Const, +} -/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a -/// similarly named label and whether or not it is reachable. -pub(crate) type LabelSuggestion = (Ident, bool); +#[derive(Subdiagnostic)] +pub(crate) enum GenericParamsFromOuterItemLabel { + #[label("can't use `Self` here")] + SelfTyParam(#[primary_span] Span), + #[label("`Self` type implicitly declared here, by this `impl`")] + SelfTyAlias(#[primary_span] Span), + #[label("type parameter from outer item")] + TyParam(#[primary_span] Span), + #[label("const parameter from outer item")] + ConstParam(#[primary_span] Span), +} -#[derive(Clone)] -pub(crate) struct StructCtor { - pub res: Res, - pub vis: Visibility, - pub field_visibilities: Vec>, +#[derive(Subdiagnostic)] +#[suggestion( + "try introducing a local generic parameter here", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemSugg { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, } -impl StructCtor { - pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { - self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) - } +#[derive(Subdiagnostic)] +#[suggestion( + "try using a local `let` binding instead", + code = "let", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct GenericParamsFromOuterItemUseLet { + #[primary_span] + pub(crate) span: Span, } -#[derive(Debug)] -pub(crate) enum SuggestionTarget { - /// The target has a similar name as the name used by the programmer (probably a typo) - SimilarlyNamed, - /// The target is the only valid item that can be used in the corresponding context - SingleItem, +#[derive(Subdiagnostic)] +#[suggestion( + "refer to the type directly here instead", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UseTypeDirectly { + #[primary_span] + pub(crate) span: Span, + pub(crate) snippet: String, } -#[derive(Debug)] -pub(crate) struct TypoSuggestion { - pub candidate: Symbol, - /// The source location where the name is defined; None if the name is not defined - /// in source e.g. primitives - pub span: Option, - pub res: Res, - pub target: SuggestionTarget, -} - -impl TypoSuggestion { - pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { - Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } - } - pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { - Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } - } +#[derive(Diagnostic)] +#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] +pub(crate) struct NameAlreadyUsedInParameterList { + #[primary_span] + #[label("already used")] + pub(crate) span: Span, + #[label("first use of `{$name}`")] + pub(crate) first_use_span: Span, + pub(crate) name: Ident, } -/// A free importable items suggested in case of resolution failure. -#[derive(Debug, Clone)] -pub(crate) struct ImportSuggestion { - pub did: Option, - pub descr: &'static str, - pub path: Path, - pub accessible: bool, - // false if the path traverses a foreign `#[doc(hidden)]` item. - pub doc_visible: bool, - pub via_import: bool, - /// An extra note that should be issued if this item is suggested - pub note: Option, - pub is_stable: bool, - pub is_exact_match: bool, -} - -/// Adjust the impl span so that just the `impl` keyword is taken by removing -/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and -/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). -/// -/// *Attention*: the method used is very fragile since it essentially duplicates the work of the -/// parser. If you need to use this function or something similar, please consider updating the -/// `source_map` functions and this function to something more robust. -fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { - let impl_span = sm.span_until_char(impl_span, '<'); - sm.span_until_whitespace(impl_span) -} - -impl<'ra, 'tcx> Resolver<'ra, 'tcx> { - /// Reports unresolved imports. - /// - /// Multiple unresolved import errors within the same use tree are combined into a single - /// diagnostic. - pub(crate) fn throw_unresolved_import_error( - &mut self, - mut errors: Vec<(Import<'_>, UnresolvedImportError)>, - glob_error: bool, - ) { - errors.retain(|(_import, err)| match err.module { - // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. - Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, - // If we've encountered something like `use _;`, we've already emitted an error stating - // that `_` is not a valid identifier, so we ignore that resolve error. - _ => err.segment.map(|s| s.name) != Some(kw::Underscore), - }); - if errors.is_empty() { - self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); - return; - } +#[derive(Diagnostic)] +#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] +pub(crate) struct MethodNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) method: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); - - let paths = errors - .iter() - .map(|(import, err)| { - let path = import_path_to_string( - &import.module_path.iter().map(|seg| seg.ident).collect::>(), - &import.kind, - err.span, - ); - format!("`{path}`") - }) - .collect::>(); - let default_message = - format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); - - // Process `import` use of the `#[diagnostic::on_unknown]` attribute. - // - // We don't need to check feature gates here; that happens on initialization of the - // `on_unknown_attr` fields. - let (mut message, label, mut notes) = - if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) { - let this = errors - .iter() - .map(|(_import, err)| { - // Is this unwrap_or reachable? - err.segment.map(|s| s.name).unwrap_or(kw::Underscore) - }) - .join(", "); - - let args = FormatArgs { unresolved: this.clone(), this, .. }; - - let CustomDiagnostic { message, label, notes, parent_label: _dead } = - directive.eval(None, &args); - - (message, label, notes) - } else { - (None, None, Vec::new()) - }; - - // `module` use of the `#[diagnostic::on_unknown]` attribute. - // We assume that someone who put the attribute on the import has more information than - // the person who put it on the module, so we choose to prioritize the import attribute. - let mut mod_diagnostics: Vec = errors - .iter() - .map(|(import, import_error)| { - if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() - && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind - { - let Some(directive) = self.on_unknown_data(def_id) else { - return CustomDiagnostic::default(); - }; - - let this = if let Some(name) = name { - name.to_string() - } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { - crate_name.to_string() - } else { - "".to_string() - }; - let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); - let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; - - directive.eval(None, &args) - } else { - CustomDiagnostic::default() - } - }) - .collect(); - - // If there is no import attribute with a message, - // but all mod messages are the same, use that. - let mod_message = - mod_diagnostics.iter_mut().flat_map(|d| d.message.take()).all_equal_value(); - if message.is_none() - && let Ok(mod_msg) = mod_message - { - message = Some(mod_msg); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated function with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedFnWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - let mut diag = if let Some(message) = message { - struct_span_code_err!(self.dcx(), span, E0432, "{message}").with_note(default_message) - } else { - struct_span_code_err!(self.dcx(), span, E0432, "{default_message}") - }; - - for mod_diag in mod_diagnostics.iter_mut() { - for mod_note in mod_diag.notes.drain(..) { - if !notes.contains(&mod_note) { - notes.push(mod_note); - } - } - } +#[derive(Diagnostic)] +#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] +pub(crate) struct TypeNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) type_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - if !notes.is_empty() { - for note in notes { - diag.note(note); - } - } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = - errors.iter().last() - { - diag.note(note.clone()); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated type with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedTypeWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - /// Upper limit on the number of `span_label` messages. - const MAX_LABEL_COUNT: usize = 10; - let mod_labels = mod_diagnostics.into_iter().map(|cd| cd.label); - - for ((import, err), mod_label) in errors.into_iter().zip(mod_labels).take(MAX_LABEL_COUNT) { - let label_span = match err.segment { - Some(segment) => segment.span, - None => err.span, - }; - if let Some(label) = &label { - diag.span_label(label_span, label.clone()); - } else if let Some(label) = mod_label { - diag.span_label(label_span, label); - } else if let Some(label) = &err.label { - diag.span_label(label_span, label.clone()); - } +#[derive(Diagnostic)] +#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] +pub(crate) struct ConstNotMemberOfTrait { + #[primary_span] + #[label("not a member of trait `{$trait_}`")] + pub(crate) span: Span, + pub(crate) const_: Ident, + pub(crate) trait_: String, + #[subdiagnostic] + pub(crate) sub: Option, +} - if let Some((suggestions, msg, applicability)) = err.suggestion { - if suggestions.is_empty() { - diag.help(msg); - continue; - } - diag.multipart_suggestion(msg, suggestions, applicability); - } +#[derive(Subdiagnostic)] +#[suggestion( + "there is an associated constant with a similar name", + code = "{candidate}", + applicability = "maybe-incorrect" +)] +pub(crate) struct AssociatedConstWithSimilarNameExists { + #[primary_span] + pub(crate) span: Span, + pub(crate) candidate: Symbol, +} - if let Some(candidates) = &err.candidates { - match &import.kind { - ImportKind::Single { nested: false, source, target, .. } => import_candidates( - self.tcx, - &mut diag, - Some(err.span), - candidates, - DiagMode::Import { append: false, unresolved_import: true }, - (source != target) - .then(|| format!(" as {target}")) - .as_deref() - .unwrap_or(""), - ), - ImportKind::Single { nested: true, source, target, .. } => { - import_candidates( - self.tcx, - &mut diag, - None, - candidates, - DiagMode::Normal, - (source != target) - .then(|| format!(" as {target}")) - .as_deref() - .unwrap_or(""), - ); - } - _ => {} - } - } +#[derive(Diagnostic)] +#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] +pub(crate) struct VariableBoundWithDifferentMode { + #[primary_span] + #[label("bound in different ways")] + pub(crate) span: Span, + #[label("first binding")] + pub(crate) first_binding_span: Span, + pub(crate) variable_name: Ident, +} - if matches!(import.kind, ImportKind::Single { .. }) - && let Some(segment) = err.segment - && let Some(module) = err.module - { - self.find_cfg_stripped(&mut diag, &segment.name, module) - } - } +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] +pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { + #[primary_span] + #[label("used as parameter more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} - let guar = diag.emit(); - if glob_error { - self.glob_error = Some(guar); - } - } +#[derive(Diagnostic)] +#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] +pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { + #[primary_span] + #[label("used in a pattern more than once")] + pub(crate) span: Span, + pub(crate) identifier: Ident, +} - pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { - self.tcx.dcx() - } +#[derive(Diagnostic)] +#[diag("use of undeclared label `{$name}`", code = E0426)] +pub(crate) struct UndeclaredLabel { + #[primary_span] + #[label("undeclared label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) sub_reachable: Option, + #[subdiagnostic] + pub(crate) sub_reachable_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_unreachable: Option, +} - pub(crate) fn report_errors(&mut self, krate: &Crate) { - self.report_delayed_vis_resolution_errors(); - self.report_with_use_injections(krate); - - for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { - self.lint_buffer.buffer_lint( - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, - CRATE_NODE_ID, - span_use, - diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths { - definition: span_def, - }, - ); - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); + +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct TryUsingSimilarlyNamedLabel { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} - for ambiguity_error in &self.ambiguity_errors { - let mut diag = self.ambiguity_diagnostic(ambiguity_error); - - if let Some(ambiguity_warning) = ambiguity_error.warning { - let node_id = match ambiguity_error.b1.0.kind { - DeclKind::Import { import, .. } => import.root_id, - DeclKind::Def(_) => CRATE_NODE_ID, - }; - - let lint = match ambiguity_warning { - _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, - AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, - AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, - }; - - self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); - } else { - diag.is_error = true; - self.dcx().emit_err(diag); - } - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is unreachable")] +pub(crate) struct UnreachableLabelWithSimilarNameExists { + #[primary_span] + pub(crate) ident_span: Span, +} - let mut reported_spans = FxHashSet::default(); - for error in mem::take(&mut self.privacy_errors) { - if reported_spans.insert(error.dedup_span) { - self.report_privacy_error(&error); - } - } - } +#[derive(Diagnostic)] +#[diag("can't capture dynamic environment in a fn item", code = E0434)] +#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] +pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { + #[primary_span] + pub(crate) span: Span, +} - fn report_delayed_vis_resolution_errors(&mut self) { - for DelayedVisResolutionError { vis, parent_scope, error } in - mem::take(&mut self.delayed_vis_resolution_errors) - { - match self.try_resolve_visibility(&parent_scope, &vis, true) { - Ok(_) => self.report_vis_error(error), - Err(error) => self.report_vis_error(error), - }; - } - } +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant", code = E0435)] +pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) with: Option>, + #[subdiagnostic] + pub(crate) with_label: Option, + #[subdiagnostic] + pub(crate) without: Option>, +} - fn report_with_use_injections(&mut self, krate: &Crate) { - for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in - mem::take(&mut self.use_injections) - { - let (span, found_use) = if node_id != DUMMY_NODE_ID { - UsePlacementFinder::check(krate, node_id) - } else { - (None, FoundUse::No) - }; - - if !candidates.is_empty() { - show_candidates( - self.tcx, - &mut err, - span, - &candidates, - if instead { Instead::Yes } else { Instead::No }, - found_use, - DiagMode::Normal, - path, - "", - ); - err.emit(); - } else if let Some((span, msg, sugg, appl)) = suggestion { - err.span_suggestion_verbose(span, msg, sugg, appl); - err.emit(); - } else if let [segment] = path.as_slice() - && is_call - { - err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); - } else { - err.emit(); - } - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider using `{$suggestion}` instead of `{$current}`", + style = "verbose", + applicability = "has-placeholders" +)] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { + // #[primary_span] + #[suggestion_part(code = "{suggestion} ")] + pub(crate) span: Span, + pub(crate) suggestion: &'a str, + #[suggestion_part(code = ": /* Type */")] + pub(crate) type_span: Option, + pub(crate) current: &'a str, +} - pub(crate) fn report_conflict( - &mut self, - ident: IdentKey, - ns: Namespace, - old_binding: Decl<'ra>, - new_binding: Decl<'ra>, - ) { - // Error on the second of two conflicting names - if old_binding.span.lo() > new_binding.span.lo() { - return self.report_conflict(ident, ns, new_binding, old_binding); - } +#[derive(Subdiagnostic)] +#[label("non-constant value")] +pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { + #[primary_span] + pub(crate) span: Span, +} - let container = match old_binding.parent_module.unwrap().expect_local().kind { - // Avoid using TyCtxt::def_kind_descr in the resolver, because it - // indirectly *calls* the resolver, and would cause a query cycle. - ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), - ModuleKind::Block => "block", - }; +#[derive(Subdiagnostic)] +#[label("this would need to be a `{$suggestion}`")] +pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { + #[primary_span] + pub(crate) ident_span: Span, + pub(crate) suggestion: &'a str, +} - let (name, span) = - (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); +#[derive(Diagnostic)] +#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] +pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { + #[primary_span] + #[label("cannot be named the same as {$article} {$shadowed_binding}")] + pub(crate) span: Span, + pub(crate) shadowing_binding: PatternSource, + pub(crate) shadowed_binding: Res, + pub(crate) article: &'a str, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] + pub(crate) shadowed_binding_span: Span, + pub(crate) participle: &'a str, + pub(crate) name: Symbol, +} - if self.name_already_seen.get(&name) == Some(&span) { - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "try specify the pattern arguments", + code = "{name}(..)", + applicability = "unspecified" +)] +pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} - let old_kind = match (ns, old_binding.res()) { - (ValueNS, _) => "value", - (MacroNS, _) => "macro", - (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", - (TypeNS, Res::Def(DefKind::Mod, _)) => "module", - (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", - (TypeNS, _) => "type", - }; - - let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { - (true, true) => E0259, - (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { - true => E0254, - false => E0260, - }, - _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { - (false, false) => E0428, - (true, true) => E0252, - _ => E0255, - }, - }; - - let label = match new_binding.is_import_user_facing() { - true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name }, - false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name }, - }; - - let old_binding_label = - (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { - let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); - match old_binding.is_import_user_facing() { - true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import { - span, - old_kind, - name, - }, - false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition { - span, - old_kind, - name, - }, - } - }); - - let mut err = self - .dcx() - .create_err(diagnostics::NameDefinedMultipleTime { - span, - name, - descr: ns.descr(), - container, - label, - old_binding_label, - }) - .with_code(code); - - // See https://github.com/rust-lang/rust/issues/32354 - use DeclKind::Import; - let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { - !binding.span.is_dummy() - && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) - }; - let import = match (&new_binding.kind, &old_binding.kind) { - // If there are two imports where one or both have attributes then prefer removing the - // import without attributes. - (Import { import: new, .. }, Import { import: old, .. }) - if { - (new.has_attributes || old.has_attributes) - && can_suggest(old_binding, *old) - && can_suggest(new_binding, *new) - } => - { - if old.has_attributes { - Some((*new, new_binding.span, true)) - } else { - Some((*old, old_binding.span, true)) - } - } - // Otherwise prioritize the new binding. - (Import { import, .. }, other) if can_suggest(new_binding, *import) => { - Some((*import, new_binding.span, other.is_import())) - } - (other, Import { import, .. }) if can_suggest(old_binding, *import) => { - Some((*import, old_binding.span, other.is_import())) - } - _ => None, - }; - - // Check if the target of the use for both bindings is the same. - let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); - let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); - let from_item = - self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); - // Only suggest removing an import if both bindings are to the same def, if both spans - // aren't dummy spans. Further, if both bindings are imports, then the ident must have - // been introduced by an item. - let should_remove_import = duplicate - && !has_dummy_span - && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); - - match import { - Some((import, span, true)) if should_remove_import && import.is_nested() => { - self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); - } - Some((import, _, true)) if should_remove_import && !import.is_glob() => { - // Simple case - remove the entire import. Due to the above match arm, this can - // only be a single use so just remove it entirely. - err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } - Some((import, span, _)) => { - self.add_suggestion_for_rename_of_use(&mut err, name, import, span); - } - _ => {} - } +#[derive(Diagnostic)] +#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] +pub(crate) struct ForwardDeclaredGenericParam { + #[primary_span] + #[label("cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} - err.emit(); - self.name_already_seen.insert(name, span); - } +#[derive(Diagnostic)] +#[diag("const parameter types cannot reference parameters before they are declared")] +pub(crate) struct ForwardDeclaredGenericInConstParamTy { + #[primary_span] + #[label("const parameter type cannot reference `{$param}` before it is declared")] + pub(crate) span: Span, + pub(crate) param: Symbol, +} - /// This function adds a suggestion to change the binding name of a new import that conflicts - /// with an existing import. - /// - /// ```text,ignore (diagnostic) - /// help: you can use `as` to change the binding name of the import - /// | - /// LL | use foo::bar as other_bar; - /// | ^^^^^^^^^^^^^^^^^^^^^ - /// ``` - fn add_suggestion_for_rename_of_use( - &self, - err: &mut Diag<'_>, - name: Symbol, - import: Import<'_>, - binding_span: Span, - ) { - let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { - format!("Other{name}") - } else { - format!("other_{name}") - }; - - let mut suggestion = None; - let mut span = binding_span; - match import.kind { - ImportKind::Single { source, .. } => { - if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) - && pos as usize <= snippet.len() - { - span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( - binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), - ); - suggestion = Some(format!(" as {suggested_name}")); - } - } - ImportKind::ExternCrate { source, target, .. } => { - suggestion = Some(format!( - "extern crate {} as {};", - source.unwrap_or(target.name), - suggested_name, - )) - } - _ => unreachable!(), - } +#[derive(Diagnostic)] +#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] +pub(crate) struct ParamInTyOfConstParam { + #[primary_span] + #[label("the type must not depend on the parameter `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, +} - if let Some(suggestion) = suggestion { - err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); - } else { - err.subdiagnostic(ChangeImportBinding { span }); - } - } +#[derive(Diagnostic)] +#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] +pub(crate) struct SelfInGenericParamDefault { + #[primary_span] + pub(crate) span: Span, +} - /// This function adds a suggestion to remove an unnecessary binding from an import that is - /// nested. In the following example, this function will be invoked to remove the `a` binding - /// in the second use statement: - /// - /// ```ignore (diagnostic) - /// use issue_52891::a; - /// use issue_52891::{d, a, e}; - /// ``` - /// - /// The following suggestion will be added: - /// - /// ```ignore (diagnostic) - /// use issue_52891::{d, a, e}; - /// ^-- help: remove unnecessary import - /// ``` - /// - /// If the nested use contains only one import then the suggestion will remove the entire - /// line. - /// - /// It is expected that the provided import is nested - this isn't checked by the - /// function. If this invariant is not upheld, this function's behaviour will be unexpected - /// as characters expected by span manipulations won't be present. - fn add_suggestion_for_duplicate_nested_use( - &self, - err: &mut Diag<'_>, - import: Import<'_>, - binding_span: Span, - ) { - assert!(import.is_nested()); - - // Two examples will be used to illustrate the span manipulations we're doing: - // - // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, a, e};`. - // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is - // `a` and `import.use_span` is `issue_52891::{d, e, a};`. - - let (found_closing_brace, span) = - find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); - - // If there was a closing brace then identify the span to remove any trailing commas from - // previous imports. - if found_closing_brace { - if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { - err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span }); - } else { - // Remove the entire line if we cannot extend the span back, this indicates an - // `issue_52891::{self}` case. - err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { - span: import.use_span_with_attributes, - }); - } +#[derive(Diagnostic)] +#[diag("cannot use `Self` in const parameter type")] +pub(crate) struct SelfInConstGenericTy { + #[primary_span] + pub(crate) span: Span, +} - return; - } +#[derive(Diagnostic)] +#[diag( + "{$is_gca -> + [true] generic parameters in const blocks are not allowed; use a named `const` item instead + *[false] generic parameters may not be used in const operations +}" +)] +pub(crate) struct ParamInNonTrivialAnonConst { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInNonTrivialAnonConst, + #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] + pub(crate) help: bool, + pub(crate) is_gca: bool, + #[help( + "consider factoring the expression into a `type const` item and use it as the const argument instead" + )] + pub(crate) help_gca: bool, +} - err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span }); - } +#[derive(Debug)] +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInNonTrivialAnonConst { + #[note("type parameters may not be used in const expressions")] + Type, + #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] + Const { name: Symbol }, + #[note("lifetime parameters may not be used in const expressions")] + Lifetime, +} - pub(crate) fn lint_if_path_starts_with_module( - &mut self, - finalize: Finalize, - path: &[Segment], - second_binding: Option>, - ) { - let Finalize { node_id, root_span, .. } = finalize; - - let first_name = match path.get(0) { - // In the 2018 edition this lint is a hard error, so nothing to do - Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { - seg.ident.name - } - _ => return, - }; +#[derive(Diagnostic)] +#[diag("use of unreachable label `{$name}`", code = E0767)] +#[note("labels are unreachable through functions, closures, async blocks and modules")] +pub(crate) struct UnreachableLabel { + #[primary_span] + #[label("unreachable label `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[label("unreachable label defined here")] + pub(crate) definition_span: Span, + #[subdiagnostic] + pub(crate) sub_suggestion: Option, + #[subdiagnostic] + pub(crate) sub_suggestion_label: Option, + #[subdiagnostic] + pub(crate) sub_unreachable_label: Option, +} - // We're only interested in `use` paths which should start with - // `{{root}}` currently. - if first_name != kw::PathRoot { - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "try using similarly named label", + code = "{ident_name}", + applicability = "maybe-incorrect" +)] +pub(crate) struct UnreachableLabelSubSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident_name: Symbol, +} - match path.get(1) { - // If this import looks like `crate::...` it's already good - Some(Segment { ident, .. }) if ident.name == kw::Crate => return, - // Otherwise go below to see if it's an extern crate - Some(_) => {} - // If the path has length one (and it's `PathRoot` most likely) - // then we don't know whether we're gonna be importing a crate or an - // item in our crate. Defer this lint to elsewhere - None => return, - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name is reachable")] +pub(crate) struct UnreachableLabelSubLabel { + #[primary_span] + pub(crate) ident_span: Span, +} - // If the first element of our path was actually resolved to an - // `ExternCrate` (also used for `crate::...`) then no need to issue a - // warning, this looks all good! - if let Some(binding) = second_binding - && let DeclKind::Import { import, .. } = binding.kind - // Careful: we still want to rewrite paths from renamed extern crates. - && let ImportKind::ExternCrate { source: None, .. } = import.kind - { - return; - } +#[derive(Subdiagnostic)] +#[label("a label with a similar name exists but is also unreachable")] +pub(crate) struct UnreachableLabelSubLabelUnreachable { + #[primary_span] + pub(crate) ident_span: Span, +} - self.lint_buffer.dyn_buffer_lint_any( - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - node_id, - root_span, - move |dcx, level, sess| { - let (replacement, applicability) = match sess - .downcast_ref::() - .expect("expected a `Session`") - .source_map() - .span_to_snippet(root_span) - { - Ok(ref s) => { - // FIXME(Manishearth) ideally the emitting code - // can tell us whether or not this is global - let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; - - (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) - } - Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), - }; - diagnostics::AbsPathWithModule { - sugg: diagnostics::AbsPathWithModuleSugg { - span: root_span, - applicability, - replacement, - }, - } - .into_diag(dcx, level) - }, - ); - } +#[derive(Diagnostic)] +#[diag("invalid `sym` operand")] +#[help("`sym` operands must refer to either a function or a static")] +pub(crate) struct InvalidAsmSym { + #[primary_span] + #[label("is a local variable")] + pub(crate) span: Span, +} - pub(crate) fn add_module_candidates( - &self, - module: Module<'ra>, - names: &mut Vec, - filter_fn: &impl Fn(Res) -> bool, - ctxt: Option, - ) { - module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { - let res = binding.res(); - if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { - names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); - } - }); - } +#[derive(Diagnostic)] +#[diag("attempt to use a non-constant value in a constant")] +pub(crate) struct LowercaseSelf { + #[primary_span] + #[suggestion( + "try using `Self`", + code = "Self", + applicability = "maybe-incorrect", + style = "short" + )] + pub(crate) span: Span, +} - /// Combines an error with provided span and emits it. - /// - /// This takes the error provided, combines it with the span and any additional spans inside the - /// error and emits it. - pub(crate) fn report_error( - &mut self, - span: Span, - resolution_error: ResolutionError<'ra>, - ) -> ErrorGuaranteed { - self.into_struct_error(span, resolution_error).emit() - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("never patterns cannot contain variable bindings")] +pub(crate) struct BindingInNeverPattern { + #[primary_span] + #[suggestion( + "use a wildcard `_` instead", + code = "_", + applicability = "machine-applicable", + style = "short" + )] + pub(crate) span: Span, +} - pub(crate) fn into_struct_error( - &mut self, - span: Span, - resolution_error: ResolutionError<'ra>, - ) -> Diag<'_> { - match resolution_error { - ResolutionError::GenericParamsFromOuterItem { - outer_res, - has_generic_params, - def_kind, - inner_item, - current_self_ty, - } => { - use diagnostics::GenericParamsFromOuterItemLabel as Label; - let static_or_const = match def_kind { - DefKind::Static { .. } => { - Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static) - } - DefKind::Const { .. } => { - Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const) - } - _ => None, - }; - let is_self = - matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); - let mut err = diagnostics::GenericParamsFromOuterItem { - span, - label: None, - refer_to_type_directly: None, - use_let: None, - sugg: None, - static_or_const, - is_self, - item: inner_item.as_ref().map(|(label_span, _, kind)| { - diagnostics::GenericParamsFromOuterItemInnerItem { - span: *label_span, - descr: kind.descr().to_string(), - is_self, - } - }), - }; - - let sm = self.tcx.sess.source_map(); - // Note: do not early return for missing def_id here, - // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. - let def_id = match outer_res { - Res::SelfTyParam { .. } => { - err.label = Some(Label::SelfTyParam(span)); - None - } - Res::SelfTyAlias { alias_to: def_id, .. } => { - err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( - sm, - self.def_span(def_id), - ))); - err.refer_to_type_directly = current_self_ty - .map(|snippet| diagnostics::UseTypeDirectly { span, snippet }); - None - } - Res::Def(DefKind::TyParam, def_id) => { - err.label = Some(Label::TyParam(self.def_span(def_id))); - Some(def_id) - } - Res::Def(DefKind::ConstParam, def_id) => { - err.label = Some(Label::ConstParam(self.def_span(def_id))); - Some(def_id) - } - _ => { - bug!( - "GenericParamsFromOuterItem should only be used with \ - Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ - DefKind::ConstParam" - ); - } - }; - - if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { - err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet { - span: sm.span_until_whitespace(*item_span), - }); - } - - if let Some(def_id) = def_id - && let HasGenericParams::Yes(span) = has_generic_params - && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) - { - let name = self.tcx.item_name(def_id); - let (span, snippet) = if span.is_empty() { - let snippet = format!("<{name}>"); - (span, snippet) - } else { - let span = sm.span_through_char(span, '<').shrink_to_hi(); - let snippet = format!("{name}, "); - (span, snippet) - }; - err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet }); - } - - self.dcx().create_err(err) - } - ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { - self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList { - span, - first_use_span, - name, - }) - } - ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { - self.dcx().create_err(diagnostics::MethodNotMemberOfTrait { - span, - method, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists { - span: method.span, - candidate: c, - }), - }) - } - ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { - self.dcx().create_err(diagnostics::TypeNotMemberOfTrait { - span, - type_, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists { - span: type_.span, - candidate: c, - }), - }) - } - ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { - self.dcx().create_err(diagnostics::ConstNotMemberOfTrait { - span, - const_, - trait_, - sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists { - span: const_.span, - candidate: c, - }), - }) - } - ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { - let BindingError { name, target, origin, could_be_path } = binding_error; - - let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); - target_sp.sort(); - target_sp.dedup(); - let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); - origin_sp.sort(); - origin_sp.dedup(); - - let msp = MultiSpan::from_spans(target_sp.clone()); - let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns { - multispan: msp, - name, - }); - for sp in target_sp { - err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name }); - } - for sp in &origin_sp { - err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp }); - } - let mut suggested_typo = false; - if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) - && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) - { - // The check above is so that when we encounter `match foo { (a | b) => {} }`, - // we don't suggest `(a | a) => {}`, which would never be what the user wants. - let mut target_visitor = BindingVisitor::default(); - for pat in &target { - target_visitor.visit_pat(pat); - } - target_visitor.identifiers.sort(); - target_visitor.identifiers.dedup(); - let mut origin_visitor = BindingVisitor::default(); - for (_, pat) in &origin { - origin_visitor.visit_pat(pat); - } - origin_visitor.identifiers.sort(); - origin_visitor.identifiers.dedup(); - // Find if the binding could have been a typo - if let Some(typo) = - find_best_match_for_name(&target_visitor.identifiers, name.name, None) - && !origin_visitor.identifiers.contains(&typo) - { - err.subdiagnostic(diagnostics::PatternBindingTypo { - spans: origin_sp, - typo, - }); - suggested_typo = true; - } - } - if could_be_path { - let import_suggestions = self.lookup_import_candidates( - name, - Namespace::ValueNS, - &parent_scope, - &|res: Res| { - matches!( - res, - Res::Def( - DefKind::Ctor(CtorOf::Variant, CtorKind::Const) - | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) - | DefKind::Const { .. } - | DefKind::AssocConst { .. }, - _, - ) - ) - }, - ); - - if import_suggestions.is_empty() && !suggested_typo { - let kind_matches: [fn(DefKind) -> bool; 4] = [ - |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), - |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), - |kind| matches!(kind, DefKind::Const { .. }), - |kind| matches!(kind, DefKind::AssocConst { .. }), - ]; - let mut local_names = vec![]; - self.add_module_candidates( - parent_scope.module, - &mut local_names, - &|res| matches!(res, Res::Def(_, _)), - None, - ); - let local_names: FxHashSet<_> = local_names - .into_iter() - .filter_map(|s| match s.res { - Res::Def(_, def_id) => Some(def_id), - _ => None, - }) - .collect(); - - let mut local_suggestions = vec![]; - let mut suggestions = vec![]; - for matches_kind in kind_matches { - if let Some(suggestion) = self.early_lookup_typo_candidate( - ScopeSet::All(Namespace::ValueNS), - &parent_scope, - name, - &|res: Res| match res { - Res::Def(k, _) => matches_kind(k), - _ => false, - }, - ) && let Res::Def(kind, mut def_id) = suggestion.res - { - if let DefKind::Ctor(_, _) = kind { - def_id = self.tcx.parent(def_id); - } - let kind = kind.descr(def_id); - if local_names.contains(&def_id) { - // The item is available in the current scope. Very likely to - // be a typo. Don't use the full path. - local_suggestions.push(( - suggestion.candidate, - suggestion.candidate.to_string(), - kind, - )); - } else { - suggestions.push(( - suggestion.candidate, - self.def_path_str(def_id), - kind, - )); - } - } - } - let suggestions = if !local_suggestions.is_empty() { - // There is at least one item available in the current scope that is a - // likely typo. We only show those. - local_suggestions - } else { - suggestions - }; - for (name, sugg, kind) in suggestions { - err.span_suggestion_verbose( - span, - format!( - "you might have meant to use the similarly named {kind} `{name}`", - ), - sugg, - Applicability::MaybeIncorrect, - ); - suggested_typo = true; - } - } - if import_suggestions.is_empty() && !suggested_typo { - let help_msg = format!( - "if you meant to match on a unit struct, unit variant or a `const` \ - item, consider making the path in the pattern qualified: \ - `path::to::ModOrType::{name}`", - ); - err.span_help(span, help_msg); - } - show_candidates( - self.tcx, - &mut err, - Some(span), - &import_suggestions, - Instead::No, - FoundUse::Yes, - DiagMode::Pattern, - vec![], - "", - ); - } - err - } - ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { - self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode { - span, - first_binding_span, - variable_name, - }) - } - ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { - self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList { - span, - identifier, - }) - } - ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { - self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern { - span, - identifier, - }) - } - ResolutionError::UndeclaredLabel { name, suggestion } => { - let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion - { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(diagnostics::LabelWithSimilarNameReachable(ident.span)), - Some(diagnostics::TryUsingSimilarlyNamedLabel { - span, - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(diagnostics::UnreachableLabelWithSimilarNameExists { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(diagnostics::UndeclaredLabel { - span, - name, - sub_reachable, - sub_reachable_suggestion, - sub_unreachable, - }) - } - ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { - let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); - err.span_label(span, label); - - if let Some((suggestions, msg, applicability)) = suggestion { - if suggestions.is_empty() { - err.help(msg); - return err; - } - err.multipart_suggestion(msg, suggestions, applicability); - } - - let module = match module { - Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, - _ => CRATE_DEF_ID.to_def_id(), - }; - self.find_cfg_stripped(&mut err, &segment, module); - - err - } - ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { - self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span }) - } - ResolutionError::AttemptToUseNonConstantValueInConstant { - ident, - suggestion, - current, - type_span, - } => { - // let foo =... - // ^^^ given this Span - // ------- get this Span to have an applicable suggestion - - // edit: - // only do this if the const and usage of the non-constant value are on the same line - // the further the two are apart, the higher the chance of the suggestion being wrong - - let sp = self - .tcx - .sess - .source_map() - .span_extend_to_prev_str(ident.span, current, true, false); - - let (with, with_label, without) = match sp { - Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { - let sp = sp - .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) - .until(ident.span); - - // Only suggest replacing the binding keyword if this is a simple - // binding. - // - // Note: this approach still incorrectly suggests for irrefutable - // patterns like `if let x = 1 { const { x } }`, since the text - // between `let` and the identifier is just whitespace. - // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs - let is_simple_binding = - self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { - let after_keyword = snippet[current.len()..].trim(); - after_keyword.is_empty() || after_keyword == "mut" - }); - - if is_simple_binding { - ( - Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion { - span: sp, - suggestion, - current, - type_span, - }), - Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } else { - ( - None, - Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), - None, - ) - } - } - _ => ( - None, - None, - Some( - diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion { - ident_span: ident.span, - suggestion, - }, - ), - ), - }; - - self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant { - span, - with, - with_label, - without, - }) - } - ResolutionError::BindingShadowsSomethingUnacceptable { - shadowing_binding, - name, - participle, - article, - shadowed_binding, - shadowed_binding_span, - } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable { - span, - shadowing_binding, - shadowed_binding, - article, - sub_suggestion: match (shadowing_binding, shadowed_binding) { - ( - PatternSource::Match, - Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), - ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion { - span, - name, - }), - _ => None, - }, - shadowed_binding_span, - participle, - name, - }), - ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span }) - } - ForwardGenericParamBanReason::ConstParamTy => self - .dcx() - .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }), - }, - ResolutionError::ParamInTyOfConstParam { name } => { - self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) - } - ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { - self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { - span, - name, - param_kind: is_type, - help: self.tcx.sess.is_nightly_build(), - is_gca, - help_gca: is_gca, - }) - } - ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { - self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { - span, - name, - param_kind: is_type, - }) - } - ResolutionError::ForwardDeclaredSelf(reason) => match reason { - ForwardGenericParamBanReason::Default => { - self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) - } - ForwardGenericParamBanReason::ConstParamTy => { - self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) - } - }, - ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { - let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = - match suggestion { - // A reachable label with a similar name exists. - Some((ident, true)) => ( - ( - Some(diagnostics::UnreachableLabelSubLabel { - ident_span: ident.span, - }), - Some(diagnostics::UnreachableLabelSubSuggestion { - span, - // intentionally taking 'ident.name' instead of 'ident' itself, as this - // could be used in suggestion context - ident_name: ident.name, - }), - ), - None, - ), - // An unreachable label with a similar name exists. - Some((ident, false)) => ( - (None, None), - Some(diagnostics::UnreachableLabelSubLabelUnreachable { - ident_span: ident.span, - }), - ), - // No similarly-named labels exist. - None => ((None, None), None), - }; - self.dcx().create_err(diagnostics::UnreachableLabel { - span, - name, - definition_span, - sub_suggestion, - sub_suggestion_label, - sub_unreachable_label, - }) - } - ResolutionError::TraitImplMismatch { - name, - kind, - code, - trait_item_span, - trait_path, - } => self - .dcx() - .create_err(diagnostics::TraitImplMismatch { - span, - name, - kind, - trait_path, - trait_item_span, - }) - .with_code(code), - ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { - self.dcx().create_err(diagnostics::TraitImplDuplicate { - span, - name, - trait_item_span, - old_span, - }) - } - ResolutionError::InvalidAsmSym => { - self.dcx().create_err(diagnostics::InvalidAsmSym { span }) - } - ResolutionError::LowercaseSelf => { - self.dcx().create_err(diagnostics::LowercaseSelf { span }) - } - ResolutionError::BindingInNeverPattern => { - self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) - } - } - } +#[derive(Diagnostic)] +#[diag("duplicate definitions with name `{$name}`:", code = E0201)] +pub(crate) struct TraitImplDuplicate { + #[primary_span] + #[label("duplicate definition")] + pub(crate) span: Span, + #[label("previous definition here")] + pub(crate) old_span: Span, + #[label("item in trait")] + pub(crate) trait_item_span: Span, + pub(crate) name: Ident, +} - pub(crate) fn report_vis_error( - &mut self, - vis_resolution_error: VisResolutionError, - ) -> ErrorGuaranteed { - match vis_resolution_error { - VisResolutionError::Relative2018(span, path) => { - self.dcx().create_err(diagnostics::Relative2018 { - span, - path_span: path.span, - // intentionally converting to String, as the text would also be used as - // in suggestion context - path_str: pprust::path_to_string(&path), - }) - } - VisResolutionError::AncestorOnly(span) => { - self.dcx().create_err(diagnostics::AncestorOnly(span)) - } - VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self - .into_struct_error( - span, - ResolutionError::FailedToResolve { - segment, - label, - suggestion, - module: None, - message, - }, - ), - VisResolutionError::ExpectedFound(span, path_str, res) => { - self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str }) - } - VisResolutionError::Indeterminate(span) => { - self.dcx().create_err(diagnostics::Indeterminate(span)) - } - VisResolutionError::ModuleOnly(span) => { - self.dcx().create_err(diagnostics::ModuleOnly(span)) - } - } - .emit() - } +#[derive(Diagnostic)] +#[diag("relative paths are not supported in visibilities in 2018 edition or later")] +pub(crate) struct Relative2018 { + #[primary_span] + pub(crate) span: Span, + #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] + pub(crate) path_span: Span, + pub(crate) path_str: String, +} - pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - path.push(def_id); - if def_id.is_top_level_module() { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - path.into_iter() - .rev() - .map(|def_id| { - self.tcx - .opt_item_name(def_id) - .map(|name| { - match ( - def_id.is_top_level_module(), - def_id.is_local(), - self.tcx.sess.edition(), - ) { - (true, true, Edition::Edition2015) => String::new(), - (true, true, _) => kw::Crate.to_string(), - (true, false, _) | (false, _, _) => name.to_string(), - } - }) - .unwrap_or_else(|| "_".to_string()) - }) - .collect::>() - .join("::") - } +#[derive(Diagnostic)] +#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] +pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] +pub(crate) struct ExpectedModuleFound { + #[primary_span] + #[label("not a module")] + pub(crate) span: Span, + pub(crate) res: Res, + pub(crate) path_str: String, +} - pub(crate) fn add_scope_set_candidates( - &mut self, - suggestions: &mut Vec, - scope_set: ScopeSet<'ra>, - ps: &ParentScope<'ra>, - sp: Span, - filter_fn: &impl Fn(Res) -> bool, - ) { - let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); - self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { - match scope { - Scope::DeriveHelpers(expn_id) => { - let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); - if filter_fn(res) { - suggestions.extend( - this.helper_attrs.get(&expn_id).into_iter().flatten().map( - |&(ident, orig_ident_span, _)| { - TypoSuggestion::new(ident.name, orig_ident_span, res) - }, - ), - ); - } - } - Scope::DeriveHelpersCompat => { - // Never recommend deprecated helper attributes. - } - Scope::MacroRules(macro_rules_scope) => { - if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { - let res = macro_rules_def.decl.res(); - if filter_fn(res) { - suggestions.push(TypoSuggestion::new( - macro_rules_def.ident.name, - macro_rules_def.orig_ident_span, - res, - )) - } - } - } - Scope::ModuleNonGlobs(module, _) => { - this.add_module_candidates(module, suggestions, filter_fn, None); - } - Scope::ModuleGlobs(..) => { - // Already handled in `ModuleNonGlobs`. - } - Scope::MacroUsePrelude => { - suggestions.extend(this.macro_use_prelude.iter().filter_map( - |(name, binding)| { - let res = binding.res(); - filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) - }, - )); - } - Scope::BuiltinAttrs => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); - if filter_fn(res) { - suggestions.extend( - BUILTIN_ATTRIBUTES - .iter() - // These trace attributes are compiler-generated and have - // deliberately invalid names. - .filter(|attr| { - !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) - }) - .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), - ); - } - } - Scope::ExternPreludeItems => { - // Add idents from both item and flag scopes. - suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { - let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); - filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) - })); - } - Scope::ExternPreludeFlags => {} - Scope::ToolPrelude => { - let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); - suggestions.extend( - this.registered_tools - .iter() - .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), - ); - } - Scope::StdLibPrelude => { - if let Some(prelude) = this.prelude { - let mut tmp_suggestions = Vec::new(); - this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); - suggestions.extend( - tmp_suggestions - .into_iter() - .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), - ); - } - } - Scope::BuiltinTypes => { - suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { - let res = Res::PrimTy(*prim_ty); - filter_fn(res) - .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) - })) - } - } +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the visibility", code = E0578)] +pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("trait implementation can only be restricted to ancestor modules")] +pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("cannot use a tool module through an import")] +pub(crate) struct ToolModuleImported { + #[primary_span] + pub(crate) span: Span, + #[note("the tool module imported here")] + pub(crate) import: Span, +} - ControlFlow::<()>::Continue(()) - }); - } +#[derive(Diagnostic)] +#[diag("visibility must resolve to a module")] +pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); + +#[derive(Diagnostic)] +#[diag("expected {$expected}, found {$found} `{$macro_path}`")] +pub(crate) struct MacroExpectedFound<'a> { + #[primary_span] + #[label("not {$article} {$expected}")] + pub(crate) span: Span, + pub(crate) found: &'a str, + pub(crate) article: &'static str, + pub(crate) expected: &'a str, + pub(crate) macro_path: &'a str, + #[subdiagnostic] + pub(crate) remove_surrounding_derive: Option, + #[subdiagnostic] + pub(crate) add_as_non_derive: Option>, +} - /// Lookup typo candidate in scope for a macro or import. - fn early_lookup_typo_candidate( - &mut self, - scope_set: ScopeSet<'ra>, - parent_scope: &ParentScope<'ra>, - ident: Ident, - filter_fn: &impl Fn(Res) -> bool, - ) -> Option { - let mut suggestions = Vec::new(); - self.add_scope_set_candidates( - &mut suggestions, - scope_set, - parent_scope, - ident.span, - filter_fn, - ); - - // Make sure error reporting is deterministic. - suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); - - match find_best_match_for_name( - &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), - ident.name, - None, - ) { - Some(found) if found != ident.name => { - suggestions.into_iter().find(|suggestion| suggestion.candidate == found) - } - _ => None, - } - } +#[derive(Subdiagnostic)] +#[help("remove from the surrounding `derive()`")] +pub(crate) struct RemoveSurroundingDerive { + #[primary_span] + pub(crate) span: Span, +} - fn lookup_import_candidates_from_module( - &self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - start_module: Module<'ra>, - crate_path: ThinVec, - ident_filter_fn: IdentFilterFn, - filter_fn: FilterFn, - ) -> Vec - where - IdentFilterFn: Fn(Ident, Ident) -> bool, - FilterFn: Fn(Res) -> bool, - { - let mut candidates = Vec::new(); - let mut seen_modules = FxHashSet::default(); - let start_did = start_module.def_id(); - let mut worklist = vec![( - start_module, - ThinVec::::new(), - true, - start_did.is_local() || !self.tcx.is_doc_hidden(start_did), - true, - )]; - let mut worklist_via_import = vec![]; - - while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = - match worklist.pop() { - None => worklist_via_import.pop(), - Some(x) => Some(x), - } - { - let in_module_is_extern = !in_module.def_id().is_local(); - in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { - // Avoid non-importable candidates. - if name_binding.is_assoc_item() - && !this.tcx.features().import_trait_associated_functions() - { - return; - } - - if ident.name == kw::Underscore { - return; - } - - let child_accessible = - accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); - - // do not venture inside inaccessible items of other crates - if in_module_is_extern && !child_accessible { - return; - } - - let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); - - // There is an assumption elsewhere that paths of variants are in the enum's - // declaration and not imported. With this assumption, the variant component is - // chopped and the rest of the path is assumed to be the enum's own path. For - // errors where a variant is used as the type instead of the enum, this causes - // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. - if via_import && name_binding.is_possibly_imported_variant() { - return; - } - - // #90113: Do not count an inaccessible reexported item as a candidate. - if let DeclKind::Import { source_decl, .. } = name_binding.kind - && this.is_accessible_from(source_decl.vis(), parent_scope.module) - && !this.is_accessible_from(name_binding.vis(), parent_scope.module) - { - return; - } - - let res = name_binding.res(); - let did = match res { - Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), - _ => res.opt_def_id(), - }; - let child_doc_visible = doc_visible - && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); - - // collect results based on the filter function - // avoid suggesting anything from the same module in which we are resolving - // avoid suggesting anything with a hygienic name - if ident_filter_fn(ident.orig(orig_ident_span), lookup_ident) - && ns == namespace - && in_module != parent_scope.module - && ident.ctxt.is_root() - && filter_fn(res) - { - // create the path - let mut segms = if lookup_ident.span.at_least_rust_2018() { - // crate-local absolute paths start with `crate::` in edition 2018 - // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) - crate_path.clone() - } else { - ThinVec::new() - }; - segms.append(&mut path_segments.clone()); - - segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - let path = Path { span: name_binding.span, segments: segms, tokens: None }; - - if child_accessible - // Remove invisible match if exists - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.accessible) - { - candidates.remove(idx); - } - - let is_stable = if is_stable - && let Some(did) = did - && this.is_stable(did, path.span) - { - true - } else { - false - }; - - // Rreplace unstable suggestions if we meet a new stable one, - // and do nothing if any other situation. For example, if we - // meet `std::ops::Range` after `std::range::legacy::Range`, - // we will remove the latter and then insert the former. - if is_stable - && let Some(idx) = candidates - .iter() - .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) - { - candidates.remove(idx); - } - - if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { - // See if we're recommending TryFrom, TryInto, or FromIterator and add - // a note about editions - let note = if let Some(did) = did { - let requires_note = !did.is_local() - && find_attr!( - this.tcx, - did, - RustcDiagnosticItem( - sym::TryInto | sym::TryFrom | sym::FromIterator - ) - ); - requires_note.then(|| { - format!( - "'{}' is included in the prelude starting in Edition 2021", - path_names_to_string(&path) - ) - }) - } else { - None - }; - - candidates.push(ImportSuggestion { - did, - descr: res.descr(), - path, - accessible: child_accessible, - doc_visible: child_doc_visible, - note, - via_import, - is_stable, - is_exact_match: ident.name == lookup_ident.name, - }); - } - } - - // collect submodules to explore - if let Some(def_id) = name_binding.res().module_like_def_id() { - // form the path - let mut path_segments = path_segments.clone(); - path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); - - let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind - && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind - && import.parent_scope.expansion == parent_scope.expansion - { - true - } else { - false - }; - - let is_extern_crate_that_also_appears_in_prelude = - name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); - - if !is_extern_crate_that_also_appears_in_prelude || alias_import { - // add the module to the lookup - if seen_modules.insert(def_id) { - if via_import { &mut worklist_via_import } else { &mut worklist }.push( - ( - this.expect_module(def_id), - path_segments, - child_accessible, - child_doc_visible, - is_stable && this.is_stable(def_id, name_binding.span), - ), - ); - } - } - } - }) - } +#[derive(Subdiagnostic)] +#[help( + " + add as non-Derive macro + `#[{$macro_path}]`" +)] +pub(crate) struct AddAsNonDerive<'a> { + pub(crate) macro_path: &'a str, +} - candidates - } +#[derive(Diagnostic)] +#[diag("can't use a procedural macro from the same crate that defines it")] +pub(crate) struct ProcMacroSameCrate { + #[primary_span] + pub(crate) span: Span, + #[help("you can define integration tests in a directory named `tests`")] + pub(crate) is_test: bool, +} - fn is_stable(&self, did: DefId, span: Span) -> bool { - if did.is_local() { - return true; - } +#[derive(Diagnostic)] +#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] +pub(crate) struct ProcMacroDeriveResolutionFallback { + #[label("names from parent modules are not accessible without an explicit import")] + pub span: Span, + pub ns_descr: &'static str, + pub ident: Symbol, +} - match self.tcx.lookup_stability(did) { - Some(Stability { - level: StabilityLevel::Unstable { implied_by, .. }, feature, .. - }) => { - if span.allows_unstable(feature) { - true - } else if self.tcx.features().enabled(feature) { - true - } else if let Some(implied_by) = implied_by - && self.tcx.features().enabled(implied_by) - { - true - } else { - false - } - } - Some(_) => true, - None => false, - } - } +#[derive(Diagnostic)] +#[diag( + "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" +)] +pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { + #[note("the macro is defined here")] + pub definition: Span, +} - /// When name resolution fails, this method can be used to look up candidate - /// entities with the expected name. It allows filtering them using the - /// supplied predicate (which should be used to only accept the types of - /// definitions expected, e.g., traits). The lookup spans across all crates. - /// - /// N.B., the method does not look into imports, but this is not a problem, - /// since we report the definitions (thus, the de-aliased imports). - /// - /// The method is implemented in `lookup_import_candidates_impl`. The `_impl` method allows applying a different filter function on the ident than the exact match function used by default here. - pub(crate) fn lookup_import_candidates( - &mut self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - filter_fn: FilterFn, - ) -> Vec - where - FilterFn: Fn(Res) -> bool, - { - self.lookup_import_candidates_impl( - lookup_ident, - namespace, - parent_scope, - |ident: Ident, lookup_ident: Ident| ident.name == lookup_ident.name, - filter_fn, - ) - } +#[derive(Diagnostic)] +#[diag("`#[macro_use]` is not supported on `extern crate self`")] +pub(crate) struct MacroUseExternCrateSelf { + #[primary_span] + pub(crate) span: Span, +} - /// The actual impl of the `lookup_import_candidates function`. - pub(crate) fn lookup_import_candidates_impl( - &mut self, - lookup_ident: Ident, - namespace: Namespace, - parent_scope: &ParentScope<'ra>, - ident_filter_fn: IdentFilterFn, - filter_fn: FilterFn, - ) -> Vec - where - IdentFilterFn: Fn(Ident, Ident) -> bool, - FilterFn: Fn(Res) -> bool, - { - let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; - let mut suggestions = self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - self.graph_root.to_module(), - crate_path, - &ident_filter_fn, - &filter_fn, - ); - - if lookup_ident.span.at_least_rust_2018() { - for (ident, entry) in &self.extern_prelude { - if entry.span().from_expansion() { - // Idents are adjusted to the root context before being - // resolved in the extern prelude, so reporting this to the - // user is no help. This skips the injected - // `extern crate std` in the 2018 edition, which would - // otherwise cause duplicate suggestions. - continue; - } - let Some(crate_id) = - self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) - else { - continue; - }; - - let crate_def_id = crate_id.as_def_id(); - let crate_root = self.expect_module(crate_def_id); - - // Check if there's already an item in scope with the same name as the crate. - // If so, we have to disambiguate the potential import suggestions by making - // the paths *global* (i.e., by prefixing them with `::`). - let needs_disambiguation = - self.resolutions(parent_scope.module).borrow().iter().any( - |(key, name_resolution)| { - if key.ns == TypeNS - && key.ident == *ident - && let Some(decl) = name_resolution.borrow().best_decl() - { - match decl.res() { - // No disambiguation needed if the identically named item we - // found in scope actually refers to the crate in question. - Res::Def(_, def_id) => def_id != crate_def_id, - Res::PrimTy(_) => true, - _ => false, - } - } else { - false - } - }, - ); - let mut crate_path = ThinVec::new(); - if needs_disambiguation { - crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); - } - crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); - - suggestions.extend(self.lookup_import_candidates_from_module( - lookup_ident, - namespace, - parent_scope, - crate_root, - crate_path, - &ident_filter_fn, - &filter_fn, - )); - } - } +#[derive(Diagnostic)] +#[diag("not sure whether the path is accessible or not")] +#[note("the type may have associated items, but we are currently not checking them")] +pub(crate) struct CfgAccessibleUnsure { + #[primary_span] + pub(crate) span: Span, +} - suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); - suggestions - } +#[derive(Debug)] +#[derive(Diagnostic)] +#[diag("generic parameters may not be used in enum discriminant values")] +pub(crate) struct ParamInEnumDiscriminant { + #[primary_span] + #[label("cannot perform const operation using `{$name}`")] + pub(crate) span: Span, + pub(crate) name: Symbol, + #[subdiagnostic] + pub(crate) param_kind: ParamKindInEnumDiscriminant, +} - pub(crate) fn unresolved_macro_suggestions( - &mut self, - err: &mut Diag<'_>, - macro_kind: MacroKind, - parent_scope: &ParentScope<'ra>, - ident: Ident, - krate: &Crate, - sugg_span: Option, - ) { - // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for - // suggestions. - self.register_macros_for_all_crates(); - - let is_expected = - &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); - let suggestion = self.early_lookup_typo_candidate( - ScopeSet::Macro(macro_kind), - parent_scope, - ident, - is_expected, - ); - if !self.add_typo_suggestion(err, suggestion, ident.span) { - self.detect_derive_attribute(err, ident, parent_scope, sugg_span); - } +#[derive(Debug)] +#[derive(Subdiagnostic)] +pub(crate) enum ParamKindInEnumDiscriminant { + #[note("type parameters may not be used in enum discriminant values")] + Type, + #[note("const parameters may not be used in enum discriminant values")] + Const, + #[note("lifetime parameters may not be used in enum discriminant values")] + Lifetime, +} - let import_suggestions = - self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); - let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { - DUMMY_NODE_ID => (None, FoundUse::No), - node_id => UsePlacementFinder::check(krate, node_id), - }; - show_candidates( - self.tcx, - err, - span, - &import_suggestions, - Instead::No, - found_use, - DiagMode::Normal, - vec![], - "", - ); - - if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { - let label_span = ident.span.shrink_to_hi(); - let mut spans = MultiSpan::from_span(label_span); - spans.push_span_label(label_span, "put a macro name here"); - err.subdiagnostic(MaybeMissingMacroRulesName { spans }); - return; - } +#[derive(Subdiagnostic)] +#[label("you can use `as` to change the binding name of the import")] +pub(crate) struct ChangeImportBinding { + #[primary_span] + pub(crate) span: Span, +} - if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { - err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); - return; - } +#[derive(Subdiagnostic)] +#[suggestion( + "you can use `as` to change the binding name of the import", + code = "{suggestion}", + applicability = "maybe-incorrect" +)] +pub(crate) struct ChangeImportBindingSuggestion { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} - let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { - if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } - }); - - if let Some((def_id, unused_ident)) = unused_macro { - let scope = self.local_macro_def_scopes[&def_id]; - let parent_nearest = parent_scope.module.nearest_parent_mod(); - let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); - if !unused_macro_kinds.contains(macro_kind.into()) { - match macro_kind { - MacroKind::Bang => { - err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); - } - MacroKind::Attr => { - err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); - } - MacroKind::Derive => { - err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); - } - } - return; - } - if Some(parent_nearest) == scope.opt_def_id() { - err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); - err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); - return; - } - } +#[derive(Diagnostic)] +#[diag("imports cannot refer to {$what}")] +pub(crate) struct ImportsCannotReferTo<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) what: &'a str, +} - if ident.name == kw::Default - && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind - { - let span = self.def_span(def_id); - let source_map = self.tcx.sess.source_map(); - let head_span = source_map.guess_head_span(span); - err.subdiagnostic(ConsiderAddingADerive { - span: head_span.shrink_to_lo(), - suggestion: "#[derive(Default)]\n".to_string(), - }); - } - for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { - let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns), - parent_scope, - None, - None, - None, - ) else { - continue; - }; - - let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { - "a function-like macro".to_string() - } - Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { - format!("an attribute: `#[{ident}]`") - } - Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { - format!("a derive macro: `#[derive({ident})]`") - } - Res::Def(DefKind::Macro(kinds), _) => { - format!("{} {}", kinds.article(), kinds.descr()) - } - Res::ToolMod | Res::OpenMod(..) => { - // Don't confuse the user with tool modules or open modules. - continue; - } - Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { - "only a trait, without a derive macro".to_string() - } - res => format!( - "{} {}, not {} {}", - res.article(), - res.descr(), - macro_kind.article(), - macro_kind.descr_expected(), - ), - }; - if let crate::DeclKind::Import { import, .. } = binding.kind - && !import.span.is_dummy() - { - let note = diagnostics::IdentImporterHereButItIsDesc { - span: import.span, - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - // Silence the 'unused import' warning we might get, - // since this diagnostic already covers that import. - self.record_use(ident, binding, Used::Other); - return; - } - let note = diagnostics::IdentInScopeButItIsDesc { - imported_ident: ident, - imported_ident_desc: &desc, - }; - err.subdiagnostic(note); - return; - } +#[derive(Diagnostic)] +#[diag("cannot find {$expected} `{$ident}` in this scope")] +pub(crate) struct CannotFindIdentInThisScope<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) expected: &'a str, + pub(crate) ident: Ident, +} - if self.macro_names.contains(&IdentKey::new(ident)) { - err.subdiagnostic(AddedMacroUse); - return; - } - } +#[derive(Subdiagnostic)] +#[note("unsafe traits like `{$ident}` should be implemented explicitly")] +pub(crate) struct ExplicitUnsafeTraits { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - /// Given an attribute macro that failed to be resolved, look for `derive` macros that could - /// provide it, either as-is or with small typos. - fn detect_derive_attribute( - &self, - err: &mut Diag<'_>, - ident: Ident, - parent_scope: &ParentScope<'ra>, - sugg_span: Option, - ) { - // Find all of the `derive`s in scope and collect their corresponding declared - // attributes. - // FIXME: this only works if the crate that owns the macro that has the helper_attr - // has already been imported. - let mut derives = vec![]; - let mut all_attrs: UnordMap> = UnordMap::default(); - // We're collecting these in a hashmap, and handle ordering the output further down. - #[allow(rustc::potential_query_instability)] - for (def_id, ext) in self - .local_macro_map - .iter() - .map(|(local_id, ext)| (local_id.to_def_id(), ext)) - .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) - { - for helper_attr in &ext.helper_attrs { - let item_name = self.tcx.item_name(def_id); - all_attrs.entry(*helper_attr).or_default().push(item_name); - if helper_attr == &ident.name { - derives.push(item_name); - } - } - } - let kind = MacroKind::Derive.descr(); - if !derives.is_empty() { - // We found an exact match for the missing attribute in a `derive` macro. Suggest it. - let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); - derives.sort(); - derives.dedup(); - let msg = match &derives[..] { - [derive] => format!(" `{derive}`"), - [start @ .., last] => format!( - "s {} and `{last}`", - start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") - ), - [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), - }; - let msg = format!( - "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ - missing a `derive` attribute", - ident.name, - ); - let sugg_span = - if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { - let span = self.def_span(id); - if span.from_expansion() { - None - } else { - // For enum variants sugg_span is empty but we can get the enum's Span. - Some(span.shrink_to_lo()) - } - } else { - // For items this `Span` will be populated, everything else it'll be None. - sugg_span - }; - match sugg_span { - Some(span) => { - err.span_suggestion_verbose( - span, - msg, - format!("#[derive({})]\n", derives.join(", ")), - Applicability::MaybeIncorrect, - ); - } - None => { - err.note(msg); - } - } - } else { - // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. - let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); - if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) - && let Some(macros) = all_attrs.get(&best_match) - { - let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); - macros.sort(); - macros.dedup(); - let msg = match ¯os[..] { - [] => return, - [name] => format!(" `{name}` accepts"), - [start @ .., end] => format!( - "s {} and `{end}` accept", - start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), - ), - }; - let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); - err.span_suggestion_verbose( - ident.span, - msg, - best_match, - Applicability::MaybeIncorrect, - ); - } - } - } +#[derive(Subdiagnostic)] +#[note("a macro with the same name exists, but it appears later")] +pub(crate) struct MacroDefinedLater { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[label("consider moving the definition of `{$ident}` before this call")] +pub(crate) struct MacroSuggMovePosition { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - pub(crate) fn add_typo_suggestion( - &self, - err: &mut Diag<'_>, - suggestion: Option, +#[derive(Subdiagnostic)] +pub(crate) enum MacroRulesNot { + #[label("`{$ident}` exists, but has no rules for function-like invocation")] + Func { + #[primary_span] span: Span, - ) -> bool { - let suggestion = match suggestion { - None => return false, - // We shouldn't suggest underscore. - Some(suggestion) if suggestion.candidate == kw::Underscore => return false, - Some(suggestion) => suggestion, - }; - - let mut did_label_def_span = false; - - if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { - if span.overlaps(def_span) { - // Don't suggest typo suggestion for itself like in the following: - // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` - // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 - // | - // LL | struct X {} - // | ----------- `X` defined here - // LL | - // LL | const Y: X = X("ö"); - // | -------------^^^^^^- similarly named constant `Y` defined here - // | - // help: use struct literal syntax instead - // | - // LL | const Y: X = X {}; - // | ^^^^ - // help: a constant with a similar name exists - // | - // LL | const Y: X = Y("ö"); - // | ^ - return false; - } - let span = self.tcx.sess.source_map().guess_head_span(def_span); - let candidate_descr = suggestion.res.descr(); - let candidate = suggestion.candidate; - let label = match suggestion.target { - SuggestionTarget::SimilarlyNamed => { - diagnostics::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } - } - SuggestionTarget::SingleItem => { - diagnostics::DefinedHere::SingleItem { span, candidate_descr, candidate } - } - }; - did_label_def_span = true; - err.subdiagnostic(label); - } + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `attr` rules")] + Attr { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("`{$ident}` exists, but has no `derive` rules")] + Derive { + #[primary_span] + span: Span, + ident: Ident, + }, +} - let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && let Some(span) = suggestion.span - && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') - && snippet == candidate - { - let candidate = suggestion.candidate; - // When the suggested binding change would be from `x` to `_x`, suggest changing the - // original binding definition instead. (#60164) - let msg = format!( - "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" - ); - if !did_label_def_span { - err.span_label(span, format!("`{candidate}` defined here")); - } - (span, msg, snippet) - } else { - let msg = match suggestion.target { - SuggestionTarget::SimilarlyNamed => format!( - "{} {} with a similar name exists", - suggestion.res.article(), - suggestion.res.descr() - ), - SuggestionTarget::SingleItem => { - format!("maybe you meant this {}", suggestion.res.descr()) - } - }; - (span, msg, suggestion.candidate.to_ident_string()) - }; - err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); - true - } +#[derive(Subdiagnostic)] +#[note("maybe you have forgotten to define a name for this `macro_rules!`")] +pub(crate) struct MaybeMissingMacroRulesName { + #[primary_span] + pub(crate) spans: MultiSpan, +} - fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { - let res = b.res(); - if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { - let (built_in, from) = match scope { - Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), - Scope::ExternPreludeFlags - if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() - || matches!(res, Res::OpenMod(..)) => - { - ("", " passed with `--extern`") - } - _ => { - if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { - // These already contain the "built-in" prefix or look bad with it. - ("", "") - } else { - (" built-in", "") - } - } - }; - - let a = if built_in.is_empty() { res.article() } else { "a" }; - format!("{a}{built_in} {thing}{from}", thing = res.descr()) - } else { - let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; - format!("the {thing} {introduced} here", thing = res.descr()) - } - } +#[derive(Subdiagnostic)] +#[help("have you added the `#[macro_use]` on the module/import?")] +pub(crate) struct AddedMacroUse; + +#[derive(Subdiagnostic)] +#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] +pub(crate) struct ConsiderAddingADerive { + #[primary_span] + pub(crate) span: Span, + pub(crate) suggestion: String, +} + +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the import")] +pub(crate) struct CannotDetermineImportResolution { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("cannot determine resolution for the {$kind} `{$path}`")] +#[note("import resolution is stuck, try simplifying macro imports")] +pub(crate) struct CannotDetermineMacroResolution { + #[primary_span] + pub(crate) span: Span, + pub(crate) kind: &'static str, + pub(crate) path: String, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] +pub(crate) struct CannotBeReexportedPrivate { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] +pub(crate) struct CannotBeReexportedCratePublic { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedPrivateNS { + #[primary_span] + #[label("re-export of private `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] +#[note("consider declaring type or module `{$ident}` with `pub`")] +pub(crate) struct CannotBeReexportedCratePublicNS { + #[primary_span] + #[label("re-export of crate public `{$ident}`")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] +pub(crate) struct PrivateExternCrateReexport { + pub ident: Ident, + #[suggestion( + "consider making the `extern crate` item publicly accessible", + code = "pub ", + style = "verbose", + applicability = "maybe-incorrect" + )] + pub sugg: Span, +} + +#[derive(Subdiagnostic)] +#[help("consider adding a `#[macro_export]` to the macro in the imported module")] +pub(crate) struct ConsiderAddingMacroExport { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", + code = "pub(crate)", + applicability = "maybe-incorrect" +)] +pub(crate) struct ConsiderMarkingAsPubCrate { + #[primary_span] + pub(crate) vis_span: Span, +} + +#[derive(Subdiagnostic)] +#[note("consider marking `{$ident}` as `pub` in the imported module")] +pub(crate) struct ConsiderMarkingAsPub { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} + +#[derive(Diagnostic)] +#[diag("cannot glob-import all possible crates")] +pub(crate) struct CannotGlobImportAllCrates { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to write a const parameter here", + code = "const ", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyToConstParamSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", + code = "{snippet}", + applicability = "machine-applicable", + style = "verbose" +)] +pub(crate) struct UnexpectedMissingConstParameter { + #[primary_span] + pub span: Span, + pub snippet: String, + pub item_name: String, + pub item_location: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to write a const parameter here", + applicability = "has-placeholders", + style = "verbose" +)] +pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { + #[suggestion_part(code = "const ")] + pub before: Span, + #[suggestion_part(code = ": /* Type */")] + pub after: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion( + "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", + code = "{snippet}", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { + #[primary_span] + pub span: Span, + pub ident: Ident, + pub snippet: String, +} + +#[derive(Diagnostic)] +#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] +pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`extern crate self;` requires renaming")] +pub(crate) struct ExternCrateSelfRequiresRenaming { + #[primary_span] + #[suggestion( + "rename the `self` crate to be able to import it", + code = "extern crate self as name;", + applicability = "has-placeholders" + )] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`{$name}` is already in scope")] +#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] +pub(crate) struct MacroUseNameAlreadyInUse { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, +} + +#[derive(Diagnostic)] +#[diag("imported macro not found", code = E0469)] +pub(crate) struct ImportedMacroNotFound { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Diagnostic)] +#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] +pub(crate) struct MacroExternDeprecated { + #[primary_span] + pub(crate) span: Span, + #[help("try an outer attribute: `#[macro_use]`")] + pub inner_attribute: bool, +} - fn ambiguity_diagnostic( - &self, - ambiguity_error: &AmbiguityError<'ra>, - ) -> diagnostics::Ambiguity { - let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = - *ambiguity_error; - let extern_prelude_ambiguity = || { - // Note: b1 may come from a module scope, as an extern crate item in module. - matches!(scope2, Scope::ExternPreludeFlags) - && self - .extern_prelude - .get(&IdentKey::new(ident)) - .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) - }; - let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { - // We have to print the span-less alternative first, otherwise formatting looks bad. - (b2, b1, scope2, scope1, true) - } else { - (b1, b2, scope1, scope2, false) - }; - - let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { - let what = self.decl_description(b, ident, scope); - let note_msg = format!("`{ident}` could{also} refer to {what}"); - - let thing = b.res().descr(); - let mut help_msgs = Vec::new(); - if b.is_glob_import() - && (kind == AmbiguityKind::GlobVsGlob - || kind == AmbiguityKind::GlobVsExpanded - || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) - { - help_msgs.push(format!( - "consider adding an explicit import of `{ident}` to disambiguate" - )) - } - if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() - { - help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) - } +#[derive(Diagnostic)] +#[diag("arguments to `macro_use` are not allowed here")] +pub(crate) struct ArgumentsMacroUseNotAllowed { + #[primary_span] + pub(crate) span: Span, +} - if kind != AmbiguityKind::GlobVsGlob { - if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { - if module == self.graph_root.to_module() { - help_msgs.push(format!( - "use `crate::{ident}` to refer to this {thing} unambiguously" - )); - } else if module.is_normal() { - help_msgs.push(format!( - "use `self::{ident}` to refer to this {thing} unambiguously" - )); - } - } - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "try renaming it with a name", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct UnnamedImportSugg { + #[suggestion_part(code = "{ident} as name")] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - ( - Spanned { node: note_msg, span: b.span }, - help_msgs - .iter() - .enumerate() - .map(|(i, help_msg)| { - let or = if i == 0 { "" } else { "or " }; - format!("{or}{help_msg}") - }) - .collect::>(), - ) - }; - let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); - let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); - let help = if kind == AmbiguityKind::GlobVsGlob - && b1 - .parent_module - .and_then(|m| m.opt_def_id()) - .map(|d| !d.is_local()) - .unwrap_or_default() - { - Some(&[ - "consider updating this dependency to resolve this error", - "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", - ] as &[_]) - } else { - None - }; - - let ambig_vis = ambig_vis.map(|(vis1, vis2)| { - format!( - "{} or {}", - vis1.to_string(CRATE_DEF_ID, self.tcx), - vis2.to_string(CRATE_DEF_ID, self.tcx) - ) - }); - - diagnostics::Ambiguity { - ident, - help, - ambig_vis, - kind: kind.descr(), - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error: false, - } - } +#[derive(Diagnostic)] +#[diag("imports need to be explicitly named")] +pub(crate) struct UnnamedImport { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) sugg: UnnamedImportSugg, +} - /// If the binding refers to a tuple struct constructor with fields, - /// returns the span of its fields. - fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { - let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = - decl.kind - else { - return None; - }; - - let def_id = self.tcx.parent(ctor_def_id); - self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` - } +#[derive(Diagnostic)] +#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] +pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { + #[primary_span] + pub(crate) span: Span, +} - /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. - /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. - /// Returns `None` for block modules that don't have a `DefId`. - fn module_path_names(&self, module: Module<'ra>) -> Option> { - let mut path = Vec::new(); - let mut def_id = module.opt_def_id()?; - while let Some(parent) = self.tcx.opt_parent(def_id) { - if let Some(name) = self.tcx.opt_item_name(def_id) { - path.push(name); - } - if parent.is_top_level_module() { - break; - } - def_id = parent; - } - path.reverse(); - path.insert(0, kw::Crate); - Some(path) - } +#[derive(Diagnostic)] +#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] +pub(crate) struct ElidedAnonymousLifetimeReportError { + #[primary_span] + #[label("explicit lifetime name needed here")] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) suggestion: Option, +} - /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) - /// relative to the current scope, if possible. Only applies to crate-local items and - /// only when the resulting path is actually shorter than the original. - fn shorten_candidate_path( - &self, - suggestion: &mut ImportSuggestion, - current_module: Module<'ra>, - ) { - const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; - - // Only shorten local items. - if suggestion.did.is_none_or(|did| !did.is_local()) { - return; - } +#[derive(Diagnostic)] +#[diag( + "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" +)] +pub(crate) struct LendingIteratorReportError { + #[primary_span] + pub(crate) lifetime: Span, + #[note( + "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" + )] + pub(crate) ty: Span, +} - // Build current module path: [Crate, foo, bar, ...]. - let Some(current_mod_path) = self.module_path_names(current_module) else { - return; - }; - - // Normalise candidate path: filter out `PathRoot` (`::`), and if the path - // doesn't start with `Crate`, prepend it (edition 2015 paths are relative - // to the crate root without an explicit `crate::` prefix). - let candidate_names = { - let filtered_segments: Vec<_> = suggestion - .path - .segments - .iter() - .filter(|segment| segment.ident.name != kw::PathRoot) - .collect(); - - let mut candidate_names: Vec = - filtered_segments.iter().map(|segment| segment.ident.name).collect(); - if candidate_names.first() != Some(&kw::Crate) { - candidate_names.insert(0, kw::Crate); - } - if candidate_names.len() < 2 { - return; - } - candidate_names - }; - - // The candidate's module path is everything except the last segment (the item name). - let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; - - // Find the longest common prefix between the current module and candidate module paths. - let common_prefix_length = current_mod_path - .iter() - .zip(candidate_mod_names.iter()) - .take_while(|(current, candidate)| current == candidate) - .count(); - - // Non-crate-local item; keep the full absolute path. - if common_prefix_length == 0 { - return; - } +#[derive(Diagnostic)] +#[diag("missing lifetime in associated type")] +pub(crate) struct AnonymousLifetimeNonGatReportError { + #[primary_span] + #[label("this lifetime must come from the implemented type")] + pub(crate) lifetime: Span, + #[note( + "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" + )] + pub(crate) decl: MultiSpan, +} - let super_count = current_mod_path.len() - common_prefix_length; +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "consider introducing a higher-ranked lifetime here", + applicability = "machine-applicable" +)] +pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { + #[suggestion_part(code = "for<'a> ")] + pub(crate) lo: Span, + #[suggestion_part(code = "'a ")] + pub(crate) hi: Span, +} - // At the crate root, `use` paths resolve from the crate root anyway, so we can - // drop the `crate::` prefix entirely instead of replacing it with `self::`. - let at_crate_root = current_mod_path.len() == 1; +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +pub(crate) struct ExplicitAnonymousLifetimeReportError { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - let mut new_segments = if super_count == 0 && at_crate_root { - ThinVec::new() - } else { - let prefix_keyword = match super_count { - 0 => kw::SelfLower, - 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, - _ => return, // Too many `super` levels; keep the full absolute path. - }; - thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] - }; - for &name in &candidate_names[common_prefix_length..] { - new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); - } +#[derive(Diagnostic)] +#[diag("implicit elided lifetime not allowed here", code = E0726)] +pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { + #[primary_span] + pub(crate) span: Span, + #[subdiagnostic] + pub(crate) subdiag: ElidedLifetimeInPathSubdiag, +} - // Only apply if the result is strictly shorter than the original path. - if new_segments.len() >= suggestion.path.segments.len() { - return; - } +#[derive(Diagnostic)] +#[diag("`'_` cannot be used here", code = E0637)] +#[help("use another lifetime specifier")] +pub(crate) struct UnderscoreLifetimeIsReserved { + #[primary_span] + #[label("`'_` is a reserved lifetime name")] + pub(crate) span: Span, +} - suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; - } +#[derive(Diagnostic)] +#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] +pub(crate) struct StaticLifetimeIsReserved { + #[primary_span] + #[label("'static is a reserved lifetime name")] + pub(crate) span: Span, + pub(crate) lifetime: Ident, +} - fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { - let PrivacyError { - ident, - decl, - outermost_res, - parent_scope, - single_nested, - dedup_span, - ref source, - } = *privacy_error; - - let res = decl.res(); - let ctor_fields_span = self.ctor_fields_span(decl); - let plain_descr = res.descr().to_string(); - let nonimport_descr = - if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; - let import_descr = nonimport_descr.clone() + " import"; - let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; - - // Print the primary message. - let ident_descr = get_descr(decl); - let mut err = - self.dcx().create_err(diagnostics::IsPrivate { span: ident.span, ident_descr, ident }); - - self.mention_default_field_values(source, ident, &mut err); - - let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { - let mut import_suggestions = self.lookup_import_candidates( - outer_ident, - this_res.ns().unwrap_or(Namespace::TypeNS), - &parent_scope, - &|res: Res| res == this_res, - ); - // Shorten candidate paths using `super::` or `self::` when possible. - for suggestion in &mut import_suggestions { - self.shorten_candidate_path(suggestion, parent_scope.module); - } - let point_to_def = !show_candidates( - self.tcx, - &mut err, - Some(dedup_span.until(outer_ident.span.shrink_to_hi())), - &import_suggestions, - Instead::Yes, - FoundUse::Yes, - DiagMode::Import { append: single_nested, unresolved_import: false }, - vec![], - "", - ); - // If we suggest importing a public re-export, don't point at the definition. - if point_to_def && ident.span != outer_ident.span { - let label = diagnostics::OuterIdentIsNotPubliclyReexported { - span: outer_ident.span, - outer_ident_descr: this_res.descr(), - outer_ident, - }; - err.subdiagnostic(label); - } - !point_to_def - } else { - false - }; - - let mut non_exhaustive = None; - // If an ADT is foreign and marked as `non_exhaustive`, then that's - // probably why we have the privacy error. - // Otherwise, point out if the struct has any private fields. - if let Some(def_id) = res.opt_def_id() - && !def_id.is_local() - && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) - { - non_exhaustive = Some(attr_span); - } else if let Some(span) = ctor_fields_span { - let label = diagnostics::ConstructorPrivateIfAnyFieldPrivate { span }; - err.subdiagnostic(label); - if let Res::Def(_, d) = res - && let Some(fields) = self.field_visibility_spans.get(&d) - { - let spans = fields.iter().map(|span| *span).collect(); - let sugg = diagnostics::ConsiderMakingTheFieldPublic { - spans, - number_of_fields: fields.len(), - }; - err.subdiagnostic(sugg); - } - } +#[derive(Diagnostic)] +#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] +pub(crate) struct VariableIsNotBoundInAllPatterns { + #[primary_span] + pub(crate) multispan: MultiSpan, + pub(crate) name: Ident, +} - let mut sugg_paths: Vec<(Vec, bool)> = vec![]; - if let Some(mut def_id) = res.opt_def_id() { - // We can't use `def_path_str` in resolve. - let mut path = vec![def_id]; - while let Some(parent) = self.tcx.opt_parent(def_id) { - def_id = parent; - if !def_id.is_top_level_module() { - path.push(def_id); - } else { - break; - } - } - // We will only suggest importing directly if it is accessible through that path. - let path_names: Option> = path - .iter() - .rev() - .map(|def_id| { - self.tcx.opt_item_name(*def_id).map(|name| { - Ident::with_dummy_span(if def_id.is_top_level_module() { - kw::Crate - } else { - name - }) - }) - }) - .collect(); - if let Some(&def_id) = path.get(0) - && let Some(path) = path_names - { - if let Some(def_id) = def_id.as_local() { - if self.effective_visibilities.is_directly_public(def_id) { - sugg_paths.push((path, false)); - } - } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) - { - sugg_paths.push((path, false)); - } - } - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("pattern doesn't bind `{$name}`")] +pub(crate) struct PatternDoesntBindName { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Ident, +} - // Print the whole import chain to make it easier to see what happens. - let first_binding = decl; - let mut next_binding = Some(decl); - let mut next_ident = ident; - while let Some(binding) = next_binding { - let name = next_ident; - next_binding = match binding.kind { - _ if res == Res::Err => None, - DeclKind::Import { source_decl, import, .. } => match import.kind { - _ if source_decl.span.is_dummy() => None, - ImportKind::Single { source, .. } => { - next_ident = source; - Some(source_decl) - } - ImportKind::Glob { .. } - | ImportKind::MacroUse { .. } - | ImportKind::MacroExport => Some(source_decl), - ImportKind::ExternCrate { .. } => None, - }, - _ => None, - }; - - match binding.kind { - DeclKind::Import { source_decl, import, .. } => { - // Don't include `{{root}}` in suggestions - it's an internal symbol - // that should never be shown to users. - let path = import - .module_path - .iter() - .filter(|seg| seg.ident.name != kw::PathRoot) - .map(|seg| seg.ident.clone()) - .chain(std::iter::once(ident)) - .collect::>(); - let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); - sugg_paths.push((path, through_reexport)); - } - DeclKind::Def(_) => {} - } - let first = binding == first_binding; - let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); - let mut note_span = MultiSpan::from_span(def_span); - if !first && binding.vis().is_public() { - let desc = match binding.kind { - DeclKind::Import { .. } => "re-export", - _ => "directly", - }; - note_span.push_span_label(def_span, format!("you could import this {desc}")); - } - // Final step in the import chain, point out if the ADT is `non_exhaustive` - // which is probably why this privacy violation occurred. - if next_binding.is_none() - && let Some(span) = non_exhaustive - { - note_span.push_span_label( - span, - "cannot be constructed because it is `#[non_exhaustive]`", - ); - } - let note = diagnostics::NoteAndRefersToTheItemDefinedHere { - span: note_span, - binding_descr: get_descr(binding), - binding_name: name, - first, - dots: next_binding.is_some(), - }; - err.subdiagnostic(note); - } - // The suggestion replaces `dedup_span` with a path reaching the failing ident. - // That's valid only when - // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments - // get dropped, and - // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. - // - // See issue #156060. - let can_replace_use = !shown_candidates - && !single_nested - && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); - if can_replace_use { - // We prioritize shorter paths, non-core imports and direct imports over the - // alternatives. - sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); - for (sugg, reexport) in sugg_paths { - if sugg.len() <= 1 { - // A single path segment suggestion is wrong. This happens on circular - // imports. `tests/ui/imports/issue-55884-2.rs` - continue; - } - let path = join_path_idents(sugg); - let sugg = if reexport { - diagnostics::ImportIdent::ThroughReExport { span: dedup_span, ident, path } - } else { - diagnostics::ImportIdent::Directly { span: dedup_span, ident, path } - }; - err.subdiagnostic(sugg); - break; - } - } +#[derive(Subdiagnostic, Debug, Clone)] +#[label("variable not in all patterns")] +pub(crate) struct VariableNotInAllPatterns { + #[primary_span] + pub(crate) span: Span, +} - err.emit(); - } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "you might have meant to use the similarly named previously used binding `{$typo}`", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct PatternBindingTypo { + #[suggestion_part(code = "{typo}")] + pub(crate) spans: Vec, + pub(crate) typo: Symbol, +} - /// When a private field is being set that has a default field value, we suggest using `..` and - /// setting the value of that field implicitly with its default. - /// - /// If we encounter code like - /// ```text - /// struct Priv; - /// pub struct S { - /// pub field: Priv = Priv, - /// } - /// ``` - /// which is used from a place where `Priv` isn't accessible - /// ```text - /// let _ = S { field: m::Priv1 {} }; - /// // ^^^^^ private struct - /// ``` - /// we will suggest instead using the `default_field_values` syntax instead: - /// ```text - /// let _ = S { .. }; - /// ``` - fn mention_default_field_values( - &self, - source: &Option, - ident: Ident, - err: &mut Diag<'_>, - ) { - let Some(expr) = source else { return }; - let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; - // We don't have to handle type-relative paths because they're forbidden in ADT - // expressions, but that would change with `#[feature(more_qualified_paths)]`. - let Some(segment) = struct_expr.path.segments.last() else { return }; - let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; - let Some(Res::Def(_, def_id)) = partial_res.full_res() else { - return; - }; - let Some(default_fields) = self.field_defaults(def_id) else { return }; - if struct_expr.fields.is_empty() { - return; - } - let last_span = struct_expr.fields.iter().last().unwrap().span; - let mut iter = struct_expr.fields.iter().peekable(); - let mut prev: Option = None; - while let Some(field) = iter.next() { - if field.expr.span.overlaps(ident.span) { - err.span_label(field.ident.span, "while setting this field"); - if default_fields.contains(&field.ident.name) { - let sugg = if last_span == field.span { - vec![(field.span, "..".to_string())] - } else { - vec![ - ( - // Account for trailing commas and ensure we remove them. - match (prev, iter.peek()) { - (_, Some(next)) => field.span.with_hi(next.span.lo()), - (Some(prev), _) => field.span.with_lo(prev.hi()), - (None, None) => field.span, - }, - String::new(), - ), - (last_span.shrink_to_hi(), ", ..".to_string()), - ] - }; - err.multipart_suggestion( - format!( - "the type `{ident}` of field `{}` is private, but you can construct \ - the default value defined for it in `{}` using `..` in the struct \ - initializer expression", - field.ident, - self.tcx.item_name(def_id), - ), - sugg, - Applicability::MachineApplicable, - ); - break; - } - } - prev = Some(field.span); - } - } +#[derive(Diagnostic)] +#[diag("the name `{$name}` is defined multiple times")] +#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] +pub(crate) struct NameDefinedMultipleTime { + #[primary_span] + pub(crate) span: Span, + pub(crate) name: Symbol, + pub(crate) descr: &'static str, + pub(crate) container: &'static str, + #[subdiagnostic] + pub(crate) label: NameDefinedMultipleTimeLabel, + #[subdiagnostic] + pub(crate) old_binding_label: Option, +} - pub(crate) fn find_similarly_named_module_or_crate( - &self, - ident: Symbol, - current_module: Module<'ra>, - ) -> Option { - let mut candidates = self - .extern_prelude - .keys() - .map(|ident| ident.name) - .chain( - self.local_module_map - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .chain( - self.extern_module_map - .borrow() - .iter() - .filter(|(_, module)| { - let module = module.to_module(); - current_module.is_ancestor_of(module) && current_module != module - }) - .flat_map(|(_, module)| module.name()), - ) - .filter(|c| !c.to_string().is_empty()) - .collect::>(); - candidates.sort(); - candidates.dedup(); - find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeLabel { + #[label("`{$name}` reimported here")] + Reimported { + #[primary_span] + span: Span, + name: Symbol, + }, + #[label("`{$name}` redefined here")] + Redefined { + #[primary_span] + span: Span, + name: Symbol, + }, +} - pub(crate) fn report_path_resolution_error( - &mut self, - path: &[Segment], - opt_ns: Option, // `None` indicates a module path in import - parent_scope: &ParentScope<'ra>, - ribs: Option<&PerNS>>>, - ignore_decl: Option>, - ignore_import: Option>, - module: Option>, - failed_segment_idx: usize, - ident: Ident, - diag_metadata: Option<&DiagMetadata<'_>>, - ) -> (String, String, Option) { - let is_last = failed_segment_idx == path.len() - 1; - let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; - let module_def_id = match module { - Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), - _ => None, - }; - let scope = match &path[..failed_segment_idx] { - [.., prev] => { - if prev.ident.name == kw::PathRoot { - format!("the crate root") - } else { - format!("`{}`", prev.ident) - } - } - _ => format!("this scope"), - }; - let message = format!("cannot find `{ident}` in {scope}"); - - if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { - let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); - let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); - candidates - .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); - if let Some(candidate) = candidates.get(0) { - let path = { - // remove the possible common prefix of the path - let len = candidate.path.segments.len(); - let start_index = (0..=failed_segment_idx.min(len - 1)) - .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) - .unwrap_or_default(); - let segments = - (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); - Path { segments, span: Span::default(), tokens: None } - }; - ( - message, - String::from("unresolved import"), - Some(( - vec![(ident.span, pprust::path_to_string(&path))], - String::from("a similar path exists"), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == sym::core { - ( - message, - format!("you might be missing crate `{ident}`"), - Some(( - vec![(ident.span, "std".to_string())], - "try using `std` instead of `core`".to_string(), - Applicability::MaybeIncorrect, - )), - ) - } else if ident.name == kw::Underscore { - ( - "invalid crate or module name `_`".to_string(), - "`_` is not a valid crate or module name".to_string(), - None, - ) - } else if self.tcx.sess.is_rust_2015() { - ( - format!("cannot find module or crate `{ident}` in {scope}"), - format!("use of unresolved module or unlinked crate `{ident}`"), - Some(( - vec![( - self.current_crate_outer_attr_insert_span, - format!("extern crate {ident};\n"), - )], - if was_invoked_from_cargo() { - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add \ - {ident}` to add it to your `Cargo.toml` and import it in your \ - code", - ) - } else { - format!( - "you might be missing a crate named `{ident}`, add it to your \ - project and import it in your code", - ) - }, - Applicability::MaybeIncorrect, - )), - ) - } else { - (message, format!("could not find `{ident}` in the crate root"), None) - } - } else if failed_segment_idx > 0 { - let parent = path[failed_segment_idx - 1].ident.name; - let parent = match parent { - // ::foo is mounted at the crate root for 2015, and is the extern - // prelude for 2018+ - kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { - "the list of imported crates".to_owned() - } - kw::PathRoot | kw::Crate => "the crate root".to_owned(), - _ => format!("`{parent}`"), - }; - - let mut msg = format!("could not find `{ident}` in {parent}"); - if ns == TypeNS || ns == ValueNS { - let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; - let binding = if let Some(module) = module { - self.cm() - .resolve_ident_in_module( - module, - ident, - ns_to_try, - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - } else if let Some(ribs) = ribs - && let Some(TypeNS | ValueNS) = opt_ns - { - assert!(ignore_import.is_none()); - match self.resolve_ident_in_lexical_scope( - ident, - ns_to_try, - parent_scope, - None, - &ribs[ns_to_try], - ignore_decl, - diag_metadata, - ) { - // we found a locally-imported or available item/module - Some(LateDecl::Decl(binding)) => Some(binding), - _ => None, - } - } else { - self.cm() - .resolve_ident_in_scope_set( - ident, - ScopeSet::All(ns_to_try), - parent_scope, - None, - ignore_decl, - ignore_import, - ) - .ok() - }; - if let Some(binding) = binding { - msg = format!( - "expected {}, found {} `{ident}` in {parent}", - ns.descr(), - binding.res().descr(), - ); - }; - } - (message, msg, None) - } else if ident.name == kw::SelfUpper { - // As mentioned above, `opt_ns` being `None` indicates a module path in import. - // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an - // impl - if opt_ns.is_none() { - (message, "`Self` cannot be used in imports".to_string(), None) - } else { - ( - message, - "`Self` is only available in impls, traits, and type definitions".to_string(), - None, - ) - } - } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { - // Check whether the name refers to an item in the value namespace. - let binding = if let Some(ribs) = ribs { - assert!(ignore_import.is_none()); - self.resolve_ident_in_lexical_scope( - ident, - ValueNS, - parent_scope, - None, - &ribs[ValueNS], - ignore_decl, - diag_metadata, - ) - } else { - None - }; - let match_span = match binding { - // Name matches a local variable. For example: - // ``` - // fn f() { - // let Foo: &str = ""; - // println!("{}", Foo::Bar); // Name refers to local - // // variable `Foo`. - // } - // ``` - Some(LateDecl::RibDef(Res::Local(id))) => { - Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) - } - // Name matches item from a local name binding - // created by `use` declaration. For example: - // ``` - // pub const Foo: &str = ""; - // - // mod submod { - // use super::Foo; - // println!("{}", Foo::Bar); // Name refers to local - // // binding `Foo`. - // } - // ``` - Some(LateDecl::Decl(name_binding)) => Some(( - name_binding.span, - name_binding.res().article(), - name_binding.res().descr(), - )), - _ => None, - }; - - let message = format!("cannot find type `{ident}` in {scope}"); - let label = if let Some((span, article, descr)) = match_span { - format!( - "`{ident}` is declared as {article} {descr} at `{}`, not a type", - self.tcx - .sess - .source_map() - .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) - ) - } else { - format!("use of undeclared type `{ident}`") - }; - (message, label, None) - } else { - let mut suggestion = None; - if ident.name == sym::alloc { - suggestion = Some(( - vec![], - String::from("add `extern crate alloc` to use the `alloc` crate"), - Applicability::MaybeIncorrect, - )) - } +#[derive(Subdiagnostic)] +pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { + #[label("previous import of the {$old_kind} `{$name}` here")] + Import { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, + #[label("previous definition of the {$old_kind} `{$name}` here")] + Definition { + #[primary_span] + span: Span, + old_kind: &'static str, + name: Symbol, + }, +} - suggestion = suggestion.or_else(|| { - self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( - |sugg| { - ( - vec![(ident.span, sugg.to_string())], - String::from("there is a crate or module with a similar name"), - Applicability::MaybeIncorrect, - ) - }, - ) - }); - if let Ok(binding) = self.cm().resolve_ident_in_scope_set( - ident, - ScopeSet::All(ValueNS), - parent_scope, - None, - ignore_decl, - ignore_import, - ) { - let descr = binding.res().descr(); - let message = format!("cannot find module or crate `{ident}` in {scope}"); - (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) - } else { - let suggestion = if suggestion.is_some() { - suggestion - } else if let Some(m) = self.undeclared_module_exists(ident) { - self.undeclared_module_suggest_declare(ident, m) - } else if was_invoked_from_cargo() { - Some(( - vec![], - format!( - "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ - to add it to your `Cargo.toml`", - ), - Applicability::MaybeIncorrect, - )) - } else { - Some(( - vec![], - format!("you might be missing a crate named `{ident}`",), - Applicability::MaybeIncorrect, - )) - }; - let message = format!("cannot find module or crate `{ident}` in {scope}"); - ( - message, - format!("use of unresolved module or unlinked crate `{ident}`"), - suggestion, - ) - } - } - } +#[derive(Diagnostic)] +#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] +pub(crate) struct IsPrivate<'a> { + #[primary_span] + #[label("private {$ident_descr}")] + pub(crate) span: Span, + pub(crate) ident_descr: &'a str, + pub(crate) ident: Ident, +} - fn undeclared_module_suggest_declare( - &self, - ident: Ident, - path: std::path::PathBuf, - ) -> Option<(Vec<(Span, String)>, String, Applicability)> { - Some(( - vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], - format!( - "to make use of source file {}, use `mod {ident}` \ - in this file to declare the module", - path.display() - ), - Applicability::MaybeIncorrect, - )) - } +#[derive(Diagnostic)] +#[diag("generic arguments in macro path")] +pub(crate) struct GenericArgumentsInMacroPath { + #[primary_span] + pub(crate) span: Span, +} - fn undeclared_module_exists(&self, ident: Ident) -> Option { - let map = self.tcx.sess.source_map(); - - let src = map.span_to_filename(ident.span).into_local_path()?; - let i = ident.as_str(); - // FIXME: add case where non parent using undeclared module (hard?) - let dir = src.parent()?; - let src = src.file_stem()?.to_str()?; - for file in [ - // …/x.rs - dir.join(i).with_extension("rs"), - // …/x/mod.rs - dir.join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - if !matches!(src, "main" | "lib" | "mod") { - for file in [ - // …/x/y.rs - dir.join(src).join(i).with_extension("rs"), - // …/x/y/mod.rs - dir.join(src).join(i).join("mod.rs"), - ] { - if file.exists() { - return Some(file); - } - } - } - None - } +#[derive(Diagnostic)] +#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] +pub(crate) struct AttributesStartingWithRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - /// Adds suggestions for a path that cannot be resolved. - #[instrument(level = "debug", skip(self, parent_scope))] - pub(crate) fn make_path_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - match path[..] { - // `{{root}}::ident::...` on both editions. - // On 2015 `{{root}}` is usually added implicitly. - [first, second, ..] - if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} - // `ident::...` on 2018. - [first, ..] - if first.ident.span.at_least_rust_2018() - && !first.ident.is_path_segment_keyword() => - { - // Insert a placeholder that's later replaced by `self`/`super`/etc. - path.insert(0, Segment::from_ident(Ident::dummy())); - } - _ => return None, - } +#[derive(Diagnostic)] +#[diag( + "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" +)] +pub(crate) struct AttributesContainingRustcAreReserved { + #[primary_span] + pub(crate) span: Span, +} - self.make_missing_self_suggestion(path.clone(), parent_scope) - .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) - .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) - } +#[derive(Diagnostic)] +#[diag("cannot use {$article} {$descr} through an import")] +pub(crate) struct CannotUseThroughAnImport { + #[primary_span] + pub(crate) span: Span, + pub(crate) article: &'static str, + pub(crate) descr: &'static str, + #[note("the {$descr} imported here")] + pub(crate) binding_span: Option, +} - /// Suggest a missing `self::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `self::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_self_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `self` and check if that is valid. - path[0].ident.name = kw::SelfLower; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag("name `{$ident}` is reserved in attribute namespace")] +pub(crate) struct NameReservedInAttributeNamespace { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Symbol, +} - /// Suggests a missing `crate::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `crate::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Crate; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { - Some(( - path, - Some( - "`use` statements changed in Rust 2018; read more at \ - " - .to_string(), - ), - )) - } else { - None - } - } +#[derive(Diagnostic)] +#[diag("cannot find a built-in macro with name `{$ident}`")] +pub(crate) struct CannotFindBuiltinMacroWithName { + #[primary_span] + pub(crate) span: Span, + pub(crate) ident: Ident, +} - /// Suggests a missing `super::` if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foo::Bar; - /// | ^^^ did you mean `super::foo`? - /// ``` - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_missing_super_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - // Replace first ident with `crate` and check if that is valid. - path[0].ident.name = kw::Super; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?result); - if let PathResult::Module(..) = result { Some((path, None)) } else { None } - } +#[derive(Diagnostic)] +#[diag("tool `{$tool}` was already registered")] +pub(crate) struct ToolWasAlreadyRegistered { + #[primary_span] + pub(crate) span: Span, + pub(crate) tool: Ident, + #[label("already registered here")] + pub(crate) old_ident_span: Span, +} - /// Suggests a missing external crate name if that resolves to an correct module. - /// - /// ```text - /// | - /// LL | use foobar::Baz; - /// | ^^^^^^ did you mean `baz::foobar`? - /// ``` - /// - /// Used when importing a submodule of an external crate but missing that crate's - /// name as the first part of path. - #[instrument(level = "debug", skip(self, parent_scope))] - fn make_external_crate_suggestion( - &mut self, - mut path: Vec, - parent_scope: &ParentScope<'ra>, - ) -> Option<(Vec, Option)> { - if path[1].ident.span.is_rust_2015() { - return None; - } +#[derive(Subdiagnostic)] +pub(crate) enum DefinedHere { + #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] + SimilarlyNamed { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, + #[label("{$candidate_descr} `{$candidate}` defined here")] + SingleItem { + #[primary_span] + span: Span, + candidate_descr: &'static str, + candidate: Symbol, + }, +} - // Sort extern crate names in *reverse* order to get - // 1) some consistent ordering for emitted diagnostics, and - // 2) `std` suggestions before `core` suggestions. - let mut extern_crate_names = - self.extern_prelude.keys().map(|ident| ident.name).collect::>(); - extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); - - for name in extern_crate_names.into_iter() { - // Replace first ident with a crate name and check if that is valid. - path[0].ident.name = name; - let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); - debug!(?path, ?name, ?result); - if let PathResult::Module(..) = result { - return Some((path, None)); - } - } +#[derive(Subdiagnostic)] +#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] +pub(crate) struct OuterIdentIsNotPubliclyReexported { + #[primary_span] + pub(crate) span: Span, + pub(crate) outer_ident_descr: &'static str, + pub(crate) outer_ident: Ident, +} - None - } +#[derive(Subdiagnostic)] +#[label("a constructor is private if any of the fields is private")] +pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { + #[primary_span] + pub(crate) span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "{ $number_of_fields -> + [one] consider making the field publicly accessible + *[other] consider making the fields publicly accessible + }", + applicability = "maybe-incorrect", + style = "verbose" +)] +pub(crate) struct ConsiderMakingTheFieldPublic { + #[suggestion_part(code = "pub ")] + pub(crate) spans: Vec, + pub(crate) number_of_fields: usize, +} - /// Suggests importing a macro from the root of the crate rather than a module within - /// the crate. - /// - /// ```text - /// help: a macro with this name exists at the root of the crate - /// | - /// LL | use issue_59764::makro; - /// | ^^^^^^^^^^^^^^^^^^ - /// | - /// = note: this could be because a macro annotated with `#[macro_export]` will be exported - /// at the root of the crate instead of the module where it is defined - /// ``` - pub(crate) fn check_for_module_export_macro( - &mut self, - import: Import<'ra>, - module: ModuleOrUniformRoot<'ra>, +#[derive(Subdiagnostic)] +pub(crate) enum ImportIdent { + #[suggestion( + "import `{$ident}` through the re-export", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + ThroughReExport { + #[primary_span] + span: Span, ident: Ident, - ) -> Option<(Option, Option)> { - let ModuleOrUniformRoot::Module(mut crate_module) = module else { - return None; - }; + path: String, + }, + #[suggestion( + "import `{$ident}` directly", + code = "{path}", + applicability = "machine-applicable", + style = "verbose" + )] + Directly { + #[primary_span] + span: Span, + ident: Ident, + path: String, + }, +} - while let Some(parent) = crate_module.parent { - crate_module = parent; - } +#[derive(Subdiagnostic)] +#[note( + "{$first -> + [true] {$dots -> + [true] the {$binding_descr} `{$binding_name}` is defined here... + *[false] the {$binding_descr} `{$binding_name}` is defined here + } + *[false] {$dots -> + [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... + *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here + } + }" +)] +pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { + #[primary_span] + pub(crate) span: MultiSpan, + pub(crate) binding_descr: &'a str, + pub(crate) binding_name: Ident, + pub(crate) first: bool, + pub(crate) dots: bool, +} - if module == ModuleOrUniformRoot::Module(crate_module) { - // Don't make a suggestion if the import was already from the root of the crate. - return None; - } +#[derive(Subdiagnostic)] +#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] +pub(crate) struct RemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); - let binding = self.resolution(crate_module, binding_key)?.best_decl()?; - let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { - return None; - }; - if !kinds.contains(MacroKinds::BANG) { - return None; - } - let module_name = crate_module.name().unwrap_or(kw::Crate); - let import_snippet = match import.kind { - ImportKind::Single { source, target, .. } if source != target => { - format!("{source} as {target}") - } - _ => format!("{ident}"), - }; - - let mut corrections: Vec<(Span, String)> = Vec::new(); - if !import.is_nested() { - // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove - // intermediate segments. - corrections.push((import.span, format!("{module_name}::{import_snippet}"))); - } else { - // Find the binding span (and any trailing commas and spaces). - // i.e. `use a::b::{c, d, e};` - // ^^^ - let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( - self.tcx.sess, - import.span, - import.use_span, - ); - debug!(found_closing_brace, ?binding_span); - - let mut removal_span = binding_span; - - // If the binding span ended with a closing brace, as in the below example: - // i.e. `use a::b::{c, d};` - // ^ - // Then expand the span of characters to remove to include the previous - // binding's trailing comma. - // i.e. `use a::b::{c, d};` - // ^^^ - if found_closing_brace - && let Some(previous_span) = - extend_span_to_previous_binding(self.tcx.sess, binding_span) - { - debug!(?previous_span); - removal_span = removal_span.with_lo(previous_span.lo()); - } - debug!(?removal_span); - - // Remove the `removal_span`. - corrections.push((removal_span, "".to_string())); - - // Find the span after the crate name and if it has nested imports immediately - // after the crate name already. - // i.e. `use a::b::{c, d};` - // ^^^^^^^^^ - // or `use a::{b, c, d}};` - // ^^^^^^^^^^^ - let (has_nested, after_crate_name) = - find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); - debug!(has_nested, ?after_crate_name); - - let source_map = self.tcx.sess.source_map(); - - // Make sure this is actually crate-relative. - let is_definitely_crate = import - .module_path - .first() - .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); - - // Add the import to the start, with a `{` if required. - let start_point = source_map.start_point(after_crate_name); - if is_definitely_crate - && let Ok(start_snippet) = source_map.span_to_snippet(start_point) - { - corrections.push(( - start_point, - if has_nested { - // In this case, `start_snippet` must equal '{'. - format!("{start_snippet}{import_snippet}, ") - } else { - // In this case, add a `{`, then the moved import, then whatever - // was there before. - format!("{{{import_snippet}, {start_snippet}") - }, - )); - - // Add a `};` to the end if nested, matching the `{` added at the start. - if !has_nested { - corrections.push((source_map.end_point(after_crate_name), "};".to_string())); - } - } else { - // If the root import is module-relative, add the import separately - corrections.push(( - import.use_span.shrink_to_lo(), - format!("use {module_name}::{import_snippet};\n"), - )); - } - } +#[derive(Subdiagnostic)] +#[suggestion( + "remove unnecessary import", + code = "", + applicability = "maybe-incorrect", + style = "tool-only" +)] +pub(crate) struct ToolOnlyRemoveUnnecessaryImport { + #[primary_span] + pub(crate) span: Span, +} - let suggestion = Some(( - corrections, - String::from("a macro with this name exists at the root of the crate"), - Applicability::MaybeIncorrect, - )); - Some(( - suggestion, - Some( - "this could be because a macro annotated with `#[macro_export]` will be exported \ - at the root of the crate instead of the module where it is defined" - .to_string(), - ), - )) - } +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] +pub(crate) struct IdentImporterHereButItIsDesc<'a> { + #[primary_span] + pub(crate) span: Span, + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - /// Finds a cfg-ed out item inside `module` with the matching name. - pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { - let local_items; - let symbols = if module.is_local() { - local_items = self - .stripped_cfg_items - .iter() - .filter_map(|item| { - let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { - ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { - Some(def_id) - } - _ => None, - })?; - Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) - }) - .collect::>(); - local_items.as_slice() - } else { - self.tcx.stripped_cfg_items(module.krate) - }; +#[derive(Subdiagnostic)] +#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] +pub(crate) struct IdentInScopeButItIsDesc<'a> { + pub(crate) imported_ident: Ident, + pub(crate) imported_ident_desc: &'a str, +} - for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { - if ident.name != *segment { - continue; - } +pub(crate) struct FoundItemConfigureOut { + pub(crate) span: Span, + pub(crate) item_was: ItemWas, +} - let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); - - fn comes_from_same_module_for_glob( - r: &Resolver<'_, '_>, - parent_module: DefId, - module: DefId, - visited: &mut FxHashMap, - ) -> bool { - if let Some(&cached) = visited.get(&parent_module) { - // this branch is prevent from being called recursively infinity, - // because there has some cycles in globs imports, - // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` - return cached; - } - visited.insert(parent_module, false); - let mut res = false; - let m = r.expect_module(parent_module); - if m.is_local() { - for importer in m.glob_importers.borrow().iter() { - if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() - { - if next_parent_module == module - || comes_from_same_module_for_glob( - r, - next_parent_module, - module, - visited, - ) - { - res = true; - break; - } - } - } - } - visited.insert(parent_module, res); - res - } +pub(crate) enum ItemWas { + BehindFeature { feature: Symbol, span: Span }, + CfgOut { span: Span }, +} - let comes_from_same_module = parent_module == module - || comes_from_same_module_for_glob( - self, - parent_module, - module, - &mut Default::default(), - ); - if !comes_from_same_module { - continue; +impl Subdiagnostic for FoundItemConfigureOut { + fn add_to_diag(self, diag: &mut Diag<'_, G>) { + let mut multispan: MultiSpan = self.span.into(); + match self.item_was { + ItemWas::BehindFeature { feature, span } => { + let value = feature.into_diag_arg(&mut None); + let msg = msg!("the item is gated behind the `{$feature}` feature") + .arg("feature", value) + .format(); + multispan.push_span_label(span, msg); } - - let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { - diagnostics::ItemWas::BehindFeature { feature, span: cfg.1 } - } else { - diagnostics::ItemWas::CfgOut { span: cfg.1 } - }; - let note = diagnostics::FoundItemConfigureOut { span: ident.span, item_was }; - err.subdiagnostic(note); - } - } - - pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { - match def_id.as_local() { - Some(def_id) => self.struct_ctors.get(&def_id).cloned(), - None => { - self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { - let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); - let vis = self.tcx.visibility(ctor_def_id); - let field_visibilities = self - .tcx - .associated_item_def_ids(def_id) - .iter() - .map(|&field_id| self.tcx.visibility(field_id)) - .collect(); - StructCtor { res, vis, field_visibilities } - }) + ItemWas::CfgOut { span } => { + multispan.push_span_label(span, msg!("the item is gated here")); } } + diag.span_note(multispan, msg!("found an item that was configured out")); } +} - /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. - fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { - match def_id.as_local() { - Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), - None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), - } - } +#[derive(Diagnostic)] +#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] +pub(crate) struct TraitImplMismatch { + #[primary_span] + #[label("does not match trait")] + pub(crate) span: Span, + pub(crate) name: Ident, + pub(crate) kind: &'static str, + pub(crate) trait_path: String, + #[label("item in trait")] + pub(crate) trait_item_span: Span, } -/// Given a `binding_span` of a binding within a use statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^ -/// ``` -/// -/// then return the span until the next binding or the end of the statement: -/// -/// ```ignore (illustrative) -/// use foo::{a, b, c}; -/// // ^^^ -/// ``` -fn find_span_of_binding_until_next_binding( - sess: &Session, - binding_span: Span, - use_span: Span, -) -> (bool, Span) { - let source_map = sess.source_map(); - - // Find the span of everything after the binding. - // i.e. `a, e};` or `a};` - let binding_until_end = binding_span.with_hi(use_span.hi()); - - // Find everything after the binding but not including the binding. - // i.e. `, e};` or `};` - let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); - - // Keep characters in the span until we encounter something that isn't a comma or - // whitespace. - // i.e. `, ` or ``. - // - // Also note whether a closing brace character was encountered. If there - // was, then later go backwards to remove any trailing commas that are left. - let mut found_closing_brace = false; - let after_binding_until_next_binding = - source_map.span_take_while(after_binding_until_end, |&ch| { - if ch == '}' { - found_closing_brace = true; - } - ch == ' ' || ch == ',' - }); - - // Combine the two spans. - // i.e. `a, ` or `a`. - // - // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` - let span = binding_span.with_hi(after_binding_until_next_binding.hi()); - - (found_closing_brace, span) -} - -/// Given a `binding_span`, return the span through to the comma or opening brace of the previous -/// binding. -/// -/// ```ignore (illustrative) -/// use foo::a::{a, b, c}; -/// // ^^--- binding span -/// // | -/// // returned span -/// -/// use foo::{a, b, c}; -/// // --- binding span -/// ``` -fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { - let source_map = sess.source_map(); - - // `prev_source` will contain all of the source that came before the span. - // Then split based on a command and take the first (i.e. closest to our span) - // snippet. In the example, this is a space. - let prev_source = source_map.span_to_prev_source(binding_span).ok()?; - - let prev_comma = prev_source.rsplit(',').collect::>(); - let prev_starting_brace = prev_source.rsplit('{').collect::>(); - if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { - return None; - } +#[derive(Diagnostic)] +#[diag("derive helper attribute is used before it is introduced")] +pub(crate) struct LegacyDeriveHelpers { + #[label("the attribute is introduced here")] + pub span: Span, +} - let prev_comma = prev_comma.first().unwrap(); - let prev_starting_brace = prev_starting_brace.first().unwrap(); +#[derive(Diagnostic)] +#[diag("unused extern crate")] +pub(crate) struct UnusedExternCrate { + #[label("unused")] + pub span: Span, + #[suggestion( + "remove the unused `extern crate`", + code = "", + applicability = "machine-applicable", + style = "verbose" + )] + pub removal_span: Span, +} - // If the amount of source code before the comma is greater than - // the amount of source code before the starting brace then we've only - // got one item in the nested item (eg. `issue_52891::{self}`). - if prev_comma.len() > prev_starting_brace.len() { - return None; - } +#[derive(Diagnostic)] +#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] +pub(crate) struct ReexportPrivateDependency { + pub name: Symbol, + pub kind: &'static str, + pub krate: Symbol, +} - Some(binding_span.with_lo(BytePos( - // Take away the number of bytes for the characters we've found and an - // extra for the comma. - binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, - ))) -} - -/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if -/// it is a nested use tree. -/// -/// ```ignore (illustrative) -/// use foo::a::{b, c}; -/// // ^^^^^^^^^^ -- false -/// -/// use foo::{a, b, c}; -/// // ^^^^^^^^^^ -- true -/// -/// use foo::{a, b::{c, d}}; -/// // ^^^^^^^^^^^^^^^ -- true -/// ``` -#[instrument(level = "debug", skip(sess))] -fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { - let source_map = sess.source_map(); - - // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. - let mut num_colons = 0; - // Find second colon.. `use issue_59764:` - let until_second_colon = source_map.span_take_while(use_span, |c| { - if *c == ':' { - num_colons += 1; - } - !matches!(c, ':' if num_colons == 2) - }); - // Find everything after the second colon.. `foo::{baz, makro};` - let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); - - let mut found_a_non_whitespace_character = false; - // Find the first non-whitespace character in `from_second_colon`.. `f` - let after_second_colon = source_map.span_take_while(from_second_colon, |c| { - if found_a_non_whitespace_character { - return false; - } - if !c.is_whitespace() { - found_a_non_whitespace_character = true; - } - true - }); +#[derive(Diagnostic)] +#[diag("unused label")] +pub(crate) struct UnusedLabel; - // Find the first `{` in from_second_colon.. `foo::{` - let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); +#[derive(Diagnostic)] +#[diag("unused `#[macro_use]` import")] +pub(crate) struct UnusedMacroUse; - (next_left_bracket == after_second_colon, from_second_colon) +#[derive(Diagnostic)] +#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] +#[help("remove it and import macros at use sites with a `use` item instead")] +pub(crate) struct MacroUseDeprecated; + +#[derive(Diagnostic)] +#[diag("macro `{$ident}` is private")] +pub(crate) struct MacroIsPrivate { + pub ident: Ident, } -/// A suggestion has already been emitted, change the wording slightly to clarify that both are -/// independent options. -enum Instead { - Yes, - No, +#[derive(Diagnostic)] +#[diag("unused macro definition: `{$name}`")] +pub(crate) struct UnusedMacroDefinition { + pub name: Symbol, } -/// Whether an existing place with an `use` item was found. -enum FoundUse { - Yes, - No, +#[derive(Diagnostic)] +#[diag("rule #{$n} of macro `{$name}` is never used")] +pub(crate) struct MacroRuleNeverUsed { + pub n: usize, + pub name: Symbol, } -/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. -pub(crate) enum DiagMode { - Normal, - /// The binding is part of a pattern - Pattern, - /// The binding is part of a use statement - Import { - /// `true` means diagnostics is for unresolved import - unresolved_import: bool, - /// `true` mean add the tips afterward for case `use a::{b,c}`, - /// rather than replacing within. - append: bool, +#[derive(Diagnostic)] +#[diag("`extern crate` is not idiomatic in the new edition")] +pub(crate) struct ExternCrateNotIdiomatic { + #[suggestion( + "convert it to a `use`", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + pub code: &'static str, +} + +#[derive(Diagnostic)] +#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] +#[help("import `macro_rules` with `use` to make it callable above its definition")] +pub(crate) struct OutOfScopeMacroCalls { + #[label("not found from {$location}")] + pub span: Span, + pub path: String, + pub location: String, +} + +#[derive(Diagnostic)] +#[diag( + "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" +)] +pub(crate) struct RedundantImportVisibility { + #[note("the most public imported item is `{$max_vis}`")] + pub span: Span, + #[help("reduce the glob import's visibility or increase visibility of imported items")] + pub help: (), + pub import_vis: String, + pub max_vis: String, +} + +#[derive(Diagnostic)] +#[diag("unknown diagnostic attribute")] +pub(crate) struct UnknownDiagnosticAttribute { + #[subdiagnostic] + pub help: Option, +} + +#[derive(Subdiagnostic)] +pub(crate) enum UnknownDiagnosticAttributeHelp { + #[suggestion( + "an attribute with a similar name exists", + style = "verbose", + code = "{typo_name}", + applicability = "machine-applicable" + )] + Typo { + #[primary_span] + span: Span, + typo_name: Symbol, }, + #[help("add `#![feature({$feature})]` to the crate attributes to enable")] + UseFeature { feature: Symbol }, } -pub(crate) fn import_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - mode: DiagMode, - append: &str, -) { - show_candidates( - tcx, - err, - use_placement_span, - candidates, - Instead::Yes, - FoundUse::Yes, - mode, - vec![], - append, - ); -} - -type PathString<'a> = (String, &'a str, Option, &'a Option, bool, bool); - -/// When an entity with a given name is not available in scope, we search for -/// entities with that name in all crates. This method allows outputting the -/// results of this search in a programmer-friendly way. If any entities are -/// found and suggested, returns `true`, otherwise returns `false`. -fn show_candidates( - tcx: TyCtxt<'_>, - err: &mut Diag<'_>, - // This is `None` if all placement locations are inside expansions - use_placement_span: Option, - candidates: &[ImportSuggestion], - instead: Instead, - found_use: FoundUse, - mode: DiagMode, - path: Vec, - append: &str, -) -> bool { - if candidates.is_empty() { - return false; - } +// FIXME: Make this properly translatable. +pub(crate) struct Ambiguity { + pub ident: Ident, + pub ambig_vis: Option, + pub kind: &'static str, + pub help: Option<&'static [&'static str]>, + pub b1_note: Spanned, + pub b1_help_msgs: Vec, + pub b2_note: Spanned, + pub b2_help_msgs: Vec, + /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. + pub is_error: bool, +} - let mut showed = false; - let mut accessible_path_strings: Vec> = Vec::new(); - let mut inaccessible_path_strings: Vec> = Vec::new(); - - candidates.iter().for_each(|c| { - if c.accessible { - // Don't suggest `#[doc(hidden)]` items from other crates - if c.doc_visible { - accessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - c.is_exact_match, - )) - } - } else { - inaccessible_path_strings.push(( - pprust::path_to_string(&c.path), - c.descr, - c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), - &c.note, - c.via_import, - c.is_exact_match, - )) +impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { + fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { + let Self { + ident, + ambig_vis, + kind, + help, + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error, + } = self; + + let mut diag = Diag::new(dcx, level, "").with_span(ident.span); + if is_error { + diag.code(E0659); } - }); - - // we want consistent results across executions, but candidates are produced - // by iterating through a hash map, so make sure they are ordered: - for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { - path_strings.sort_by(|a, b| a.0.cmp(&b.0)); - path_strings.dedup_by(|a, b| a.0 == b.0); - let core_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); - let std_path_strings = - path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); - let foreign_crate_path_strings = - path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); - - // We list the `crate` local paths first. - // Then we list the `std`/`core` paths. - if std_path_strings.len() == core_path_strings.len() { - // Do not list `core::` paths if we are already listing the `std::` ones. - path_strings.extend(std_path_strings); + if let Some(ambig_vis) = ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); } else { - path_strings.extend(std_path_strings); - path_strings.extend(core_path_strings); + diag.primary_message(format!("`{}` is ambiguous", ident)); + diag.span_label(ident.span, "ambiguous name"); } - // List all paths from foreign crates last. - path_strings.extend(foreign_crate_path_strings); - } - - if !accessible_path_strings.is_empty() { - let (determiner, kind, s, name, through) = - if let [(name, descr, _, _, via_import, is_exact_match)] = &accessible_path_strings[..] - { - ( - if *is_exact_match { "this" } else { "this similarly named" }, - *descr, - "", - format!(" `{name}`"), - if *via_import { " through its public re-export" } else { "" }, - ) - } else { - // Get the unique item kinds and if there's only one, we use the right kind name - // instead of the more generic "items". - let kinds = accessible_path_strings - .iter() - .map(|(_, descr, _, _, _, _)| *descr) - .collect::>(); - let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; - let s = if kind.ends_with('s') { "es" } else { "s" }; - // we should only suggest case insensitive suggestion if no case sensitive match was found, - // so all the suggestion should have the same is_exact_match value. - - ( - if accessible_path_strings[0].5 { - "one of these" - } else { - "one of these similarly named" - }, - kind, - s, - String::new(), - "", - ) - }; - - let instead = if let Instead::Yes = instead { " instead" } else { "" }; - let mut msg = if let DiagMode::Pattern = mode { - format!( - "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ - pattern", - ) - } else { - format!("consider importing {determiner} {kind}{s}{through}{instead}") - }; - - for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); + diag.note(format!("ambiguous because of {}", kind)); + diag.span_note(b1_note.span, b1_note.node); + if let Some(help) = help { + for help in help { + diag.help(*help); + } } + for help_msg in b1_help_msgs { + diag.help(help_msg); + } + diag.span_note(b2_note.span, b2_note.node); + for help_msg in b2_help_msgs { + diag.help(help_msg); + } + diag + } +} - let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { - msg.push(':'); +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` never used")] +pub(crate) struct UnusedLifetime { + #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] + pub deletion_span: Option, - for candidate in accessible_path_strings { - msg.push('\n'); - msg.push_str(&candidate.0); - } - }; - - if let Some(span) = use_placement_span { - let (add_use, trailing) = match mode { - DiagMode::Pattern => { - err.span_suggestions( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - ); - return true; - } - DiagMode::Import { .. } => ("", ""), - DiagMode::Normal => ("use ", ";\n"), - }; - for candidate in &mut accessible_path_strings { - // produce an additional newline to separate the new use statement - // from the directly following item. - let additional_newline = if let FoundUse::No = found_use - && let DiagMode::Normal = mode - { - "\n" - } else { - "" - }; - candidate.0 = - format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); - } + pub ident: Ident, +} - match mode { - DiagMode::Import { append: true, .. } => { - append_candidates(&mut msg, accessible_path_strings); - err.span_help(span, msg); - } - _ => { - err.span_suggestions_with_style( - span, - msg, - accessible_path_strings.into_iter().map(|a| a.0), - Applicability::MaybeIncorrect, - SuggestionStyle::ShowAlways, - ); - } - } +#[derive(Diagnostic)] +#[diag("ambiguous glob re-exports")] +pub(crate) struct AmbiguousGlobReexports { + #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] + pub first_reexport: Span, + #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] + pub duplicate_reexport: Span, - if let [first, .., last] = &path[..] { - let sp = first.ident.span.until(last.ident.span); - // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). - // Can happen for derive-generated spans. - if sp.can_be_used_for_suggestions() && !sp.is_empty() { - err.span_suggestion_verbose( - sp, - format!("if you import `{}`, refer to it directly", last.ident), - "", - Applicability::Unspecified, - ); - } - } - } else { - append_candidates(&mut msg, accessible_path_strings); - err.help(msg); - } - showed = true; - } - if !inaccessible_path_strings.is_empty() - && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) - { - let prefix = - if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; - if let [(name, descr, source_span, note, _, is_exact_match)] = - &inaccessible_path_strings[..] - { - let msg = format!( - "{prefix}{}{descr} `{name}`{} exists but is inaccessible", - if *is_exact_match { "" } else { "similarly named " }, - if let DiagMode::Pattern = mode { ", which" } else { "" } - ); - - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - let mut multi_span = MultiSpan::from_span(span); - multi_span.push_span_label(span, "not accessible"); - err.span_note(multi_span, msg); - } else { - err.note(msg); - } - if let Some(note) = (*note).as_deref() { - err.note(note.to_string()); - } - } else { - let descr = inaccessible_path_strings - .iter() - .map(|&(_, descr, _, _, _, _)| descr) - .all_equal_value() - .unwrap_or("item"); - let plural_descr = - if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; - let are_exact_matches = inaccessible_path_strings[0].5; - let mut msg = format!( - "{prefix}these {}{plural_descr} exist but are inaccessible", - if are_exact_matches { "" } else { "similarly named " }, - ); - let mut has_colon = false; - - let mut spans = Vec::new(); - for (name, _, source_span, _, _, _) in &inaccessible_path_strings { - if let Some(source_span) = source_span { - let span = tcx.sess.source_map().guess_head_span(*source_span); - spans.push((name, span)); - } else { - if !has_colon { - msg.push(':'); - has_colon = true; - } - msg.push('\n'); - msg.push_str(name); - } - } + pub name: String, + pub namespace: String, +} - let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); - for (name, span) in spans { - multi_span.push_span_label(span, format!("`{name}`: not accessible")); - } +#[derive(Diagnostic)] +#[diag("private item shadows public glob re-export")] +pub(crate) struct HiddenGlobReexports { + #[note( + "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" + )] + pub glob_reexport: Span, + #[note("but the private item here shadows it")] + pub private_item: Span, + + pub name: String, + pub namespace: String, +} - for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { - err.note(note.clone()); - } +#[derive(Diagnostic)] +#[diag("the item `{$ident}` is imported redundantly")] +pub(crate) struct RedundantImport { + #[subdiagnostic] + pub subs: Vec, + pub ident: Ident, +} - err.span_note(multi_span, msg); - } - showed = true; - } - showed +#[derive(Subdiagnostic)] +pub(crate) enum RedundantImportSub { + #[label("the item `{$ident}` is already imported here")] + ImportedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined here")] + DefinedHere { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already imported by the extern prelude")] + ImportedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, + #[label("the item `{$ident}` is already defined by the extern prelude")] + DefinedPrelude { + #[primary_span] + span: Span, + ident: Ident, + }, } -#[derive(Debug)] -struct UsePlacementFinder { - target_module: NodeId, - first_legal_span: Option, - first_use_span: Option, -} - -impl UsePlacementFinder { - fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { - let mut finder = - UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; - finder.visit_crate(krate); - if let Some(use_span) = finder.first_use_span { - (Some(use_span), FoundUse::Yes) - } else { - (finder.first_legal_span, FoundUse::No) - } - } +#[derive(Diagnostic)] +#[diag("unnecessary qualification")] +pub(crate) struct UnusedQualifications { + #[suggestion( + "remove the unnecessary path segments", + style = "verbose", + code = "", + applicability = "machine-applicable" + )] + pub removal_span: Span, } -impl<'tcx> Visitor<'tcx> for UsePlacementFinder { - fn visit_crate(&mut self, c: &Crate) { - if self.target_module == CRATE_NODE_ID { - let inject = c.spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(&c.items); - } else { - visit::walk_crate(self, c); - } - } +#[derive(Diagnostic)] +#[diag( + "{$elided -> + [true] `&` without an explicit lifetime name cannot be used here + *[false] `'_` cannot be used here + }" +)] +pub(crate) struct AssociatedConstElidedLifetime { + #[suggestion( + "use the `'static` lifetime", + style = "verbose", + code = "{code}", + applicability = "machine-applicable" + )] + pub span: Span, + + pub code: &'static str, + pub elided: bool, + #[note("cannot automatically infer `'static` because of other lifetimes in scope")] + pub lifetimes_in_scope: MultiSpan, +} - fn visit_item(&mut self, item: &'tcx ast::Item) { - if self.target_module == item.id { - if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { - let inject = mod_spans.inject_use_span; - if is_span_suitable_for_use_injection(inject) { - self.first_legal_span = Some(inject); - } - self.first_use_span = search_for_any_use_in_items(items); - } - } else { - visit::walk_item(self, item); - } - } +#[derive(Diagnostic)] +#[diag("lifetime parameter `{$ident}` only used once")] +pub(crate) struct SingleUseLifetime { + #[label("this lifetime...")] + pub param_span: Span, + #[label("...is used only here")] + pub use_span: Span, + #[subdiagnostic] + pub suggestion: Option, + + pub ident: Ident, } -#[derive(Default)] -struct BindingVisitor { - identifiers: Vec, - spans: FxHashMap>, +#[derive(Subdiagnostic)] +#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] +pub(crate) struct SingleUseLifetimeSugg { + #[suggestion_part(code = "")] + pub deletion_span: Option, + #[suggestion_part(code = "{replace_lt}")] + pub use_span: Span, + + pub replace_lt: String, } -impl<'tcx> Visitor<'tcx> for BindingVisitor { - fn visit_pat(&mut self, pat: &ast::Pat) { - if let ast::PatKind::Ident(_, ident, _) = pat.kind { - self.identifiers.push(ident.name); - self.spans.entry(ident.name).or_default().push(ident.span); - } - visit::walk_pat(self, pat); - } +#[derive(Diagnostic)] +#[diag( + "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" +)] +pub(crate) struct AbsPathWithModule { + #[subdiagnostic] + pub sugg: AbsPathWithModuleSugg, } -fn search_for_any_use_in_items(items: &[Box]) -> Option { - for item in items { - if let ItemKind::Use(..) = item.kind - && is_span_suitable_for_use_injection(item.span) - { - let mut lo = item.span.lo(); - for attr in &item.attrs { - if attr.span.eq_ctxt(item.span) { - lo = std::cmp::min(lo, attr.span.lo()); - } - } - return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); - } - } - None +#[derive(Subdiagnostic)] +#[suggestion("use `crate`", code = "{replacement}")] +pub(crate) struct AbsPathWithModuleSugg { + #[primary_span] + pub span: Span, + #[applicability] + pub applicability: Applicability, + pub replacement: String, } -fn is_span_suitable_for_use_injection(s: Span) -> bool { - // don't suggest placing a use before the prelude - // import or other generated ones - !s.from_expansion() +#[derive(Diagnostic)] +#[diag("hidden lifetime parameters in types are deprecated")] +pub(crate) struct ElidedLifetimesInPaths { + #[subdiagnostic] + pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, } -#[derive(Debug, Clone, Default)] -pub(crate) struct OnUnknownData { - pub(crate) directive: Box, +#[derive(Diagnostic)] +#[diag( + "{$num_snippets -> + [one] unused import: {$span_snippets} + *[other] unused imports: {$span_snippets} + }" +)] +pub(crate) struct UnusedImports { + #[subdiagnostic] + pub sugg: Option, + #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] + pub test_module_span: Option, + + pub span_snippets: DiagArgValue, + pub num_snippets: usize, } -impl OnUnknownData { - pub(crate) fn from_attrs<'tcx>( - tcx: TyCtxt<'tcx>, - attrs: &[ast::Attribute], - ) -> Option { - if tcx.features().diagnostic_on_unknown() - && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = - AttributeParser::parse_limited(tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown]) - { - Some(Self { directive: directive? }) - } else { - None - } - } +#[derive(Subdiagnostic)] +pub(crate) enum UnusedImportsSugg { + #[suggestion( + "remove the whole `use` item", + applicability = "machine-applicable", + code = "", + style = "tool-only" + )] + RemoveWholeUse { + #[primary_span] + span: Span, + }, + #[multipart_suggestion( + "{$num_to_remove -> + [one] remove the unused import + *[other] remove the unused imports + }", + applicability = "machine-applicable", + style = "tool-only" + )] + RemoveImports { + #[suggestion_part(code = "")] + remove_spans: Vec, + num_to_remove: usize, + }, } diff --git a/compiler/rustc_resolve/src/error_helper.rs b/compiler/rustc_resolve/src/error_helper.rs index 2baf423e296d6..c6cc58a6d3a30 100644 --- a/compiler/rustc_resolve/src/error_helper.rs +++ b/compiler/rustc_resolve/src/error_helper.rs @@ -1,1778 +1,4211 @@ +// ignore-tidy-filelength +use std::mem; +use std::ops::ControlFlow; + +use itertools::Itertools as _; +use rustc_ast::visit::{self, Visitor}; +use rustc_ast::{ + self as ast, CRATE_NODE_ID, Crate, DUMMY_NODE_ID, ItemKind, ModKind, NodeId, Path, + join_path_idents, +}; +use rustc_ast_pretty::pprust; +use rustc_attr_parsing::AttributeParser; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::codes::*; -use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, ElidedLifetimeInPathSubdiag, - EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, + Applicability, Diag, DiagCtxtHandle, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, + pluralize, struct_span_code_err, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; -use rustc_span::{Ident, Span, Spanned, Symbol}; - -use crate::Res; -use crate::late::PatternSource; - -#[derive(Diagnostic)] -#[diag("can't use {$is_self -> - [true] `Self` - *[false] generic parameters - } from outer item", code = E0401)] -#[note( - "nested items are independent from their parent item for everything except for privacy and name resolution" -)] -pub(crate) struct GenericParamsFromOuterItem { - #[primary_span] - #[label( - "use of {$is_self -> - [true] `Self` - *[false] generic parameter - } from outer item" - )] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) label: Option, - #[subdiagnostic] - pub(crate) refer_to_type_directly: Option, - #[subdiagnostic] - pub(crate) use_let: Option, - #[subdiagnostic] - pub(crate) sugg: Option, - #[subdiagnostic] - pub(crate) static_or_const: Option, - pub(crate) is_self: bool, - #[subdiagnostic] - pub(crate) item: Option, -} - -#[derive(Subdiagnostic)] -#[label( - "{$is_self -> - [true] `Self` - *[false] generic parameter - } used in this inner {$descr}" -)] -pub(crate) struct GenericParamsFromOuterItemInnerItem { - #[primary_span] - pub(crate) span: Span, - pub(crate) descr: String, - pub(crate) is_self: bool, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemStaticOrConst { - #[note("a `static` is a separate item from the item that contains it")] - Static, - #[note("a `const` is a separate item from the item that contains it")] - Const, -} - -#[derive(Subdiagnostic)] -pub(crate) enum GenericParamsFromOuterItemLabel { - #[label("can't use `Self` here")] - SelfTyParam(#[primary_span] Span), - #[label("`Self` type implicitly declared here, by this `impl`")] - SelfTyAlias(#[primary_span] Span), - #[label("type parameter from outer item")] - TyParam(#[primary_span] Span), - #[label("const parameter from outer item")] - ConstParam(#[primary_span] Span), -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try introducing a local generic parameter here", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemSugg { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try using a local `let` binding instead", - code = "let", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct GenericParamsFromOuterItemUseLet { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "refer to the type directly here instead", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UseTypeDirectly { - #[primary_span] - pub(crate) span: Span, - pub(crate) snippet: String, -} - -#[derive(Diagnostic)] -#[diag("the name `{$name}` is already used for a generic parameter in this item's generic parameters", code = E0403)] -pub(crate) struct NameAlreadyUsedInParameterList { - #[primary_span] - #[label("already used")] - pub(crate) span: Span, - #[label("first use of `{$name}`")] - pub(crate) first_use_span: Span, - pub(crate) name: Ident, -} - -#[derive(Diagnostic)] -#[diag("method `{$method}` is not a member of trait `{$trait_}`", code = E0407)] -pub(crate) struct MethodNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) method: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated function with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedFnWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("type `{$type_}` is not a member of trait `{$trait_}`", code = E0437)] -pub(crate) struct TypeNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) type_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated type with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedTypeWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("const `{$const_}` is not a member of trait `{$trait_}`", code = E0438)] -pub(crate) struct ConstNotMemberOfTrait { - #[primary_span] - #[label("not a member of trait `{$trait_}`")] - pub(crate) span: Span, - pub(crate) const_: Ident, - pub(crate) trait_: String, - #[subdiagnostic] - pub(crate) sub: Option, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "there is an associated constant with a similar name", - code = "{candidate}", - applicability = "maybe-incorrect" -)] -pub(crate) struct AssociatedConstWithSimilarNameExists { - #[primary_span] - pub(crate) span: Span, - pub(crate) candidate: Symbol, -} - -#[derive(Diagnostic)] -#[diag("variable `{$variable_name}` is bound inconsistently across alternatives separated by `|`", code = E0409)] -pub(crate) struct VariableBoundWithDifferentMode { - #[primary_span] - #[label("bound in different ways")] - pub(crate) span: Span, - #[label("first binding")] - pub(crate) first_binding_span: Span, - pub(crate) variable_name: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in this parameter list", code = E0415)] -pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { - #[primary_span] - #[label("used as parameter more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("identifier `{$identifier}` is bound more than once in the same pattern", code = E0416)] -pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { - #[primary_span] - #[label("used in a pattern more than once")] - pub(crate) span: Span, - pub(crate) identifier: Ident, -} - -#[derive(Diagnostic)] -#[diag("use of undeclared label `{$name}`", code = E0426)] -pub(crate) struct UndeclaredLabel { - #[primary_span] - #[label("undeclared label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) sub_reachable: Option, - #[subdiagnostic] - pub(crate) sub_reachable_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_unreachable: Option, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct LabelWithSimilarNameReachable(#[primary_span] pub(crate) Span); - -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct TryUsingSimilarlyNamedLabel { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, -} - -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is unreachable")] -pub(crate) struct UnreachableLabelWithSimilarNameExists { - #[primary_span] - pub(crate) ident_span: Span, -} - -#[derive(Diagnostic)] -#[diag("can't capture dynamic environment in a fn item", code = E0434)] -#[help("use the `|| {\"{\"} ... {\"}\"}` closure form instead")] -pub(crate) struct CannotCaptureDynamicEnvironmentInFnItem { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant", code = E0435)] -pub(crate) struct AttemptToUseNonConstantValueInConstant<'a> { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) with: Option>, - #[subdiagnostic] - pub(crate) with_label: Option, - #[subdiagnostic] - pub(crate) without: Option>, -} - -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider using `{$suggestion}` instead of `{$current}`", - style = "verbose", - applicability = "has-placeholders" -)] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithSuggestion<'a> { - // #[primary_span] - #[suggestion_part(code = "{suggestion} ")] - pub(crate) span: Span, - pub(crate) suggestion: &'a str, - #[suggestion_part(code = ": /* Type */")] - pub(crate) type_span: Option, - pub(crate) current: &'a str, -} - -#[derive(Subdiagnostic)] -#[label("non-constant value")] -pub(crate) struct AttemptToUseNonConstantValueInConstantLabelWithSuggestion { - #[primary_span] - pub(crate) span: Span, -} - -#[derive(Subdiagnostic)] -#[label("this would need to be a `{$suggestion}`")] -pub(crate) struct AttemptToUseNonConstantValueInConstantWithoutSuggestion<'a> { - #[primary_span] - pub(crate) ident_span: Span, - pub(crate) suggestion: &'a str, -} - -#[derive(Diagnostic)] -#[diag("{$shadowing_binding}s cannot shadow {$shadowed_binding}s", code = E0530)] -pub(crate) struct BindingShadowsSomethingUnacceptable<'a> { - #[primary_span] - #[label("cannot be named the same as {$article} {$shadowed_binding}")] - pub(crate) span: Span, - pub(crate) shadowing_binding: PatternSource, - pub(crate) shadowed_binding: Res, - pub(crate) article: &'a str, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[label("the {$shadowed_binding} `{$name}` is {$participle} here")] - pub(crate) shadowed_binding_span: Span, - pub(crate) participle: &'a str, - pub(crate) name: Symbol, -} - -#[derive(Subdiagnostic)] -#[suggestion( - "try specify the pattern arguments", - code = "{name}(..)", - applicability = "unspecified" -)] -pub(crate) struct BindingShadowsSomethingUnacceptableSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} - -#[derive(Diagnostic)] -#[diag("generic parameter defaults cannot reference parameters before they are declared", code = E0128)] -pub(crate) struct ForwardDeclaredGenericParam { - #[primary_span] - #[label("cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} +use rustc_feature::BUILTIN_ATTRIBUTES; +use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; +use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; +use rustc_hir::def::Namespace::{self, *}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; +use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; +use rustc_hir::{Attribute, PrimTy, Stability, StabilityLevel, find_attr}; +use rustc_middle::bug; +use rustc_middle::ty::{TyCtxt, Visibility}; +use rustc_session::Session; +use rustc_session::lint::builtin::{ + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, +}; +use rustc_session::utils::was_invoked_from_cargo; +use rustc_span::edit_distance::find_best_match_for_name; +use rustc_span::edition::Edition; +use rustc_span::hygiene::MacroKind; +use rustc_span::source_map::SourceMap; +use rustc_span::{ + BytePos, Ident, RemapPathScopeComponents, Span, Spanned, Symbol, SyntaxContext, kw, sym, +}; +use thin_vec::{ThinVec, thin_vec}; +use tracing::{debug, instrument}; -#[derive(Diagnostic)] -#[diag("const parameter types cannot reference parameters before they are declared")] -pub(crate) struct ForwardDeclaredGenericInConstParamTy { - #[primary_span] - #[label("const parameter type cannot reference `{$param}` before it is declared")] - pub(crate) span: Span, - pub(crate) param: Symbol, -} +use crate::diagnostics::{ + self, AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, + ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, + MaybeMissingMacroRulesName, +}; +use crate::hygiene::Macros20NormalizedSyntaxContext; +use crate::imports::{Import, ImportKind, UnresolvedImportError, import_path_to_string}; +use crate::late::{DiagMetadata, PatternSource, Rib}; +use crate::{ + AmbiguityError, AmbiguityKind, AmbiguityWarning, BindingError, BindingKey, Decl, DeclKind, + DelayedVisResolutionError, Finalize, ForwardGenericParamBanReason, HasGenericParams, IdentKey, + LateDecl, MacroRulesScope, Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, + PrivacyError, Res, ResolutionError, Resolver, Scope, ScopeSet, Segment, UseError, Used, + VisResolutionError, path_names_to_string, +}; -#[derive(Diagnostic)] -#[diag("the type of const parameters must not depend on other generic parameters", code = E0770)] -pub(crate) struct ParamInTyOfConstParam { - #[primary_span] - #[label("the type must not depend on the parameter `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, -} +/// A vector of spans and replacements, a message and applicability. +pub(crate) type Suggestion = (Vec<(Span, String)>, String, Applicability); -#[derive(Diagnostic)] -#[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] -pub(crate) struct SelfInGenericParamDefault { - #[primary_span] - pub(crate) span: Span, -} +/// Potential candidate for an undeclared or out-of-scope label - contains the ident of a +/// similarly named label and whether or not it is reachable. +pub(crate) type LabelSuggestion = (Ident, bool); -#[derive(Diagnostic)] -#[diag("cannot use `Self` in const parameter type")] -pub(crate) struct SelfInConstGenericTy { - #[primary_span] - pub(crate) span: Span, +#[derive(Clone)] +pub(crate) struct StructCtor { + pub res: Res, + pub vis: Visibility, + pub field_visibilities: Vec>, } -#[derive(Diagnostic)] -#[diag( - "{$is_gca -> - [true] generic parameters in const blocks are not allowed; use a named `const` item instead - *[false] generic parameters may not be used in const operations -}" -)] -pub(crate) struct ParamInNonTrivialAnonConst { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInNonTrivialAnonConst, - #[help("add `#![feature(generic_const_exprs)]` to allow generic const expressions")] - pub(crate) help: bool, - pub(crate) is_gca: bool, - #[help( - "consider factoring the expression into a `type const` item and use it as the const argument instead" - )] - pub(crate) help_gca: bool, +impl StructCtor { + pub(crate) fn has_private_fields<'ra>(&self, m: Module<'ra>, r: &Resolver<'ra, '_>) -> bool { + self.field_visibilities.iter().any(|&vis| !r.is_accessible_from(vis, m)) + } } #[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInNonTrivialAnonConst { - #[note("type parameters may not be used in const expressions")] - Type, - #[help("const parameters may only be used as standalone arguments here, i.e. `{$name}`")] - Const { name: Symbol }, - #[note("lifetime parameters may not be used in const expressions")] - Lifetime, -} - -#[derive(Diagnostic)] -#[diag("use of unreachable label `{$name}`", code = E0767)] -#[note("labels are unreachable through functions, closures, async blocks and modules")] -pub(crate) struct UnreachableLabel { - #[primary_span] - #[label("unreachable label `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[label("unreachable label defined here")] - pub(crate) definition_span: Span, - #[subdiagnostic] - pub(crate) sub_suggestion: Option, - #[subdiagnostic] - pub(crate) sub_suggestion_label: Option, - #[subdiagnostic] - pub(crate) sub_unreachable_label: Option, +pub(crate) enum SuggestionTarget { + /// The target has a similar name as the name used by the programmer (probably a typo) + SimilarlyNamed, + /// The target is the only valid item that can be used in the corresponding context + SingleItem, } -#[derive(Subdiagnostic)] -#[suggestion( - "try using similarly named label", - code = "{ident_name}", - applicability = "maybe-incorrect" -)] -pub(crate) struct UnreachableLabelSubSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident_name: Symbol, +#[derive(Debug)] +pub(crate) struct TypoSuggestion { + pub candidate: Symbol, + /// The source location where the name is defined; None if the name is not defined + /// in source e.g. primitives + pub span: Option, + pub res: Res, + pub target: SuggestionTarget, +} + +impl TypoSuggestion { + pub(crate) fn new(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn typo_from_name(candidate: Symbol, res: Res) -> TypoSuggestion { + Self { candidate, span: None, res, target: SuggestionTarget::SimilarlyNamed } + } + pub(crate) fn single_item(candidate: Symbol, span: Span, res: Res) -> TypoSuggestion { + Self { candidate, span: Some(span), res, target: SuggestionTarget::SingleItem } + } } -#[derive(Subdiagnostic)] -#[label("a label with a similar name is reachable")] -pub(crate) struct UnreachableLabelSubLabel { - #[primary_span] - pub(crate) ident_span: Span, -} +/// A free importable items suggested in case of resolution failure. +#[derive(Debug, Clone)] +pub(crate) struct ImportSuggestion { + pub did: Option, + pub descr: &'static str, + pub path: Path, + pub accessible: bool, + // false if the path traverses a foreign `#[doc(hidden)]` item. + pub doc_visible: bool, + pub via_import: bool, + /// An extra note that should be issued if this item is suggested + pub note: Option, + pub is_stable: bool, + pub is_exact_match: bool, +} + +/// Adjust the impl span so that just the `impl` keyword is taken by removing +/// everything after `<` (`"impl Iterator for A {}" -> "impl"`) and +/// everything after the first whitespace (`"impl Iterator for A" -> "impl"`). +/// +/// *Attention*: the method used is very fragile since it essentially duplicates the work of the +/// parser. If you need to use this function or something similar, please consider updating the +/// `source_map` functions and this function to something more robust. +fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span { + let impl_span = sm.span_until_char(impl_span, '<'); + sm.span_until_whitespace(impl_span) +} + +impl<'ra, 'tcx> Resolver<'ra, 'tcx> { + /// Reports unresolved imports. + /// + /// Multiple unresolved import errors within the same use tree are combined into a single + /// diagnostic. + pub(crate) fn throw_unresolved_import_error( + &mut self, + mut errors: Vec<(Import<'_>, UnresolvedImportError)>, + glob_error: bool, + ) { + errors.retain(|(_import, err)| match err.module { + // Skip `use` errors for `use foo::Bar;` if `foo.rs` has unrecovered parse errors. + Some(def_id) if self.mods_with_parse_errors.contains(&def_id) => false, + // If we've encountered something like `use _;`, we've already emitted an error stating + // that `_` is not a valid identifier, so we ignore that resolve error. + _ => err.segment.map(|s| s.name) != Some(kw::Underscore), + }); + if errors.is_empty() { + self.tcx.dcx().delayed_bug("expected a parse or \"`_` can't be an identifier\" error"); + return; + } -#[derive(Subdiagnostic)] -#[label("a label with a similar name exists but is also unreachable")] -pub(crate) struct UnreachableLabelSubLabelUnreachable { - #[primary_span] - pub(crate) ident_span: Span, -} + let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect()); + + let paths = errors + .iter() + .map(|(import, err)| { + let path = import_path_to_string( + &import.module_path.iter().map(|seg| seg.ident).collect::>(), + &import.kind, + err.span, + ); + format!("`{path}`") + }) + .collect::>(); + let default_message = + format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),); + + // Process `import` use of the `#[diagnostic::on_unknown]` attribute. + // + // We don't need to check feature gates here; that happens on initialization of the + // `on_unknown_attr` fields. + let (mut message, label, mut notes) = + if let Some(directive) = errors[0].1.on_unknown_attr.as_ref().map(|a| &a.directive) { + let this = errors + .iter() + .map(|(_import, err)| { + // Is this unwrap_or reachable? + err.segment.map(|s| s.name).unwrap_or(kw::Underscore) + }) + .join(", "); + + let args = FormatArgs { unresolved: this.clone(), this, .. }; + + let CustomDiagnostic { message, label, notes, parent_label: _dead } = + directive.eval(None, &args); + + (message, label, notes) + } else { + (None, None, Vec::new()) + }; + + // `module` use of the `#[diagnostic::on_unknown]` attribute. + // We assume that someone who put the attribute on the import has more information than + // the person who put it on the module, so we choose to prioritize the import attribute. + let mut mod_diagnostics: Vec = errors + .iter() + .map(|(import, import_error)| { + if let Some(ModuleOrUniformRoot::Module(module_data)) = import.imported_module.get() + && let ModuleKind::Def(DefKind::Mod, def_id, _, name) = module_data.kind + { + let Some(directive) = self.on_unknown_data(def_id) else { + return CustomDiagnostic::default(); + }; + + let this = if let Some(name) = name { + name.to_string() + } else if let Some(crate_name) = &self.tcx.sess.opts.crate_name { + crate_name.to_string() + } else { + "".to_string() + }; + let unresolved = import_error.segment.map(|s| s.name).unwrap_or(kw::Underscore); + let args = FormatArgs { this, unresolved: unresolved.to_string(), .. }; + + directive.eval(None, &args) + } else { + CustomDiagnostic::default() + } + }) + .collect(); + + // If there is no import attribute with a message, + // but all mod messages are the same, use that. + let mod_message = + mod_diagnostics.iter_mut().flat_map(|d| d.message.take()).all_equal_value(); + if message.is_none() + && let Ok(mod_msg) = mod_message + { + message = Some(mod_msg); + } -#[derive(Diagnostic)] -#[diag("invalid `sym` operand")] -#[help("`sym` operands must refer to either a function or a static")] -pub(crate) struct InvalidAsmSym { - #[primary_span] - #[label("is a local variable")] - pub(crate) span: Span, -} + let mut diag = if let Some(message) = message { + struct_span_code_err!(self.dcx(), span, E0432, "{message}").with_note(default_message) + } else { + struct_span_code_err!(self.dcx(), span, E0432, "{default_message}") + }; + + for mod_diag in mod_diagnostics.iter_mut() { + for mod_note in mod_diag.notes.drain(..) { + if !notes.contains(&mod_note) { + notes.push(mod_note); + } + } + } -#[derive(Diagnostic)] -#[diag("attempt to use a non-constant value in a constant")] -pub(crate) struct LowercaseSelf { - #[primary_span] - #[suggestion( - "try using `Self`", - code = "Self", - applicability = "maybe-incorrect", - style = "short" - )] - pub(crate) span: Span, -} + if !notes.is_empty() { + for note in notes { + diag.note(note); + } + } else if let Some((_, UnresolvedImportError { note: Some(note), .. })) = + errors.iter().last() + { + diag.note(note.clone()); + } -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("never patterns cannot contain variable bindings")] -pub(crate) struct BindingInNeverPattern { - #[primary_span] - #[suggestion( - "use a wildcard `_` instead", - code = "_", - applicability = "machine-applicable", - style = "short" - )] - pub(crate) span: Span, -} + /// Upper limit on the number of `span_label` messages. + const MAX_LABEL_COUNT: usize = 10; + let mod_labels = mod_diagnostics.into_iter().map(|cd| cd.label); + + for ((import, err), mod_label) in errors.into_iter().zip(mod_labels).take(MAX_LABEL_COUNT) { + let label_span = match err.segment { + Some(segment) => segment.span, + None => err.span, + }; + if let Some(label) = &label { + diag.span_label(label_span, label.clone()); + } else if let Some(label) = mod_label { + diag.span_label(label_span, label); + } else if let Some(label) = &err.label { + diag.span_label(label_span, label.clone()); + } -#[derive(Diagnostic)] -#[diag("duplicate definitions with name `{$name}`:", code = E0201)] -pub(crate) struct TraitImplDuplicate { - #[primary_span] - #[label("duplicate definition")] - pub(crate) span: Span, - #[label("previous definition here")] - pub(crate) old_span: Span, - #[label("item in trait")] - pub(crate) trait_item_span: Span, - pub(crate) name: Ident, -} + if let Some((suggestions, msg, applicability)) = err.suggestion { + if suggestions.is_empty() { + diag.help(msg); + continue; + } + diag.multipart_suggestion(msg, suggestions, applicability); + } -#[derive(Diagnostic)] -#[diag("relative paths are not supported in visibilities in 2018 edition or later")] -pub(crate) struct Relative2018 { - #[primary_span] - pub(crate) span: Span, - #[suggestion("try", code = "crate::{path_str}", applicability = "maybe-incorrect")] - pub(crate) path_span: Span, - pub(crate) path_str: String, -} + if let Some(candidates) = &err.candidates { + match &import.kind { + ImportKind::Single { nested: false, source, target, .. } => import_candidates( + self.tcx, + &mut diag, + Some(err.span), + candidates, + DiagMode::Import { append: false, unresolved_import: true }, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ), + ImportKind::Single { nested: true, source, target, .. } => { + import_candidates( + self.tcx, + &mut diag, + None, + candidates, + DiagMode::Normal, + (source != target) + .then(|| format!(" as {target}")) + .as_deref() + .unwrap_or(""), + ); + } + _ => {} + } + } -#[derive(Diagnostic)] -#[diag("visibilities can only be restricted to ancestor modules", code = E0742)] -pub(crate) struct AncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected module, found {$res} `{$path_str}`", code = E0577)] -pub(crate) struct ExpectedModuleFound { - #[primary_span] - #[label("not a module")] - pub(crate) span: Span, - pub(crate) res: Res, - pub(crate) path_str: String, -} + if matches!(import.kind, ImportKind::Single { .. }) + && let Some(segment) = err.segment + && let Some(module) = err.module + { + self.find_cfg_stripped(&mut diag, &segment.name, module) + } + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the visibility", code = E0578)] -pub(crate) struct Indeterminate(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("trait implementation can only be restricted to ancestor modules")] -pub(crate) struct RestrictionAncestorOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("cannot use a tool module through an import")] -pub(crate) struct ToolModuleImported { - #[primary_span] - pub(crate) span: Span, - #[note("the tool module imported here")] - pub(crate) import: Span, -} + let guar = diag.emit(); + if glob_error { + self.glob_error = Some(guar); + } + } -#[derive(Diagnostic)] -#[diag("visibility must resolve to a module")] -pub(crate) struct ModuleOnly(#[primary_span] pub(crate) Span); - -#[derive(Diagnostic)] -#[diag("expected {$expected}, found {$found} `{$macro_path}`")] -pub(crate) struct MacroExpectedFound<'a> { - #[primary_span] - #[label("not {$article} {$expected}")] - pub(crate) span: Span, - pub(crate) found: &'a str, - pub(crate) article: &'static str, - pub(crate) expected: &'a str, - pub(crate) macro_path: &'a str, - #[subdiagnostic] - pub(crate) remove_surrounding_derive: Option, - #[subdiagnostic] - pub(crate) add_as_non_derive: Option>, -} + pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> { + self.tcx.dcx() + } -#[derive(Subdiagnostic)] -#[help("remove from the surrounding `derive()`")] -pub(crate) struct RemoveSurroundingDerive { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn report_errors(&mut self, krate: &Crate) { + self.report_delayed_vis_resolution_errors(); + self.report_with_use_injections(krate); + + for &(span_use, span_def) in &self.macro_expanded_macro_export_errors { + self.lint_buffer.buffer_lint( + MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + CRATE_NODE_ID, + span_use, + diagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths { + definition: span_def, + }, + ); + } -#[derive(Subdiagnostic)] -#[help( - " - add as non-Derive macro - `#[{$macro_path}]`" -)] -pub(crate) struct AddAsNonDerive<'a> { - pub(crate) macro_path: &'a str, -} + for ambiguity_error in &self.ambiguity_errors { + let mut diag = self.ambiguity_diagnostic(ambiguity_error); + + if let Some(ambiguity_warning) = ambiguity_error.warning { + let node_id = match ambiguity_error.b1.0.kind { + DeclKind::Import { import, .. } => import.root_id, + DeclKind::Def(_) => CRATE_NODE_ID, + }; + + let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, + AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, + AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, + }; + + self.lint_buffer.buffer_lint(lint, node_id, diag.ident.span, diag); + } else { + diag.is_error = true; + self.dcx().emit_err(diag); + } + } -#[derive(Diagnostic)] -#[diag("can't use a procedural macro from the same crate that defines it")] -pub(crate) struct ProcMacroSameCrate { - #[primary_span] - pub(crate) span: Span, - #[help("you can define integration tests in a directory named `tests`")] - pub(crate) is_test: bool, -} + let mut reported_spans = FxHashSet::default(); + for error in mem::take(&mut self.privacy_errors) { + if reported_spans.insert(error.dedup_span) { + self.report_privacy_error(&error); + } + } + } -#[derive(Diagnostic)] -#[diag("cannot find {$ns_descr} `{$ident}` in this scope")] -pub(crate) struct ProcMacroDeriveResolutionFallback { - #[label("names from parent modules are not accessible without an explicit import")] - pub span: Span, - pub ns_descr: &'static str, - pub ident: Symbol, -} + fn report_delayed_vis_resolution_errors(&mut self) { + for DelayedVisResolutionError { vis, parent_scope, error } in + mem::take(&mut self.delayed_vis_resolution_errors) + { + match self.try_resolve_visibility(&parent_scope, &vis, true) { + Ok(_) => self.report_vis_error(error), + Err(error) => self.report_vis_error(error), + }; + } + } -#[derive(Diagnostic)] -#[diag( - "macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths" -)] -pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths { - #[note("the macro is defined here")] - pub definition: Span, -} + fn report_with_use_injections(&mut self, krate: &Crate) { + for UseError { mut err, candidates, node_id, instead, suggestion, path, is_call } in + mem::take(&mut self.use_injections) + { + let (span, found_use) = if node_id != DUMMY_NODE_ID { + UsePlacementFinder::check(krate, node_id) + } else { + (None, FoundUse::No) + }; + + if !candidates.is_empty() { + show_candidates( + self.tcx, + &mut err, + span, + &candidates, + if instead { Instead::Yes } else { Instead::No }, + found_use, + DiagMode::Normal, + path, + "", + ); + err.emit(); + } else if let Some((span, msg, sugg, appl)) = suggestion { + err.span_suggestion_verbose(span, msg, sugg, appl); + err.emit(); + } else if let [segment] = path.as_slice() + && is_call + { + err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod); + } else { + err.emit(); + } + } + } -#[derive(Diagnostic)] -#[diag("`#[macro_use]` is not supported on `extern crate self`")] -pub(crate) struct MacroUseExternCrateSelf { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn report_conflict( + &mut self, + ident: IdentKey, + ns: Namespace, + old_binding: Decl<'ra>, + new_binding: Decl<'ra>, + ) { + // Error on the second of two conflicting names + if old_binding.span.lo() > new_binding.span.lo() { + return self.report_conflict(ident, ns, new_binding, old_binding); + } -#[derive(Diagnostic)] -#[diag("not sure whether the path is accessible or not")] -#[note("the type may have associated items, but we are currently not checking them")] -pub(crate) struct CfgAccessibleUnsure { - #[primary_span] - pub(crate) span: Span, -} + let container = match old_binding.parent_module.unwrap().expect_local().kind { + // Avoid using TyCtxt::def_kind_descr in the resolver, because it + // indirectly *calls* the resolver, and would cause a query cycle. + ModuleKind::Def(kind, def_id, _, _) => kind.descr(def_id), + ModuleKind::Block => "block", + }; -#[derive(Debug)] -#[derive(Diagnostic)] -#[diag("generic parameters may not be used in enum discriminant values")] -pub(crate) struct ParamInEnumDiscriminant { - #[primary_span] - #[label("cannot perform const operation using `{$name}`")] - pub(crate) span: Span, - pub(crate) name: Symbol, - #[subdiagnostic] - pub(crate) param_kind: ParamKindInEnumDiscriminant, -} + let (name, span) = + (ident.name, self.tcx.sess.source_map().guess_head_span(new_binding.span)); -#[derive(Debug)] -#[derive(Subdiagnostic)] -pub(crate) enum ParamKindInEnumDiscriminant { - #[note("type parameters may not be used in enum discriminant values")] - Type, - #[note("const parameters may not be used in enum discriminant values")] - Const, - #[note("lifetime parameters may not be used in enum discriminant values")] - Lifetime, -} + if self.name_already_seen.get(&name) == Some(&span) { + return; + } -#[derive(Subdiagnostic)] -#[label("you can use `as` to change the binding name of the import")] -pub(crate) struct ChangeImportBinding { - #[primary_span] - pub(crate) span: Span, -} + let old_kind = match (ns, old_binding.res()) { + (ValueNS, _) => "value", + (MacroNS, _) => "macro", + (TypeNS, _) if old_binding.is_extern_crate() => "extern crate", + (TypeNS, Res::Def(DefKind::Mod, _)) => "module", + (TypeNS, Res::Def(DefKind::Trait, _)) => "trait", + (TypeNS, _) => "type", + }; + + let code = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) { + (true, true) => E0259, + (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() { + true => E0254, + false => E0260, + }, + _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) { + (false, false) => E0428, + (true, true) => E0252, + _ => E0255, + }, + }; + + let label = match new_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeLabel::Reimported { span, name }, + false => diagnostics::NameDefinedMultipleTimeLabel::Redefined { span, name }, + }; + + let old_binding_label = + (!old_binding.span.is_dummy() && old_binding.span != span).then(|| { + let span = self.tcx.sess.source_map().guess_head_span(old_binding.span); + match old_binding.is_import_user_facing() { + true => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Import { + span, + old_kind, + name, + }, + false => diagnostics::NameDefinedMultipleTimeOldBindingLabel::Definition { + span, + old_kind, + name, + }, + } + }); + + let mut err = self + .dcx() + .create_err(diagnostics::NameDefinedMultipleTime { + span, + name, + descr: ns.descr(), + container, + label, + old_binding_label, + }) + .with_code(code); + + // See https://github.com/rust-lang/rust/issues/32354 + use DeclKind::Import; + let can_suggest = |binding: Decl<'_>, import: self::Import<'_>| { + !binding.span.is_dummy() + && !matches!(import.kind, ImportKind::MacroUse { .. } | ImportKind::MacroExport) + }; + let import = match (&new_binding.kind, &old_binding.kind) { + // If there are two imports where one or both have attributes then prefer removing the + // import without attributes. + (Import { import: new, .. }, Import { import: old, .. }) + if { + (new.has_attributes || old.has_attributes) + && can_suggest(old_binding, *old) + && can_suggest(new_binding, *new) + } => + { + if old.has_attributes { + Some((*new, new_binding.span, true)) + } else { + Some((*old, old_binding.span, true)) + } + } + // Otherwise prioritize the new binding. + (Import { import, .. }, other) if can_suggest(new_binding, *import) => { + Some((*import, new_binding.span, other.is_import())) + } + (other, Import { import, .. }) if can_suggest(old_binding, *import) => { + Some((*import, old_binding.span, other.is_import())) + } + _ => None, + }; + + // Check if the target of the use for both bindings is the same. + let duplicate = new_binding.res().opt_def_id() == old_binding.res().opt_def_id(); + let has_dummy_span = new_binding.span.is_dummy() || old_binding.span.is_dummy(); + let from_item = + self.extern_prelude.get(&ident).is_none_or(|entry| entry.introduced_by_item()); + // Only suggest removing an import if both bindings are to the same def, if both spans + // aren't dummy spans. Further, if both bindings are imports, then the ident must have + // been introduced by an item. + let should_remove_import = duplicate + && !has_dummy_span + && ((new_binding.is_extern_crate() || old_binding.is_extern_crate()) || from_item); + + match import { + Some((import, span, true)) if should_remove_import && import.is_nested() => { + self.add_suggestion_for_duplicate_nested_use(&mut err, import, span); + } + Some((import, _, true)) if should_remove_import && !import.is_glob() => { + // Simple case - remove the entire import. Due to the above match arm, this can + // only be a single use so just remove it entirely. + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } + Some((import, span, _)) => { + self.add_suggestion_for_rename_of_use(&mut err, name, import, span); + } + _ => {} + } -#[derive(Subdiagnostic)] -#[suggestion( - "you can use `as` to change the binding name of the import", - code = "{suggestion}", - applicability = "maybe-incorrect" -)] -pub(crate) struct ChangeImportBindingSuggestion { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} + err.emit(); + self.name_already_seen.insert(name, span); + } -#[derive(Diagnostic)] -#[diag("imports cannot refer to {$what}")] -pub(crate) struct ImportsCannotReferTo<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) what: &'a str, -} + /// This function adds a suggestion to change the binding name of a new import that conflicts + /// with an existing import. + /// + /// ```text,ignore (diagnostic) + /// help: you can use `as` to change the binding name of the import + /// | + /// LL | use foo::bar as other_bar; + /// | ^^^^^^^^^^^^^^^^^^^^^ + /// ``` + fn add_suggestion_for_rename_of_use( + &self, + err: &mut Diag<'_>, + name: Symbol, + import: Import<'_>, + binding_span: Span, + ) { + let suggested_name = if name.as_str().chars().next().unwrap().is_uppercase() { + format!("Other{name}") + } else { + format!("other_{name}") + }; + + let mut suggestion = None; + let mut span = binding_span; + match import.kind { + ImportKind::Single { source, .. } => { + if let Some(pos) = source.span.hi().0.checked_sub(binding_span.lo().0) + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(binding_span) + && pos as usize <= snippet.len() + { + span = binding_span.with_lo(binding_span.lo() + BytePos(pos)).with_hi( + binding_span.hi() - BytePos(if snippet.ends_with(';') { 1 } else { 0 }), + ); + suggestion = Some(format!(" as {suggested_name}")); + } + } + ImportKind::ExternCrate { source, target, .. } => { + suggestion = Some(format!( + "extern crate {} as {};", + source.unwrap_or(target.name), + suggested_name, + )) + } + _ => unreachable!(), + } -#[derive(Diagnostic)] -#[diag("cannot find {$expected} `{$ident}` in this scope")] -pub(crate) struct CannotFindIdentInThisScope<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) expected: &'a str, - pub(crate) ident: Ident, -} + if let Some(suggestion) = suggestion { + err.subdiagnostic(ChangeImportBindingSuggestion { span, suggestion }); + } else { + err.subdiagnostic(ChangeImportBinding { span }); + } + } -#[derive(Subdiagnostic)] -#[note("unsafe traits like `{$ident}` should be implemented explicitly")] -pub(crate) struct ExplicitUnsafeTraits { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + /// This function adds a suggestion to remove an unnecessary binding from an import that is + /// nested. In the following example, this function will be invoked to remove the `a` binding + /// in the second use statement: + /// + /// ```ignore (diagnostic) + /// use issue_52891::a; + /// use issue_52891::{d, a, e}; + /// ``` + /// + /// The following suggestion will be added: + /// + /// ```ignore (diagnostic) + /// use issue_52891::{d, a, e}; + /// ^-- help: remove unnecessary import + /// ``` + /// + /// If the nested use contains only one import then the suggestion will remove the entire + /// line. + /// + /// It is expected that the provided import is nested - this isn't checked by the + /// function. If this invariant is not upheld, this function's behaviour will be unexpected + /// as characters expected by span manipulations won't be present. + fn add_suggestion_for_duplicate_nested_use( + &self, + err: &mut Diag<'_>, + import: Import<'_>, + binding_span: Span, + ) { + assert!(import.is_nested()); + + // Two examples will be used to illustrate the span manipulations we're doing: + // + // - Given `use issue_52891::{d, a, e};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, a, e};`. + // - Given `use issue_52891::{d, e, a};` where `a` is a duplicate then `binding_span` is + // `a` and `import.use_span` is `issue_52891::{d, e, a};`. + + let (found_closing_brace, span) = + find_span_of_binding_until_next_binding(self.tcx.sess, binding_span, import.use_span); + + // If there was a closing brace then identify the span to remove any trailing commas from + // previous imports. + if found_closing_brace { + if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) { + err.subdiagnostic(diagnostics::ToolOnlyRemoveUnnecessaryImport { span }); + } else { + // Remove the entire line if we cannot extend the span back, this indicates an + // `issue_52891::{self}` case. + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { + span: import.use_span_with_attributes, + }); + } -#[derive(Subdiagnostic)] -#[note("a macro with the same name exists, but it appears later")] -pub(crate) struct MacroDefinedLater { - #[primary_span] - pub(crate) span: Span, -} + return; + } -#[derive(Subdiagnostic)] -#[label("consider moving the definition of `{$ident}` before this call")] -pub(crate) struct MacroSuggMovePosition { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + err.subdiagnostic(diagnostics::RemoveUnnecessaryImport { span }); + } -#[derive(Subdiagnostic)] -pub(crate) enum MacroRulesNot { - #[label("`{$ident}` exists, but has no rules for function-like invocation")] - Func { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `attr` rules")] - Attr { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("`{$ident}` exists, but has no `derive` rules")] - Derive { - #[primary_span] - span: Span, - ident: Ident, - }, -} + pub(crate) fn lint_if_path_starts_with_module( + &mut self, + finalize: Finalize, + path: &[Segment], + second_binding: Option>, + ) { + let Finalize { node_id, root_span, .. } = finalize; + + let first_name = match path.get(0) { + // In the 2018 edition this lint is a hard error, so nothing to do + Some(seg) if seg.ident.span.is_rust_2015() && self.tcx.sess.is_rust_2015() => { + seg.ident.name + } + _ => return, + }; -#[derive(Subdiagnostic)] -#[note("maybe you have forgotten to define a name for this `macro_rules!`")] -pub(crate) struct MaybeMissingMacroRulesName { - #[primary_span] - pub(crate) spans: MultiSpan, -} + // We're only interested in `use` paths which should start with + // `{{root}}` currently. + if first_name != kw::PathRoot { + return; + } -#[derive(Subdiagnostic)] -#[help("have you added the `#[macro_use]` on the module/import?")] -pub(crate) struct AddedMacroUse; + match path.get(1) { + // If this import looks like `crate::...` it's already good + Some(Segment { ident, .. }) if ident.name == kw::Crate => return, + // Otherwise go below to see if it's an extern crate + Some(_) => {} + // If the path has length one (and it's `PathRoot` most likely) + // then we don't know whether we're gonna be importing a crate or an + // item in our crate. Defer this lint to elsewhere + None => return, + } -#[derive(Subdiagnostic)] -#[suggestion("consider adding a derive", code = "{suggestion}", applicability = "maybe-incorrect")] -pub(crate) struct ConsiderAddingADerive { - #[primary_span] - pub(crate) span: Span, - pub(crate) suggestion: String, -} + // If the first element of our path was actually resolved to an + // `ExternCrate` (also used for `crate::...`) then no need to issue a + // warning, this looks all good! + if let Some(binding) = second_binding + && let DeclKind::Import { import, .. } = binding.kind + // Careful: we still want to rewrite paths from renamed extern crates. + && let ImportKind::ExternCrate { source: None, .. } = import.kind + { + return; + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the import")] -pub(crate) struct CannotDetermineImportResolution { - #[primary_span] - pub(crate) span: Span, -} + self.lint_buffer.dyn_buffer_lint_any( + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, + node_id, + root_span, + move |dcx, level, sess| { + let (replacement, applicability) = match sess + .downcast_ref::() + .expect("expected a `Session`") + .source_map() + .span_to_snippet(root_span) + { + Ok(ref s) => { + // FIXME(Manishearth) ideally the emitting code + // can tell us whether or not this is global + let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" }; + + (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable) + } + Err(_) => ("crate::".to_string(), Applicability::HasPlaceholders), + }; + diagnostics::AbsPathWithModule { + sugg: diagnostics::AbsPathWithModuleSugg { + span: root_span, + applicability, + replacement, + }, + } + .into_diag(dcx, level) + }, + ); + } -#[derive(Diagnostic)] -#[diag("cannot determine resolution for the {$kind} `{$path}`")] -#[note("import resolution is stuck, try simplifying macro imports")] -pub(crate) struct CannotDetermineMacroResolution { - #[primary_span] - pub(crate) span: Span, - pub(crate) kind: &'static str, - pub(crate) path: String, -} + pub(crate) fn add_module_candidates( + &self, + module: Module<'ra>, + names: &mut Vec, + filter_fn: &impl Fn(Res) -> bool, + ctxt: Option, + ) { + module.for_each_child(self, |_this, ident, orig_ident_span, _ns, binding| { + let res = binding.res(); + if filter_fn(res) && ctxt.is_none_or(|ctxt| ctxt == *ident.ctxt) { + names.push(TypoSuggestion::new(ident.name, orig_ident_span, res)); + } + }); + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0364)] -pub(crate) struct CannotBeReexportedPrivate { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + /// Combines an error with provided span and emits it. + /// + /// This takes the error provided, combines it with the span and any additional spans inside the + /// error and emits it. + pub(crate) fn report_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> ErrorGuaranteed { + self.into_struct_error(span, resolution_error).emit() + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0364)] -pub(crate) struct CannotBeReexportedCratePublic { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn into_struct_error( + &mut self, + span: Span, + resolution_error: ResolutionError<'ra>, + ) -> Diag<'_> { + match resolution_error { + ResolutionError::GenericParamsFromOuterItem { + outer_res, + has_generic_params, + def_kind, + inner_item, + current_self_ty, + } => { + use diagnostics::GenericParamsFromOuterItemLabel as Label; + let static_or_const = match def_kind { + DefKind::Static { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Static) + } + DefKind::Const { .. } => { + Some(diagnostics::GenericParamsFromOuterItemStaticOrConst::Const) + } + _ => None, + }; + let is_self = + matches!(outer_res, Res::SelfTyParam { .. } | Res::SelfTyAlias { .. }); + let mut err = diagnostics::GenericParamsFromOuterItem { + span, + label: None, + refer_to_type_directly: None, + use_let: None, + sugg: None, + static_or_const, + is_self, + item: inner_item.as_ref().map(|(label_span, _, kind)| { + diagnostics::GenericParamsFromOuterItemInnerItem { + span: *label_span, + descr: kind.descr().to_string(), + is_self, + } + }), + }; + + let sm = self.tcx.sess.source_map(); + // Note: do not early return for missing def_id here, + // we still want to provide suggestions for `Res::SelfTyParam` and `Res::SelfTyAlias`. + let def_id = match outer_res { + Res::SelfTyParam { .. } => { + err.label = Some(Label::SelfTyParam(span)); + None + } + Res::SelfTyAlias { alias_to: def_id, .. } => { + err.label = Some(Label::SelfTyAlias(reduce_impl_span_to_impl_keyword( + sm, + self.def_span(def_id), + ))); + err.refer_to_type_directly = current_self_ty + .map(|snippet| diagnostics::UseTypeDirectly { span, snippet }); + None + } + Res::Def(DefKind::TyParam, def_id) => { + err.label = Some(Label::TyParam(self.def_span(def_id))); + Some(def_id) + } + Res::Def(DefKind::ConstParam, def_id) => { + err.label = Some(Label::ConstParam(self.def_span(def_id))); + Some(def_id) + } + _ => { + bug!( + "GenericParamsFromOuterItem should only be used with \ + Res::SelfTyParam, Res::SelfTyAlias, DefKind::TyParam or \ + DefKind::ConstParam" + ); + } + }; + + if let Some((_, item_span, ItemKind::Const(_))) = inner_item.as_ref() { + err.use_let = Some(diagnostics::GenericParamsFromOuterItemUseLet { + span: sm.span_until_whitespace(*item_span), + }); + } + + if let Some(def_id) = def_id + && let HasGenericParams::Yes(span) = has_generic_params + && !matches!(inner_item, Some((_, _, ItemKind::Delegation(..)))) + { + let name = self.tcx.item_name(def_id); + let (span, snippet) = if span.is_empty() { + let snippet = format!("<{name}>"); + (span, snippet) + } else { + let span = sm.span_through_char(span, '<').shrink_to_hi(); + let snippet = format!("{name}, "); + (span, snippet) + }; + err.sugg = Some(diagnostics::GenericParamsFromOuterItemSugg { span, snippet }); + } + + self.dcx().create_err(err) + } + ResolutionError::NameAlreadyUsedInParameterList(name, first_use_span) => { + self.dcx().create_err(diagnostics::NameAlreadyUsedInParameterList { + span, + first_use_span, + name, + }) + } + ResolutionError::MethodNotMemberOfTrait(method, trait_, candidate) => { + self.dcx().create_err(diagnostics::MethodNotMemberOfTrait { + span, + method, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedFnWithSimilarNameExists { + span: method.span, + candidate: c, + }), + }) + } + ResolutionError::TypeNotMemberOfTrait(type_, trait_, candidate) => { + self.dcx().create_err(diagnostics::TypeNotMemberOfTrait { + span, + type_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedTypeWithSimilarNameExists { + span: type_.span, + candidate: c, + }), + }) + } + ResolutionError::ConstNotMemberOfTrait(const_, trait_, candidate) => { + self.dcx().create_err(diagnostics::ConstNotMemberOfTrait { + span, + const_, + trait_, + sub: candidate.map(|c| diagnostics::AssociatedConstWithSimilarNameExists { + span: const_.span, + candidate: c, + }), + }) + } + ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => { + let BindingError { name, target, origin, could_be_path } = binding_error; + + let mut target_sp = target.iter().map(|pat| pat.span).collect::>(); + target_sp.sort(); + target_sp.dedup(); + let mut origin_sp = origin.iter().map(|(span, _)| *span).collect::>(); + origin_sp.sort(); + origin_sp.dedup(); + + let msp = MultiSpan::from_spans(target_sp.clone()); + let mut err = self.dcx().create_err(diagnostics::VariableIsNotBoundInAllPatterns { + multispan: msp, + name, + }); + for sp in target_sp { + err.subdiagnostic(diagnostics::PatternDoesntBindName { span: sp, name }); + } + for sp in &origin_sp { + err.subdiagnostic(diagnostics::VariableNotInAllPatterns { span: *sp }); + } + let mut suggested_typo = false; + if !target.iter().all(|pat| matches!(pat.kind, ast::PatKind::Ident(..))) + && !origin.iter().all(|(_, pat)| matches!(pat.kind, ast::PatKind::Ident(..))) + { + // The check above is so that when we encounter `match foo { (a | b) => {} }`, + // we don't suggest `(a | a) => {}`, which would never be what the user wants. + let mut target_visitor = BindingVisitor::default(); + for pat in &target { + target_visitor.visit_pat(pat); + } + target_visitor.identifiers.sort(); + target_visitor.identifiers.dedup(); + let mut origin_visitor = BindingVisitor::default(); + for (_, pat) in &origin { + origin_visitor.visit_pat(pat); + } + origin_visitor.identifiers.sort(); + origin_visitor.identifiers.dedup(); + // Find if the binding could have been a typo + if let Some(typo) = + find_best_match_for_name(&target_visitor.identifiers, name.name, None) + && !origin_visitor.identifiers.contains(&typo) + { + err.subdiagnostic(diagnostics::PatternBindingTypo { + spans: origin_sp, + typo, + }); + suggested_typo = true; + } + } + if could_be_path { + let import_suggestions = self.lookup_import_candidates( + name, + Namespace::ValueNS, + &parent_scope, + &|res: Res| { + matches!( + res, + Res::Def( + DefKind::Ctor(CtorOf::Variant, CtorKind::Const) + | DefKind::Ctor(CtorOf::Struct, CtorKind::Const) + | DefKind::Const { .. } + | DefKind::AssocConst { .. }, + _, + ) + ) + }, + ); + + if import_suggestions.is_empty() && !suggested_typo { + let kind_matches: [fn(DefKind) -> bool; 4] = [ + |kind| matches!(kind, DefKind::Ctor(CtorOf::Variant, CtorKind::Const)), + |kind| matches!(kind, DefKind::Ctor(CtorOf::Struct, CtorKind::Const)), + |kind| matches!(kind, DefKind::Const { .. }), + |kind| matches!(kind, DefKind::AssocConst { .. }), + ]; + let mut local_names = vec![]; + self.add_module_candidates( + parent_scope.module, + &mut local_names, + &|res| matches!(res, Res::Def(_, _)), + None, + ); + let local_names: FxHashSet<_> = local_names + .into_iter() + .filter_map(|s| match s.res { + Res::Def(_, def_id) => Some(def_id), + _ => None, + }) + .collect(); + + let mut local_suggestions = vec![]; + let mut suggestions = vec![]; + for matches_kind in kind_matches { + if let Some(suggestion) = self.early_lookup_typo_candidate( + ScopeSet::All(Namespace::ValueNS), + &parent_scope, + name, + &|res: Res| match res { + Res::Def(k, _) => matches_kind(k), + _ => false, + }, + ) && let Res::Def(kind, mut def_id) = suggestion.res + { + if let DefKind::Ctor(_, _) = kind { + def_id = self.tcx.parent(def_id); + } + let kind = kind.descr(def_id); + if local_names.contains(&def_id) { + // The item is available in the current scope. Very likely to + // be a typo. Don't use the full path. + local_suggestions.push(( + suggestion.candidate, + suggestion.candidate.to_string(), + kind, + )); + } else { + suggestions.push(( + suggestion.candidate, + self.def_path_str(def_id), + kind, + )); + } + } + } + let suggestions = if !local_suggestions.is_empty() { + // There is at least one item available in the current scope that is a + // likely typo. We only show those. + local_suggestions + } else { + suggestions + }; + for (name, sugg, kind) in suggestions { + err.span_suggestion_verbose( + span, + format!( + "you might have meant to use the similarly named {kind} `{name}`", + ), + sugg, + Applicability::MaybeIncorrect, + ); + suggested_typo = true; + } + } + if import_suggestions.is_empty() && !suggested_typo { + let help_msg = format!( + "if you meant to match on a unit struct, unit variant or a `const` \ + item, consider making the path in the pattern qualified: \ + `path::to::ModOrType::{name}`", + ); + err.span_help(span, help_msg); + } + show_candidates( + self.tcx, + &mut err, + Some(span), + &import_suggestions, + Instead::No, + FoundUse::Yes, + DiagMode::Pattern, + vec![], + "", + ); + } + err + } + ResolutionError::VariableBoundWithDifferentMode(variable_name, first_binding_span) => { + self.dcx().create_err(diagnostics::VariableBoundWithDifferentMode { + span, + first_binding_span, + variable_name, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInParameterList(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInParameterList { + span, + identifier, + }) + } + ResolutionError::IdentifierBoundMoreThanOnceInSamePattern(identifier) => { + self.dcx().create_err(diagnostics::IdentifierBoundMoreThanOnceInSamePattern { + span, + identifier, + }) + } + ResolutionError::UndeclaredLabel { name, suggestion } => { + let ((sub_reachable, sub_reachable_suggestion), sub_unreachable) = match suggestion + { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::LabelWithSimilarNameReachable(ident.span)), + Some(diagnostics::TryUsingSimilarlyNamedLabel { + span, + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelWithSimilarNameExists { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UndeclaredLabel { + span, + name, + sub_reachable, + sub_reachable_suggestion, + sub_unreachable, + }) + } + ResolutionError::FailedToResolve { segment, label, suggestion, module, message } => { + let mut err = struct_span_code_err!(self.dcx(), span, E0433, "{message}"); + err.span_label(span, label); + + if let Some((suggestions, msg, applicability)) = suggestion { + if suggestions.is_empty() { + err.help(msg); + return err; + } + err.multipart_suggestion(msg, suggestions, applicability); + } + + let module = match module { + Some(ModuleOrUniformRoot::Module(m)) if let Some(id) = m.opt_def_id() => id, + _ => CRATE_DEF_ID.to_def_id(), + }; + self.find_cfg_stripped(&mut err, &segment, module); + + err + } + ResolutionError::CannotCaptureDynamicEnvironmentInFnItem => { + self.dcx().create_err(diagnostics::CannotCaptureDynamicEnvironmentInFnItem { span }) + } + ResolutionError::AttemptToUseNonConstantValueInConstant { + ident, + suggestion, + current, + type_span, + } => { + // let foo =... + // ^^^ given this Span + // ------- get this Span to have an applicable suggestion + + // edit: + // only do this if the const and usage of the non-constant value are on the same line + // the further the two are apart, the higher the chance of the suggestion being wrong + + let sp = self + .tcx + .sess + .source_map() + .span_extend_to_prev_str(ident.span, current, true, false); + + let (with, with_label, without) = match sp { + Some(sp) if !self.tcx.sess.source_map().is_multiline(sp) => { + let sp = sp + .with_lo(BytePos(sp.lo().0 - (current.len() as u32))) + .until(ident.span); + + // Only suggest replacing the binding keyword if this is a simple + // binding. + // + // Note: this approach still incorrectly suggests for irrefutable + // patterns like `if let x = 1 { const { x } }`, since the text + // between `let` and the identifier is just whitespace. + // See tests/ui/consts/non-const-value-in-const-irrefutable-pat-binding.rs + let is_simple_binding = + self.tcx.sess.source_map().span_to_snippet(sp).is_ok_and(|snippet| { + let after_keyword = snippet[current.len()..].trim(); + after_keyword.is_empty() || after_keyword == "mut" + }); + + if is_simple_binding { + ( + Some(diagnostics::AttemptToUseNonConstantValueInConstantWithSuggestion { + span: sp, + suggestion, + current, + type_span, + }), + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } else { + ( + None, + Some(diagnostics::AttemptToUseNonConstantValueInConstantLabelWithSuggestion { span }), + None, + ) + } + } + _ => ( + None, + None, + Some( + diagnostics::AttemptToUseNonConstantValueInConstantWithoutSuggestion { + ident_span: ident.span, + suggestion, + }, + ), + ), + }; + + self.dcx().create_err(diagnostics::AttemptToUseNonConstantValueInConstant { + span, + with, + with_label, + without, + }) + } + ResolutionError::BindingShadowsSomethingUnacceptable { + shadowing_binding, + name, + participle, + article, + shadowed_binding, + shadowed_binding_span, + } => self.dcx().create_err(diagnostics::BindingShadowsSomethingUnacceptable { + span, + shadowing_binding, + shadowed_binding, + article, + sub_suggestion: match (shadowing_binding, shadowed_binding) { + ( + PatternSource::Match, + Res::Def(DefKind::Ctor(CtorOf::Variant | CtorOf::Struct, CtorKind::Fn), _), + ) => Some(diagnostics::BindingShadowsSomethingUnacceptableSuggestion { + span, + name, + }), + _ => None, + }, + shadowed_binding_span, + participle, + name, + }), + ResolutionError::ForwardDeclaredGenericParam(param, reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::ForwardDeclaredGenericParam { param, span }) + } + ForwardGenericParamBanReason::ConstParamTy => self + .dcx() + .create_err(diagnostics::ForwardDeclaredGenericInConstParamTy { param, span }), + }, + ResolutionError::ParamInTyOfConstParam { name } => { + self.dcx().create_err(diagnostics::ParamInTyOfConstParam { span, name }) + } + ResolutionError::ParamInNonTrivialAnonConst { is_gca, name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInNonTrivialAnonConst { + span, + name, + param_kind: is_type, + help: self.tcx.sess.is_nightly_build(), + is_gca, + help_gca: is_gca, + }) + } + ResolutionError::ParamInEnumDiscriminant { name, param_kind: is_type } => { + self.dcx().create_err(diagnostics::ParamInEnumDiscriminant { + span, + name, + param_kind: is_type, + }) + } + ResolutionError::ForwardDeclaredSelf(reason) => match reason { + ForwardGenericParamBanReason::Default => { + self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) + } + ForwardGenericParamBanReason::ConstParamTy => { + self.dcx().create_err(diagnostics::SelfInConstGenericTy { span }) + } + }, + ResolutionError::UnreachableLabel { name, definition_span, suggestion } => { + let ((sub_suggestion_label, sub_suggestion), sub_unreachable_label) = + match suggestion { + // A reachable label with a similar name exists. + Some((ident, true)) => ( + ( + Some(diagnostics::UnreachableLabelSubLabel { + ident_span: ident.span, + }), + Some(diagnostics::UnreachableLabelSubSuggestion { + span, + // intentionally taking 'ident.name' instead of 'ident' itself, as this + // could be used in suggestion context + ident_name: ident.name, + }), + ), + None, + ), + // An unreachable label with a similar name exists. + Some((ident, false)) => ( + (None, None), + Some(diagnostics::UnreachableLabelSubLabelUnreachable { + ident_span: ident.span, + }), + ), + // No similarly-named labels exist. + None => ((None, None), None), + }; + self.dcx().create_err(diagnostics::UnreachableLabel { + span, + name, + definition_span, + sub_suggestion, + sub_suggestion_label, + sub_unreachable_label, + }) + } + ResolutionError::TraitImplMismatch { + name, + kind, + code, + trait_item_span, + trait_path, + } => self + .dcx() + .create_err(diagnostics::TraitImplMismatch { + span, + name, + kind, + trait_path, + trait_item_span, + }) + .with_code(code), + ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => { + self.dcx().create_err(diagnostics::TraitImplDuplicate { + span, + name, + trait_item_span, + old_span, + }) + } + ResolutionError::InvalidAsmSym => { + self.dcx().create_err(diagnostics::InvalidAsmSym { span }) + } + ResolutionError::LowercaseSelf => { + self.dcx().create_err(diagnostics::LowercaseSelf { span }) + } + ResolutionError::BindingInNeverPattern => { + self.dcx().create_err(diagnostics::BindingInNeverPattern { span }) + } + } + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is private, and cannot be re-exported", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedPrivateNS { - #[primary_span] - #[label("re-export of private `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn report_vis_error( + &mut self, + vis_resolution_error: VisResolutionError, + ) -> ErrorGuaranteed { + match vis_resolution_error { + VisResolutionError::Relative2018(span, path) => { + self.dcx().create_err(diagnostics::Relative2018 { + span, + path_span: path.span, + // intentionally converting to String, as the text would also be used as + // in suggestion context + path_str: pprust::path_to_string(&path), + }) + } + VisResolutionError::AncestorOnly(span) => { + self.dcx().create_err(diagnostics::AncestorOnly(span)) + } + VisResolutionError::FailedToResolve(span, segment, label, suggestion, message) => self + .into_struct_error( + span, + ResolutionError::FailedToResolve { + segment, + label, + suggestion, + module: None, + message, + }, + ), + VisResolutionError::ExpectedFound(span, path_str, res) => { + self.dcx().create_err(diagnostics::ExpectedModuleFound { span, res, path_str }) + } + VisResolutionError::Indeterminate(span) => { + self.dcx().create_err(diagnostics::Indeterminate(span)) + } + VisResolutionError::ModuleOnly(span) => { + self.dcx().create_err(diagnostics::ModuleOnly(span)) + } + } + .emit() + } -#[derive(Diagnostic)] -#[diag("`{$ident}` is only public within the crate, and cannot be re-exported outside", code = E0365)] -#[note("consider declaring type or module `{$ident}` with `pub`")] -pub(crate) struct CannotBeReexportedCratePublicNS { - #[primary_span] - #[label("re-export of crate public `{$ident}`")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + pub(crate) fn def_path_str(&self, mut def_id: DefId) -> String { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + path.push(def_id); + if def_id.is_top_level_module() { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + path.into_iter() + .rev() + .map(|def_id| { + self.tcx + .opt_item_name(def_id) + .map(|name| { + match ( + def_id.is_top_level_module(), + def_id.is_local(), + self.tcx.sess.edition(), + ) { + (true, true, Edition::Edition2015) => String::new(), + (true, true, _) => kw::Crate.to_string(), + (true, false, _) | (false, _, _) => name.to_string(), + } + }) + .unwrap_or_else(|| "_".to_string()) + }) + .collect::>() + .join("::") + } -#[derive(Diagnostic)] -#[diag("extern crate `{$ident}` is private and cannot be re-exported", code = E0365)] -pub(crate) struct PrivateExternCrateReexport { - pub ident: Ident, - #[suggestion( - "consider making the `extern crate` item publicly accessible", - code = "pub ", - style = "verbose", - applicability = "maybe-incorrect" - )] - pub sugg: Span, -} + pub(crate) fn add_scope_set_candidates( + &mut self, + suggestions: &mut Vec, + scope_set: ScopeSet<'ra>, + ps: &ParentScope<'ra>, + sp: Span, + filter_fn: &impl Fn(Res) -> bool, + ) { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { + match scope { + Scope::DeriveHelpers(expn_id) => { + let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); + if filter_fn(res) { + suggestions.extend( + this.helper_attrs.get(&expn_id).into_iter().flatten().map( + |&(ident, orig_ident_span, _)| { + TypoSuggestion::new(ident.name, orig_ident_span, res) + }, + ), + ); + } + } + Scope::DeriveHelpersCompat => { + // Never recommend deprecated helper attributes. + } + Scope::MacroRules(macro_rules_scope) => { + if let MacroRulesScope::Def(macro_rules_def) = macro_rules_scope.get() { + let res = macro_rules_def.decl.res(); + if filter_fn(res) { + suggestions.push(TypoSuggestion::new( + macro_rules_def.ident.name, + macro_rules_def.orig_ident_span, + res, + )) + } + } + } + Scope::ModuleNonGlobs(module, _) => { + this.add_module_candidates(module, suggestions, filter_fn, None); + } + Scope::ModuleGlobs(..) => { + // Already handled in `ModuleNonGlobs`. + } + Scope::MacroUsePrelude => { + suggestions.extend(this.macro_use_prelude.iter().filter_map( + |(name, binding)| { + let res = binding.res(); + filter_fn(res).then_some(TypoSuggestion::typo_from_name(*name, res)) + }, + )); + } + Scope::BuiltinAttrs => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Builtin(sym::dummy)); + if filter_fn(res) { + suggestions.extend( + BUILTIN_ATTRIBUTES + .iter() + // These trace attributes are compiler-generated and have + // deliberately invalid names. + .filter(|attr| { + !matches!(**attr, sym::cfg_trace | sym::cfg_attr_trace) + }) + .map(|attr| TypoSuggestion::typo_from_name(*attr, res)), + ); + } + } + Scope::ExternPreludeItems => { + // Add idents from both item and flag scopes. + suggestions.extend(this.extern_prelude.iter().filter_map(|(ident, entry)| { + let res = Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()); + filter_fn(res).then_some(TypoSuggestion::new(ident.name, entry.span(), res)) + })); + } + Scope::ExternPreludeFlags => {} + Scope::ToolPrelude => { + let res = Res::NonMacroAttr(NonMacroAttrKind::Tool); + suggestions.extend( + this.registered_tools + .iter() + .map(|ident| TypoSuggestion::new(ident.name, ident.span, res)), + ); + } + Scope::StdLibPrelude => { + if let Some(prelude) = this.prelude { + let mut tmp_suggestions = Vec::new(); + this.add_module_candidates(prelude, &mut tmp_suggestions, filter_fn, None); + suggestions.extend( + tmp_suggestions + .into_iter() + .filter(|s| use_prelude.into() || this.is_builtin_macro(s.res)), + ); + } + } + Scope::BuiltinTypes => { + suggestions.extend(PrimTy::ALL.iter().filter_map(|prim_ty| { + let res = Res::PrimTy(*prim_ty); + filter_fn(res) + .then_some(TypoSuggestion::typo_from_name(prim_ty.name(), res)) + })) + } + } -#[derive(Subdiagnostic)] -#[help("consider adding a `#[macro_export]` to the macro in the imported module")] -pub(crate) struct ConsiderAddingMacroExport { - #[primary_span] - pub(crate) span: Span, -} + ControlFlow::<()>::Continue(()) + }); + } -#[derive(Subdiagnostic)] -#[suggestion( - "in case you want to use the macro within this crate only, reduce the visibility to `pub(crate)`", - code = "pub(crate)", - applicability = "maybe-incorrect" -)] -pub(crate) struct ConsiderMarkingAsPubCrate { - #[primary_span] - pub(crate) vis_span: Span, -} + /// Lookup typo candidate in scope for a macro or import. + fn early_lookup_typo_candidate( + &mut self, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + ident: Ident, + filter_fn: &impl Fn(Res) -> bool, + ) -> Option { + let mut suggestions = Vec::new(); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); + + // Make sure error reporting is deterministic. + suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); + + match find_best_match_for_name( + &suggestions.iter().map(|suggestion| suggestion.candidate).collect::>(), + ident.name, + None, + ) { + Some(found) if found != ident.name => { + suggestions.into_iter().find(|suggestion| suggestion.candidate == found) + } + _ => None, + } + } -#[derive(Subdiagnostic)] -#[note("consider marking `{$ident}` as `pub` in the imported module")] -pub(crate) struct ConsiderMarkingAsPub { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + fn lookup_import_candidates_from_module( + &self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + start_module: Module<'ra>, + crate_path: ThinVec, + ident_filter_fn: IdentFilterFn, + filter_fn: FilterFn, + ) -> Vec + where + IdentFilterFn: Fn(Ident, Ident) -> bool, + FilterFn: Fn(Res) -> bool, + { + let mut candidates = Vec::new(); + let mut seen_modules = FxHashSet::default(); + let start_did = start_module.def_id(); + let mut worklist = vec![( + start_module, + ThinVec::::new(), + true, + start_did.is_local() || !self.tcx.is_doc_hidden(start_did), + true, + )]; + let mut worklist_via_import = vec![]; + + while let Some((in_module, path_segments, accessible, doc_visible, is_stable)) = + match worklist.pop() { + None => worklist_via_import.pop(), + Some(x) => Some(x), + } + { + let in_module_is_extern = !in_module.def_id().is_local(); + in_module.for_each_child(self, |this, ident, orig_ident_span, ns, name_binding| { + // Avoid non-importable candidates. + if name_binding.is_assoc_item() + && !this.tcx.features().import_trait_associated_functions() + { + return; + } + + if ident.name == kw::Underscore { + return; + } + + let child_accessible = + accessible && this.is_accessible_from(name_binding.vis(), parent_scope.module); + + // do not venture inside inaccessible items of other crates + if in_module_is_extern && !child_accessible { + return; + } + + let via_import = name_binding.is_import() && !name_binding.is_extern_crate(); + + // There is an assumption elsewhere that paths of variants are in the enum's + // declaration and not imported. With this assumption, the variant component is + // chopped and the rest of the path is assumed to be the enum's own path. For + // errors where a variant is used as the type instead of the enum, this causes + // funny looking invalid suggestions, i.e `foo` instead of `foo::MyEnum`. + if via_import && name_binding.is_possibly_imported_variant() { + return; + } + + // #90113: Do not count an inaccessible reexported item as a candidate. + if let DeclKind::Import { source_decl, .. } = name_binding.kind + && this.is_accessible_from(source_decl.vis(), parent_scope.module) + && !this.is_accessible_from(name_binding.vis(), parent_scope.module) + { + return; + } + + let res = name_binding.res(); + let did = match res { + Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did), + _ => res.opt_def_id(), + }; + let child_doc_visible = doc_visible + && did.is_none_or(|did| did.is_local() || !this.tcx.is_doc_hidden(did)); + + // collect results based on the filter function + // avoid suggesting anything from the same module in which we are resolving + // avoid suggesting anything with a hygienic name + if ident_filter_fn(ident.orig(orig_ident_span), lookup_ident) + && ns == namespace + && in_module != parent_scope.module + && ident.ctxt.is_root() + && filter_fn(res) + { + // create the path + let mut segms = if lookup_ident.span.at_least_rust_2018() { + // crate-local absolute paths start with `crate::` in edition 2018 + // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) + crate_path.clone() + } else { + ThinVec::new() + }; + segms.append(&mut path_segments.clone()); + + segms.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + let path = Path { span: name_binding.span, segments: segms, tokens: None }; + + if child_accessible + // Remove invisible match if exists + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.accessible) + { + candidates.remove(idx); + } + + let is_stable = if is_stable + && let Some(did) = did + && this.is_stable(did, path.span) + { + true + } else { + false + }; + + // Rreplace unstable suggestions if we meet a new stable one, + // and do nothing if any other situation. For example, if we + // meet `std::ops::Range` after `std::range::legacy::Range`, + // we will remove the latter and then insert the former. + if is_stable + && let Some(idx) = candidates + .iter() + .position(|v: &ImportSuggestion| v.did == did && !v.is_stable) + { + candidates.remove(idx); + } + + if candidates.iter().all(|v: &ImportSuggestion| v.did != did) { + // See if we're recommending TryFrom, TryInto, or FromIterator and add + // a note about editions + let note = if let Some(did) = did { + let requires_note = !did.is_local() + && find_attr!( + this.tcx, + did, + RustcDiagnosticItem( + sym::TryInto | sym::TryFrom | sym::FromIterator + ) + ); + requires_note.then(|| { + format!( + "'{}' is included in the prelude starting in Edition 2021", + path_names_to_string(&path) + ) + }) + } else { + None + }; + + candidates.push(ImportSuggestion { + did, + descr: res.descr(), + path, + accessible: child_accessible, + doc_visible: child_doc_visible, + note, + via_import, + is_stable, + is_exact_match: ident.name == lookup_ident.name, + }); + } + } + + // collect submodules to explore + if let Some(def_id) = name_binding.res().module_like_def_id() { + // form the path + let mut path_segments = path_segments.clone(); + path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span))); + + let alias_import = if let DeclKind::Import { import, .. } = name_binding.kind + && let ImportKind::ExternCrate { source: Some(_), .. } = import.kind + && import.parent_scope.expansion == parent_scope.expansion + { + true + } else { + false + }; + + let is_extern_crate_that_also_appears_in_prelude = + name_binding.is_extern_crate() && lookup_ident.span.at_least_rust_2018(); + + if !is_extern_crate_that_also_appears_in_prelude || alias_import { + // add the module to the lookup + if seen_modules.insert(def_id) { + if via_import { &mut worklist_via_import } else { &mut worklist }.push( + ( + this.expect_module(def_id), + path_segments, + child_accessible, + child_doc_visible, + is_stable && this.is_stable(def_id, name_binding.span), + ), + ); + } + } + } + }) + } -#[derive(Diagnostic)] -#[diag("cannot glob-import all possible crates")] -pub(crate) struct CannotGlobImportAllCrates { - #[primary_span] - pub(crate) span: Span, -} + candidates + } -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to write a const parameter here", - code = "const ", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyToConstParamSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, -} + fn is_stable(&self, did: DefId, span: Span) -> bool { + if did.is_local() { + return true; + } -#[derive(Subdiagnostic)] -#[suggestion( - "you might have meant to introduce a const parameter `{$item_name}` on the {$item_location}", - code = "{snippet}", - applicability = "machine-applicable", - style = "verbose" -)] -pub(crate) struct UnexpectedMissingConstParameter { - #[primary_span] - pub span: Span, - pub snippet: String, - pub item_name: String, - pub item_location: String, -} + match self.tcx.lookup_stability(did) { + Some(Stability { + level: StabilityLevel::Unstable { implied_by, .. }, feature, .. + }) => { + if span.allows_unstable(feature) { + true + } else if self.tcx.features().enabled(feature) { + true + } else if let Some(implied_by) = implied_by + && self.tcx.features().enabled(implied_by) + { + true + } else { + false + } + } + Some(_) => true, + None => false, + } + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to write a const parameter here", - applicability = "has-placeholders", - style = "verbose" -)] -pub(crate) struct UnexpectedResChangeTyParamToConstParamSugg { - #[suggestion_part(code = "const ")] - pub before: Span, - #[suggestion_part(code = ": /* Type */")] - pub after: Span, -} + /// When name resolution fails, this method can be used to look up candidate + /// entities with the expected name. It allows filtering them using the + /// supplied predicate (which should be used to only accept the types of + /// definitions expected, e.g., traits). The lookup spans across all crates. + /// + /// N.B., the method does not look into imports, but this is not a problem, + /// since we report the definitions (thus, the de-aliased imports). + /// + /// The method is implemented in `lookup_import_candidates_impl`. The `_impl` method allows applying a different filter function on the ident than the exact match function used by default here. + pub(crate) fn lookup_import_candidates( + &mut self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + filter_fn: FilterFn, + ) -> Vec + where + FilterFn: Fn(Res) -> bool, + { + self.lookup_import_candidates_impl( + lookup_ident, + namespace, + parent_scope, + |ident: Ident, lookup_ident: Ident| ident.name == lookup_ident.name, + filter_fn, + ) + } -#[derive(Subdiagnostic)] -#[suggestion( - "if you meant to collect the rest of the slice in `{$ident}`, use the at operator", - code = "{snippet}", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnexpectedResUseAtOpInSlicePatWithRangeSugg { - #[primary_span] - pub span: Span, - pub ident: Ident, - pub snippet: String, -} + /// The actual impl of the `lookup_import_candidates function`. + pub(crate) fn lookup_import_candidates_impl( + &mut self, + lookup_ident: Ident, + namespace: Namespace, + parent_scope: &ParentScope<'ra>, + ident_filter_fn: IdentFilterFn, + filter_fn: FilterFn, + ) -> Vec + where + IdentFilterFn: Fn(Ident, Ident) -> bool, + FilterFn: Fn(Res) -> bool, + { + let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; + let mut suggestions = self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + self.graph_root.to_module(), + crate_path, + &ident_filter_fn, + &filter_fn, + ); + + if lookup_ident.span.at_least_rust_2018() { + for (ident, entry) in &self.extern_prelude { + if entry.span().from_expansion() { + // Idents are adjusted to the root context before being + // resolved in the extern prelude, so reporting this to the + // user is no help. This skips the injected + // `extern crate std` in the 2018 edition, which would + // otherwise cause duplicate suggestions. + continue; + } + let Some(crate_id) = + self.cstore_mut().maybe_process_path_extern(self.tcx, ident.name) + else { + continue; + }; + + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == *ident + && let Some(decl) = name_resolution.borrow().best_decl() + { + match decl.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); + } + crate_path.push(ast::PathSegment::from_ident(ident.orig(entry.span()))); + + suggestions.extend(self.lookup_import_candidates_from_module( + lookup_ident, + namespace, + parent_scope, + crate_root, + crate_path, + &ident_filter_fn, + &filter_fn, + )); + } + } -#[derive(Diagnostic)] -#[diag("an `extern crate` loading macros must be at the crate root", code = E0468)] -pub(crate) struct ExternCrateLoadingMacroNotAtCrateRoot { - #[primary_span] - pub(crate) span: Span, -} + suggestions.retain(|suggestion| suggestion.is_stable || self.tcx.sess.is_nightly_build()); + suggestions + } -#[derive(Diagnostic)] -#[diag("`extern crate self;` requires renaming")] -pub(crate) struct ExternCrateSelfRequiresRenaming { - #[primary_span] - #[suggestion( - "rename the `self` crate to be able to import it", - code = "extern crate self as name;", - applicability = "has-placeholders" - )] - pub(crate) span: Span, -} + pub(crate) fn unresolved_macro_suggestions( + &mut self, + err: &mut Diag<'_>, + macro_kind: MacroKind, + parent_scope: &ParentScope<'ra>, + ident: Ident, + krate: &Crate, + sugg_span: Option, + ) { + // Bring all unused `derive` macros into `macro_map` so we ensure they can be used for + // suggestions. + self.register_macros_for_all_crates(); + + let is_expected = + &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); + let suggestion = self.early_lookup_typo_candidate( + ScopeSet::Macro(macro_kind), + parent_scope, + ident, + is_expected, + ); + if !self.add_typo_suggestion(err, suggestion, ident.span) { + self.detect_derive_attribute(err, ident, parent_scope, sugg_span); + } -#[derive(Diagnostic)] -#[diag("`{$name}` is already in scope")] -#[note("macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)")] -pub(crate) struct MacroUseNameAlreadyInUse { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, -} + let import_suggestions = + self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected); + let (span, found_use) = match parent_scope.module.nearest_parent_mod_node_id() { + DUMMY_NODE_ID => (None, FoundUse::No), + node_id => UsePlacementFinder::check(krate, node_id), + }; + show_candidates( + self.tcx, + err, + span, + &import_suggestions, + Instead::No, + found_use, + DiagMode::Normal, + vec![], + "", + ); + + if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules { + let label_span = ident.span.shrink_to_hi(); + let mut spans = MultiSpan::from_span(label_span); + spans.push_span_label(label_span, "put a macro name here"); + err.subdiagnostic(MaybeMissingMacroRulesName { spans }); + return; + } -#[derive(Diagnostic)] -#[diag("imported macro not found", code = E0469)] -pub(crate) struct ImportedMacroNotFound { - #[primary_span] - pub(crate) span: Span, -} + if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) { + err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident }); + return; + } -#[derive(Diagnostic)] -#[diag("`#[macro_escape]` is a deprecated synonym for `#[macro_use]`")] -pub(crate) struct MacroExternDeprecated { - #[primary_span] - pub(crate) span: Span, - #[help("try an outer attribute: `#[macro_use]`")] - pub inner_attribute: bool, -} + let unused_macro = self.unused_macros.iter().find_map(|(def_id, (_, unused_ident))| { + if unused_ident.name == ident.name { Some((def_id, unused_ident)) } else { None } + }); + + if let Some((def_id, unused_ident)) = unused_macro { + let scope = self.local_macro_def_scopes[&def_id]; + let parent_nearest = parent_scope.module.nearest_parent_mod(); + let unused_macro_kinds = self.local_macro_map[def_id].macro_kinds(); + if !unused_macro_kinds.contains(macro_kind.into()) { + match macro_kind { + MacroKind::Bang => { + err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident }); + } + MacroKind::Attr => { + err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident }); + } + MacroKind::Derive => { + err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident }); + } + } + return; + } + if Some(parent_nearest) == scope.opt_def_id() { + err.subdiagnostic(MacroDefinedLater { span: unused_ident.span }); + err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident }); + return; + } + } -#[derive(Diagnostic)] -#[diag("arguments to `macro_use` are not allowed here")] -pub(crate) struct ArgumentsMacroUseNotAllowed { - #[primary_span] - pub(crate) span: Span, -} + if ident.name == kw::Default + && let ModuleKind::Def(DefKind::Enum, def_id, _, _) = parent_scope.module.kind + { + let span = self.def_span(def_id); + let source_map = self.tcx.sess.source_map(); + let head_span = source_map.guess_head_span(span); + err.subdiagnostic(ConsiderAddingADerive { + span: head_span.shrink_to_lo(), + suggestion: "#[derive(Default)]\n".to_string(), + }); + } + for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] { + let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns), + parent_scope, + None, + None, + None, + ) else { + continue; + }; + + let desc = match binding.res() { + Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { + "a function-like macro".to_string() + } + Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { + format!("an attribute: `#[{ident}]`") + } + Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { + format!("a derive macro: `#[derive({ident})]`") + } + Res::Def(DefKind::Macro(kinds), _) => { + format!("{} {}", kinds.article(), kinds.descr()) + } + Res::ToolMod | Res::OpenMod(..) => { + // Don't confuse the user with tool modules or open modules. + continue; + } + Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => { + "only a trait, without a derive macro".to_string() + } + res => format!( + "{} {}, not {} {}", + res.article(), + res.descr(), + macro_kind.article(), + macro_kind.descr_expected(), + ), + }; + if let crate::DeclKind::Import { import, .. } = binding.kind + && !import.span.is_dummy() + { + let note = diagnostics::IdentImporterHereButItIsDesc { + span: import.span, + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + // Silence the 'unused import' warning we might get, + // since this diagnostic already covers that import. + self.record_use(ident, binding, Used::Other); + return; + } + let note = diagnostics::IdentInScopeButItIsDesc { + imported_ident: ident, + imported_ident_desc: &desc, + }; + err.subdiagnostic(note); + return; + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "try renaming it with a name", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct UnnamedImportSugg { - #[suggestion_part(code = "{ident} as name")] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + if self.macro_names.contains(&IdentKey::new(ident)) { + err.subdiagnostic(AddedMacroUse); + return; + } + } -#[derive(Diagnostic)] -#[diag("imports need to be explicitly named")] -pub(crate) struct UnnamedImport { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) sugg: UnnamedImportSugg, -} + /// Given an attribute macro that failed to be resolved, look for `derive` macros that could + /// provide it, either as-is or with small typos. + fn detect_derive_attribute( + &self, + err: &mut Diag<'_>, + ident: Ident, + parent_scope: &ParentScope<'ra>, + sugg_span: Option, + ) { + // Find all of the `derive`s in scope and collect their corresponding declared + // attributes. + // FIXME: this only works if the crate that owns the macro that has the helper_attr + // has already been imported. + let mut derives = vec![]; + let mut all_attrs: UnordMap> = UnordMap::default(); + // We're collecting these in a hashmap, and handle ordering the output further down. + #[allow(rustc::potential_query_instability)] + for (def_id, ext) in self + .local_macro_map + .iter() + .map(|(local_id, ext)| (local_id.to_def_id(), ext)) + .chain(self.extern_macro_map.borrow().iter().map(|(id, d)| (*id, d))) + { + for helper_attr in &ext.helper_attrs { + let item_name = self.tcx.item_name(def_id); + all_attrs.entry(*helper_attr).or_default().push(item_name); + if helper_attr == &ident.name { + derives.push(item_name); + } + } + } + let kind = MacroKind::Derive.descr(); + if !derives.is_empty() { + // We found an exact match for the missing attribute in a `derive` macro. Suggest it. + let mut derives: Vec = derives.into_iter().map(|d| d.to_string()).collect(); + derives.sort(); + derives.dedup(); + let msg = match &derives[..] { + [derive] => format!(" `{derive}`"), + [start @ .., last] => format!( + "s {} and `{last}`", + start.iter().map(|d| format!("`{d}`")).collect::>().join(", ") + ), + [] => unreachable!("we checked for this to be non-empty 10 lines above!?"), + }; + let msg = format!( + "`{}` is an attribute that can be used by the {kind}{msg}, you might be \ + missing a `derive` attribute", + ident.name, + ); + let sugg_span = + if let ModuleKind::Def(DefKind::Enum, id, _, _) = parent_scope.module.kind { + let span = self.def_span(id); + if span.from_expansion() { + None + } else { + // For enum variants sugg_span is empty but we can get the enum's Span. + Some(span.shrink_to_lo()) + } + } else { + // For items this `Span` will be populated, everything else it'll be None. + sugg_span + }; + match sugg_span { + Some(span) => { + err.span_suggestion_verbose( + span, + msg, + format!("#[derive({})]\n", derives.join(", ")), + Applicability::MaybeIncorrect, + ); + } + None => { + err.note(msg); + } + } + } else { + // We didn't find an exact match. Look for close matches. If any, suggest fixing typo. + let all_attr_names = all_attrs.keys().map(|s| *s).into_sorted_stable_ord(); + if let Some(best_match) = find_best_match_for_name(&all_attr_names, ident.name, None) + && let Some(macros) = all_attrs.get(&best_match) + { + let mut macros: Vec = macros.into_iter().map(|d| d.to_string()).collect(); + macros.sort(); + macros.dedup(); + let msg = match ¯os[..] { + [] => return, + [name] => format!(" `{name}` accepts"), + [start @ .., end] => format!( + "s {} and `{end}` accept", + start.iter().map(|m| format!("`{m}`")).collect::>().join(", "), + ), + }; + let msg = format!("the {kind}{msg} the similarly named `{best_match}` attribute"); + err.span_suggestion_verbose( + ident.span, + msg, + best_match, + Applicability::MaybeIncorrect, + ); + } + } + } -#[derive(Diagnostic)] -#[diag("macro-expanded `extern crate` items cannot shadow names passed with `--extern`")] -pub(crate) struct MacroExpandedExternCrateCannotShadowExternArguments { - #[primary_span] - pub(crate) span: Span, -} + pub(crate) fn add_typo_suggestion( + &self, + err: &mut Diag<'_>, + suggestion: Option, + span: Span, + ) -> bool { + let suggestion = match suggestion { + None => return false, + // We shouldn't suggest underscore. + Some(suggestion) if suggestion.candidate == kw::Underscore => return false, + Some(suggestion) => suggestion, + }; + + let mut did_label_def_span = false; + + if let Some(def_span) = suggestion.res.opt_def_id().map(|def_id| self.def_span(def_id)) { + if span.overlaps(def_span) { + // Don't suggest typo suggestion for itself like in the following: + // error[E0423]: expected function, tuple struct or tuple variant, found struct `X` + // --> $DIR/unicode-string-literal-syntax-error-64792.rs:4:14 + // | + // LL | struct X {} + // | ----------- `X` defined here + // LL | + // LL | const Y: X = X("ö"); + // | -------------^^^^^^- similarly named constant `Y` defined here + // | + // help: use struct literal syntax instead + // | + // LL | const Y: X = X {}; + // | ^^^^ + // help: a constant with a similar name exists + // | + // LL | const Y: X = Y("ö"); + // | ^ + return false; + } + let span = self.tcx.sess.source_map().guess_head_span(def_span); + let candidate_descr = suggestion.res.descr(); + let candidate = suggestion.candidate; + let label = match suggestion.target { + SuggestionTarget::SimilarlyNamed => { + diagnostics::DefinedHere::SimilarlyNamed { span, candidate_descr, candidate } + } + SuggestionTarget::SingleItem => { + diagnostics::DefinedHere::SingleItem { span, candidate_descr, candidate } + } + }; + did_label_def_span = true; + err.subdiagnostic(label); + } -#[derive(Diagnostic)] -#[diag("`&` without an explicit lifetime name cannot be used here", code = E0637)] -pub(crate) struct ElidedAnonymousLifetimeReportError { - #[primary_span] - #[label("explicit lifetime name needed here")] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) suggestion: Option, -} + let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && let Some(span) = suggestion.span + && let Some(candidate) = suggestion.candidate.as_str().strip_prefix('_') + && snippet == candidate + { + let candidate = suggestion.candidate; + // When the suggested binding change would be from `x` to `_x`, suggest changing the + // original binding definition instead. (#60164) + let msg = format!( + "the leading underscore in `{candidate}` marks it as unused, consider renaming it to `{snippet}`" + ); + if !did_label_def_span { + err.span_label(span, format!("`{candidate}` defined here")); + } + (span, msg, snippet) + } else { + let msg = match suggestion.target { + SuggestionTarget::SimilarlyNamed => format!( + "{} {} with a similar name exists", + suggestion.res.article(), + suggestion.res.descr() + ), + SuggestionTarget::SingleItem => { + format!("maybe you meant this {}", suggestion.res.descr()) + } + }; + (span, msg, suggestion.candidate.to_ident_string()) + }; + err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); + true + } -#[derive(Diagnostic)] -#[diag( - "associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" -)] -pub(crate) struct LendingIteratorReportError { - #[primary_span] - pub(crate) lifetime: Span, - #[note( - "you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type" - )] - pub(crate) ty: Span, -} + fn decl_description(&self, b: Decl<'_>, ident: Ident, scope: Scope<'_>) -> String { + let res = b.res(); + if b.span.is_dummy() || !self.tcx.sess.source_map().is_span_accessible(b.span) { + let (built_in, from) = match scope { + Scope::StdLibPrelude | Scope::MacroUsePrelude => ("", " from prelude"), + Scope::ExternPreludeFlags + if self.tcx.sess.opts.externs.get(ident.as_str()).is_some() + || matches!(res, Res::OpenMod(..)) => + { + ("", " passed with `--extern`") + } + _ => { + if matches!(res, Res::NonMacroAttr(..) | Res::PrimTy(..) | Res::ToolMod) { + // These already contain the "built-in" prefix or look bad with it. + ("", "") + } else { + (" built-in", "") + } + } + }; + + let a = if built_in.is_empty() { res.article() } else { "a" }; + format!("{a}{built_in} {thing}{from}", thing = res.descr()) + } else { + let introduced = if b.is_import_user_facing() { "imported" } else { "defined" }; + format!("the {thing} {introduced} here", thing = res.descr()) + } + } -#[derive(Diagnostic)] -#[diag("missing lifetime in associated type")] -pub(crate) struct AnonymousLifetimeNonGatReportError { - #[primary_span] - #[label("this lifetime must come from the implemented type")] - pub(crate) lifetime: Span, - #[note( - "in the trait the associated type is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type" - )] - pub(crate) decl: MultiSpan, -} + fn ambiguity_diagnostic( + &self, + ambiguity_error: &AmbiguityError<'ra>, + ) -> diagnostics::Ambiguity { + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; + let extern_prelude_ambiguity = || { + // Note: b1 may come from a module scope, as an extern crate item in module. + matches!(scope2, Scope::ExternPreludeFlags) + && self + .extern_prelude + .get(&IdentKey::new(ident)) + .is_some_and(|entry| entry.item_decl.map(|(b, ..)| b) == Some(b1)) + }; + let (b1, b2, scope1, scope2, swapped) = if b2.span.is_dummy() && !b1.span.is_dummy() { + // We have to print the span-less alternative first, otherwise formatting looks bad. + (b2, b1, scope2, scope1, true) + } else { + (b1, b2, scope1, scope2, false) + }; + + let could_refer_to = |b: Decl<'_>, scope: Scope<'ra>, also: &str| { + let what = self.decl_description(b, ident, scope); + let note_msg = format!("`{ident}` could{also} refer to {what}"); + + let thing = b.res().descr(); + let mut help_msgs = Vec::new(); + if b.is_glob_import() + && (kind == AmbiguityKind::GlobVsGlob + || kind == AmbiguityKind::GlobVsExpanded + || kind == AmbiguityKind::GlobVsOuter && swapped != also.is_empty()) + { + help_msgs.push(format!( + "consider adding an explicit import of `{ident}` to disambiguate" + )) + } + if b.is_extern_crate() && ident.span.at_least_rust_2018() && !extern_prelude_ambiguity() + { + help_msgs.push(format!("use `::{ident}` to refer to this {thing} unambiguously")) + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "consider introducing a higher-ranked lifetime here", - applicability = "machine-applicable" -)] -pub(crate) struct ElidedAnonymousLifetimeReportErrorSuggestion { - #[suggestion_part(code = "for<'a> ")] - pub(crate) lo: Span, - #[suggestion_part(code = "'a ")] - pub(crate) hi: Span, -} + if kind != AmbiguityKind::GlobVsGlob { + if let Scope::ModuleNonGlobs(module, _) | Scope::ModuleGlobs(module, _) = scope { + if module == self.graph_root.to_module() { + help_msgs.push(format!( + "use `crate::{ident}` to refer to this {thing} unambiguously" + )); + } else if module.is_normal() { + help_msgs.push(format!( + "use `self::{ident}` to refer to this {thing} unambiguously" + )); + } + } + } -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -pub(crate) struct ExplicitAnonymousLifetimeReportError { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} + ( + Spanned { node: note_msg, span: b.span }, + help_msgs + .iter() + .enumerate() + .map(|(i, help_msg)| { + let or = if i == 0 { "" } else { "or " }; + format!("{or}{help_msg}") + }) + .collect::>(), + ) + }; + let (b1_note, b1_help_msgs) = could_refer_to(b1, scope1, ""); + let (b2_note, b2_help_msgs) = could_refer_to(b2, scope2, " also"); + let help = if kind == AmbiguityKind::GlobVsGlob + && b1 + .parent_module + .and_then(|m| m.opt_def_id()) + .map(|d| !d.is_local()) + .unwrap_or_default() + { + Some(&[ + "consider updating this dependency to resolve this error", + "if updating the dependency does not resolve the problem report the problem to the author of the relevant crate", + ] as &[_]) + } else { + None + }; + + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + + diagnostics::Ambiguity { + ident, + help, + ambig_vis, + kind: kind.descr(), + b1_note, + b1_help_msgs, + b2_note, + b2_help_msgs, + is_error: false, + } + } -#[derive(Diagnostic)] -#[diag("implicit elided lifetime not allowed here", code = E0726)] -pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { - #[primary_span] - pub(crate) span: Span, - #[subdiagnostic] - pub(crate) subdiag: ElidedLifetimeInPathSubdiag, -} + /// If the binding refers to a tuple struct constructor with fields, + /// returns the span of its fields. + fn ctor_fields_span(&self, decl: Decl<'_>) -> Option { + let DeclKind::Def(Res::Def(DefKind::Ctor(CtorOf::Struct, CtorKind::Fn), ctor_def_id)) = + decl.kind + else { + return None; + }; + + let def_id = self.tcx.parent(ctor_def_id); + self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()` + } -#[derive(Diagnostic)] -#[diag("`'_` cannot be used here", code = E0637)] -#[help("use another lifetime specifier")] -pub(crate) struct UnderscoreLifetimeIsReserved { - #[primary_span] - #[label("`'_` is a reserved lifetime name")] - pub(crate) span: Span, -} + /// Returns the path segments (as symbols) of a module, including `kw::Crate` at the start. + /// For example, for `crate::foo::bar`, returns `[Crate, foo, bar]`. + /// Returns `None` for block modules that don't have a `DefId`. + fn module_path_names(&self, module: Module<'ra>) -> Option> { + let mut path = Vec::new(); + let mut def_id = module.opt_def_id()?; + while let Some(parent) = self.tcx.opt_parent(def_id) { + if let Some(name) = self.tcx.opt_item_name(def_id) { + path.push(name); + } + if parent.is_top_level_module() { + break; + } + def_id = parent; + } + path.reverse(); + path.insert(0, kw::Crate); + Some(path) + } -#[derive(Diagnostic)] -#[diag("invalid lifetime parameter name: `{$lifetime}`", code = E0262)] -pub(crate) struct StaticLifetimeIsReserved { - #[primary_span] - #[label("'static is a reserved lifetime name")] - pub(crate) span: Span, - pub(crate) lifetime: Ident, -} + /// Shortens a candidate import path to use `super::` (up to 1 level) or `self::` (same module) + /// relative to the current scope, if possible. Only applies to crate-local items and + /// only when the resulting path is actually shorter than the original. + fn shorten_candidate_path( + &self, + suggestion: &mut ImportSuggestion, + current_module: Module<'ra>, + ) { + const MAX_SUPER_PATH_ITEMS_IN_SUGGESTION: usize = 1; + + // Only shorten local items. + if suggestion.did.is_none_or(|did| !did.is_local()) { + return; + } -#[derive(Diagnostic)] -#[diag("variable `{$name}` is not bound in all patterns", code = E0408)] -pub(crate) struct VariableIsNotBoundInAllPatterns { - #[primary_span] - pub(crate) multispan: MultiSpan, - pub(crate) name: Ident, -} + // Build current module path: [Crate, foo, bar, ...]. + let Some(current_mod_path) = self.module_path_names(current_module) else { + return; + }; + + // Normalise candidate path: filter out `PathRoot` (`::`), and if the path + // doesn't start with `Crate`, prepend it (edition 2015 paths are relative + // to the crate root without an explicit `crate::` prefix). + let candidate_names = { + let filtered_segments: Vec<_> = suggestion + .path + .segments + .iter() + .filter(|segment| segment.ident.name != kw::PathRoot) + .collect(); + + let mut candidate_names: Vec = + filtered_segments.iter().map(|segment| segment.ident.name).collect(); + if candidate_names.first() != Some(&kw::Crate) { + candidate_names.insert(0, kw::Crate); + } + if candidate_names.len() < 2 { + return; + } + candidate_names + }; + + // The candidate's module path is everything except the last segment (the item name). + let candidate_mod_names = &candidate_names[..candidate_names.len() - 1]; + + // Find the longest common prefix between the current module and candidate module paths. + let common_prefix_length = current_mod_path + .iter() + .zip(candidate_mod_names.iter()) + .take_while(|(current, candidate)| current == candidate) + .count(); + + // Non-crate-local item; keep the full absolute path. + if common_prefix_length == 0 { + return; + } -#[derive(Subdiagnostic, Debug, Clone)] -#[label("pattern doesn't bind `{$name}`")] -pub(crate) struct PatternDoesntBindName { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Ident, -} + let super_count = current_mod_path.len() - common_prefix_length; -#[derive(Subdiagnostic, Debug, Clone)] -#[label("variable not in all patterns")] -pub(crate) struct VariableNotInAllPatterns { - #[primary_span] - pub(crate) span: Span, -} + // At the crate root, `use` paths resolve from the crate root anyway, so we can + // drop the `crate::` prefix entirely instead of replacing it with `self::`. + let at_crate_root = current_mod_path.len() == 1; -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "you might have meant to use the similarly named previously used binding `{$typo}`", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct PatternBindingTypo { - #[suggestion_part(code = "{typo}")] - pub(crate) spans: Vec, - pub(crate) typo: Symbol, -} + let mut new_segments = if super_count == 0 && at_crate_root { + ThinVec::new() + } else { + let prefix_keyword = match super_count { + 0 => kw::SelfLower, + 1..=MAX_SUPER_PATH_ITEMS_IN_SUGGESTION => kw::Super, + _ => return, // Too many `super` levels; keep the full absolute path. + }; + thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(prefix_keyword),)] + }; + for &name in &candidate_names[common_prefix_length..] { + new_segments.push(ast::PathSegment::from_ident(Ident::with_dummy_span(name))); + } -#[derive(Diagnostic)] -#[diag("the name `{$name}` is defined multiple times")] -#[note("`{$name}` must be defined only once in the {$descr} namespace of this {$container}")] -pub(crate) struct NameDefinedMultipleTime { - #[primary_span] - pub(crate) span: Span, - pub(crate) name: Symbol, - pub(crate) descr: &'static str, - pub(crate) container: &'static str, - #[subdiagnostic] - pub(crate) label: NameDefinedMultipleTimeLabel, - #[subdiagnostic] - pub(crate) old_binding_label: Option, -} + // Only apply if the result is strictly shorter than the original path. + if new_segments.len() >= suggestion.path.segments.len() { + return; + } -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeLabel { - #[label("`{$name}` reimported here")] - Reimported { - #[primary_span] - span: Span, - name: Symbol, - }, - #[label("`{$name}` redefined here")] - Redefined { - #[primary_span] - span: Span, - name: Symbol, - }, -} + suggestion.path = Path { span: suggestion.path.span, segments: new_segments, tokens: None }; + } -#[derive(Subdiagnostic)] -pub(crate) enum NameDefinedMultipleTimeOldBindingLabel { - #[label("previous import of the {$old_kind} `{$name}` here")] - Import { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, - #[label("previous definition of the {$old_kind} `{$name}` here")] - Definition { - #[primary_span] - span: Span, - old_kind: &'static str, - name: Symbol, - }, -} + fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) { + let PrivacyError { + ident, + decl, + outermost_res, + parent_scope, + single_nested, + dedup_span, + ref source, + } = *privacy_error; + + let res = decl.res(); + let ctor_fields_span = self.ctor_fields_span(decl); + let plain_descr = res.descr().to_string(); + let nonimport_descr = + if ctor_fields_span.is_some() { plain_descr + " constructor" } else { plain_descr }; + let import_descr = nonimport_descr.clone() + " import"; + let get_descr = |b: Decl<'_>| if b.is_import() { &import_descr } else { &nonimport_descr }; + + // Print the primary message. + let ident_descr = get_descr(decl); + let mut err = + self.dcx().create_err(diagnostics::IsPrivate { span: ident.span, ident_descr, ident }); + + self.mention_default_field_values(source, ident, &mut err); + + let shown_candidates = if let Some((this_res, outer_ident)) = outermost_res { + let mut import_suggestions = self.lookup_import_candidates( + outer_ident, + this_res.ns().unwrap_or(Namespace::TypeNS), + &parent_scope, + &|res: Res| res == this_res, + ); + // Shorten candidate paths using `super::` or `self::` when possible. + for suggestion in &mut import_suggestions { + self.shorten_candidate_path(suggestion, parent_scope.module); + } + let point_to_def = !show_candidates( + self.tcx, + &mut err, + Some(dedup_span.until(outer_ident.span.shrink_to_hi())), + &import_suggestions, + Instead::Yes, + FoundUse::Yes, + DiagMode::Import { append: single_nested, unresolved_import: false }, + vec![], + "", + ); + // If we suggest importing a public re-export, don't point at the definition. + if point_to_def && ident.span != outer_ident.span { + let label = diagnostics::OuterIdentIsNotPubliclyReexported { + span: outer_ident.span, + outer_ident_descr: this_res.descr(), + outer_ident, + }; + err.subdiagnostic(label); + } + !point_to_def + } else { + false + }; + + let mut non_exhaustive = None; + // If an ADT is foreign and marked as `non_exhaustive`, then that's + // probably why we have the privacy error. + // Otherwise, point out if the struct has any private fields. + if let Some(def_id) = res.opt_def_id() + && !def_id.is_local() + && let Some(attr_span) = find_attr!(self.tcx, def_id, NonExhaustive(span) => *span) + { + non_exhaustive = Some(attr_span); + } else if let Some(span) = ctor_fields_span { + let label = diagnostics::ConstructorPrivateIfAnyFieldPrivate { span }; + err.subdiagnostic(label); + if let Res::Def(_, d) = res + && let Some(fields) = self.field_visibility_spans.get(&d) + { + let spans = fields.iter().map(|span| *span).collect(); + let sugg = diagnostics::ConsiderMakingTheFieldPublic { + spans, + number_of_fields: fields.len(), + }; + err.subdiagnostic(sugg); + } + } -#[derive(Diagnostic)] -#[diag("{$ident_descr} `{$ident}` is private", code = E0603)] -pub(crate) struct IsPrivate<'a> { - #[primary_span] - #[label("private {$ident_descr}")] - pub(crate) span: Span, - pub(crate) ident_descr: &'a str, - pub(crate) ident: Ident, -} + let mut sugg_paths: Vec<(Vec, bool)> = vec![]; + if let Some(mut def_id) = res.opt_def_id() { + // We can't use `def_path_str` in resolve. + let mut path = vec![def_id]; + while let Some(parent) = self.tcx.opt_parent(def_id) { + def_id = parent; + if !def_id.is_top_level_module() { + path.push(def_id); + } else { + break; + } + } + // We will only suggest importing directly if it is accessible through that path. + let path_names: Option> = path + .iter() + .rev() + .map(|def_id| { + self.tcx.opt_item_name(*def_id).map(|name| { + Ident::with_dummy_span(if def_id.is_top_level_module() { + kw::Crate + } else { + name + }) + }) + }) + .collect(); + if let Some(&def_id) = path.get(0) + && let Some(path) = path_names + { + if let Some(def_id) = def_id.as_local() { + if self.effective_visibilities.is_directly_public(def_id) { + sugg_paths.push((path, false)); + } + } else if self.is_accessible_from(self.tcx.visibility(def_id), parent_scope.module) + { + sugg_paths.push((path, false)); + } + } + } -#[derive(Diagnostic)] -#[diag("generic arguments in macro path")] -pub(crate) struct GenericArgumentsInMacroPath { - #[primary_span] - pub(crate) span: Span, -} + // Print the whole import chain to make it easier to see what happens. + let first_binding = decl; + let mut next_binding = Some(decl); + let mut next_ident = ident; + while let Some(binding) = next_binding { + let name = next_ident; + next_binding = match binding.kind { + _ if res == Res::Err => None, + DeclKind::Import { source_decl, import, .. } => match import.kind { + _ if source_decl.span.is_dummy() => None, + ImportKind::Single { source, .. } => { + next_ident = source; + Some(source_decl) + } + ImportKind::Glob { .. } + | ImportKind::MacroUse { .. } + | ImportKind::MacroExport => Some(source_decl), + ImportKind::ExternCrate { .. } => None, + }, + _ => None, + }; + + match binding.kind { + DeclKind::Import { source_decl, import, .. } => { + // Don't include `{{root}}` in suggestions - it's an internal symbol + // that should never be shown to users. + let path = import + .module_path + .iter() + .filter(|seg| seg.ident.name != kw::PathRoot) + .map(|seg| seg.ident.clone()) + .chain(std::iter::once(ident)) + .collect::>(); + let through_reexport = !matches!(source_decl.kind, DeclKind::Def(_)); + sugg_paths.push((path, through_reexport)); + } + DeclKind::Def(_) => {} + } + let first = binding == first_binding; + let def_span = self.tcx.sess.source_map().guess_head_span(binding.span); + let mut note_span = MultiSpan::from_span(def_span); + if !first && binding.vis().is_public() { + let desc = match binding.kind { + DeclKind::Import { .. } => "re-export", + _ => "directly", + }; + note_span.push_span_label(def_span, format!("you could import this {desc}")); + } + // Final step in the import chain, point out if the ADT is `non_exhaustive` + // which is probably why this privacy violation occurred. + if next_binding.is_none() + && let Some(span) = non_exhaustive + { + note_span.push_span_label( + span, + "cannot be constructed because it is `#[non_exhaustive]`", + ); + } + let note = diagnostics::NoteAndRefersToTheItemDefinedHere { + span: note_span, + binding_descr: get_descr(binding), + binding_name: name, + first, + dots: next_binding.is_some(), + }; + err.subdiagnostic(note); + } + // The suggestion replaces `dedup_span` with a path reaching the failing ident. + // That's valid only when + // 1) the failing ident is the imported leaf, otherwise `as` renames and trailing segments + // get dropped, and + // 2) the use isn't nested, otherwise `dedup_span` is one ident in `{...}`. + // + // See issue #156060. + let can_replace_use = !shown_candidates + && !single_nested + && !outermost_res.is_some_and(|(_, outer)| outer.span != ident.span); + if can_replace_use { + // We prioritize shorter paths, non-core imports and direct imports over the + // alternatives. + sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0].name == sym::core, *reexport)); + for (sugg, reexport) in sugg_paths { + if sugg.len() <= 1 { + // A single path segment suggestion is wrong. This happens on circular + // imports. `tests/ui/imports/issue-55884-2.rs` + continue; + } + let path = join_path_idents(sugg); + let sugg = if reexport { + diagnostics::ImportIdent::ThroughReExport { span: dedup_span, ident, path } + } else { + diagnostics::ImportIdent::Directly { span: dedup_span, ident, path } + }; + err.subdiagnostic(sugg); + break; + } + } -#[derive(Diagnostic)] -#[diag("attributes starting with `rustc` are reserved for use by the `rustc` compiler")] -pub(crate) struct AttributesStartingWithRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} + err.emit(); + } -#[derive(Diagnostic)] -#[diag( - "attributes containing a segment starting with `rustc` are reserved for use by the `rustc` compiler" -)] -pub(crate) struct AttributesContainingRustcAreReserved { - #[primary_span] - pub(crate) span: Span, -} + /// When a private field is being set that has a default field value, we suggest using `..` and + /// setting the value of that field implicitly with its default. + /// + /// If we encounter code like + /// ```text + /// struct Priv; + /// pub struct S { + /// pub field: Priv = Priv, + /// } + /// ``` + /// which is used from a place where `Priv` isn't accessible + /// ```text + /// let _ = S { field: m::Priv1 {} }; + /// // ^^^^^ private struct + /// ``` + /// we will suggest instead using the `default_field_values` syntax instead: + /// ```text + /// let _ = S { .. }; + /// ``` + fn mention_default_field_values( + &self, + source: &Option, + ident: Ident, + err: &mut Diag<'_>, + ) { + let Some(expr) = source else { return }; + let ast::ExprKind::Struct(struct_expr) = &expr.kind else { return }; + // We don't have to handle type-relative paths because they're forbidden in ADT + // expressions, but that would change with `#[feature(more_qualified_paths)]`. + let Some(segment) = struct_expr.path.segments.last() else { return }; + let Some(partial_res) = self.partial_res_map.get(&segment.id) else { return }; + let Some(Res::Def(_, def_id)) = partial_res.full_res() else { + return; + }; + let Some(default_fields) = self.field_defaults(def_id) else { return }; + if struct_expr.fields.is_empty() { + return; + } + let last_span = struct_expr.fields.iter().last().unwrap().span; + let mut iter = struct_expr.fields.iter().peekable(); + let mut prev: Option = None; + while let Some(field) = iter.next() { + if field.expr.span.overlaps(ident.span) { + err.span_label(field.ident.span, "while setting this field"); + if default_fields.contains(&field.ident.name) { + let sugg = if last_span == field.span { + vec![(field.span, "..".to_string())] + } else { + vec![ + ( + // Account for trailing commas and ensure we remove them. + match (prev, iter.peek()) { + (_, Some(next)) => field.span.with_hi(next.span.lo()), + (Some(prev), _) => field.span.with_lo(prev.hi()), + (None, None) => field.span, + }, + String::new(), + ), + (last_span.shrink_to_hi(), ", ..".to_string()), + ] + }; + err.multipart_suggestion( + format!( + "the type `{ident}` of field `{}` is private, but you can construct \ + the default value defined for it in `{}` using `..` in the struct \ + initializer expression", + field.ident, + self.tcx.item_name(def_id), + ), + sugg, + Applicability::MachineApplicable, + ); + break; + } + } + prev = Some(field.span); + } + } -#[derive(Diagnostic)] -#[diag("cannot use {$article} {$descr} through an import")] -pub(crate) struct CannotUseThroughAnImport { - #[primary_span] - pub(crate) span: Span, - pub(crate) article: &'static str, - pub(crate) descr: &'static str, - #[note("the {$descr} imported here")] - pub(crate) binding_span: Option, -} + pub(crate) fn find_similarly_named_module_or_crate( + &self, + ident: Symbol, + current_module: Module<'ra>, + ) -> Option { + let mut candidates = self + .extern_prelude + .keys() + .map(|ident| ident.name) + .chain( + self.local_module_map + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .chain( + self.extern_module_map + .borrow() + .iter() + .filter(|(_, module)| { + let module = module.to_module(); + current_module.is_ancestor_of(module) && current_module != module + }) + .flat_map(|(_, module)| module.name()), + ) + .filter(|c| !c.to_string().is_empty()) + .collect::>(); + candidates.sort(); + candidates.dedup(); + find_best_match_for_name(&candidates, ident, None).filter(|sugg| *sugg != ident) + } -#[derive(Diagnostic)] -#[diag("name `{$ident}` is reserved in attribute namespace")] -pub(crate) struct NameReservedInAttributeNamespace { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Symbol, -} + pub(crate) fn report_path_resolution_error( + &mut self, + path: &[Segment], + opt_ns: Option, // `None` indicates a module path in import + parent_scope: &ParentScope<'ra>, + ribs: Option<&PerNS>>>, + ignore_decl: Option>, + ignore_import: Option>, + module: Option>, + failed_segment_idx: usize, + ident: Ident, + diag_metadata: Option<&DiagMetadata<'_>>, + ) -> (String, String, Option) { + let is_last = failed_segment_idx == path.len() - 1; + let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; + let module_def_id = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.opt_def_id(), + _ => None, + }; + let scope = match &path[..failed_segment_idx] { + [.., prev] => { + if prev.ident.name == kw::PathRoot { + format!("the crate root") + } else { + format!("`{}`", prev.ident) + } + } + _ => format!("this scope"), + }; + let message = format!("cannot find `{ident}` in {scope}"); + + if module_def_id == Some(CRATE_DEF_ID.to_def_id()) { + let is_mod = |res| matches!(res, Res::Def(DefKind::Mod, _)); + let mut candidates = self.lookup_import_candidates(ident, TypeNS, parent_scope, is_mod); + candidates + .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path))); + if let Some(candidate) = candidates.get(0) { + let path = { + // remove the possible common prefix of the path + let len = candidate.path.segments.len(); + let start_index = (0..=failed_segment_idx.min(len - 1)) + .find(|&i| path[i].ident.name != candidate.path.segments[i].ident.name) + .unwrap_or_default(); + let segments = + (start_index..len).map(|s| candidate.path.segments[s].clone()).collect(); + Path { segments, span: Span::default(), tokens: None } + }; + ( + message, + String::from("unresolved import"), + Some(( + vec![(ident.span, pprust::path_to_string(&path))], + String::from("a similar path exists"), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == sym::core { + ( + message, + format!("you might be missing crate `{ident}`"), + Some(( + vec![(ident.span, "std".to_string())], + "try using `std` instead of `core`".to_string(), + Applicability::MaybeIncorrect, + )), + ) + } else if ident.name == kw::Underscore { + ( + "invalid crate or module name `_`".to_string(), + "`_` is not a valid crate or module name".to_string(), + None, + ) + } else if self.tcx.sess.is_rust_2015() { + ( + format!("cannot find module or crate `{ident}` in {scope}"), + format!("use of unresolved module or unlinked crate `{ident}`"), + Some(( + vec![( + self.current_crate_outer_attr_insert_span, + format!("extern crate {ident};\n"), + )], + if was_invoked_from_cargo() { + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add \ + {ident}` to add it to your `Cargo.toml` and import it in your \ + code", + ) + } else { + format!( + "you might be missing a crate named `{ident}`, add it to your \ + project and import it in your code", + ) + }, + Applicability::MaybeIncorrect, + )), + ) + } else { + (message, format!("could not find `{ident}` in the crate root"), None) + } + } else if failed_segment_idx > 0 { + let parent = path[failed_segment_idx - 1].ident.name; + let parent = match parent { + // ::foo is mounted at the crate root for 2015, and is the extern + // prelude for 2018+ + kw::PathRoot if self.tcx.sess.edition() > Edition::Edition2015 => { + "the list of imported crates".to_owned() + } + kw::PathRoot | kw::Crate => "the crate root".to_owned(), + _ => format!("`{parent}`"), + }; + + let mut msg = format!("could not find `{ident}` in {parent}"); + if ns == TypeNS || ns == ValueNS { + let ns_to_try = if ns == TypeNS { ValueNS } else { TypeNS }; + let binding = if let Some(module) = module { + self.cm() + .resolve_ident_in_module( + module, + ident, + ns_to_try, + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + } else if let Some(ribs) = ribs + && let Some(TypeNS | ValueNS) = opt_ns + { + assert!(ignore_import.is_none()); + match self.resolve_ident_in_lexical_scope( + ident, + ns_to_try, + parent_scope, + None, + &ribs[ns_to_try], + ignore_decl, + diag_metadata, + ) { + // we found a locally-imported or available item/module + Some(LateDecl::Decl(binding)) => Some(binding), + _ => None, + } + } else { + self.cm() + .resolve_ident_in_scope_set( + ident, + ScopeSet::All(ns_to_try), + parent_scope, + None, + ignore_decl, + ignore_import, + ) + .ok() + }; + if let Some(binding) = binding { + msg = format!( + "expected {}, found {} `{ident}` in {parent}", + ns.descr(), + binding.res().descr(), + ); + }; + } + (message, msg, None) + } else if ident.name == kw::SelfUpper { + // As mentioned above, `opt_ns` being `None` indicates a module path in import. + // We can use this to improve a confusing error for, e.g. `use Self::Variant` in an + // impl + if opt_ns.is_none() { + (message, "`Self` cannot be used in imports".to_string(), None) + } else { + ( + message, + "`Self` is only available in impls, traits, and type definitions".to_string(), + None, + ) + } + } else if ident.name.as_str().chars().next().is_some_and(|c| c.is_ascii_uppercase()) { + // Check whether the name refers to an item in the value namespace. + let binding = if let Some(ribs) = ribs { + assert!(ignore_import.is_none()); + self.resolve_ident_in_lexical_scope( + ident, + ValueNS, + parent_scope, + None, + &ribs[ValueNS], + ignore_decl, + diag_metadata, + ) + } else { + None + }; + let match_span = match binding { + // Name matches a local variable. For example: + // ``` + // fn f() { + // let Foo: &str = ""; + // println!("{}", Foo::Bar); // Name refers to local + // // variable `Foo`. + // } + // ``` + Some(LateDecl::RibDef(Res::Local(id))) => { + Some((*self.pat_span_map.get(&id).unwrap(), "a", "local binding")) + } + // Name matches item from a local name binding + // created by `use` declaration. For example: + // ``` + // pub const Foo: &str = ""; + // + // mod submod { + // use super::Foo; + // println!("{}", Foo::Bar); // Name refers to local + // // binding `Foo`. + // } + // ``` + Some(LateDecl::Decl(name_binding)) => Some(( + name_binding.span, + name_binding.res().article(), + name_binding.res().descr(), + )), + _ => None, + }; + + let message = format!("cannot find type `{ident}` in {scope}"); + let label = if let Some((span, article, descr)) = match_span { + format!( + "`{ident}` is declared as {article} {descr} at `{}`, not a type", + self.tcx + .sess + .source_map() + .span_to_short_string(span, RemapPathScopeComponents::DIAGNOSTICS) + ) + } else { + format!("use of undeclared type `{ident}`") + }; + (message, label, None) + } else { + let mut suggestion = None; + if ident.name == sym::alloc { + suggestion = Some(( + vec![], + String::from("add `extern crate alloc` to use the `alloc` crate"), + Applicability::MaybeIncorrect, + )) + } -#[derive(Diagnostic)] -#[diag("cannot find a built-in macro with name `{$ident}`")] -pub(crate) struct CannotFindBuiltinMacroWithName { - #[primary_span] - pub(crate) span: Span, - pub(crate) ident: Ident, -} + suggestion = suggestion.or_else(|| { + self.find_similarly_named_module_or_crate(ident.name, parent_scope.module).map( + |sugg| { + ( + vec![(ident.span, sugg.to_string())], + String::from("there is a crate or module with a similar name"), + Applicability::MaybeIncorrect, + ) + }, + ) + }); + if let Ok(binding) = self.cm().resolve_ident_in_scope_set( + ident, + ScopeSet::All(ValueNS), + parent_scope, + None, + ignore_decl, + ignore_import, + ) { + let descr = binding.res().descr(); + let message = format!("cannot find module or crate `{ident}` in {scope}"); + (message, format!("{descr} `{ident}` is not a crate or module"), suggestion) + } else { + let suggestion = if suggestion.is_some() { + suggestion + } else if let Some(m) = self.undeclared_module_exists(ident) { + self.undeclared_module_suggest_declare(ident, m) + } else if was_invoked_from_cargo() { + Some(( + vec![], + format!( + "if you wanted to use a crate named `{ident}`, use `cargo add {ident}` \ + to add it to your `Cargo.toml`", + ), + Applicability::MaybeIncorrect, + )) + } else { + Some(( + vec![], + format!("you might be missing a crate named `{ident}`",), + Applicability::MaybeIncorrect, + )) + }; + let message = format!("cannot find module or crate `{ident}` in {scope}"); + ( + message, + format!("use of unresolved module or unlinked crate `{ident}`"), + suggestion, + ) + } + } + } -#[derive(Diagnostic)] -#[diag("tool `{$tool}` was already registered")] -pub(crate) struct ToolWasAlreadyRegistered { - #[primary_span] - pub(crate) span: Span, - pub(crate) tool: Ident, - #[label("already registered here")] - pub(crate) old_ident_span: Span, -} + fn undeclared_module_suggest_declare( + &self, + ident: Ident, + path: std::path::PathBuf, + ) -> Option<(Vec<(Span, String)>, String, Applicability)> { + Some(( + vec![(self.current_crate_outer_attr_insert_span, format!("mod {ident};\n"))], + format!( + "to make use of source file {}, use `mod {ident}` \ + in this file to declare the module", + path.display() + ), + Applicability::MaybeIncorrect, + )) + } -#[derive(Subdiagnostic)] -pub(crate) enum DefinedHere { - #[label("similarly named {$candidate_descr} `{$candidate}` defined here")] - SimilarlyNamed { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, - #[label("{$candidate_descr} `{$candidate}` defined here")] - SingleItem { - #[primary_span] - span: Span, - candidate_descr: &'static str, - candidate: Symbol, - }, -} + fn undeclared_module_exists(&self, ident: Ident) -> Option { + let map = self.tcx.sess.source_map(); + + let src = map.span_to_filename(ident.span).into_local_path()?; + let i = ident.as_str(); + // FIXME: add case where non parent using undeclared module (hard?) + let dir = src.parent()?; + let src = src.file_stem()?.to_str()?; + for file in [ + // …/x.rs + dir.join(i).with_extension("rs"), + // …/x/mod.rs + dir.join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + if !matches!(src, "main" | "lib" | "mod") { + for file in [ + // …/x/y.rs + dir.join(src).join(i).with_extension("rs"), + // …/x/y/mod.rs + dir.join(src).join(i).join("mod.rs"), + ] { + if file.exists() { + return Some(file); + } + } + } + None + } -#[derive(Subdiagnostic)] -#[label("{$outer_ident_descr} `{$outer_ident}` is not publicly re-exported")] -pub(crate) struct OuterIdentIsNotPubliclyReexported { - #[primary_span] - pub(crate) span: Span, - pub(crate) outer_ident_descr: &'static str, - pub(crate) outer_ident: Ident, -} + /// Adds suggestions for a path that cannot be resolved. + #[instrument(level = "debug", skip(self, parent_scope))] + pub(crate) fn make_path_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + match path[..] { + // `{{root}}::ident::...` on both editions. + // On 2015 `{{root}}` is usually added implicitly. + [first, second, ..] + if first.ident.name == kw::PathRoot && !second.ident.is_path_segment_keyword() => {} + // `ident::...` on 2018. + [first, ..] + if first.ident.span.at_least_rust_2018() + && !first.ident.is_path_segment_keyword() => + { + // Insert a placeholder that's later replaced by `self`/`super`/etc. + path.insert(0, Segment::from_ident(Ident::dummy())); + } + _ => return None, + } -#[derive(Subdiagnostic)] -#[label("a constructor is private if any of the fields is private")] -pub(crate) struct ConstructorPrivateIfAnyFieldPrivate { - #[primary_span] - pub(crate) span: Span, -} + self.make_missing_self_suggestion(path.clone(), parent_scope) + .or_else(|| self.make_missing_crate_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_missing_super_suggestion(path.clone(), parent_scope)) + .or_else(|| self.make_external_crate_suggestion(path, parent_scope)) + } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "{ $number_of_fields -> - [one] consider making the field publicly accessible - *[other] consider making the fields publicly accessible - }", - applicability = "maybe-incorrect", - style = "verbose" -)] -pub(crate) struct ConsiderMakingTheFieldPublic { - #[suggestion_part(code = "pub ")] - pub(crate) spans: Vec, - pub(crate) number_of_fields: usize, -} + /// Suggest a missing `self::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `self::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_self_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `self` and check if that is valid. + path[0].ident.name = kw::SelfLower; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } -#[derive(Subdiagnostic)] -pub(crate) enum ImportIdent { - #[suggestion( - "import `{$ident}` through the re-export", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - ThroughReExport { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, - #[suggestion( - "import `{$ident}` directly", - code = "{path}", - applicability = "machine-applicable", - style = "verbose" - )] - Directly { - #[primary_span] - span: Span, - ident: Ident, - path: String, - }, -} + /// Suggests a missing `crate::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `crate::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Crate; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { + Some(( + path, + Some( + "`use` statements changed in Rust 2018; read more at \ + " + .to_string(), + ), + )) + } else { + None + } + } -#[derive(Subdiagnostic)] -#[note( - "{$first -> - [true] {$dots -> - [true] the {$binding_descr} `{$binding_name}` is defined here... - *[false] the {$binding_descr} `{$binding_name}` is defined here - } - *[false] {$dots -> - [true] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here... - *[false] ...and refers to the {$binding_descr} `{$binding_name}` which is defined here - } - }" -)] -pub(crate) struct NoteAndRefersToTheItemDefinedHere<'a> { - #[primary_span] - pub(crate) span: MultiSpan, - pub(crate) binding_descr: &'a str, - pub(crate) binding_name: Ident, - pub(crate) first: bool, - pub(crate) dots: bool, -} + /// Suggests a missing `super::` if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foo::Bar; + /// | ^^^ did you mean `super::foo`? + /// ``` + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_missing_super_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + // Replace first ident with `crate` and check if that is valid. + path[0].ident.name = kw::Super; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?result); + if let PathResult::Module(..) = result { Some((path, None)) } else { None } + } -#[derive(Subdiagnostic)] -#[suggestion("remove unnecessary import", code = "", applicability = "maybe-incorrect")] -pub(crate) struct RemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} + /// Suggests a missing external crate name if that resolves to an correct module. + /// + /// ```text + /// | + /// LL | use foobar::Baz; + /// | ^^^^^^ did you mean `baz::foobar`? + /// ``` + /// + /// Used when importing a submodule of an external crate but missing that crate's + /// name as the first part of path. + #[instrument(level = "debug", skip(self, parent_scope))] + fn make_external_crate_suggestion( + &mut self, + mut path: Vec, + parent_scope: &ParentScope<'ra>, + ) -> Option<(Vec, Option)> { + if path[1].ident.span.is_rust_2015() { + return None; + } -#[derive(Subdiagnostic)] -#[suggestion( - "remove unnecessary import", - code = "", - applicability = "maybe-incorrect", - style = "tool-only" -)] -pub(crate) struct ToolOnlyRemoveUnnecessaryImport { - #[primary_span] - pub(crate) span: Span, -} + // Sort extern crate names in *reverse* order to get + // 1) some consistent ordering for emitted diagnostics, and + // 2) `std` suggestions before `core` suggestions. + let mut extern_crate_names = + self.extern_prelude.keys().map(|ident| ident.name).collect::>(); + extern_crate_names.sort_by(|a, b| b.as_str().cmp(a.as_str())); + + for name in extern_crate_names.into_iter() { + // Replace first ident with a crate name and check if that is valid. + path[0].ident.name = name; + let result = self.cm().maybe_resolve_path(&path, None, parent_scope, None); + debug!(?path, ?name, ?result); + if let PathResult::Module(..) = result { + return Some((path, None)); + } + } -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is imported here, but it is {$imported_ident_desc}")] -pub(crate) struct IdentImporterHereButItIsDesc<'a> { - #[primary_span] - pub(crate) span: Span, - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} + None + } -#[derive(Subdiagnostic)] -#[note("`{$imported_ident}` is in scope, but it is {$imported_ident_desc}")] -pub(crate) struct IdentInScopeButItIsDesc<'a> { - pub(crate) imported_ident: Ident, - pub(crate) imported_ident_desc: &'a str, -} + /// Suggests importing a macro from the root of the crate rather than a module within + /// the crate. + /// + /// ```text + /// help: a macro with this name exists at the root of the crate + /// | + /// LL | use issue_59764::makro; + /// | ^^^^^^^^^^^^^^^^^^ + /// | + /// = note: this could be because a macro annotated with `#[macro_export]` will be exported + /// at the root of the crate instead of the module where it is defined + /// ``` + pub(crate) fn check_for_module_export_macro( + &mut self, + import: Import<'ra>, + module: ModuleOrUniformRoot<'ra>, + ident: Ident, + ) -> Option<(Option, Option)> { + let ModuleOrUniformRoot::Module(mut crate_module) = module else { + return None; + }; -pub(crate) struct FoundItemConfigureOut { - pub(crate) span: Span, - pub(crate) item_was: ItemWas, -} + while let Some(parent) = crate_module.parent { + crate_module = parent; + } -pub(crate) enum ItemWas { - BehindFeature { feature: Symbol, span: Span }, - CfgOut { span: Span }, -} + if module == ModuleOrUniformRoot::Module(crate_module) { + // Don't make a suggestion if the import was already from the root of the crate. + return None; + } -impl Subdiagnostic for FoundItemConfigureOut { - fn add_to_diag(self, diag: &mut Diag<'_, G>) { - let mut multispan: MultiSpan = self.span.into(); - match self.item_was { - ItemWas::BehindFeature { feature, span } => { - let value = feature.into_diag_arg(&mut None); - let msg = msg!("the item is gated behind the `{$feature}` feature") - .arg("feature", value) - .format(); - multispan.push_span_label(span, msg); + let binding_key = BindingKey::new(IdentKey::new(ident), MacroNS); + let binding = self.resolution(crate_module, binding_key)?.best_decl()?; + let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { + return None; + }; + if !kinds.contains(MacroKinds::BANG) { + return None; + } + let module_name = crate_module.name().unwrap_or(kw::Crate); + let import_snippet = match import.kind { + ImportKind::Single { source, target, .. } if source != target => { + format!("{source} as {target}") + } + _ => format!("{ident}"), + }; + + let mut corrections: Vec<(Span, String)> = Vec::new(); + if !import.is_nested() { + // Assume this is the easy case of `use issue_59764::foo::makro;` and just remove + // intermediate segments. + corrections.push((import.span, format!("{module_name}::{import_snippet}"))); + } else { + // Find the binding span (and any trailing commas and spaces). + // i.e. `use a::b::{c, d, e};` + // ^^^ + let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding( + self.tcx.sess, + import.span, + import.use_span, + ); + debug!(found_closing_brace, ?binding_span); + + let mut removal_span = binding_span; + + // If the binding span ended with a closing brace, as in the below example: + // i.e. `use a::b::{c, d};` + // ^ + // Then expand the span of characters to remove to include the previous + // binding's trailing comma. + // i.e. `use a::b::{c, d};` + // ^^^ + if found_closing_brace + && let Some(previous_span) = + extend_span_to_previous_binding(self.tcx.sess, binding_span) + { + debug!(?previous_span); + removal_span = removal_span.with_lo(previous_span.lo()); } - ItemWas::CfgOut { span } => { - multispan.push_span_label(span, msg!("the item is gated here")); + debug!(?removal_span); + + // Remove the `removal_span`. + corrections.push((removal_span, "".to_string())); + + // Find the span after the crate name and if it has nested imports immediately + // after the crate name already. + // i.e. `use a::b::{c, d};` + // ^^^^^^^^^ + // or `use a::{b, c, d}};` + // ^^^^^^^^^^^ + let (has_nested, after_crate_name) = + find_span_immediately_after_crate_name(self.tcx.sess, import.use_span); + debug!(has_nested, ?after_crate_name); + + let source_map = self.tcx.sess.source_map(); + + // Make sure this is actually crate-relative. + let is_definitely_crate = import + .module_path + .first() + .is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super); + + // Add the import to the start, with a `{` if required. + let start_point = source_map.start_point(after_crate_name); + if is_definitely_crate + && let Ok(start_snippet) = source_map.span_to_snippet(start_point) + { + corrections.push(( + start_point, + if has_nested { + // In this case, `start_snippet` must equal '{'. + format!("{start_snippet}{import_snippet}, ") + } else { + // In this case, add a `{`, then the moved import, then whatever + // was there before. + format!("{{{import_snippet}, {start_snippet}") + }, + )); + + // Add a `};` to the end if nested, matching the `{` added at the start. + if !has_nested { + corrections.push((source_map.end_point(after_crate_name), "};".to_string())); + } + } else { + // If the root import is module-relative, add the import separately + corrections.push(( + import.use_span.shrink_to_lo(), + format!("use {module_name}::{import_snippet};\n"), + )); } } - diag.span_note(multispan, msg!("found an item that was configured out")); - } -} -#[derive(Diagnostic)] -#[diag("item `{$name}` is an associated {$kind}, which doesn't match its trait `{$trait_path}`")] -pub(crate) struct TraitImplMismatch { - #[primary_span] - #[label("does not match trait")] - pub(crate) span: Span, - pub(crate) name: Ident, - pub(crate) kind: &'static str, - pub(crate) trait_path: String, - #[label("item in trait")] - pub(crate) trait_item_span: Span, -} + let suggestion = Some(( + corrections, + String::from("a macro with this name exists at the root of the crate"), + Applicability::MaybeIncorrect, + )); + Some(( + suggestion, + Some( + "this could be because a macro annotated with `#[macro_export]` will be exported \ + at the root of the crate instead of the module where it is defined" + .to_string(), + ), + )) + } -#[derive(Diagnostic)] -#[diag("derive helper attribute is used before it is introduced")] -pub(crate) struct LegacyDeriveHelpers { - #[label("the attribute is introduced here")] - pub span: Span, -} + /// Finds a cfg-ed out item inside `module` with the matching name. + pub(crate) fn find_cfg_stripped(&self, err: &mut Diag<'_>, segment: &Symbol, module: DefId) { + let local_items; + let symbols = if module.is_local() { + local_items = self + .stripped_cfg_items + .iter() + .filter_map(|item| { + let parent_scope = self.local_modules.iter().find_map(|m| match m.kind { + ModuleKind::Def(_, def_id, node_id, _) if node_id == item.parent_scope => { + Some(def_id) + } + _ => None, + })?; + Some(StrippedCfgItem { parent_scope, ident: item.ident, cfg: item.cfg.clone() }) + }) + .collect::>(); + local_items.as_slice() + } else { + self.tcx.stripped_cfg_items(module.krate) + }; -#[derive(Diagnostic)] -#[diag("unused extern crate")] -pub(crate) struct UnusedExternCrate { - #[label("unused")] - pub span: Span, - #[suggestion( - "remove the unused `extern crate`", - code = "", - applicability = "machine-applicable", - style = "verbose" - )] - pub removal_span: Span, -} + for &StrippedCfgItem { parent_scope, ident, ref cfg } in symbols { + if ident.name != *segment { + continue; + } -#[derive(Diagnostic)] -#[diag("{$kind} `{$name}` from private dependency '{$krate}' is re-exported")] -pub(crate) struct ReexportPrivateDependency { - pub name: Symbol, - pub kind: &'static str, - pub krate: Symbol, -} + let parent_module = self.get_nearest_non_block_module(parent_scope).def_id(); + + fn comes_from_same_module_for_glob( + r: &Resolver<'_, '_>, + parent_module: DefId, + module: DefId, + visited: &mut FxHashMap, + ) -> bool { + if let Some(&cached) = visited.get(&parent_module) { + // this branch is prevent from being called recursively infinity, + // because there has some cycles in globs imports, + // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` + return cached; + } + visited.insert(parent_module, false); + let mut res = false; + let m = r.expect_module(parent_module); + if m.is_local() { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + res = true; + break; + } + } + } + } + visited.insert(parent_module, res); + res + } -#[derive(Diagnostic)] -#[diag("unused label")] -pub(crate) struct UnusedLabel; + let comes_from_same_module = parent_module == module + || comes_from_same_module_for_glob( + self, + parent_module, + module, + &mut Default::default(), + ); + if !comes_from_same_module { + continue; + } -#[derive(Diagnostic)] -#[diag("unused `#[macro_use]` import")] -pub(crate) struct UnusedMacroUse; + let item_was = if let CfgEntry::NameValue { value: Some(feature), .. } = cfg.0 { + diagnostics::ItemWas::BehindFeature { feature, span: cfg.1 } + } else { + diagnostics::ItemWas::CfgOut { span: cfg.1 } + }; + let note = diagnostics::FoundItemConfigureOut { span: ident.span, item_was }; + err.subdiagnostic(note); + } + } -#[derive(Diagnostic)] -#[diag("applying the `#[macro_use]` attribute to an `extern crate` item is deprecated")] -#[help("remove it and import macros at use sites with a `use` item instead")] -pub(crate) struct MacroUseDeprecated; + pub(crate) fn struct_ctor(&self, def_id: DefId) -> Option { + match def_id.as_local() { + Some(def_id) => self.struct_ctors.get(&def_id).cloned(), + None => { + self.cstore().ctor_untracked(self.tcx, def_id).map(|(ctor_kind, ctor_def_id)| { + let res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id); + let vis = self.tcx.visibility(ctor_def_id); + let field_visibilities = self + .tcx + .associated_item_def_ids(def_id) + .iter() + .map(|&field_id| self.tcx.visibility(field_id)) + .collect(); + StructCtor { res, vis, field_visibilities } + }) + } + } + } -#[derive(Diagnostic)] -#[diag("macro `{$ident}` is private")] -pub(crate) struct MacroIsPrivate { - pub ident: Ident, + /// Gets the `#[diagnostic::on_unknown]` attribute data associated with this `DefId`. + fn on_unknown_data(&self, def_id: DefId) -> Option<&Directive> { + match def_id.as_local() { + Some(local) => Some(self.on_unknown_data.get(&local)?.directive.as_ref()), + None => find_attr!(self.tcx, def_id, OnUnknown{ directive } => directive)?.as_deref(), + } + } } -#[derive(Diagnostic)] -#[diag("unused macro definition: `{$name}`")] -pub(crate) struct UnusedMacroDefinition { - pub name: Symbol, -} +/// Given a `binding_span` of a binding within a use statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^ +/// ``` +/// +/// then return the span until the next binding or the end of the statement: +/// +/// ```ignore (illustrative) +/// use foo::{a, b, c}; +/// // ^^^ +/// ``` +fn find_span_of_binding_until_next_binding( + sess: &Session, + binding_span: Span, + use_span: Span, +) -> (bool, Span) { + let source_map = sess.source_map(); + + // Find the span of everything after the binding. + // i.e. `a, e};` or `a};` + let binding_until_end = binding_span.with_hi(use_span.hi()); + + // Find everything after the binding but not including the binding. + // i.e. `, e};` or `};` + let after_binding_until_end = binding_until_end.with_lo(binding_span.hi()); + + // Keep characters in the span until we encounter something that isn't a comma or + // whitespace. + // i.e. `, ` or ``. + // + // Also note whether a closing brace character was encountered. If there + // was, then later go backwards to remove any trailing commas that are left. + let mut found_closing_brace = false; + let after_binding_until_next_binding = + source_map.span_take_while(after_binding_until_end, |&ch| { + if ch == '}' { + found_closing_brace = true; + } + ch == ' ' || ch == ',' + }); + + // Combine the two spans. + // i.e. `a, ` or `a`. + // + // Removing these would leave `issue_52891::{d, e};` or `issue_52891::{d, e, };` + let span = binding_span.with_hi(after_binding_until_next_binding.hi()); + + (found_closing_brace, span) +} + +/// Given a `binding_span`, return the span through to the comma or opening brace of the previous +/// binding. +/// +/// ```ignore (illustrative) +/// use foo::a::{a, b, c}; +/// // ^^--- binding span +/// // | +/// // returned span +/// +/// use foo::{a, b, c}; +/// // --- binding span +/// ``` +fn extend_span_to_previous_binding(sess: &Session, binding_span: Span) -> Option { + let source_map = sess.source_map(); + + // `prev_source` will contain all of the source that came before the span. + // Then split based on a command and take the first (i.e. closest to our span) + // snippet. In the example, this is a space. + let prev_source = source_map.span_to_prev_source(binding_span).ok()?; + + let prev_comma = prev_source.rsplit(',').collect::>(); + let prev_starting_brace = prev_source.rsplit('{').collect::>(); + if prev_comma.len() <= 1 || prev_starting_brace.len() <= 1 { + return None; + } -#[derive(Diagnostic)] -#[diag("rule #{$n} of macro `{$name}` is never used")] -pub(crate) struct MacroRuleNeverUsed { - pub n: usize, - pub name: Symbol, -} + let prev_comma = prev_comma.first().unwrap(); + let prev_starting_brace = prev_starting_brace.first().unwrap(); -#[derive(Diagnostic)] -#[diag("`extern crate` is not idiomatic in the new edition")] -pub(crate) struct ExternCrateNotIdiomatic { - #[suggestion( - "convert it to a `use`", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - pub code: &'static str, -} + // If the amount of source code before the comma is greater than + // the amount of source code before the starting brace then we've only + // got one item in the nested item (eg. `issue_52891::{self}`). + if prev_comma.len() > prev_starting_brace.len() { + return None; + } -#[derive(Diagnostic)] -#[diag("cannot find macro `{$path}` in the current scope when looking from {$location}")] -#[help("import `macro_rules` with `use` to make it callable above its definition")] -pub(crate) struct OutOfScopeMacroCalls { - #[label("not found from {$location}")] - pub span: Span, - pub path: String, - pub location: String, -} + Some(binding_span.with_lo(BytePos( + // Take away the number of bytes for the characters we've found and an + // extra for the comma. + binding_span.lo().0 - (prev_comma.as_bytes().len() as u32) - 1, + ))) +} + +/// Given a `use_span` of a binding within a use statement, returns the highlighted span and if +/// it is a nested use tree. +/// +/// ```ignore (illustrative) +/// use foo::a::{b, c}; +/// // ^^^^^^^^^^ -- false +/// +/// use foo::{a, b, c}; +/// // ^^^^^^^^^^ -- true +/// +/// use foo::{a, b::{c, d}}; +/// // ^^^^^^^^^^^^^^^ -- true +/// ``` +#[instrument(level = "debug", skip(sess))] +fn find_span_immediately_after_crate_name(sess: &Session, use_span: Span) -> (bool, Span) { + let source_map = sess.source_map(); + + // Using `use issue_59764::foo::{baz, makro};` as an example throughout.. + let mut num_colons = 0; + // Find second colon.. `use issue_59764:` + let until_second_colon = source_map.span_take_while(use_span, |c| { + if *c == ':' { + num_colons += 1; + } + !matches!(c, ':' if num_colons == 2) + }); + // Find everything after the second colon.. `foo::{baz, makro};` + let from_second_colon = use_span.with_lo(until_second_colon.hi() + BytePos(1)); + + let mut found_a_non_whitespace_character = false; + // Find the first non-whitespace character in `from_second_colon`.. `f` + let after_second_colon = source_map.span_take_while(from_second_colon, |c| { + if found_a_non_whitespace_character { + return false; + } + if !c.is_whitespace() { + found_a_non_whitespace_character = true; + } + true + }); -#[derive(Diagnostic)] -#[diag( - "glob import doesn't reexport anything with visibility `{$import_vis}` because no imported item is public enough" -)] -pub(crate) struct RedundantImportVisibility { - #[note("the most public imported item is `{$max_vis}`")] - pub span: Span, - #[help("reduce the glob import's visibility or increase visibility of imported items")] - pub help: (), - pub import_vis: String, - pub max_vis: String, + // Find the first `{` in from_second_colon.. `foo::{` + let next_left_bracket = source_map.span_through_char(from_second_colon, '{'); + + (next_left_bracket == after_second_colon, from_second_colon) } -#[derive(Diagnostic)] -#[diag("unknown diagnostic attribute")] -pub(crate) struct UnknownDiagnosticAttribute { - #[subdiagnostic] - pub help: Option, +/// A suggestion has already been emitted, change the wording slightly to clarify that both are +/// independent options. +enum Instead { + Yes, + No, } -#[derive(Subdiagnostic)] -pub(crate) enum UnknownDiagnosticAttributeHelp { - #[suggestion( - "an attribute with a similar name exists", - style = "verbose", - code = "{typo_name}", - applicability = "machine-applicable" - )] - Typo { - #[primary_span] - span: Span, - typo_name: Symbol, - }, - #[help("add `#![feature({$feature})]` to the crate attributes to enable")] - UseFeature { feature: Symbol }, +/// Whether an existing place with an `use` item was found. +enum FoundUse { + Yes, + No, } -// FIXME: Make this properly translatable. -pub(crate) struct Ambiguity { - pub ident: Ident, - pub ambig_vis: Option, - pub kind: &'static str, - pub help: Option<&'static [&'static str]>, - pub b1_note: Spanned, - pub b1_help_msgs: Vec, - pub b2_note: Spanned, - pub b2_help_msgs: Vec, - /// If false, then it's a lint, if true, then it's an error with the `E0659` error code. - pub is_error: bool, +/// Whether a binding is part of a pattern or a use statement. Used for diagnostics. +pub(crate) enum DiagMode { + Normal, + /// The binding is part of a pattern + Pattern, + /// The binding is part of a use statement + Import { + /// `true` means diagnostics is for unresolved import + unresolved_import: bool, + /// `true` mean add the tips afterward for case `use a::{b,c}`, + /// rather than replacing within. + append: bool, + }, } -impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for Ambiguity { - fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - let Self { - ident, - ambig_vis, - kind, - help, - b1_note, - b1_help_msgs, - b2_note, - b2_help_msgs, - is_error, - } = self; +pub(crate) fn import_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + mode: DiagMode, + append: &str, +) { + show_candidates( + tcx, + err, + use_placement_span, + candidates, + Instead::Yes, + FoundUse::Yes, + mode, + vec![], + append, + ); +} + +type PathString<'a> = (String, &'a str, Option, &'a Option, bool, bool); + +/// When an entity with a given name is not available in scope, we search for +/// entities with that name in all crates. This method allows outputting the +/// results of this search in a programmer-friendly way. If any entities are +/// found and suggested, returns `true`, otherwise returns `false`. +fn show_candidates( + tcx: TyCtxt<'_>, + err: &mut Diag<'_>, + // This is `None` if all placement locations are inside expansions + use_placement_span: Option, + candidates: &[ImportSuggestion], + instead: Instead, + found_use: FoundUse, + mode: DiagMode, + path: Vec, + append: &str, +) -> bool { + if candidates.is_empty() { + return false; + } - let mut diag = Diag::new(dcx, level, "").with_span(ident.span); - if is_error { - diag.code(E0659); - } - if let Some(ambig_vis) = ambig_vis { - diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); - } else { - diag.primary_message(format!("`{}` is ambiguous", ident)); - diag.span_label(ident.span, "ambiguous name"); - } - diag.note(format!("ambiguous because of {}", kind)); - diag.span_note(b1_note.span, b1_note.node); - if let Some(help) = help { - for help in help { - diag.help(*help); + let mut showed = false; + let mut accessible_path_strings: Vec> = Vec::new(); + let mut inaccessible_path_strings: Vec> = Vec::new(); + + candidates.iter().for_each(|c| { + if c.accessible { + // Don't suggest `#[doc(hidden)]` items from other crates + if c.doc_visible { + accessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + c.is_exact_match, + )) } + } else { + inaccessible_path_strings.push(( + pprust::path_to_string(&c.path), + c.descr, + c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))), + &c.note, + c.via_import, + c.is_exact_match, + )) } - for help_msg in b1_help_msgs { - diag.help(help_msg); - } - diag.span_note(b2_note.span, b2_note.node); - for help_msg in b2_help_msgs { - diag.help(help_msg); + }); + + // we want consistent results across executions, but candidates are produced + // by iterating through a hash map, so make sure they are ordered: + for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] { + path_strings.sort_by(|a, b| a.0.cmp(&b.0)); + path_strings.dedup_by(|a, b| a.0 == b.0); + let core_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("core::")).collect::>(); + let std_path_strings = + path_strings.extract_if(.., |p| p.0.starts_with("std::")).collect::>(); + let foreign_crate_path_strings = + path_strings.extract_if(.., |p| !p.0.starts_with("crate::")).collect::>(); + + // We list the `crate` local paths first. + // Then we list the `std`/`core` paths. + if std_path_strings.len() == core_path_strings.len() { + // Do not list `core::` paths if we are already listing the `std::` ones. + path_strings.extend(std_path_strings); + } else { + path_strings.extend(std_path_strings); + path_strings.extend(core_path_strings); } - diag + // List all paths from foreign crates last. + path_strings.extend(foreign_crate_path_strings); } -} -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` never used")] -pub(crate) struct UnusedLifetime { - #[suggestion("elide the unused lifetime", code = "", applicability = "machine-applicable")] - pub deletion_span: Option, + if !accessible_path_strings.is_empty() { + let (determiner, kind, s, name, through) = + if let [(name, descr, _, _, via_import, is_exact_match)] = &accessible_path_strings[..] + { + ( + if *is_exact_match { "this" } else { "this similarly named" }, + *descr, + "", + format!(" `{name}`"), + if *via_import { " through its public re-export" } else { "" }, + ) + } else { + // Get the unique item kinds and if there's only one, we use the right kind name + // instead of the more generic "items". + let kinds = accessible_path_strings + .iter() + .map(|(_, descr, _, _, _, _)| *descr) + .collect::>(); + let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" }; + let s = if kind.ends_with('s') { "es" } else { "s" }; + // we should only suggest case insensitive suggestion if no case sensitive match was found, + // so all the suggestion should have the same is_exact_match value. + + ( + if accessible_path_strings[0].5 { + "one of these" + } else { + "one of these similarly named" + }, + kind, + s, + String::new(), + "", + ) + }; + + let instead = if let Instead::Yes = instead { " instead" } else { "" }; + let mut msg = if let DiagMode::Pattern = mode { + format!( + "if you meant to match on {kind}{s}{instead}{name}, use the full path in the \ + pattern", + ) + } else { + format!("consider importing {determiner} {kind}{s}{through}{instead}") + }; - pub ident: Ident, -} + for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } -#[derive(Diagnostic)] -#[diag("ambiguous glob re-exports")] -pub(crate) struct AmbiguousGlobReexports { - #[label("the name `{$name}` in the {$namespace} namespace is first re-exported here")] - pub first_reexport: Span, - #[label("but the name `{$name}` in the {$namespace} namespace is also re-exported here")] - pub duplicate_reexport: Span, + let append_candidates = |msg: &mut String, accessible_path_strings: Vec>| { + msg.push(':'); - pub name: String, - pub namespace: String, -} + for candidate in accessible_path_strings { + msg.push('\n'); + msg.push_str(&candidate.0); + } + }; + + if let Some(span) = use_placement_span { + let (add_use, trailing) = match mode { + DiagMode::Pattern => { + err.span_suggestions( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + ); + return true; + } + DiagMode::Import { .. } => ("", ""), + DiagMode::Normal => ("use ", ";\n"), + }; + for candidate in &mut accessible_path_strings { + // produce an additional newline to separate the new use statement + // from the directly following item. + let additional_newline = if let FoundUse::No = found_use + && let DiagMode::Normal = mode + { + "\n" + } else { + "" + }; + candidate.0 = + format!("{add_use}{}{append}{trailing}{additional_newline}", candidate.0); + } -#[derive(Diagnostic)] -#[diag("private item shadows public glob re-export")] -pub(crate) struct HiddenGlobReexports { - #[note( - "the name `{$name}` in the {$namespace} namespace is supposed to be publicly re-exported here" - )] - pub glob_reexport: Span, - #[note("but the private item here shadows it")] - pub private_item: Span, - - pub name: String, - pub namespace: String, -} + match mode { + DiagMode::Import { append: true, .. } => { + append_candidates(&mut msg, accessible_path_strings); + err.span_help(span, msg); + } + _ => { + err.span_suggestions_with_style( + span, + msg, + accessible_path_strings.into_iter().map(|a| a.0), + Applicability::MaybeIncorrect, + SuggestionStyle::ShowAlways, + ); + } + } -#[derive(Diagnostic)] -#[diag("the item `{$ident}` is imported redundantly")] -pub(crate) struct RedundantImport { - #[subdiagnostic] - pub subs: Vec, - pub ident: Ident, -} + if let [first, .., last] = &path[..] { + let sp = first.ident.span.until(last.ident.span); + // Our suggestion is empty, so make sure the span is not empty (or we'd ICE). + // Can happen for derive-generated spans. + if sp.can_be_used_for_suggestions() && !sp.is_empty() { + err.span_suggestion_verbose( + sp, + format!("if you import `{}`, refer to it directly", last.ident), + "", + Applicability::Unspecified, + ); + } + } + } else { + append_candidates(&mut msg, accessible_path_strings); + err.help(msg); + } + showed = true; + } + if !inaccessible_path_strings.is_empty() + && (!matches!(mode, DiagMode::Import { unresolved_import: false, .. })) + { + let prefix = + if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" }; + if let [(name, descr, source_span, note, _, is_exact_match)] = + &inaccessible_path_strings[..] + { + let msg = format!( + "{prefix}{}{descr} `{name}`{} exists but is inaccessible", + if *is_exact_match { "" } else { "similarly named " }, + if let DiagMode::Pattern = mode { ", which" } else { "" } + ); + + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + let mut multi_span = MultiSpan::from_span(span); + multi_span.push_span_label(span, "not accessible"); + err.span_note(multi_span, msg); + } else { + err.note(msg); + } + if let Some(note) = (*note).as_deref() { + err.note(note.to_string()); + } + } else { + let descr = inaccessible_path_strings + .iter() + .map(|&(_, descr, _, _, _, _)| descr) + .all_equal_value() + .unwrap_or("item"); + let plural_descr = + if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") }; + let are_exact_matches = inaccessible_path_strings[0].5; + let mut msg = format!( + "{prefix}these {}{plural_descr} exist but are inaccessible", + if are_exact_matches { "" } else { "similarly named " }, + ); + let mut has_colon = false; + + let mut spans = Vec::new(); + for (name, _, source_span, _, _, _) in &inaccessible_path_strings { + if let Some(source_span) = source_span { + let span = tcx.sess.source_map().guess_head_span(*source_span); + spans.push((name, span)); + } else { + if !has_colon { + msg.push(':'); + has_colon = true; + } + msg.push('\n'); + msg.push_str(name); + } + } -#[derive(Subdiagnostic)] -pub(crate) enum RedundantImportSub { - #[label("the item `{$ident}` is already imported here")] - ImportedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined here")] - DefinedHere { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already imported by the extern prelude")] - ImportedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, - #[label("the item `{$ident}` is already defined by the extern prelude")] - DefinedPrelude { - #[primary_span] - span: Span, - ident: Ident, - }, -} + let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect()); + for (name, span) in spans { + multi_span.push_span_label(span, format!("`{name}`: not accessible")); + } -#[derive(Diagnostic)] -#[diag("unnecessary qualification")] -pub(crate) struct UnusedQualifications { - #[suggestion( - "remove the unnecessary path segments", - style = "verbose", - code = "", - applicability = "machine-applicable" - )] - pub removal_span: Span, -} + for note in inaccessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) { + err.note(note.clone()); + } -#[derive(Diagnostic)] -#[diag( - "{$elided -> - [true] `&` without an explicit lifetime name cannot be used here - *[false] `'_` cannot be used here - }" -)] -pub(crate) struct AssociatedConstElidedLifetime { - #[suggestion( - "use the `'static` lifetime", - style = "verbose", - code = "{code}", - applicability = "machine-applicable" - )] - pub span: Span, - - pub code: &'static str, - pub elided: bool, - #[note("cannot automatically infer `'static` because of other lifetimes in scope")] - pub lifetimes_in_scope: MultiSpan, + err.span_note(multi_span, msg); + } + showed = true; + } + showed } -#[derive(Diagnostic)] -#[diag("lifetime parameter `{$ident}` only used once")] -pub(crate) struct SingleUseLifetime { - #[label("this lifetime...")] - pub param_span: Span, - #[label("...is used only here")] - pub use_span: Span, - #[subdiagnostic] - pub suggestion: Option, - - pub ident: Ident, +#[derive(Debug)] +struct UsePlacementFinder { + target_module: NodeId, + first_legal_span: Option, + first_use_span: Option, +} + +impl UsePlacementFinder { + fn check(krate: &Crate, target_module: NodeId) -> (Option, FoundUse) { + let mut finder = + UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None }; + finder.visit_crate(krate); + if let Some(use_span) = finder.first_use_span { + (Some(use_span), FoundUse::Yes) + } else { + (finder.first_legal_span, FoundUse::No) + } + } } -#[derive(Subdiagnostic)] -#[multipart_suggestion("elide the single-use lifetime", applicability = "machine-applicable")] -pub(crate) struct SingleUseLifetimeSugg { - #[suggestion_part(code = "")] - pub deletion_span: Option, - #[suggestion_part(code = "{replace_lt}")] - pub use_span: Span, +impl<'tcx> Visitor<'tcx> for UsePlacementFinder { + fn visit_crate(&mut self, c: &Crate) { + if self.target_module == CRATE_NODE_ID { + let inject = c.spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(&c.items); + } else { + visit::walk_crate(self, c); + } + } + + fn visit_item(&mut self, item: &'tcx ast::Item) { + if self.target_module == item.id { + if let ItemKind::Mod(_, _, ModKind::Loaded(items, _inline, mod_spans)) = &item.kind { + let inject = mod_spans.inject_use_span; + if is_span_suitable_for_use_injection(inject) { + self.first_legal_span = Some(inject); + } + self.first_use_span = search_for_any_use_in_items(items); + } + } else { + visit::walk_item(self, item); + } + } +} - pub replace_lt: String, +#[derive(Default)] +struct BindingVisitor { + identifiers: Vec, + spans: FxHashMap>, } -#[derive(Diagnostic)] -#[diag( - "absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition" -)] -pub(crate) struct AbsPathWithModule { - #[subdiagnostic] - pub sugg: AbsPathWithModuleSugg, +impl<'tcx> Visitor<'tcx> for BindingVisitor { + fn visit_pat(&mut self, pat: &ast::Pat) { + if let ast::PatKind::Ident(_, ident, _) = pat.kind { + self.identifiers.push(ident.name); + self.spans.entry(ident.name).or_default().push(ident.span); + } + visit::walk_pat(self, pat); + } } -#[derive(Subdiagnostic)] -#[suggestion("use `crate`", code = "{replacement}")] -pub(crate) struct AbsPathWithModuleSugg { - #[primary_span] - pub span: Span, - #[applicability] - pub applicability: Applicability, - pub replacement: String, +fn search_for_any_use_in_items(items: &[Box]) -> Option { + for item in items { + if let ItemKind::Use(..) = item.kind + && is_span_suitable_for_use_injection(item.span) + { + let mut lo = item.span.lo(); + for attr in &item.attrs { + if attr.span.eq_ctxt(item.span) { + lo = std::cmp::min(lo, attr.span.lo()); + } + } + return Some(Span::new(lo, lo, item.span.ctxt(), item.span.parent())); + } + } + None } -#[derive(Diagnostic)] -#[diag("hidden lifetime parameters in types are deprecated")] -pub(crate) struct ElidedLifetimesInPaths { - #[subdiagnostic] - pub subdiag: rustc_errors::ElidedLifetimeInPathSubdiag, +fn is_span_suitable_for_use_injection(s: Span) -> bool { + // don't suggest placing a use before the prelude + // import or other generated ones + !s.from_expansion() } -#[derive(Diagnostic)] -#[diag( - "{$num_snippets -> - [one] unused import: {$span_snippets} - *[other] unused imports: {$span_snippets} - }" -)] -pub(crate) struct UnusedImports { - #[subdiagnostic] - pub sugg: Option, - #[help("if this is a test module, consider adding a `#[cfg(test)]` to the containing module")] - pub test_module_span: Option, - - pub span_snippets: DiagArgValue, - pub num_snippets: usize, +#[derive(Debug, Clone, Default)] +pub(crate) struct OnUnknownData { + pub(crate) directive: Box, } -#[derive(Subdiagnostic)] -pub(crate) enum UnusedImportsSugg { - #[suggestion( - "remove the whole `use` item", - applicability = "machine-applicable", - code = "", - style = "tool-only" - )] - RemoveWholeUse { - #[primary_span] - span: Span, - }, - #[multipart_suggestion( - "{$num_to_remove -> - [one] remove the unused import - *[other] remove the unused imports - }", - applicability = "machine-applicable", - style = "tool-only" - )] - RemoveImports { - #[suggestion_part(code = "")] - remove_spans: Vec, - num_to_remove: usize, - }, +impl OnUnknownData { + pub(crate) fn from_attrs<'tcx>( + tcx: TyCtxt<'tcx>, + attrs: &[ast::Attribute], + ) -> Option { + if tcx.features().diagnostic_on_unknown() + && let Some(Attribute::Parsed(AttributeKind::OnUnknown { directive, .. })) = + AttributeParser::parse_limited(tcx.sess, attrs, &[sym::diagnostic, sym::on_unknown]) + { + Some(Self { directive: directive? }) + } else { + None + } + } } From 65853f45b65e4957c857e0ac527a7119af038e52 Mon Sep 17 00:00:00 2001 From: GTimothy <22472919+GTimothy@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:56:29 +0200 Subject: [PATCH 10/10] take into account legacy range change --- tests/ui/libstd-case-typo/libstd.stderr | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/libstd-case-typo/libstd.stderr b/tests/ui/libstd-case-typo/libstd.stderr index 72590e5c78d5e..e49128181cbcc 100644 --- a/tests/ui/libstd-case-typo/libstd.stderr +++ b/tests/ui/libstd-case-typo/libstd.stderr @@ -1212,10 +1212,10 @@ LL + use std::collections::btree_map::Range; | LL + use std::collections::btree_set::Range; | -LL + use std::ops::Range; - | LL + use std::range::Range; | +LL + use std::range::legacy::Range; + | error[E0425]: cannot find type `Rangefrom` in this scope --> $DIR/libstd.rs:274:23 @@ -1225,10 +1225,10 @@ LL | fn test_rangefrom(_x: Rangefrom<()>){} | help: consider importing one of these similarly named structs | -LL + use std::ops::RangeFrom; - | LL + use std::range::RangeFrom; | +LL + use std::range::legacy::RangeFrom; + | error[E0425]: cannot find type `Rangefull` in this scope --> $DIR/libstd.rs:276:23 @@ -1238,7 +1238,7 @@ LL | fn test_rangefull(_x: Rangefull<()>){} | help: consider importing this similarly named struct | -LL + use std::ops::RangeFull; +LL + use std::range::RangeFull; | error[E0425]: cannot find type `Rangeinclusive` in this scope @@ -1249,10 +1249,10 @@ LL | fn test_rangeinclusive(_x: Rangeinclusive<()>){} | help: consider importing one of these similarly named structs | -LL + use std::ops::RangeInclusive; - | LL + use std::range::RangeInclusive; | +LL + use std::range::legacy::RangeInclusive; + | error[E0425]: cannot find type `Rangeto` in this scope --> $DIR/libstd.rs:280:21 @@ -1262,7 +1262,7 @@ LL | fn test_rangeto(_x: Rangeto<()>){} | help: consider importing this similarly named struct | -LL + use std::ops::RangeTo; +LL + use std::range::RangeTo; | error[E0425]: cannot find type `RangetoInclusive` in this scope @@ -1273,10 +1273,10 @@ LL | fn test_rangetoinclusive(_x: RangetoInclusive<()>){} | help: consider importing one of these similarly named structs | -LL + use std::ops::RangeToInclusive; - | LL + use std::range::RangeToInclusive; | +LL + use std::range::legacy::RangeToInclusive; + | error[E0425]: cannot find type `AssertUnwindsafe` in this scope --> $DIR/libstd.rs:287:30