]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint_defs/src/lib.rs
Auto merge of #81756 - ehuss:update-cargo, r=ehuss
[rust.git] / compiler / rustc_lint_defs / src / lib.rs
1 #[macro_use]
2 extern crate rustc_macros;
3
4 pub use self::Level::*;
5 use rustc_ast::node_id::{NodeId, NodeMap};
6 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
7 use rustc_span::edition::Edition;
8 use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
9 use rustc_target::spec::abi::Abi;
10
11 pub mod builtin;
12
13 #[macro_export]
14 macro_rules! pluralize {
15     ($x:expr) => {
16         if $x != 1 { "s" } else { "" }
17     };
18 }
19
20 /// Indicates the confidence in the correctness of a suggestion.
21 ///
22 /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
23 /// to determine whether it should be automatically applied or if the user should be consulted
24 /// before applying the suggestion.
25 #[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
26 pub enum Applicability {
27     /// The suggestion is definitely what the user intended. This suggestion should be
28     /// automatically applied.
29     MachineApplicable,
30
31     /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
32     /// result in valid Rust code if it is applied.
33     MaybeIncorrect,
34
35     /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
36     /// cannot be applied automatically because it will not result in valid Rust code. The user
37     /// will need to fill in the placeholders.
38     HasPlaceholders,
39
40     /// The applicability of the suggestion is unknown.
41     Unspecified,
42 }
43
44 /// Setting for how to handle a lint.
45 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
46 pub enum Level {
47     Allow,
48     Warn,
49     Deny,
50     Forbid,
51 }
52
53 rustc_data_structures::impl_stable_hash_via_hash!(Level);
54
55 impl Level {
56     /// Converts a level to a lower-case string.
57     pub fn as_str(self) -> &'static str {
58         match self {
59             Level::Allow => "allow",
60             Level::Warn => "warn",
61             Level::Deny => "deny",
62             Level::Forbid => "forbid",
63         }
64     }
65
66     /// Converts a lower-case string to a level.
67     pub fn from_str(x: &str) -> Option<Level> {
68         match x {
69             "allow" => Some(Level::Allow),
70             "warn" => Some(Level::Warn),
71             "deny" => Some(Level::Deny),
72             "forbid" => Some(Level::Forbid),
73             _ => None,
74         }
75     }
76
77     /// Converts a symbol to a level.
78     pub fn from_symbol(x: Symbol) -> Option<Level> {
79         match x {
80             sym::allow => Some(Level::Allow),
81             sym::warn => Some(Level::Warn),
82             sym::deny => Some(Level::Deny),
83             sym::forbid => Some(Level::Forbid),
84             _ => None,
85         }
86     }
87 }
88
89 /// Specification of a single lint.
90 #[derive(Copy, Clone, Debug)]
91 pub struct Lint {
92     /// A string identifier for the lint.
93     ///
94     /// This identifies the lint in attributes and in command-line arguments.
95     /// In those contexts it is always lowercase, but this field is compared
96     /// in a way which is case-insensitive for ASCII characters. This allows
97     /// `declare_lint!()` invocations to follow the convention of upper-case
98     /// statics without repeating the name.
99     ///
100     /// The name is written with underscores, e.g., "unused_imports".
101     /// On the command line, underscores become dashes.
102     ///
103     /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming>
104     /// for naming guidelines.
105     pub name: &'static str,
106
107     /// Default level for the lint.
108     ///
109     /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels>
110     /// for guidelines on choosing a default level.
111     pub default_level: Level,
112
113     /// Description of the lint or the issue it detects.
114     ///
115     /// e.g., "imports that are never used"
116     pub desc: &'static str,
117
118     /// Starting at the given edition, default to the given lint level. If this is `None`, then use
119     /// `default_level`.
120     pub edition_lint_opts: Option<(Edition, Level)>,
121
122     /// `true` if this lint is reported even inside expansions of external macros.
123     pub report_in_external_macro: bool,
124
125     pub future_incompatible: Option<FutureIncompatibleInfo>,
126
127     pub is_plugin: bool,
128
129     /// `Some` if this lint is feature gated, otherwise `None`.
130     pub feature_gate: Option<Symbol>,
131
132     pub crate_level_only: bool,
133 }
134
135 /// Extra information for a future incompatibility lint.
136 #[derive(Copy, Clone, Debug)]
137 pub struct FutureIncompatibleInfo {
138     /// e.g., a URL for an issue/PR/RFC or error code
139     pub reference: &'static str,
140     /// If this is an edition fixing lint, the edition in which
141     /// this lint becomes obsolete
142     pub edition: Option<Edition>,
143     /// Information about a future breakage, which will
144     /// be emitted in JSON messages to be displayed by Cargo
145     /// for upstream deps
146     pub future_breakage: Option<FutureBreakage>,
147 }
148
149 #[derive(Copy, Clone, Debug)]
150 pub struct FutureBreakage {
151     pub date: Option<&'static str>,
152 }
153
154 impl FutureIncompatibleInfo {
155     pub const fn default_fields_for_macro() -> Self {
156         FutureIncompatibleInfo { reference: "", edition: None, future_breakage: None }
157     }
158 }
159
160 impl Lint {
161     pub const fn default_fields_for_macro() -> Self {
162         Lint {
163             name: "",
164             default_level: Level::Forbid,
165             desc: "",
166             edition_lint_opts: None,
167             is_plugin: false,
168             report_in_external_macro: false,
169             future_incompatible: None,
170             feature_gate: None,
171             crate_level_only: false,
172         }
173     }
174
175     /// Gets the lint's name, with ASCII letters converted to lowercase.
176     pub fn name_lower(&self) -> String {
177         self.name.to_ascii_lowercase()
178     }
179
180     pub fn default_level(&self, edition: Edition) -> Level {
181         self.edition_lint_opts
182             .filter(|(e, _)| *e <= edition)
183             .map(|(_, l)| l)
184             .unwrap_or(self.default_level)
185     }
186 }
187
188 /// Identifies a lint known to the compiler.
189 #[derive(Clone, Copy, Debug)]
190 pub struct LintId {
191     // Identity is based on pointer equality of this field.
192     pub lint: &'static Lint,
193 }
194
195 impl PartialEq for LintId {
196     fn eq(&self, other: &LintId) -> bool {
197         std::ptr::eq(self.lint, other.lint)
198     }
199 }
200
201 impl Eq for LintId {}
202
203 impl std::hash::Hash for LintId {
204     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
205         let ptr = self.lint as *const Lint;
206         ptr.hash(state);
207     }
208 }
209
210 impl LintId {
211     /// Gets the `LintId` for a `Lint`.
212     pub fn of(lint: &'static Lint) -> LintId {
213         LintId { lint }
214     }
215
216     pub fn lint_name_raw(&self) -> &'static str {
217         self.lint.name
218     }
219
220     /// Gets the name of the lint.
221     pub fn to_string(&self) -> String {
222         self.lint.name_lower()
223     }
224 }
225
226 impl<HCX> HashStable<HCX> for LintId {
227     #[inline]
228     fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
229         self.lint_name_raw().hash_stable(hcx, hasher);
230     }
231 }
232
233 impl<HCX> ToStableHashKey<HCX> for LintId {
234     type KeyType = &'static str;
235
236     #[inline]
237     fn to_stable_hash_key(&self, _: &HCX) -> &'static str {
238         self.lint_name_raw()
239     }
240 }
241
242 // This could be a closure, but then implementing derive trait
243 // becomes hacky (and it gets allocated).
244 #[derive(PartialEq)]
245 pub enum BuiltinLintDiagnostics {
246     Normal,
247     BareTraitObject(Span, /* is_global */ bool),
248     AbsPathWithModule(Span),
249     ProcMacroDeriveResolutionFallback(Span),
250     MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
251     ElidedLifetimesInPaths(usize, Span, bool, Span, String),
252     UnknownCrateTypes(Span, String, String),
253     UnusedImports(String, Vec<(Span, String)>),
254     RedundantImport(Vec<(Span, bool)>, Ident),
255     DeprecatedMacro(Option<Symbol>, Span),
256     MissingAbi(Span, Abi),
257     UnusedDocComment(Span),
258     PatternsInFnsWithoutBody(Span, Ident),
259 }
260
261 /// Lints that are buffered up early on in the `Session` before the
262 /// `LintLevels` is calculated.
263 #[derive(PartialEq)]
264 pub struct BufferedEarlyLint {
265     /// The span of code that we are linting on.
266     pub span: MultiSpan,
267
268     /// The lint message.
269     pub msg: String,
270
271     /// The `NodeId` of the AST node that generated the lint.
272     pub node_id: NodeId,
273
274     /// A lint Id that can be passed to
275     /// `rustc_lint::early::EarlyContextAndPass::check_id`.
276     pub lint_id: LintId,
277
278     /// Customization of the `DiagnosticBuilder<'_>` for the lint.
279     pub diagnostic: BuiltinLintDiagnostics,
280 }
281
282 #[derive(Default)]
283 pub struct LintBuffer {
284     pub map: NodeMap<Vec<BufferedEarlyLint>>,
285 }
286
287 impl LintBuffer {
288     pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
289         let arr = self.map.entry(early_lint.node_id).or_default();
290         if !arr.contains(&early_lint) {
291             arr.push(early_lint);
292         }
293     }
294
295     pub fn add_lint(
296         &mut self,
297         lint: &'static Lint,
298         node_id: NodeId,
299         span: MultiSpan,
300         msg: &str,
301         diagnostic: BuiltinLintDiagnostics,
302     ) {
303         let lint_id = LintId::of(lint);
304         let msg = msg.to_string();
305         self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
306     }
307
308     pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
309         self.map.remove(&id).unwrap_or_default()
310     }
311
312     pub fn buffer_lint(
313         &mut self,
314         lint: &'static Lint,
315         id: NodeId,
316         sp: impl Into<MultiSpan>,
317         msg: &str,
318     ) {
319         self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
320     }
321
322     pub fn buffer_lint_with_diagnostic(
323         &mut self,
324         lint: &'static Lint,
325         id: NodeId,
326         sp: impl Into<MultiSpan>,
327         msg: &str,
328         diagnostic: BuiltinLintDiagnostics,
329     ) {
330         self.add_lint(lint, id, sp.into(), msg, diagnostic)
331     }
332 }
333
334 /// Declares a static item of type `&'static Lint`.
335 ///
336 /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for
337 /// documentation and guidelines on writing lints.
338 ///
339 /// The macro call should start with a doc comment explaining the lint
340 /// which will be embedded in the rustc user documentation book. It should
341 /// be written in markdown and have a format that looks like this:
342 ///
343 /// ```rust,ignore (doc-example)
344 /// /// The `my_lint_name` lint detects [short explanation here].
345 /// ///
346 /// /// ### Example
347 /// ///
348 /// /// ```rust
349 /// /// [insert a concise example that triggers the lint]
350 /// /// ```
351 /// ///
352 /// /// {{produces}}
353 /// ///
354 /// /// ### Explanation
355 /// ///
356 /// /// This should be a detailed explanation of *why* the lint exists,
357 /// /// and also include suggestions on how the user should fix the problem.
358 /// /// Try to keep the text simple enough that a beginner can understand,
359 /// /// and include links to other documentation for terminology that a
360 /// /// beginner may not be familiar with. If this is "allow" by default,
361 /// /// it should explain why (are there false positives or other issues?). If
362 /// /// this is a future-incompatible lint, it should say so, with text that
363 /// /// looks roughly like this:
364 /// ///
365 /// /// This is a [future-incompatible] lint to transition this to a hard
366 /// /// error in the future. See [issue #xxxxx] for more details.
367 /// ///
368 /// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx
369 /// ```
370 ///
371 /// The `{{produces}}` tag will be automatically replaced with the output from
372 /// the example by the build system. If the lint example is too complex to run
373 /// as a simple example (for example, it needs an extern crate), mark the code
374 /// block with `ignore` and manually replace the `{{produces}}` line with the
375 /// expected output in a `text` code block.
376 ///
377 /// If this is a rustdoc-only lint, then only include a brief introduction
378 /// with a link with the text `[rustdoc book]` so that the validator knows
379 /// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
380 ///
381 /// Commands to view and test the documentation:
382 ///
383 /// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
384 /// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
385 ///   correct style, and that the code example actually emits the expected
386 ///   lint.
387 ///
388 /// If you have already built the compiler, and you want to make changes to
389 /// just the doc comments, then use the `--keep-stage=0` flag with the above
390 /// commands to avoid rebuilding the compiler.
391 #[macro_export]
392 macro_rules! declare_lint {
393     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
394         $crate::declare_lint!(
395             $(#[$attr])* $vis $NAME, $Level, $desc,
396         );
397     );
398     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
399      $(@feature_gate = $gate:expr;)?
400      $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)*  }; )?
401      $($v:ident),*) => (
402         $(#[$attr])*
403         $vis static $NAME: &$crate::Lint = &$crate::Lint {
404             name: stringify!($NAME),
405             default_level: $crate::$Level,
406             desc: $desc,
407             edition_lint_opts: None,
408             is_plugin: false,
409             $($v: true,)*
410             $(feature_gate: Some($gate),)*
411             $(future_incompatible: Some($crate::FutureIncompatibleInfo {
412                 $($field: $val,)*
413                 ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
414             }),)*
415             ..$crate::Lint::default_fields_for_macro()
416         };
417     );
418     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
419      $lint_edition: expr => $edition_level: ident
420     ) => (
421         $(#[$attr])*
422         $vis static $NAME: &$crate::Lint = &$crate::Lint {
423             name: stringify!($NAME),
424             default_level: $crate::$Level,
425             desc: $desc,
426             edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
427             report_in_external_macro: false,
428             is_plugin: false,
429         };
430     );
431 }
432
433 #[macro_export]
434 macro_rules! declare_tool_lint {
435     (
436         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
437     ) => (
438         $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false}
439     );
440     (
441         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
442         report_in_external_macro: $rep:expr
443     ) => (
444          $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep}
445     );
446     (
447         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
448         $external:expr
449     ) => (
450         $(#[$attr])*
451         $vis static $NAME: &$crate::Lint = &$crate::Lint {
452             name: &concat!(stringify!($tool), "::", stringify!($NAME)),
453             default_level: $crate::$Level,
454             desc: $desc,
455             edition_lint_opts: None,
456             report_in_external_macro: $external,
457             future_incompatible: None,
458             is_plugin: true,
459             feature_gate: None,
460             crate_level_only: false,
461         };
462     );
463 }
464
465 /// Declares a static `LintArray` and return it as an expression.
466 #[macro_export]
467 macro_rules! lint_array {
468     ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
469     ($( $lint:expr ),*) => {{
470         vec![$($lint),*]
471     }}
472 }
473
474 pub type LintArray = Vec<&'static Lint>;
475
476 pub trait LintPass {
477     fn name(&self) -> &'static str;
478 }
479
480 /// Implements `LintPass for $ty` with the given list of `Lint` statics.
481 #[macro_export]
482 macro_rules! impl_lint_pass {
483     ($ty:ty => [$($lint:expr),* $(,)?]) => {
484         impl $crate::LintPass for $ty {
485             fn name(&self) -> &'static str { stringify!($ty) }
486         }
487         impl $ty {
488             pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
489         }
490     };
491 }
492
493 /// Declares a type named `$name` which implements `LintPass`.
494 /// To the right of `=>` a comma separated list of `Lint` statics is given.
495 #[macro_export]
496 macro_rules! declare_lint_pass {
497     ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
498         $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
499         $crate::impl_lint_pass!($name => [$($lint),*]);
500     };
501 }