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