rustc_lint_defs/
lib.rs

1use rustc_abi::ExternAbi;
2use rustc_ast::AttrId;
3use rustc_ast::attr::AttributeExt;
4use rustc_ast::node_id::NodeId;
5use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
6use rustc_data_structures::stable_hasher::{
7    HashStable, StableCompare, StableHasher, ToStableHashKey,
8};
9use rustc_error_messages::{DiagMessage, MultiSpan};
10use rustc_hir::def::Namespace;
11use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind};
12use rustc_macros::{Decodable, Encodable, HashStable_Generic};
13pub use rustc_span::edition::Edition;
14use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
15use serde::{Deserialize, Serialize};
16
17pub use self::Level::*;
18
19pub mod builtin;
20
21#[macro_export]
22macro_rules! pluralize {
23    // Pluralize based on count (e.g., apples)
24    ($x:expr) => {
25        if $x == 1 { "" } else { "s" }
26    };
27    ("has", $x:expr) => {
28        if $x == 1 { "has" } else { "have" }
29    };
30    ("is", $x:expr) => {
31        if $x == 1 { "is" } else { "are" }
32    };
33    ("was", $x:expr) => {
34        if $x == 1 { "was" } else { "were" }
35    };
36    ("this", $x:expr) => {
37        if $x == 1 { "this" } else { "these" }
38    };
39}
40
41/// Grammatical tool for displaying messages to end users in a nice form.
42///
43/// Take a list of items and a function to turn those items into a `String`, and output a display
44/// friendly comma separated list of those items.
45// FIXME(estebank): this needs to be changed to go through the translation machinery.
46pub fn listify<T>(list: &[T], fmt: impl Fn(&T) -> String) -> Option<String> {
47    Some(match list {
48        [only] => fmt(&only),
49        [others @ .., last] => format!(
50            "{} and {}",
51            others.iter().map(|i| fmt(i)).collect::<Vec<_>>().join(", "),
52            fmt(&last),
53        ),
54        [] => return None,
55    })
56}
57
58/// Indicates the confidence in the correctness of a suggestion.
59///
60/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
61/// to determine whether it should be automatically applied or if the user should be consulted
62/// before applying the suggestion.
63#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
64#[derive(PartialEq, Eq, PartialOrd, Ord)]
65pub enum Applicability {
66    /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
67    /// This suggestion should be automatically applied.
68    ///
69    /// In case of multiple `MachineApplicable` suggestions (whether as part of
70    /// the same `multipart_suggestion` or not), all of them should be
71    /// automatically applied.
72    MachineApplicable,
73
74    /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
75    /// result in valid Rust code if it is applied.
76    MaybeIncorrect,
77
78    /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
79    /// cannot be applied automatically because it will not result in valid Rust code. The user
80    /// will need to fill in the placeholders.
81    HasPlaceholders,
82
83    /// The applicability of the suggestion is unknown.
84    Unspecified,
85}
86
87/// Each lint expectation has a `LintExpectationId` assigned by the `LintLevelsBuilder`.
88/// Expected diagnostics get the lint level `Expect` which stores the `LintExpectationId`
89/// to match it with the actual expectation later on.
90///
91/// The `LintExpectationId` has to be stable between compilations, as diagnostic
92/// instances might be loaded from cache. Lint messages can be emitted during an
93/// `EarlyLintPass` operating on the AST and during a `LateLintPass` traversing the
94/// HIR tree. The AST doesn't have enough information to create a stable id. The
95/// `LintExpectationId` will instead store the [`AttrId`] defining the expectation.
96/// These `LintExpectationId` will be updated to use the stable [`HirId`] once the
97/// AST has been lowered. The transformation is done by the `LintLevelsBuilder`
98///
99/// Each lint inside the `expect` attribute is tracked individually, the `lint_index`
100/// identifies the lint inside the attribute and ensures that the IDs are unique.
101///
102/// The index values have a type of `u16` to reduce the size of the `LintExpectationId`.
103/// It's reasonable to assume that no user will define 2^16 attributes on one node or
104/// have that amount of lints listed. `u16` values should therefore suffice.
105#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)]
106pub enum LintExpectationId {
107    /// Used for lints emitted during the `EarlyLintPass`. This id is not
108    /// hash stable and should not be cached.
109    Unstable { attr_id: AttrId, lint_index: Option<u16> },
110    /// The [`HirId`] that the lint expectation is attached to. This id is
111    /// stable and can be cached. The additional index ensures that nodes with
112    /// several expectations can correctly match diagnostics to the individual
113    /// expectation.
114    Stable { hir_id: HirId, attr_index: u16, lint_index: Option<u16> },
115}
116
117impl LintExpectationId {
118    pub fn is_stable(&self) -> bool {
119        match self {
120            LintExpectationId::Unstable { .. } => false,
121            LintExpectationId::Stable { .. } => true,
122        }
123    }
124
125    pub fn get_lint_index(&self) -> Option<u16> {
126        let (LintExpectationId::Unstable { lint_index, .. }
127        | LintExpectationId::Stable { lint_index, .. }) = self;
128
129        *lint_index
130    }
131
132    pub fn set_lint_index(&mut self, new_lint_index: Option<u16>) {
133        let (LintExpectationId::Unstable { lint_index, .. }
134        | LintExpectationId::Stable { lint_index, .. }) = self;
135
136        *lint_index = new_lint_index
137    }
138}
139
140impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
141    #[inline]
142    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
143        match self {
144            LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
145                hir_id.hash_stable(hcx, hasher);
146                attr_index.hash_stable(hcx, hasher);
147                lint_index.hash_stable(hcx, hasher);
148            }
149            _ => {
150                unreachable!(
151                    "HashStable should only be called for filled and stable `LintExpectationId`"
152                )
153            }
154        }
155    }
156}
157
158impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
159    type KeyType = (HirId, u16, u16);
160
161    #[inline]
162    fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
163        match self {
164            LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
165                (*hir_id, *attr_index, *lint_index)
166            }
167            _ => {
168                unreachable!("HashStable should only be called for a filled `LintExpectationId`")
169            }
170        }
171    }
172}
173
174/// Setting for how to handle a lint.
175///
176/// See: <https://doc.rust-lang.org/rustc/lints/levels.html>
177#[derive(
178    Clone,
179    Copy,
180    PartialEq,
181    PartialOrd,
182    Eq,
183    Ord,
184    Debug,
185    Hash,
186    Encodable,
187    Decodable,
188    HashStable_Generic
189)]
190pub enum Level {
191    /// The `allow` level will not issue any message.
192    Allow,
193    /// The `expect` level will suppress the lint message but in turn produce a message
194    /// if the lint wasn't issued in the expected scope. `Expect` should not be used as
195    /// an initial level for a lint.
196    ///
197    /// Note that this still means that the lint is enabled in this position and should
198    /// be emitted, this will in turn fulfill the expectation and suppress the lint.
199    ///
200    /// See RFC 2383.
201    ///
202    /// The [`LintExpectationId`] is used to later link a lint emission to the actual
203    /// expectation. It can be ignored in most cases.
204    Expect(LintExpectationId),
205    /// The `warn` level will produce a warning if the lint was violated, however the
206    /// compiler will continue with its execution.
207    Warn,
208    /// This lint level is a special case of [`Warn`], that can't be overridden. This is used
209    /// to ensure that a lint can't be suppressed. This lint level can currently only be set
210    /// via the console and is therefore session specific.
211    ///
212    /// The [`LintExpectationId`] is intended to fulfill expectations marked via the
213    /// `#[expect]` attribute, that will still be suppressed due to the level.
214    ForceWarn(Option<LintExpectationId>),
215    /// The `deny` level will produce an error and stop further execution after the lint
216    /// pass is complete.
217    Deny,
218    /// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous
219    /// levels.
220    Forbid,
221}
222
223impl Level {
224    /// Converts a level to a lower-case string.
225    pub fn as_str(self) -> &'static str {
226        match self {
227            Level::Allow => "allow",
228            Level::Expect(_) => "expect",
229            Level::Warn => "warn",
230            Level::ForceWarn(_) => "force-warn",
231            Level::Deny => "deny",
232            Level::Forbid => "forbid",
233        }
234    }
235
236    /// Converts a lower-case string to a level. This will never construct the expect
237    /// level as that would require a [`LintExpectationId`].
238    pub fn from_str(x: &str) -> Option<Self> {
239        match x {
240            "allow" => Some(Level::Allow),
241            "warn" => Some(Level::Warn),
242            "deny" => Some(Level::Deny),
243            "forbid" => Some(Level::Forbid),
244            "expect" | _ => None,
245        }
246    }
247
248    /// Converts an `Attribute` to a level.
249    pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> {
250        Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
251    }
252
253    /// Converts a `Symbol` to a level.
254    pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> {
255        match s {
256            sym::allow => Some(Level::Allow),
257            sym::expect => {
258                if let Some(attr_id) = id() {
259                    Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
260                } else {
261                    None
262                }
263            }
264            sym::warn => Some(Level::Warn),
265            sym::deny => Some(Level::Deny),
266            sym::forbid => Some(Level::Forbid),
267            _ => None,
268        }
269    }
270
271    pub fn to_cmd_flag(self) -> &'static str {
272        match self {
273            Level::Warn => "-W",
274            Level::Deny => "-D",
275            Level::Forbid => "-F",
276            Level::Allow => "-A",
277            Level::ForceWarn(_) => "--force-warn",
278            Level::Expect(_) => {
279                unreachable!("the expect level does not have a commandline flag")
280            }
281        }
282    }
283
284    pub fn is_error(self) -> bool {
285        match self {
286            Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false,
287            Level::Deny | Level::Forbid => true,
288        }
289    }
290
291    pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
292        match self {
293            Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
294            _ => None,
295        }
296    }
297}
298
299/// Specification of a single lint.
300#[derive(Copy, Clone, Debug)]
301pub struct Lint {
302    /// A string identifier for the lint.
303    ///
304    /// This identifies the lint in attributes and in command-line arguments.
305    /// In those contexts it is always lowercase, but this field is compared
306    /// in a way which is case-insensitive for ASCII characters. This allows
307    /// `declare_lint!()` invocations to follow the convention of upper-case
308    /// statics without repeating the name.
309    ///
310    /// The name is written with underscores, e.g., "unused_imports".
311    /// On the command line, underscores become dashes.
312    ///
313    /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming>
314    /// for naming guidelines.
315    pub name: &'static str,
316
317    /// Default level for the lint.
318    ///
319    /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels>
320    /// for guidelines on choosing a default level.
321    pub default_level: Level,
322
323    /// Description of the lint or the issue it detects.
324    ///
325    /// e.g., "imports that are never used"
326    pub desc: &'static str,
327
328    /// Starting at the given edition, default to the given lint level. If this is `None`, then use
329    /// `default_level`.
330    pub edition_lint_opts: Option<(Edition, Level)>,
331
332    /// `true` if this lint is reported even inside expansions of external macros.
333    pub report_in_external_macro: bool,
334
335    pub future_incompatible: Option<FutureIncompatibleInfo>,
336
337    /// `true` if this lint is being loaded by another tool (e.g. Clippy).
338    pub is_externally_loaded: bool,
339
340    /// `Some` if this lint is feature gated, otherwise `None`.
341    pub feature_gate: Option<Symbol>,
342
343    pub crate_level_only: bool,
344
345    /// `true` if this lint should not be filtered out under any circustamces
346    /// (e.g. the unknown_attributes lint)
347    pub eval_always: bool,
348}
349
350/// Extra information for a future incompatibility lint.
351#[derive(Copy, Clone, Debug)]
352pub struct FutureIncompatibleInfo {
353    /// e.g., a URL for an issue/PR/RFC or error code
354    pub reference: &'static str,
355    /// The reason for the lint used by diagnostics to provide
356    /// the right help message
357    pub reason: FutureIncompatibilityReason,
358    /// Whether to explain the reason to the user.
359    ///
360    /// Set to false for lints that already include a more detailed
361    /// explanation.
362    pub explain_reason: bool,
363}
364
365/// The reason for future incompatibility
366///
367/// Future-incompatible lints come in roughly two categories:
368///
369/// 1. There was a mistake in the compiler (such as a soundness issue), and
370///    we're trying to fix it, but it may be a breaking change.
371/// 2. A change across an Edition boundary, typically used for the
372///    introduction of new language features that can't otherwise be
373///    introduced in a backwards-compatible way.
374///
375/// See <https://rustc-dev-guide.rust-lang.org/bug-fix-procedure.html> and
376/// <https://rustc-dev-guide.rust-lang.org/diagnostics.html#future-incompatible-lints>
377/// for more information.
378#[derive(Copy, Clone, Debug)]
379pub enum FutureIncompatibilityReason {
380    /// This will be an error in a future release for all editions
381    ///
382    /// This will *not* show up in cargo's future breakage report.
383    /// The warning will hence only be seen in local crates, not in dependencies.
384    ///
385    /// Choose this variant when you are first introducing a "future
386    /// incompatible" warning that is intended to eventually be fixed in the
387    /// future. This allows crate developers an opportunity to fix the warning
388    /// before blasting all dependents with a warning they can't fix
389    /// (dependents have to wait for a new release of the affected crate to be
390    /// published).
391    ///
392    /// After a lint has been in this state for a while, consider graduating
393    /// it to [`FutureIncompatibilityReason::FutureReleaseErrorReportInDeps`].
394    FutureReleaseErrorDontReportInDeps,
395    /// This will be an error in a future release, and
396    /// Cargo should create a report even for dependencies
397    ///
398    /// This is the *only* reason that will make future incompatibility warnings show up in cargo's
399    /// reports. All other future incompatibility warnings are not visible when they occur in a
400    /// dependency.
401    ///
402    /// Choose this variant after the lint has been sitting in the
403    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`]
404    /// state for a while, and you feel like it is ready to graduate to
405    /// warning everyone. It is a good signal that it is ready if you can
406    /// determine that all or most affected crates on crates.io have been
407    /// updated.
408    ///
409    /// After some period of time, lints with this variant can be turned into
410    /// hard errors (and the lint removed). Preferably when there is some
411    /// confidence that the number of impacted projects is very small (few
412    /// should have a broken dependency in their dependency tree).
413    ///
414    /// [`EditionAndFutureReleaseError`]: FutureIncompatibilityReason::EditionAndFutureReleaseError
415    FutureReleaseErrorReportInDeps,
416    /// Code that changes meaning in some way in a
417    /// future release.
418    ///
419    /// Choose this variant when the semantics of existing code is changing,
420    /// (as opposed to
421    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`],
422    /// which is for when code is going to be rejected in the future).
423    FutureReleaseSemanticsChange,
424    /// Previously accepted code that will become an
425    /// error in the provided edition
426    ///
427    /// Choose this variant for code that you want to start rejecting across
428    /// an edition boundary. This will automatically include the lint in the
429    /// `rust-20xx-compatibility` lint group, which is used by `cargo fix
430    /// --edition` to do migrations. The lint *should* be auto-fixable with
431    /// [`Applicability::MachineApplicable`].
432    ///
433    /// The lint can either be `Allow` or `Warn` by default. If it is `Allow`,
434    /// users usually won't see this warning unless they are doing an edition
435    /// migration manually or there is a problem during the migration (cargo's
436    /// automatic migrations will force the level to `Warn`). If it is `Warn`
437    /// by default, users on all editions will see this warning (only do this
438    /// if you think it is important for everyone to be aware of the change,
439    /// and to encourage people to update their code on all editions).
440    ///
441    /// See also [`FutureIncompatibilityReason::EditionSemanticsChange`] if
442    /// you have code that is changing semantics across the edition (as
443    /// opposed to being rejected).
444    EditionError(Edition),
445    /// Code that changes meaning in some way in
446    /// the provided edition
447    ///
448    /// This is the same as [`FutureIncompatibilityReason::EditionError`],
449    /// except for situations where the semantics change across an edition. It
450    /// slightly changes the text of the diagnostic, but is otherwise the
451    /// same.
452    EditionSemanticsChange(Edition),
453    /// This will be an error in the provided edition *and* in a future
454    /// release.
455    ///
456    /// This variant a combination of [`FutureReleaseErrorDontReportInDeps`]
457    /// and [`EditionError`]. This is useful in rare cases when we
458    /// want to have "preview" of a breaking change in an edition, but do a
459    /// breaking change later on all editions anyway.
460    ///
461    /// [`EditionError`]: FutureIncompatibilityReason::EditionError
462    /// [`FutureReleaseErrorDontReportInDeps`]: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps
463    EditionAndFutureReleaseError(Edition),
464    /// This will change meaning in the provided edition *and* in a future
465    /// release.
466    ///
467    /// This variant a combination of [`FutureReleaseSemanticsChange`]
468    /// and [`EditionSemanticsChange`]. This is useful in rare cases when we
469    /// want to have "preview" of a breaking change in an edition, but do a
470    /// breaking change later on all editions anyway.
471    ///
472    /// [`EditionSemanticsChange`]: FutureIncompatibilityReason::EditionSemanticsChange
473    /// [`FutureReleaseSemanticsChange`]: FutureIncompatibilityReason::FutureReleaseSemanticsChange
474    EditionAndFutureReleaseSemanticsChange(Edition),
475    /// A custom reason.
476    ///
477    /// Choose this variant if the built-in text of the diagnostic of the
478    /// other variants doesn't match your situation. This is behaviorally
479    /// equivalent to
480    /// [`FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps`].
481    Custom(&'static str),
482}
483
484impl FutureIncompatibilityReason {
485    pub fn edition(self) -> Option<Edition> {
486        match self {
487            Self::EditionError(e)
488            | Self::EditionSemanticsChange(e)
489            | Self::EditionAndFutureReleaseError(e)
490            | Self::EditionAndFutureReleaseSemanticsChange(e) => Some(e),
491
492            FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps
493            | FutureIncompatibilityReason::FutureReleaseErrorReportInDeps
494            | FutureIncompatibilityReason::FutureReleaseSemanticsChange
495            | FutureIncompatibilityReason::Custom(_) => None,
496        }
497    }
498
499    pub fn has_future_breakage(self) -> bool {
500        match self {
501            FutureIncompatibilityReason::FutureReleaseErrorReportInDeps => true,
502
503            FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps
504            | FutureIncompatibilityReason::FutureReleaseSemanticsChange
505            | FutureIncompatibilityReason::EditionError(_)
506            | FutureIncompatibilityReason::EditionSemanticsChange(_)
507            | FutureIncompatibilityReason::EditionAndFutureReleaseError(_)
508            | FutureIncompatibilityReason::EditionAndFutureReleaseSemanticsChange(_)
509            | FutureIncompatibilityReason::Custom(_) => false,
510        }
511    }
512}
513
514impl FutureIncompatibleInfo {
515    pub const fn default_fields_for_macro() -> Self {
516        FutureIncompatibleInfo {
517            reference: "",
518            reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
519            explain_reason: true,
520        }
521    }
522}
523
524impl Lint {
525    pub const fn default_fields_for_macro() -> Self {
526        Lint {
527            name: "",
528            default_level: Level::Forbid,
529            desc: "",
530            edition_lint_opts: None,
531            is_externally_loaded: false,
532            report_in_external_macro: false,
533            future_incompatible: None,
534            feature_gate: None,
535            crate_level_only: false,
536            eval_always: false,
537        }
538    }
539
540    /// Gets the lint's name, with ASCII letters converted to lowercase.
541    pub fn name_lower(&self) -> String {
542        self.name.to_ascii_lowercase()
543    }
544
545    pub fn default_level(&self, edition: Edition) -> Level {
546        self.edition_lint_opts
547            .filter(|(e, _)| *e <= edition)
548            .map(|(_, l)| l)
549            .unwrap_or(self.default_level)
550    }
551}
552
553/// Identifies a lint known to the compiler.
554#[derive(Clone, Copy, Debug)]
555pub struct LintId {
556    // Identity is based on pointer equality of this field.
557    pub lint: &'static Lint,
558}
559
560impl PartialEq for LintId {
561    fn eq(&self, other: &LintId) -> bool {
562        std::ptr::eq(self.lint, other.lint)
563    }
564}
565
566impl Eq for LintId {}
567
568impl std::hash::Hash for LintId {
569    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
570        let ptr = self.lint as *const Lint;
571        ptr.hash(state);
572    }
573}
574
575impl LintId {
576    /// Gets the `LintId` for a `Lint`.
577    pub fn of(lint: &'static Lint) -> LintId {
578        LintId { lint }
579    }
580
581    pub fn lint_name_raw(&self) -> &'static str {
582        self.lint.name
583    }
584
585    /// Gets the name of the lint.
586    pub fn to_string(&self) -> String {
587        self.lint.name_lower()
588    }
589}
590
591impl<HCX> HashStable<HCX> for LintId {
592    #[inline]
593    fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
594        self.lint_name_raw().hash_stable(hcx, hasher);
595    }
596}
597
598impl<HCX> ToStableHashKey<HCX> for LintId {
599    type KeyType = &'static str;
600
601    #[inline]
602    fn to_stable_hash_key(&self, _: &HCX) -> &'static str {
603        self.lint_name_raw()
604    }
605}
606
607impl StableCompare for LintId {
608    const CAN_USE_UNSTABLE_SORT: bool = true;
609
610    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
611        self.lint_name_raw().cmp(&other.lint_name_raw())
612    }
613}
614
615#[derive(Debug)]
616pub struct AmbiguityErrorDiag {
617    pub msg: String,
618    pub span: Span,
619    pub label_span: Span,
620    pub label_msg: String,
621    pub note_msg: String,
622    pub b1_span: Span,
623    pub b1_note_msg: String,
624    pub b1_help_msgs: Vec<String>,
625    pub b2_span: Span,
626    pub b2_note_msg: String,
627    pub b2_help_msgs: Vec<String>,
628}
629
630#[derive(Debug, Clone)]
631pub enum DeprecatedSinceKind {
632    InEffect,
633    InFuture,
634    InVersion(String),
635}
636
637#[derive(Debug)]
638pub enum ElidedLifetimeResolution {
639    Static,
640    Param(Symbol, Span),
641}
642
643// This could be a closure, but then implementing derive trait
644// becomes hacky (and it gets allocated).
645#[derive(Debug)]
646pub enum BuiltinLintDiag {
647    AbsPathWithModule(Span),
648    ProcMacroDeriveResolutionFallback {
649        span: Span,
650        ns: Namespace,
651        ident: Ident,
652    },
653    MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
654    ElidedLifetimesInPaths(usize, Span, bool, Span),
655    ElidedNamedLifetimes {
656        elided: (Span, MissingLifetimeKind),
657        resolution: ElidedLifetimeResolution,
658    },
659    UnknownCrateTypes {
660        span: Span,
661        candidate: Option<Symbol>,
662    },
663    UnusedImports {
664        remove_whole_use: bool,
665        num_to_remove: usize,
666        remove_spans: Vec<Span>,
667        test_module_span: Option<Span>,
668        span_snippets: Vec<String>,
669    },
670    RedundantImport(Vec<(Span, bool)>, Ident),
671    DeprecatedMacro {
672        suggestion: Option<Symbol>,
673        suggestion_span: Span,
674        note: Option<Symbol>,
675        path: String,
676        since_kind: DeprecatedSinceKind,
677    },
678    MissingAbi(Span, ExternAbi),
679    UnusedDocComment(Span),
680    UnusedBuiltinAttribute {
681        attr_name: Symbol,
682        macro_name: String,
683        invoc_span: Span,
684    },
685    PatternsInFnsWithoutBody {
686        span: Span,
687        ident: Ident,
688        is_foreign: bool,
689    },
690    LegacyDeriveHelpers(Span),
691    OrPatternsBackCompat(Span, String),
692    ReservedPrefix(Span, String),
693    /// `'r#` in edition < 2021.
694    RawPrefix(Span),
695    /// `##` or `#"` in edition < 2024.
696    ReservedString {
697        is_string: bool,
698        suggestion: Span,
699    },
700    TrailingMacro(bool, Ident),
701    BreakWithLabelAndLoop(Span),
702    UnicodeTextFlow(Span, String),
703    UnexpectedCfgName((Symbol, Span), Option<(Symbol, Span)>),
704    UnexpectedCfgValue((Symbol, Span), Option<(Symbol, Span)>),
705    DeprecatedWhereclauseLocation(Span, Option<(Span, String)>),
706    MissingUnsafeOnExtern {
707        suggestion: Span,
708    },
709    SingleUseLifetime {
710        /// Span of the parameter which declares this lifetime.
711        param_span: Span,
712        /// Span of the code that should be removed when eliding this lifetime.
713        /// This span should include leading or trailing comma.
714        deletion_span: Option<Span>,
715        /// Span of the single use, or None if the lifetime is never used.
716        /// If true, the lifetime will be fully elided.
717        use_span: Option<(Span, bool)>,
718        ident: Ident,
719    },
720    NamedArgumentUsedPositionally {
721        /// Span where the named argument is used by position and will be replaced with the named
722        /// argument name
723        position_sp_to_replace: Option<Span>,
724        /// Span where the named argument is used by position and is used for lint messages
725        position_sp_for_msg: Option<Span>,
726        /// Span where the named argument's name is (so we know where to put the warning message)
727        named_arg_sp: Span,
728        /// String containing the named arguments name
729        named_arg_name: String,
730        /// Indicates if the named argument is used as a width/precision for formatting
731        is_formatting_arg: bool,
732    },
733    ByteSliceInPackedStructWithDerive {
734        // FIXME: enum of byte/string
735        ty: String,
736    },
737    UnusedExternCrate {
738        removal_span: Span,
739    },
740    ExternCrateNotIdiomatic {
741        vis_span: Span,
742        ident_span: Span,
743    },
744    AmbiguousGlobImports {
745        diag: AmbiguityErrorDiag,
746    },
747    AmbiguousGlobReexports {
748        /// The name for which collision(s) have occurred.
749        name: String,
750        /// The name space for which the collision(s) occurred in.
751        namespace: String,
752        /// Span where the name is first re-exported.
753        first_reexport_span: Span,
754        /// Span where the same name is also re-exported.
755        duplicate_reexport_span: Span,
756    },
757    HiddenGlobReexports {
758        /// The name of the local binding which shadows the glob re-export.
759        name: String,
760        /// The namespace for which the shadowing occurred in.
761        namespace: String,
762        /// The glob reexport that is shadowed by the local binding.
763        glob_reexport_span: Span,
764        /// The local binding that shadows the glob reexport.
765        private_item_span: Span,
766    },
767    UnusedQualifications {
768        /// The span of the unnecessarily-qualified path to remove.
769        removal_span: Span,
770    },
771    UnsafeAttrOutsideUnsafe {
772        attribute_name_span: Span,
773        sugg_spans: (Span, Span),
774    },
775    AssociatedConstElidedLifetime {
776        elided: bool,
777        span: Span,
778        lifetimes_in_scope: MultiSpan,
779    },
780    RedundantImportVisibility {
781        span: Span,
782        max_vis: String,
783        import_vis: String,
784    },
785    UnknownDiagnosticAttribute {
786        span: Span,
787        typo_name: Option<Symbol>,
788    },
789    MacroUseDeprecated,
790    UnusedMacroUse,
791    PrivateExternCrateReexport {
792        source: Ident,
793        extern_crate_span: Span,
794    },
795    UnusedLabel,
796    MacroIsPrivate(Ident),
797    UnusedMacroDefinition(Symbol),
798    MacroRuleNeverUsed(usize, Symbol),
799    UnstableFeature(DiagMessage),
800    AvoidUsingIntelSyntax,
801    AvoidUsingAttSyntax,
802    IncompleteInclude,
803    UnnameableTestItems,
804    DuplicateMacroAttribute,
805    CfgAttrNoAttributes,
806    MissingFragmentSpecifier,
807    MetaVariableStillRepeating(MacroRulesNormalizedIdent),
808    MetaVariableWrongOperator,
809    DuplicateMatcherBinding,
810    UnknownMacroVariable(MacroRulesNormalizedIdent),
811    UnusedCrateDependency {
812        extern_crate: Symbol,
813        local_crate: Symbol,
814    },
815    IllFormedAttributeInput {
816        suggestions: Vec<String>,
817    },
818    InnerAttributeUnstable {
819        is_macro: bool,
820    },
821    OutOfScopeMacroCalls {
822        span: Span,
823        path: String,
824        location: String,
825    },
826    UnexpectedBuiltinCfg {
827        cfg: String,
828        cfg_name: Symbol,
829        controlled_by: &'static str,
830    },
831}
832
833/// Lints that are buffered up early on in the `Session` before the
834/// `LintLevels` is calculated.
835#[derive(Debug)]
836pub struct BufferedEarlyLint {
837    /// The span of code that we are linting on.
838    pub span: Option<MultiSpan>,
839
840    /// The `NodeId` of the AST node that generated the lint.
841    pub node_id: NodeId,
842
843    /// A lint Id that can be passed to
844    /// `rustc_lint::early::EarlyContextAndPass::check_id`.
845    pub lint_id: LintId,
846
847    /// Customization of the `Diag<'_>` for the lint.
848    pub diagnostic: BuiltinLintDiag,
849}
850
851#[derive(Default, Debug)]
852pub struct LintBuffer {
853    pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
854}
855
856impl LintBuffer {
857    pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
858        self.map.entry(early_lint.node_id).or_default().push(early_lint);
859    }
860
861    pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
862        // FIXME(#120456) - is `swap_remove` correct?
863        self.map.swap_remove(&id).unwrap_or_default()
864    }
865
866    pub fn buffer_lint(
867        &mut self,
868        lint: &'static Lint,
869        node_id: NodeId,
870        span: impl Into<MultiSpan>,
871        diagnostic: BuiltinLintDiag,
872    ) {
873        self.add_early_lint(BufferedEarlyLint {
874            lint_id: LintId::of(lint),
875            node_id,
876            span: Some(span.into()),
877            diagnostic,
878        });
879    }
880}
881
882pub type RegisteredTools = FxIndexSet<Ident>;
883
884/// Declares a static item of type `&'static Lint`.
885///
886/// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for
887/// documentation and guidelines on writing lints.
888///
889/// The macro call should start with a doc comment explaining the lint
890/// which will be embedded in the rustc user documentation book. It should
891/// be written in markdown and have a format that looks like this:
892///
893/// ```rust,ignore (doc-example)
894/// /// The `my_lint_name` lint detects [short explanation here].
895/// ///
896/// /// ### Example
897/// ///
898/// /// ```rust
899/// /// [insert a concise example that triggers the lint]
900/// /// ```
901/// ///
902/// /// {{produces}}
903/// ///
904/// /// ### Explanation
905/// ///
906/// /// This should be a detailed explanation of *why* the lint exists,
907/// /// and also include suggestions on how the user should fix the problem.
908/// /// Try to keep the text simple enough that a beginner can understand,
909/// /// and include links to other documentation for terminology that a
910/// /// beginner may not be familiar with. If this is "allow" by default,
911/// /// it should explain why (are there false positives or other issues?). If
912/// /// this is a future-incompatible lint, it should say so, with text that
913/// /// looks roughly like this:
914/// ///
915/// /// This is a [future-incompatible] lint to transition this to a hard
916/// /// error in the future. See [issue #xxxxx] for more details.
917/// ///
918/// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx
919/// ```
920///
921/// The `{{produces}}` tag will be automatically replaced with the output from
922/// the example by the build system. If the lint example is too complex to run
923/// as a simple example (for example, it needs an extern crate), mark the code
924/// block with `ignore` and manually replace the `{{produces}}` line with the
925/// expected output in a `text` code block.
926///
927/// If this is a rustdoc-only lint, then only include a brief introduction
928/// with a link with the text `[rustdoc book]` so that the validator knows
929/// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
930///
931/// Commands to view and test the documentation:
932///
933/// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
934/// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
935///   correct style, and that the code example actually emits the expected
936///   lint.
937///
938/// If you have already built the compiler, and you want to make changes to
939/// just the doc comments, then use the `--keep-stage=0` flag with the above
940/// commands to avoid rebuilding the compiler.
941#[macro_export]
942macro_rules! declare_lint {
943    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
944        $crate::declare_lint!(
945            $(#[$attr])* $vis $NAME, $Level, $desc,
946        );
947    );
948    ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
949     $(@eval_always = $eval_always:literal)?
950     $(@feature_gate = $gate:ident;)?
951     $(@future_incompatible = FutureIncompatibleInfo {
952        reason: $reason:expr,
953        $($field:ident : $val:expr),* $(,)*
954     }; )?
955     $(@edition $lint_edition:ident => $edition_level:ident;)?
956     $($v:ident),*) => (
957        $(#[$attr])*
958        $vis static $NAME: &$crate::Lint = &$crate::Lint {
959            name: stringify!($NAME),
960            default_level: $crate::$Level,
961            desc: $desc,
962            is_externally_loaded: false,
963            $($v: true,)*
964            $(feature_gate: Some(rustc_span::sym::$gate),)?
965            $(future_incompatible: Some($crate::FutureIncompatibleInfo {
966                reason: $reason,
967                $($field: $val,)*
968                ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
969            }),)?
970            $(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)?
971            $(eval_always: $eval_always,)?
972            ..$crate::Lint::default_fields_for_macro()
973        };
974    );
975}
976
977#[macro_export]
978macro_rules! declare_tool_lint {
979    (
980        $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
981        $(, @eval_always = $eval_always:literal)?
982        $(, @feature_gate = $gate:ident;)?
983    ) => (
984        $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?}
985    );
986    (
987        $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
988        report_in_external_macro: $rep:expr
989        $(, @eval_always = $eval_always: literal)?
990        $(, @feature_gate = $gate:ident;)?
991    ) => (
992         $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep  $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?}
993    );
994    (
995        $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
996        $external:expr
997        $(, @eval_always = $eval_always: literal)?
998        $(, @feature_gate = $gate:ident;)?
999    ) => (
1000        $(#[$attr])*
1001        $vis static $NAME: &$crate::Lint = &$crate::Lint {
1002            name: &concat!(stringify!($tool), "::", stringify!($NAME)),
1003            default_level: $crate::$Level,
1004            desc: $desc,
1005            edition_lint_opts: None,
1006            report_in_external_macro: $external,
1007            future_incompatible: None,
1008            is_externally_loaded: true,
1009            $(feature_gate: Some(rustc_span::sym::$gate),)?
1010            crate_level_only: false,
1011            $(eval_always: $eval_always,)?
1012            ..$crate::Lint::default_fields_for_macro()
1013        };
1014    );
1015}
1016
1017pub type LintVec = Vec<&'static Lint>;
1018
1019pub trait LintPass {
1020    fn name(&self) -> &'static str;
1021    fn get_lints(&self) -> LintVec;
1022}
1023
1024/// Implements `LintPass for $ty` with the given list of `Lint` statics.
1025#[macro_export]
1026macro_rules! impl_lint_pass {
1027    ($ty:ty => [$($lint:expr),* $(,)?]) => {
1028        impl $crate::LintPass for $ty {
1029            fn name(&self) -> &'static str { stringify!($ty) }
1030            fn get_lints(&self) -> $crate::LintVec { vec![$($lint),*] }
1031        }
1032        impl $ty {
1033            #[allow(unused)]
1034            pub fn lint_vec() -> $crate::LintVec { vec![$($lint),*] }
1035        }
1036    };
1037}
1038
1039/// Declares a type named `$name` which implements `LintPass`.
1040/// To the right of `=>` a comma separated list of `Lint` statics is given.
1041#[macro_export]
1042macro_rules! declare_lint_pass {
1043    ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
1044        $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
1045        $crate::impl_lint_pass!($name => [$($lint),*]);
1046    };
1047}