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