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