]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_lint_defs/src/lib.rs
New upstream version 1.59.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,
dfeec247
XL
288 AbsPathWithModule(Span),
289 ProcMacroDeriveResolutionFallback(Span),
290 MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
dfeec247 291 UnknownCrateTypes(Span, String, String),
a2a8927a 292 UnusedImports(String, Vec<(Span, String)>, Option<Span>),
dfeec247
XL
293 RedundantImport(Vec<(Span, bool)>, Ident),
294 DeprecatedMacro(Option<Symbol>, Span),
5869c6ff 295 MissingAbi(Span, Abi),
74b04a01 296 UnusedDocComment(Span),
94222f64 297 UnusedBuiltinAttribute { attr_name: Symbol, macro_name: String, invoc_span: Span },
5869c6ff 298 PatternsInFnsWithoutBody(Span, Ident),
6a06907d
XL
299 LegacyDeriveHelpers(Span),
300 ExternDepSpec(String, ExternDepSpec),
301 ProcMacroBackCompat(String),
cdc7bbd5 302 OrPatternsBackCompat(Span, String),
136023e0 303 ReservedPrefix(Span),
94222f64
XL
304 TrailingMacro(bool, Ident),
305 BreakWithLabelAndLoop(Span),
306 NamedAsmLabel(String),
c295e0f8 307 UnicodeTextFlow(Span, String),
dfeec247
XL
308}
309
310/// Lints that are buffered up early on in the `Session` before the
ba9703b0 311/// `LintLevels` is calculated.
dfeec247 312#[derive(PartialEq)]
60c5eb7d
XL
313pub struct BufferedEarlyLint {
314 /// The span of code that we are linting on.
dfeec247
XL
315 pub span: MultiSpan,
316
317 /// The lint message.
318 pub msg: String,
60c5eb7d 319
dfeec247
XL
320 /// The `NodeId` of the AST node that generated the lint.
321 pub node_id: NodeId,
60c5eb7d 322
ba9703b0
XL
323 /// A lint Id that can be passed to
324 /// `rustc_lint::early::EarlyContextAndPass::check_id`.
dfeec247 325 pub lint_id: LintId,
60c5eb7d 326
dfeec247
XL
327 /// Customization of the `DiagnosticBuilder<'_>` for the lint.
328 pub diagnostic: BuiltinLintDiagnostics,
329}
330
331#[derive(Default)]
332pub struct LintBuffer {
333 pub map: NodeMap<Vec<BufferedEarlyLint>>,
334}
335
336impl LintBuffer {
337 pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
338 let arr = self.map.entry(early_lint.node_id).or_default();
339 if !arr.contains(&early_lint) {
340 arr.push(early_lint);
341 }
342 }
343
344 pub fn add_lint(
345 &mut self,
346 lint: &'static Lint,
347 node_id: NodeId,
348 span: MultiSpan,
349 msg: &str,
350 diagnostic: BuiltinLintDiagnostics,
351 ) {
352 let lint_id = LintId::of(lint);
353 let msg = msg.to_string();
354 self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
355 }
356
357 pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
358 self.map.remove(&id).unwrap_or_default()
359 }
360
361 pub fn buffer_lint(
362 &mut self,
363 lint: &'static Lint,
364 id: NodeId,
365 sp: impl Into<MultiSpan>,
366 msg: &str,
367 ) {
368 self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
369 }
370
371 pub fn buffer_lint_with_diagnostic(
372 &mut self,
373 lint: &'static Lint,
374 id: NodeId,
375 sp: impl Into<MultiSpan>,
376 msg: &str,
377 diagnostic: BuiltinLintDiagnostics,
378 ) {
379 self.add_lint(lint, id, sp.into(), msg, diagnostic)
380 }
60c5eb7d
XL
381}
382
383/// Declares a static item of type `&'static Lint`.
1b1a35ee 384///
29967ef6
XL
385/// See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for
386/// documentation and guidelines on writing lints.
1b1a35ee
XL
387///
388/// The macro call should start with a doc comment explaining the lint
389/// which will be embedded in the rustc user documentation book. It should
390/// be written in markdown and have a format that looks like this:
391///
392/// ```rust,ignore (doc-example)
393/// /// The `my_lint_name` lint detects [short explanation here].
394/// ///
395/// /// ### Example
396/// ///
397/// /// ```rust
398/// /// [insert a concise example that triggers the lint]
399/// /// ```
400/// ///
401/// /// {{produces}}
402/// ///
403/// /// ### Explanation
404/// ///
405/// /// This should be a detailed explanation of *why* the lint exists,
406/// /// and also include suggestions on how the user should fix the problem.
407/// /// Try to keep the text simple enough that a beginner can understand,
408/// /// and include links to other documentation for terminology that a
409/// /// beginner may not be familiar with. If this is "allow" by default,
410/// /// it should explain why (are there false positives or other issues?). If
411/// /// this is a future-incompatible lint, it should say so, with text that
412/// /// looks roughly like this:
413/// ///
414/// /// This is a [future-incompatible] lint to transition this to a hard
415/// /// error in the future. See [issue #xxxxx] for more details.
416/// ///
417/// /// [issue #xxxxx]: https://github.com/rust-lang/rust/issues/xxxxx
418/// ```
419///
420/// The `{{produces}}` tag will be automatically replaced with the output from
fc512014
XL
421/// the example by the build system. If the lint example is too complex to run
422/// as a simple example (for example, it needs an extern crate), mark the code
423/// block with `ignore` and manually replace the `{{produces}}` line with the
424/// expected output in a `text` code block.
425///
426/// If this is a rustdoc-only lint, then only include a brief introduction
427/// with a link with the text `[rustdoc book]` so that the validator knows
428/// that this is for rustdoc only (see BROKEN_INTRA_DOC_LINKS as an example).
429///
430/// Commands to view and test the documentation:
431///
432/// * `./x.py doc --stage=1 src/doc/rustc --open`: Builds the rustc book and opens it.
433/// * `./x.py test src/tools/lint-docs`: Validates that the lint docs have the
434/// correct style, and that the code example actually emits the expected
435/// lint.
436///
437/// If you have already built the compiler, and you want to make changes to
438/// just the doc comments, then use the `--keep-stage=0` flag with the above
439/// commands to avoid rebuilding the compiler.
60c5eb7d
XL
440#[macro_export]
441macro_rules! declare_lint {
1b1a35ee 442 ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
60c5eb7d 443 $crate::declare_lint!(
1b1a35ee 444 $(#[$attr])* $vis $NAME, $Level, $desc,
60c5eb7d
XL
445 );
446 );
1b1a35ee 447 ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
f035d41b 448 $(@feature_gate = $gate:expr;)?
29967ef6 449 $(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )?
f035d41b 450 $($v:ident),*) => (
1b1a35ee 451 $(#[$attr])*
29967ef6 452 $vis static $NAME: &$crate::Lint = &$crate::Lint {
60c5eb7d 453 name: stringify!($NAME),
29967ef6 454 default_level: $crate::$Level,
60c5eb7d
XL
455 desc: $desc,
456 edition_lint_opts: None,
457 is_plugin: false,
458 $($v: true,)*
f035d41b 459 $(feature_gate: Some($gate),)*
29967ef6
XL
460 $(future_incompatible: Some($crate::FutureIncompatibleInfo {
461 $($field: $val,)*
462 ..$crate::FutureIncompatibleInfo::default_fields_for_macro()
463 }),)*
464 ..$crate::Lint::default_fields_for_macro()
60c5eb7d
XL
465 };
466 );
1b1a35ee 467 ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
60c5eb7d
XL
468 $lint_edition: expr => $edition_level: ident
469 ) => (
1b1a35ee 470 $(#[$attr])*
29967ef6 471 $vis static $NAME: &$crate::Lint = &$crate::Lint {
60c5eb7d 472 name: stringify!($NAME),
29967ef6 473 default_level: $crate::$Level,
60c5eb7d 474 desc: $desc,
29967ef6 475 edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
60c5eb7d
XL
476 report_in_external_macro: false,
477 is_plugin: false,
478 };
479 );
480}
481
482#[macro_export]
483macro_rules! declare_tool_lint {
484 (
485 $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
486 ) => (
487 $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false}
488 );
489 (
490 $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
491 report_in_external_macro: $rep:expr
492 ) => (
493 $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep}
494 );
495 (
496 $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
497 $external:expr
498 ) => (
499 $(#[$attr])*
29967ef6 500 $vis static $NAME: &$crate::Lint = &$crate::Lint {
60c5eb7d 501 name: &concat!(stringify!($tool), "::", stringify!($NAME)),
29967ef6 502 default_level: $crate::$Level,
60c5eb7d
XL
503 desc: $desc,
504 edition_lint_opts: None,
505 report_in_external_macro: $external,
506 future_incompatible: None,
507 is_plugin: true,
f035d41b
XL
508 feature_gate: None,
509 crate_level_only: false,
60c5eb7d
XL
510 };
511 );
512}
dfeec247
XL
513
514/// Declares a static `LintArray` and return it as an expression.
515#[macro_export]
516macro_rules! lint_array {
517 ($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
518 ($( $lint:expr ),*) => {{
519 vec![$($lint),*]
520 }}
521}
522
523pub type LintArray = Vec<&'static Lint>;
524
525pub trait LintPass {
526 fn name(&self) -> &'static str;
527}
528
f9f354fc 529/// Implements `LintPass for $ty` with the given list of `Lint` statics.
dfeec247
XL
530#[macro_export]
531macro_rules! impl_lint_pass {
f9f354fc 532 ($ty:ty => [$($lint:expr),* $(,)?]) => {
29967ef6 533 impl $crate::LintPass for $ty {
f9f354fc 534 fn name(&self) -> &'static str { stringify!($ty) }
dfeec247 535 }
f9f354fc 536 impl $ty {
29967ef6 537 pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
dfeec247
XL
538 }
539 };
540}
541
542/// Declares a type named `$name` which implements `LintPass`.
543/// To the right of `=>` a comma separated list of `Lint` statics is given.
544#[macro_export]
545macro_rules! declare_lint_pass {
546 ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
547 $(#[$m])* #[derive(Copy, Clone)] pub struct $name;
548 $crate::impl_lint_pass!($name => [$($lint),*]);
549 };
550}