]> git.lizzy.rs Git - rust.git/blob - src/librustc/lint/mod.rs
Add an early lint pass for lints that operate on the AST
[rust.git] / src / librustc / lint / mod.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Lints, aka compiler warnings.
12 //!
13 //! A 'lint' check is a kind of miscellaneous constraint that a user _might_
14 //! want to enforce, but might reasonably want to permit as well, on a
15 //! module-by-module basis. They contrast with static constraints enforced by
16 //! other phases of the compiler, which are generally required to hold in order
17 //! to compile the program at all.
18 //!
19 //! Most lints can be written as `LintPass` instances. These run just before
20 //! translation to LLVM bytecode. The `LintPass`es built into rustc are defined
21 //! within `builtin.rs`, which has further comments on how to add such a lint.
22 //! rustc can also load user-defined lint plugins via the plugin mechanism.
23 //!
24 //! Some of rustc's lints are defined elsewhere in the compiler and work by
25 //! calling `add_lint()` on the overall `Session` object. This works when
26 //! it happens before the main lint pass, which emits the lints stored by
27 //! `add_lint()`. To emit lints after the main lint pass (from trans, for
28 //! example) requires more effort. See `emit_lint` and `GatherNodeLevels`
29 //! in `context.rs`.
30
31 pub use self::Level::*;
32 pub use self::LintSource::*;
33
34 use std::hash;
35 use std::ascii::AsciiExt;
36 use syntax::codemap::Span;
37 use rustc_front::visit::FnKind;
38 use syntax::visit as ast_visit;
39 use syntax::ast;
40 use rustc_front::hir;
41
42 pub use lint::context::{LateContext, EarlyContext, Context, LintContext, LintStore,
43                         raw_emit_lint, check_crate, check_ast_crate, gather_attrs,
44                         GatherNodeLevels};
45
46 /// Specification of a single lint.
47 #[derive(Copy, Clone, Debug)]
48 pub struct Lint {
49     /// A string identifier for the lint.
50     ///
51     /// This identifies the lint in attributes and in command-line arguments.
52     /// In those contexts it is always lowercase, but this field is compared
53     /// in a way which is case-insensitive for ASCII characters. This allows
54     /// `declare_lint!()` invocations to follow the convention of upper-case
55     /// statics without repeating the name.
56     ///
57     /// The name is written with underscores, e.g. "unused_imports".
58     /// On the command line, underscores become dashes.
59     pub name: &'static str,
60
61     /// Default level for the lint.
62     pub default_level: Level,
63
64     /// Description of the lint or the issue it detects.
65     ///
66     /// e.g. "imports that are never used"
67     pub desc: &'static str,
68 }
69
70 impl Lint {
71     /// Get the lint's name, with ASCII letters converted to lowercase.
72     pub fn name_lower(&self) -> String {
73         self.name.to_ascii_lowercase()
74     }
75 }
76
77 /// Build a `Lint` initializer.
78 #[macro_export]
79 macro_rules! lint_initializer {
80     ($name:ident, $level:ident, $desc:expr) => (
81         ::rustc::lint::Lint {
82             name: stringify!($name),
83             default_level: ::rustc::lint::$level,
84             desc: $desc,
85         }
86     )
87 }
88
89 /// Declare a static item of type `&'static Lint`.
90 #[macro_export]
91 macro_rules! declare_lint {
92     // FIXME(#14660): deduplicate
93     (pub $name:ident, $level:ident, $desc:expr) => (
94         pub static $name: &'static ::rustc::lint::Lint
95             = &lint_initializer!($name, $level, $desc);
96     );
97     ($name:ident, $level:ident, $desc:expr) => (
98         static $name: &'static ::rustc::lint::Lint
99             = &lint_initializer!($name, $level, $desc);
100     );
101 }
102
103 /// Declare a static `LintArray` and return it as an expression.
104 #[macro_export]
105 macro_rules! lint_array { ($( $lint:expr ),*) => (
106     {
107         static ARRAY: LintArray = &[ $( &$lint ),* ];
108         ARRAY
109     }
110 ) }
111
112 pub type LintArray = &'static [&'static &'static Lint];
113
114 /// Trait for types providing lint checks.
115 ///
116 /// Each `check` method checks a single syntax node, and should not
117 /// invoke methods recursively (unlike `Visitor`). By default they
118 /// do nothing.
119 //
120 // FIXME: eliminate the duplication with `Visitor`. But this also
121 // contains a few lint-specific methods with no equivalent in `Visitor`.
122 pub trait LintPass {
123     /// Get descriptions of the lints this `LintPass` object can emit.
124     ///
125     /// NB: there is no enforcement that the object only emits lints it registered.
126     /// And some `rustc` internal `LintPass`es register lints to be emitted by other
127     /// parts of the compiler. If you want enforced access restrictions for your
128     /// `Lint`, make it a private `static` item in its own module.
129     fn get_lints(&self) -> LintArray;
130
131     fn check_ident(&mut self, _: &LateContext, _: Span, _: ast::Ident) { }
132     fn check_crate(&mut self, _: &LateContext, _: &hir::Crate) { }
133     fn check_mod(&mut self, _: &LateContext, _: &hir::Mod, _: Span, _: ast::NodeId) { }
134     fn check_foreign_item(&mut self, _: &LateContext, _: &hir::ForeignItem) { }
135     fn check_item(&mut self, _: &LateContext, _: &hir::Item) { }
136     fn check_local(&mut self, _: &LateContext, _: &hir::Local) { }
137     fn check_block(&mut self, _: &LateContext, _: &hir::Block) { }
138     fn check_stmt(&mut self, _: &LateContext, _: &hir::Stmt) { }
139     fn check_arm(&mut self, _: &LateContext, _: &hir::Arm) { }
140     fn check_pat(&mut self, _: &LateContext, _: &hir::Pat) { }
141     fn check_decl(&mut self, _: &LateContext, _: &hir::Decl) { }
142     fn check_expr(&mut self, _: &LateContext, _: &hir::Expr) { }
143     fn check_expr_post(&mut self, _: &LateContext, _: &hir::Expr) { }
144     fn check_ty(&mut self, _: &LateContext, _: &hir::Ty) { }
145     fn check_generics(&mut self, _: &LateContext, _: &hir::Generics) { }
146     fn check_fn(&mut self, _: &LateContext,
147         _: FnKind, _: &hir::FnDecl, _: &hir::Block, _: Span, _: ast::NodeId) { }
148     fn check_trait_item(&mut self, _: &LateContext, _: &hir::TraitItem) { }
149     fn check_impl_item(&mut self, _: &LateContext, _: &hir::ImplItem) { }
150     fn check_struct_def(&mut self, _: &LateContext,
151         _: &hir::StructDef, _: ast::Ident, _: &hir::Generics, _: ast::NodeId) { }
152     fn check_struct_def_post(&mut self, _: &LateContext,
153         _: &hir::StructDef, _: ast::Ident, _: &hir::Generics, _: ast::NodeId) { }
154     fn check_struct_field(&mut self, _: &LateContext, _: &hir::StructField) { }
155     fn check_variant(&mut self, _: &LateContext, _: &hir::Variant, _: &hir::Generics) { }
156     fn check_variant_post(&mut self, _: &LateContext, _: &hir::Variant, _: &hir::Generics) { }
157     fn check_opt_lifetime_ref(&mut self, _: &LateContext, _: Span, _: &Option<hir::Lifetime>) { }
158     fn check_lifetime_ref(&mut self, _: &LateContext, _: &hir::Lifetime) { }
159     fn check_lifetime_def(&mut self, _: &LateContext, _: &hir::LifetimeDef) { }
160     fn check_explicit_self(&mut self, _: &LateContext, _: &hir::ExplicitSelf) { }
161     // Note that you shouldn't implement both check_mac and check_ast_mac,
162     // because then your lint will be called twice. Prefer check_ast_mac.
163     fn check_mac(&mut self, _: &LateContext, _: &ast::Mac) { }
164     fn check_path(&mut self, _: &LateContext, _: &hir::Path, _: ast::NodeId) { }
165     fn check_attribute(&mut self, _: &LateContext, _: &ast::Attribute) { }
166
167     fn check_ast_ident(&mut self, _: &EarlyContext, _: Span, _: ast::Ident) { }
168     fn check_ast_crate(&mut self, _: &EarlyContext, _: &ast::Crate) { }
169     fn check_ast_mod(&mut self, _: &EarlyContext, _: &ast::Mod, _: Span, _: ast::NodeId) { }
170     fn check_ast_foreign_item(&mut self, _: &EarlyContext, _: &ast::ForeignItem) { }
171     fn check_ast_item(&mut self, _: &EarlyContext, _: &ast::Item) { }
172     fn check_ast_local(&mut self, _: &EarlyContext, _: &ast::Local) { }
173     fn check_ast_block(&mut self, _: &EarlyContext, _: &ast::Block) { }
174     fn check_ast_stmt(&mut self, _: &EarlyContext, _: &ast::Stmt) { }
175     fn check_ast_arm(&mut self, _: &EarlyContext, _: &ast::Arm) { }
176     fn check_ast_pat(&mut self, _: &EarlyContext, _: &ast::Pat) { }
177     fn check_ast_decl(&mut self, _: &EarlyContext, _: &ast::Decl) { }
178     fn check_ast_expr(&mut self, _: &EarlyContext, _: &ast::Expr) { }
179     fn check_ast_expr_post(&mut self, _: &EarlyContext, _: &ast::Expr) { }
180     fn check_ast_ty(&mut self, _: &EarlyContext, _: &ast::Ty) { }
181     fn check_ast_generics(&mut self, _: &EarlyContext, _: &ast::Generics) { }
182     fn check_ast_fn(&mut self, _: &EarlyContext,
183         _: ast_visit::FnKind, _: &ast::FnDecl, _: &ast::Block, _: Span, _: ast::NodeId) { }
184     fn check_ast_trait_item(&mut self, _: &EarlyContext, _: &ast::TraitItem) { }
185     fn check_ast_impl_item(&mut self, _: &EarlyContext, _: &ast::ImplItem) { }
186     fn check_ast_struct_def(&mut self, _: &EarlyContext,
187         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
188     fn check_ast_struct_def_post(&mut self, _: &EarlyContext,
189         _: &ast::StructDef, _: ast::Ident, _: &ast::Generics, _: ast::NodeId) { }
190     fn check_ast_struct_field(&mut self, _: &EarlyContext, _: &ast::StructField) { }
191     fn check_ast_variant(&mut self, _: &EarlyContext, _: &ast::Variant, _: &ast::Generics) { }
192     fn check_ast_variant_post(&mut self, _: &EarlyContext, _: &ast::Variant, _: &ast::Generics) { }
193     fn check_ast_opt_lifetime_ref(&mut self,
194                                   _: &EarlyContext,
195                                   _: Span,
196                                   _: &Option<ast::Lifetime>) { }
197     fn check_ast_lifetime_ref(&mut self, _: &EarlyContext, _: &ast::Lifetime) { }
198     fn check_ast_lifetime_def(&mut self, _: &EarlyContext, _: &ast::LifetimeDef) { }
199     fn check_ast_explicit_self(&mut self, _: &EarlyContext, _: &ast::ExplicitSelf) { }
200     fn check_ast_mac(&mut self, _: &EarlyContext, _: &ast::Mac) { }
201     fn check_ast_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { }
202     fn check_ast_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { }
203
204     /// Called when entering a syntax node that can have lint attributes such
205     /// as `#[allow(...)]`. Called with *all* the attributes of that node.
206     fn enter_lint_attrs(&mut self, _: &LateContext, _: &[ast::Attribute]) { }
207
208     /// Counterpart to `enter_lint_attrs`.
209     fn exit_lint_attrs(&mut self, _: &LateContext, _: &[ast::Attribute]) { }
210
211     /// Called when entering a syntax node that can have lint attributes such
212     /// as `#[allow(...)]`. Called with *all* the attributes of that node.
213     fn ast_enter_lint_attrs(&mut self, _: &EarlyContext, _: &[ast::Attribute]) { }
214
215     /// Counterpart to `ast_enter_lint_attrs`.
216     fn ast_exit_lint_attrs(&mut self, _: &EarlyContext, _: &[ast::Attribute]) { }
217 }
218
219 /// A lint pass boxed up as a trait object.
220 pub type LintPassObject = Box<LintPass + 'static>;
221
222 /// Identifies a lint known to the compiler.
223 #[derive(Clone, Copy)]
224 pub struct LintId {
225     // Identity is based on pointer equality of this field.
226     lint: &'static Lint,
227 }
228
229 impl PartialEq for LintId {
230     fn eq(&self, other: &LintId) -> bool {
231         (self.lint as *const Lint) == (other.lint as *const Lint)
232     }
233 }
234
235 impl Eq for LintId { }
236
237 impl hash::Hash for LintId {
238     fn hash<H: hash::Hasher>(&self, state: &mut H) {
239         let ptr = self.lint as *const Lint;
240         ptr.hash(state);
241     }
242 }
243
244 impl LintId {
245     /// Get the `LintId` for a `Lint`.
246     pub fn of(lint: &'static Lint) -> LintId {
247         LintId {
248             lint: lint,
249         }
250     }
251
252     /// Get the name of the lint.
253     pub fn as_str(&self) -> String {
254         self.lint.name_lower()
255     }
256 }
257
258 /// Setting for how to handle a lint.
259 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
260 pub enum Level {
261     Allow, Warn, Deny, Forbid
262 }
263
264 impl Level {
265     /// Convert a level to a lower-case string.
266     pub fn as_str(self) -> &'static str {
267         match self {
268             Allow => "allow",
269             Warn => "warn",
270             Deny => "deny",
271             Forbid => "forbid",
272         }
273     }
274
275     /// Convert a lower-case string to a level.
276     pub fn from_str(x: &str) -> Option<Level> {
277         match x {
278             "allow" => Some(Allow),
279             "warn" => Some(Warn),
280             "deny" => Some(Deny),
281             "forbid" => Some(Forbid),
282             _ => None,
283         }
284     }
285 }
286
287 /// How a lint level was set.
288 #[derive(Clone, Copy, PartialEq, Eq)]
289 pub enum LintSource {
290     /// Lint is at the default level as declared
291     /// in rustc or a plugin.
292     Default,
293
294     /// Lint level was set by an attribute.
295     Node(Span),
296
297     /// Lint level was set by a command-line flag.
298     CommandLine,
299 }
300
301 pub type LevelSource = (Level, LintSource);
302
303 pub mod builtin;
304
305 mod context;