]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_lint_defs/src/lib.rs
Set `LintExpectationId` in level and collect fulfilled ones (RFC-2383)
[rust.git] / compiler / rustc_lint_defs / src / lib.rs
1 #![feature(min_specialization)]
2
3 #[macro_use]
4 extern crate rustc_macros;
5
6 pub use self::Level::*;
7 use rustc_ast::node_id::{NodeId, NodeMap};
8 use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
9 use rustc_serialize::json::Json;
10 use rustc_span::edition::Edition;
11 use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
12 use rustc_target::spec::abi::Abi;
13
14 pub mod builtin;
15
16 #[macro_export]
17 macro_rules! pluralize {
18     ($x:expr) => {
19         if $x != 1 { "s" } else { "" }
20     };
21 }
22
23 /// Indicates the confidence in the correctness of a suggestion.
24 ///
25 /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
26 /// to determine whether it should be automatically applied or if the user should be consulted
27 /// before applying the suggestion.
28 #[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
29 pub enum Applicability {
30     /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
31     /// This suggestion should be automatically applied.
32     ///
33     /// In case of multiple `MachineApplicable` suggestions (whether as part of
34     /// the same `multipart_suggestion` or not), all of them should be
35     /// automatically applied.
36     MachineApplicable,
37
38     /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
39     /// result in valid Rust code if it is applied.
40     MaybeIncorrect,
41
42     /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
43     /// cannot be applied automatically because it will not result in valid Rust code. The user
44     /// will need to fill in the placeholders.
45     HasPlaceholders,
46
47     /// The applicability of the suggestion is unknown.
48     Unspecified,
49 }
50
51 rustc_index::newtype_index! {
52     /// FIXME: The lint expectation ID is currently a simple copy of the `AttrId`
53     /// that the expectation originated from. In the future it should be generated
54     /// by other means. This is for one to keep the IDs independent of each other
55     /// and also to ensure that it is actually stable between compilation sessions.
56     /// (The `AttrId` for instance, is not stable).
57     ///
58     /// Additionally, it would be nice if this generation could be moved into
59     /// [`Level::from_symbol`] to have it all contained in one module and to
60     /// make it simpler to use.
61     pub struct LintExpectationId {
62         DEBUG_FORMAT = "LintExpectationId({})"
63     }
64 }
65
66 rustc_data_structures::impl_stable_hash_via_hash!(LintExpectationId);
67
68 impl<HCX> ToStableHashKey<HCX> for LintExpectationId {
69     type KeyType = u32;
70
71     #[inline]
72     fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
73         self.as_u32()
74     }
75 }
76
77 /// Setting for how to handle a lint.
78 ///
79 /// See: https://doc.rust-lang.org/rustc/lints/levels.html
80 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
81 pub enum Level {
82     /// The `allow` level will not issue any message.
83     Allow,
84     /// The `expect` level will suppress the lint message but intern produce a message
85     /// if the lint wasn't issued in the expected scope. `Expect` should not be used as
86     /// an initial level for a lint.
87     ///
88     /// Note that this still means that the lint is enabled in this position and should
89     /// be emitted, this will intern fulfill the expectation and suppress the lint.
90     ///
91     /// See RFC 2383.
92     ///
93     /// The `LintExpectationId` is used to later link a lint emission to the actual
94     /// expectation. It can be ignored in most cases.
95     Expect(LintExpectationId),
96     /// The `warn` level will produce a warning if the lint was violated, however the
97     /// compiler will continue with its execution.
98     Warn,
99     ForceWarn,
100     /// The `deny` level will produce an error and stop further execution after the lint
101     /// pass is complete.
102     Deny,
103     /// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous
104     /// levels.
105     Forbid,
106 }
107
108 rustc_data_structures::impl_stable_hash_via_hash!(Level);
109
110 impl Level {
111     /// Converts a level to a lower-case string.
112     pub fn as_str(self) -> &'static str {
113         match self {
114             Level::Allow => "allow",
115             Level::Expect(_) => "expect",
116             Level::Warn => "warn",
117             Level::ForceWarn => "force-warn",
118             Level::Deny => "deny",
119             Level::Forbid => "forbid",
120         }
121     }
122
123     /// Converts a lower-case string to a level. This will never construct the expect
124     /// level as that would require a [`LintExpectationId`]
125     pub fn from_str(x: &str) -> Option<Level> {
126         match x {
127             "allow" => Some(Level::Allow),
128             "warn" => Some(Level::Warn),
129             "deny" => Some(Level::Deny),
130             "forbid" => Some(Level::Forbid),
131             "expect" | _ => None,
132         }
133     }
134
135     /// Converts a symbol to a level.
136     pub fn from_symbol(x: Symbol, possible_lint_expect_id: u32) -> Option<Level> {
137         match x {
138             sym::allow => Some(Level::Allow),
139             sym::expect => Some(Level::Expect(LintExpectationId::from(possible_lint_expect_id))),
140             sym::warn => Some(Level::Warn),
141             sym::deny => Some(Level::Deny),
142             sym::forbid => Some(Level::Forbid),
143             _ => None,
144         }
145     }
146 }
147
148 /// Specification of a single lint.
149 #[derive(Copy, Clone, Debug)]
150 pub struct Lint {
151     /// A string identifier for the lint.
152     ///
153     /// This identifies the lint in attributes and in command-line arguments.
154     /// In those contexts it is always lowercase, but this field is compared
155     /// in a way which is case-insensitive for ASCII characters. This allows
156     /// `declare_lint!()` invocations to follow the convention of upper-case
157     /// statics without repeating the name.
158     ///
159     /// The name is written with underscores, e.g., "unused_imports".
160     /// On the command line, underscores become dashes.
161     ///
162     /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#lint-naming>
163     /// for naming guidelines.
164     pub name: &'static str,
165
166     /// Default level for the lint.
167     ///
168     /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-levels>
169     /// for guidelines on choosing a default level.
170     pub default_level: Level,
171
172     /// Description of the lint or the issue it detects.
173     ///
174     /// e.g., "imports that are never used"
175     pub desc: &'static str,
176
177     /// Starting at the given edition, default to the given lint level. If this is `None`, then use
178     /// `default_level`.
179     pub edition_lint_opts: Option<(Edition, Level)>,
180
181     /// `true` if this lint is reported even inside expansions of external macros.
182     pub report_in_external_macro: bool,
183
184     pub future_incompatible: Option<FutureIncompatibleInfo>,
185
186     pub is_plugin: bool,
187
188     /// `Some` if this lint is feature gated, otherwise `None`.
189     pub feature_gate: Option<Symbol>,
190
191     pub crate_level_only: bool,
192 }
193
194 /// Extra information for a future incompatibility lint.
195 #[derive(Copy, Clone, Debug)]
196 pub struct FutureIncompatibleInfo {
197     /// e.g., a URL for an issue/PR/RFC or error code
198     pub reference: &'static str,
199     /// The reason for the lint used by diagnostics to provide
200     /// the right help message
201     pub reason: FutureIncompatibilityReason,
202     /// Whether to explain the reason to the user.
203     ///
204     /// Set to false for lints that already include a more detailed
205     /// explanation.
206     pub explain_reason: bool,
207 }
208
209 /// The reason for future incompatibility
210 #[derive(Copy, Clone, Debug)]
211 pub enum FutureIncompatibilityReason {
212     /// This will be an error in a future release
213     /// for all editions
214     FutureReleaseError,
215     /// This will be an error in a future release, and
216     /// Cargo should create a report even for dependencies
217     FutureReleaseErrorReportNow,
218     /// Code that changes meaning in some way in a
219     /// future release.
220     FutureReleaseSemanticsChange,
221     /// Previously accepted code that will become an
222     /// error in the provided edition
223     EditionError(Edition),
224     /// Code that changes meaning in some way in
225     /// the provided edition
226     EditionSemanticsChange(Edition),
227     /// A custom reason.
228     Custom(&'static str),
229 }
230
231 impl FutureIncompatibilityReason {
232     pub fn edition(self) -> Option<Edition> {
233         match self {
234             Self::EditionError(e) => Some(e),
235             Self::EditionSemanticsChange(e) => Some(e),
236             _ => None,
237         }
238     }
239 }
240
241 impl FutureIncompatibleInfo {
242     pub const fn default_fields_for_macro() -> Self {
243         FutureIncompatibleInfo {
244             reference: "",
245             reason: FutureIncompatibilityReason::FutureReleaseError,
246             explain_reason: true,
247         }
248     }
249 }
250
251 impl Lint {
252     pub const fn default_fields_for_macro() -> Self {
253         Lint {
254             name: "",
255             default_level: Level::Forbid,
256             desc: "",
257             edition_lint_opts: None,
258             is_plugin: false,
259             report_in_external_macro: false,
260             future_incompatible: None,
261             feature_gate: None,
262             crate_level_only: false,
263         }
264     }
265
266     /// Gets the lint's name, with ASCII letters converted to lowercase.
267     pub fn name_lower(&self) -> String {
268         self.name.to_ascii_lowercase()
269     }
270
271     pub fn default_level(&self, edition: Edition) -> Level {
272         self.edition_lint_opts
273             .filter(|(e, _)| *e <= edition)
274             .map(|(_, l)| l)
275             .unwrap_or(self.default_level)
276     }
277 }
278
279 /// Identifies a lint known to the compiler.
280 #[derive(Clone, Copy, Debug)]
281 pub struct LintId {
282     // Identity is based on pointer equality of this field.
283     pub lint: &'static Lint,
284 }
285
286 impl PartialEq for LintId {
287     fn eq(&self, other: &LintId) -> bool {
288         std::ptr::eq(self.lint, other.lint)
289     }
290 }
291
292 impl Eq for LintId {}
293
294 impl std::hash::Hash for LintId {
295     fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
296         let ptr = self.lint as *const Lint;
297         ptr.hash(state);
298     }
299 }
300
301 impl LintId {
302     /// Gets the `LintId` for a `Lint`.
303     pub fn of(lint: &'static Lint) -> LintId {
304         LintId { lint }
305     }
306
307     pub fn lint_name_raw(&self) -> &'static str {
308         self.lint.name
309     }
310
311     /// Gets the name of the lint.
312     pub fn to_string(&self) -> String {
313         self.lint.name_lower()
314     }
315 }
316
317 impl<HCX> HashStable<HCX> for LintId {
318     #[inline]
319     fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
320         self.lint_name_raw().hash_stable(hcx, hasher);
321     }
322 }
323
324 impl<HCX> ToStableHashKey<HCX> for LintId {
325     type KeyType = &'static str;
326
327     #[inline]
328     fn to_stable_hash_key(&self, _: &HCX) -> &'static str {
329         self.lint_name_raw()
330     }
331 }
332
333 // Duplicated from rustc_session::config::ExternDepSpec to avoid cyclic dependency
334 #[derive(PartialEq, Debug)]
335 pub enum ExternDepSpec {
336     Json(Json),
337     Raw(String),
338 }
339
340 // This could be a closure, but then implementing derive trait
341 // becomes hacky (and it gets allocated).
342 #[derive(Debug)]
343 pub enum BuiltinLintDiagnostics {
344     Normal,
345     AbsPathWithModule(Span),
346     ProcMacroDeriveResolutionFallback(Span),
347     MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
348     UnknownCrateTypes(Span, String, String),
349     UnusedImports(String, Vec<(Span, String)>, Option<Span>),
350     RedundantImport(Vec<(Span, bool)>, Ident),
351     DeprecatedMacro(Option<Symbol>, Span),
352     MissingAbi(Span, Abi),
353     UnusedDocComment(Span),
354     UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span },
355     PatternsInFnsWithoutBody(Span, Ident),
356     LegacyDeriveHelpers(Span),
357     ExternDepSpec(String, ExternDepSpec),
358     ProcMacroBackCompat(String),
359     OrPatternsBackCompat(Span, String),
360     ReservedPrefix(Span),
361     TrailingMacro(bool, Ident),
362     BreakWithLabelAndLoop(Span),
363     NamedAsmLabel(String),
364     UnicodeTextFlow(Span, String),
365     UnexpectedCfg(Span, Symbol, Option<Symbol>),
366 }
367
368 /// Lints that are buffered up early on in the `Session` before the
369 /// `LintLevels` is calculated.
370 pub struct BufferedEarlyLint {
371     /// The span of code that we are linting on.
372     pub span: MultiSpan,
373
374     /// The lint message.
375     pub msg: String,
376
377     /// The `NodeId` of the AST node that generated the lint.
378     pub node_id: NodeId,
379
380     /// A lint Id that can be passed to
381     /// `rustc_lint::early::EarlyContextAndPass::check_id`.
382     pub lint_id: LintId,
383
384     /// Customization of the `DiagnosticBuilder<'_>` for the lint.
385     pub diagnostic: BuiltinLintDiagnostics,
386 }
387
388 #[derive(Default)]
389 pub struct LintBuffer {
390     pub map: NodeMap<Vec<BufferedEarlyLint>>,
391 }
392
393 impl LintBuffer {
394     pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
395         let arr = self.map.entry(early_lint.node_id).or_default();
396         arr.push(early_lint);
397     }
398
399     pub fn add_lint(
400         &mut self,
401         lint: &'static Lint,
402         node_id: NodeId,
403         span: MultiSpan,
404         msg: &str,
405         diagnostic: BuiltinLintDiagnostics,
406     ) {
407         let lint_id = LintId::of(lint);
408         let msg = msg.to_string();
409         self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
410     }
411
412     pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
413         self.map.remove(&id).unwrap_or_default()
414     }
415
416     pub fn buffer_lint(
417         &mut self,
418         lint: &'static Lint,
419         id: NodeId,
420         sp: impl Into<MultiSpan>,
421         msg: &str,
422     ) {
423         self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
424     }
425
426     pub fn buffer_lint_with_diagnostic(
427         &mut self,
428         lint: &'static Lint,
429         id: NodeId,
430         sp: impl Into<MultiSpan>,
431         msg: &str,
432         diagnostic: BuiltinLintDiagnostics,
433     ) {
434         self.add_lint(lint, id, sp.into(), msg, diagnostic)
435     }
436 }
437
438 /// Declares a static item of type `&'static Lint`.
439 ///
440 /// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for
441 /// documentation and guidelines on writing lints.
442 ///
443 /// The macro call should start with a doc comment explaining the lint
444 /// which will be embedded in the rustc user documentation book. It should
445 /// be written in markdown and have a format that looks like this:
446 ///
447 /// ```rust,ignore (doc-example)
448 /// /// The `my_lint_name` lint detects [short explanation here].
449 /// ///
450 /// /// ### Example
451 /// ///
452 /// /// ```rust
453 /// /// [insert a concise example that triggers the lint]
454 /// /// ```
455 /// ///
456 /// /// {{produces}}
457 /// ///
458 /// /// ### Explanation
459 /// ///
460 /// /// This should be a detailed explanation of *why* the lint exists,
461 /// /// and also include suggestions on how the user should fix the problem.
462 /// /// Try to keep the text simple enough that a beginner can understand,
463 /// /// and include links to other documentation for terminology that a
464 /// /// beginner may not be familiar with. If this is "allow" by default,
465 /// /// it should explain why (are there false positives or other issues?). If
466 /// /// this is a future-incompatible lint, it should say so, with text that
467 /// /// looks roughly like this:
468 /// ///
469 /// /// This is a [future-incompatible] lint to transition this to a hard
470 /// /// error in the future. See [issue #xxxxx] for more details.
471 /// ///
472 /// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx
473 /// ```
474 ///
475 /// The `{{produces}}` tag will be automatically replaced with the output from
476 /// the example by the build system. If the lint example is too complex to run
477 /// as a simple example (for example, it needs an extern crate), mark the code
478 /// block with `ignore` and manually replace the `{{produces}}` line with the
479 /// expected output in a `text` code block.
480 ///
481 /// If this is a rustdoc-only lint, then only include a brief introduction
482 /// with a link with the text `[rustdoc book]` so that the validator knows
483 /// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
484 ///
485 /// Commands to view and test the documentation:
486 ///
487 /// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
488 /// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
489 ///   correct style, and that the code example actually emits the expected
490 ///   lint.
491 ///
492 /// If you have already built the compiler, and you want to make changes to
493 /// just the doc comments, then use the `--keep-stage=0` flag with the above
494 /// commands to avoid rebuilding the compiler.
495 #[macro_export]
496 macro_rules! declare_lint {
497     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
498         $crate::declare_lint!(
499             $(#[$attr])* $vis $NAME, $Level, $desc,
500         );
501     );
502     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
503      $(@feature_gate = $gate:expr;)?
504      $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)*  }; )?
505      $($v:ident),*) => (
506         $(#[$attr])*
507         $vis static $NAME: &$crate::Lint = &$crate::Lint {
508             name: stringify!($NAME),
509             default_level: $crate::$Level,
510             desc: $desc,
511             edition_lint_opts: None,
512             is_plugin: false,
513             $($v: true,)*
514             $(feature_gate: Some($gate),)*
515             $(future_incompatible: Some($crate::FutureIncompatibleInfo {
516                 $($field: $val,)*
517                 ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
518             }),)*
519             ..$crate::Lint::default_fields_for_macro()
520         };
521     );
522     ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
523      $lint_edition: expr => $edition_level: ident
524     ) => (
525         $(#[$attr])*
526         $vis static $NAME: &$crate::Lint = &$crate::Lint {
527             name: stringify!($NAME),
528             default_level: $crate::$Level,
529             desc: $desc,
530             edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
531             report_in_external_macro: false,
532             is_plugin: false,
533         };
534     );
535 }
536
537 #[macro_export]
538 macro_rules! declare_tool_lint {
539     (
540         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
541     ) => (
542         $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false}
543     );
544     (
545         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
546         report_in_external_macro: $rep:expr
547     ) => (
548          $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep}
549     );
550     (
551         $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
552         $external:expr
553     ) => (
554         $(#[$attr])*
555         $vis static $NAME: &$crate::Lint = &$crate::Lint {
556             name: &concat!(stringify!($tool), "::", stringify!($NAME)),
557             default_level: $crate::$Level,
558             desc: $desc,
559             edition_lint_opts: None,
560             report_in_external_macro: $external,
561             future_incompatible: None,
562             is_plugin: true,
563             feature_gate: None,
564             crate_level_only: false,
565         };
566     );
567 }
568
569 /// Declares a static `LintArray` and return it as an expression.
570 #[macro_export]
571 macro_rules! lint_array {
572     ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
573     ($( $lint:expr ),*) => {{
574         vec![$($lint),*]
575     }}
576 }
577
578 pub type LintArray = Vec<&'static Lint>;
579
580 pub trait LintPass {
581     fn name(&self) -> &'static str;
582 }
583
584 /// Implements `LintPass for $ty` with the given list of `Lint` statics.
585 #[macro_export]
586 macro_rules! impl_lint_pass {
587     ($ty:ty => [$($lint:expr),* $(,)?]) => {
588         impl $crate::LintPass for $ty {
589             fn name(&self) -> &'static str { stringify!($ty) }
590         }
591         impl $ty {
592             pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
593         }
594     };
595 }
596
597 /// Declares a type named `$name` which implements `LintPass`.
598 /// To the right of `=>` a comma separated list of `Lint` statics is given.
599 #[macro_export]
600 macro_rules! declare_lint_pass {
601     ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
602         $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
603         $crate::impl_lint_pass!($name => [$($lint),*]);
604     };
605 }