]>
Commit | Line | Data |
---|---|---|
c34b1796 | 1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | //! Implementation of lint checking. | |
12 | //! | |
13 | //! The lint checking is mostly consolidated into one pass which runs just | |
14 | //! before translation to LLVM bytecode. Throughout compilation, lint warnings | |
15 | //! can be added via the `add_lint` method on the Session structure. This | |
16 | //! requires a span and an id of the node that the lint is being added to. The | |
17 | //! lint isn't actually emitted at that time because it is unknown what the | |
18 | //! actual lint level at that location is. | |
19 | //! | |
20 | //! To actually emit lint warnings/errors, a separate pass is used just before | |
21 | //! translation. A context keeps track of the current state of all lint levels. | |
22 | //! Upon entering a node of the ast which can modify the lint settings, the | |
23 | //! previous lint state is pushed onto a stack and the ast is then recursed | |
24 | //! upon. As the ast is traversed, this keeps track of the current lint level | |
25 | //! for all lint attributes. | |
26 | use self::TargetLint::*; | |
27 | ||
9cc50fc6 | 28 | use dep_graph::DepNode; |
92a42be0 | 29 | use middle::privacy::AccessLevels; |
54a0048b | 30 | use ty::TyCtxt; |
9cc50fc6 | 31 | use session::{config, early_error, Session}; |
b039eaaf SL |
32 | use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass}; |
33 | use lint::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject}; | |
62682a34 | 34 | use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; |
1a4d82fc JJ |
35 | use lint::builtin; |
36 | use util::nodemap::FnvHashMap; | |
37 | ||
38 | use std::cell::RefCell; | |
c1a9b12d | 39 | use std::cmp; |
9cc50fc6 | 40 | use std::default::Default as StdDefault; |
1a4d82fc | 41 | use std::mem; |
b039eaaf | 42 | use syntax::attr::{self, AttrMetaMethods}; |
1a4d82fc | 43 | use syntax::codemap::Span; |
9cc50fc6 | 44 | use syntax::errors::DiagnosticBuilder; |
1a4d82fc | 45 | use syntax::parse::token::InternedString; |
e9174d1e | 46 | use syntax::ast; |
92a42be0 | 47 | use syntax::attr::ThinAttributesExt; |
54a0048b SL |
48 | use hir; |
49 | use hir::intravisit as hir_visit; | |
50 | use hir::intravisit::{IdVisitor, IdVisitingOperation}; | |
b039eaaf | 51 | use syntax::visit as ast_visit; |
1a4d82fc JJ |
52 | |
53 | /// Information about the registered lints. | |
54 | /// | |
55 | /// This is basically the subset of `Context` that we can | |
56 | /// build early in the compile pipeline. | |
57 | pub struct LintStore { | |
58 | /// Registered lints. The bool is true if the lint was | |
59 | /// added by a plugin. | |
60 | lints: Vec<(&'static Lint, bool)>, | |
61 | ||
62 | /// Trait objects for each lint pass. | |
63 | /// This is only `None` while iterating over the objects. See the definition | |
64 | /// of run_lints. | |
b039eaaf SL |
65 | early_passes: Option<Vec<EarlyLintPassObject>>, |
66 | late_passes: Option<Vec<LateLintPassObject>>, | |
1a4d82fc JJ |
67 | |
68 | /// Lints indexed by name. | |
69 | by_name: FnvHashMap<String, TargetLint>, | |
70 | ||
71 | /// Current levels of each lint, and where they were set. | |
72 | levels: FnvHashMap<LintId, LevelSource>, | |
73 | ||
74 | /// Map of registered lint groups to what lints they expand to. The bool | |
75 | /// is true if the lint group was added by a plugin. | |
76 | lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>, | |
c1a9b12d | 77 | |
9cc50fc6 SL |
78 | /// Extra info for future incompatibility lints, descibing the |
79 | /// issue or RFC that caused the incompatibility. | |
80 | future_incompatible: FnvHashMap<LintId, FutureIncompatibleInfo>, | |
81 | ||
c1a9b12d SL |
82 | /// Maximum level a lint can be |
83 | lint_cap: Option<Level>, | |
1a4d82fc JJ |
84 | } |
85 | ||
9cc50fc6 SL |
86 | /// Extra information for a future incompatibility lint. See the call |
87 | /// to `register_future_incompatible` in `librustc_lint/lib.rs` for | |
88 | /// guidelines. | |
89 | pub struct FutureIncompatibleInfo { | |
90 | pub id: LintId, | |
91 | pub reference: &'static str // e.g., a URL for an issue/PR/RFC or error code | |
92 | } | |
93 | ||
1a4d82fc JJ |
94 | /// The targed of the `by_name` map, which accounts for renaming/deprecation. |
95 | enum TargetLint { | |
96 | /// A direct lint target | |
97 | Id(LintId), | |
98 | ||
99 | /// Temporary renaming, used for easing migration pain; see #16545 | |
100 | Renamed(String, LintId), | |
c1a9b12d SL |
101 | |
102 | /// Lint with this name existed previously, but has been removed/deprecated. | |
103 | /// The string argument is the reason for removal. | |
104 | Removed(String), | |
105 | } | |
106 | ||
107 | enum FindLintError { | |
108 | NotFound, | |
109 | Removed | |
1a4d82fc JJ |
110 | } |
111 | ||
112 | impl LintStore { | |
113 | fn get_level_source(&self, lint: LintId) -> LevelSource { | |
114 | match self.levels.get(&lint) { | |
115 | Some(&s) => s, | |
116 | None => (Allow, Default), | |
117 | } | |
118 | } | |
119 | ||
c1a9b12d SL |
120 | fn set_level(&mut self, lint: LintId, mut lvlsrc: LevelSource) { |
121 | if let Some(cap) = self.lint_cap { | |
122 | lvlsrc.0 = cmp::min(lvlsrc.0, cap); | |
123 | } | |
1a4d82fc JJ |
124 | if lvlsrc.0 == Allow { |
125 | self.levels.remove(&lint); | |
126 | } else { | |
127 | self.levels.insert(lint, lvlsrc); | |
128 | } | |
129 | } | |
130 | ||
131 | pub fn new() -> LintStore { | |
132 | LintStore { | |
133 | lints: vec!(), | |
b039eaaf SL |
134 | early_passes: Some(vec!()), |
135 | late_passes: Some(vec!()), | |
85aaf69f SL |
136 | by_name: FnvHashMap(), |
137 | levels: FnvHashMap(), | |
9cc50fc6 | 138 | future_incompatible: FnvHashMap(), |
85aaf69f | 139 | lint_groups: FnvHashMap(), |
c1a9b12d | 140 | lint_cap: None, |
1a4d82fc JJ |
141 | } |
142 | } | |
143 | ||
144 | pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { | |
c34b1796 | 145 | &self.lints |
1a4d82fc JJ |
146 | } |
147 | ||
148 | pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> { | |
149 | self.lint_groups.iter().map(|(k, v)| (*k, | |
150 | v.0.clone(), | |
151 | v.1)).collect() | |
152 | } | |
153 | ||
b039eaaf SL |
154 | pub fn register_early_pass(&mut self, |
155 | sess: Option<&Session>, | |
156 | from_plugin: bool, | |
157 | pass: EarlyLintPassObject) { | |
158 | self.push_pass(sess, from_plugin, &pass); | |
159 | self.early_passes.as_mut().unwrap().push(pass); | |
160 | } | |
161 | ||
162 | pub fn register_late_pass(&mut self, | |
163 | sess: Option<&Session>, | |
164 | from_plugin: bool, | |
165 | pass: LateLintPassObject) { | |
166 | self.push_pass(sess, from_plugin, &pass); | |
167 | self.late_passes.as_mut().unwrap().push(pass); | |
168 | } | |
169 | ||
170 | // Helper method for register_early/late_pass | |
171 | fn push_pass<P: LintPass + ?Sized + 'static>(&mut self, | |
172 | sess: Option<&Session>, | |
173 | from_plugin: bool, | |
174 | pass: &Box<P>) { | |
85aaf69f | 175 | for &lint in pass.get_lints() { |
1a4d82fc JJ |
176 | self.lints.push((*lint, from_plugin)); |
177 | ||
178 | let id = LintId::of(*lint); | |
179 | if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { | |
180 | let msg = format!("duplicate specification of lint {}", lint.name_lower()); | |
181 | match (sess, from_plugin) { | |
182 | // We load builtin lints first, so a duplicate is a compiler bug. | |
183 | // Use early_error when handling -W help with no crate. | |
9cc50fc6 | 184 | (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]), |
54a0048b | 185 | (Some(_), false) => bug!("{}", msg), |
1a4d82fc JJ |
186 | |
187 | // A duplicate name from a plugin is a user error. | |
85aaf69f | 188 | (Some(sess), true) => sess.err(&msg[..]), |
1a4d82fc JJ |
189 | } |
190 | } | |
191 | ||
192 | if lint.default_level != Allow { | |
193 | self.levels.insert(id, (lint.default_level, Default)); | |
194 | } | |
195 | } | |
1a4d82fc JJ |
196 | } |
197 | ||
9cc50fc6 SL |
198 | pub fn register_future_incompatible(&mut self, |
199 | sess: Option<&Session>, | |
200 | lints: Vec<FutureIncompatibleInfo>) { | |
201 | let ids = lints.iter().map(|f| f.id).collect(); | |
202 | self.register_group(sess, false, "future_incompatible", ids); | |
203 | for info in lints { | |
204 | self.future_incompatible.insert(info.id, info); | |
205 | } | |
206 | } | |
207 | ||
208 | pub fn future_incompatible(&self, id: LintId) -> Option<&FutureIncompatibleInfo> { | |
209 | self.future_incompatible.get(&id) | |
210 | } | |
211 | ||
1a4d82fc JJ |
212 | pub fn register_group(&mut self, sess: Option<&Session>, |
213 | from_plugin: bool, name: &'static str, | |
214 | to: Vec<LintId>) { | |
215 | let new = self.lint_groups.insert(name, (to, from_plugin)).is_none(); | |
216 | ||
217 | if !new { | |
218 | let msg = format!("duplicate specification of lint group {}", name); | |
219 | match (sess, from_plugin) { | |
220 | // We load builtin lints first, so a duplicate is a compiler bug. | |
221 | // Use early_error when handling -W help with no crate. | |
9cc50fc6 | 222 | (None, _) => early_error(config::ErrorOutputType::default(), &msg[..]), |
54a0048b | 223 | (Some(_), false) => bug!("{}", msg), |
1a4d82fc JJ |
224 | |
225 | // A duplicate name from a plugin is a user error. | |
85aaf69f | 226 | (Some(sess), true) => sess.err(&msg[..]), |
1a4d82fc JJ |
227 | } |
228 | } | |
229 | } | |
230 | ||
c34b1796 | 231 | pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { |
1a4d82fc JJ |
232 | let target = match self.by_name.get(new_name) { |
233 | Some(&Id(lint_id)) => lint_id.clone(), | |
54a0048b | 234 | _ => bug!("invalid lint renaming of {} to {}", old_name, new_name) |
1a4d82fc JJ |
235 | }; |
236 | self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); | |
237 | } | |
238 | ||
c1a9b12d SL |
239 | pub fn register_removed(&mut self, name: &str, reason: &str) { |
240 | self.by_name.insert(name.into(), Removed(reason.into())); | |
241 | } | |
242 | ||
1a4d82fc JJ |
243 | #[allow(unused_variables)] |
244 | fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>) | |
c1a9b12d | 245 | -> Result<LintId, FindLintError> |
1a4d82fc JJ |
246 | { |
247 | match self.by_name.get(lint_name) { | |
c1a9b12d | 248 | Some(&Id(lint_id)) => Ok(lint_id), |
92a42be0 | 249 | Some(&Renamed(_, lint_id)) => { |
c1a9b12d SL |
250 | Ok(lint_id) |
251 | }, | |
252 | Some(&Removed(ref reason)) => { | |
c1a9b12d SL |
253 | Err(FindLintError::Removed) |
254 | }, | |
255 | None => Err(FindLintError::NotFound) | |
1a4d82fc JJ |
256 | } |
257 | } | |
258 | ||
259 | pub fn process_command_line(&mut self, sess: &Session) { | |
85aaf69f | 260 | for &(ref lint_name, level) in &sess.opts.lint_opts { |
92a42be0 SL |
261 | check_lint_name_cmdline(sess, self, |
262 | &lint_name[..], level); | |
263 | ||
85aaf69f | 264 | match self.find_lint(&lint_name[..], sess, None) { |
c1a9b12d | 265 | Ok(lint_id) => self.set_level(lint_id, (level, CommandLine)), |
92a42be0 | 266 | Err(FindLintError::Removed) => { } |
c1a9b12d | 267 | Err(_) => { |
1a4d82fc JJ |
268 | match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone())) |
269 | .collect::<FnvHashMap<&'static str, | |
270 | Vec<LintId>>>() | |
85aaf69f | 271 | .get(&lint_name[..]) { |
1a4d82fc JJ |
272 | Some(v) => { |
273 | v.iter() | |
274 | .map(|lint_id: &LintId| | |
275 | self.set_level(*lint_id, (level, CommandLine))) | |
276 | .collect::<Vec<()>>(); | |
277 | } | |
92a42be0 SL |
278 | None => { |
279 | // The lint or lint group doesn't exist. | |
280 | // This is an error, but it was handled | |
281 | // by check_lint_name_cmdline. | |
282 | } | |
1a4d82fc JJ |
283 | } |
284 | } | |
285 | } | |
286 | } | |
c1a9b12d SL |
287 | |
288 | self.lint_cap = sess.opts.lint_cap; | |
289 | if let Some(cap) = self.lint_cap { | |
290 | for level in self.levels.iter_mut().map(|p| &mut (p.1).0) { | |
291 | *level = cmp::min(*level, cap); | |
292 | } | |
293 | } | |
1a4d82fc | 294 | } |
1a4d82fc JJ |
295 | } |
296 | ||
b039eaaf SL |
297 | /// Context for lint checking after type checking. |
298 | pub struct LateContext<'a, 'tcx: 'a> { | |
1a4d82fc | 299 | /// Type context we're checking in. |
54a0048b | 300 | pub tcx: &'a TyCtxt<'tcx>, |
1a4d82fc JJ |
301 | |
302 | /// The crate being checked. | |
e9174d1e | 303 | pub krate: &'a hir::Crate, |
1a4d82fc | 304 | |
92a42be0 SL |
305 | /// Items accessible from the crate being checked. |
306 | pub access_levels: &'a AccessLevels, | |
1a4d82fc JJ |
307 | |
308 | /// The store of registered lints. | |
309 | lints: LintStore, | |
310 | ||
311 | /// When recursing into an attributed node of the ast which modifies lint | |
312 | /// levels, this stack keeps track of the previous lint levels of whatever | |
313 | /// was modified. | |
314 | level_stack: Vec<(LintId, LevelSource)>, | |
315 | ||
316 | /// Level of lints for certain NodeIds, stored here because the body of | |
317 | /// the lint needs to run in trans. | |
318 | node_levels: RefCell<FnvHashMap<(ast::NodeId, LintId), LevelSource>>, | |
319 | } | |
320 | ||
b039eaaf SL |
321 | /// Context for lint checking of the AST, after expansion, before lowering to |
322 | /// HIR. | |
323 | pub struct EarlyContext<'a> { | |
324 | /// Type context we're checking in. | |
325 | pub sess: &'a Session, | |
326 | ||
327 | /// The crate being checked. | |
328 | pub krate: &'a ast::Crate, | |
329 | ||
330 | /// The store of registered lints. | |
331 | lints: LintStore, | |
332 | ||
333 | /// When recursing into an attributed node of the ast which modifies lint | |
334 | /// levels, this stack keeps track of the previous lint levels of whatever | |
335 | /// was modified. | |
336 | level_stack: Vec<(LintId, LevelSource)>, | |
337 | } | |
338 | ||
1a4d82fc | 339 | /// Convenience macro for calling a `LintPass` method on every pass in the context. |
b039eaaf | 340 | macro_rules! run_lints { ($cx:expr, $f:ident, $ps:ident, $($args:expr),*) => ({ |
1a4d82fc JJ |
341 | // Move the vector of passes out of `$cx` so that we can |
342 | // iterate over it mutably while passing `$cx` to the methods. | |
b039eaaf | 343 | let mut passes = $cx.mut_lints().$ps.take().unwrap(); |
85aaf69f | 344 | for obj in &mut passes { |
1a4d82fc JJ |
345 | obj.$f($cx, $($args),*); |
346 | } | |
b039eaaf | 347 | $cx.mut_lints().$ps = Some(passes); |
1a4d82fc JJ |
348 | }) } |
349 | ||
350 | /// Parse the lint attributes into a vector, with `Err`s for malformed lint | |
351 | /// attributes. Writing this as an iterator is an enormous mess. | |
e9174d1e | 352 | // See also the hir version just below. |
b039eaaf | 353 | pub fn gather_attrs(attrs: &[ast::Attribute]) |
1a4d82fc JJ |
354 | -> Vec<Result<(InternedString, Level, Span), Span>> { |
355 | let mut out = vec!(); | |
85aaf69f | 356 | for attr in attrs { |
92a42be0 SL |
357 | let r = gather_attr(attr); |
358 | out.extend(r.into_iter()); | |
359 | } | |
360 | out | |
361 | } | |
1a4d82fc | 362 | |
92a42be0 SL |
363 | pub fn gather_attr(attr: &ast::Attribute) |
364 | -> Vec<Result<(InternedString, Level, Span), Span>> { | |
365 | let mut out = vec!(); | |
1a4d82fc | 366 | |
92a42be0 SL |
367 | let level = match Level::from_str(&attr.name()) { |
368 | None => return out, | |
369 | Some(lvl) => lvl, | |
370 | }; | |
1a4d82fc | 371 | |
92a42be0 SL |
372 | attr::mark_used(attr); |
373 | ||
374 | let meta = &attr.node.value; | |
375 | let metas = match meta.node { | |
7453a54e | 376 | ast::MetaItemKind::List(_, ref metas) => metas, |
92a42be0 SL |
377 | _ => { |
378 | out.push(Err(meta.span)); | |
379 | return out; | |
1a4d82fc | 380 | } |
92a42be0 SL |
381 | }; |
382 | ||
383 | for meta in metas { | |
384 | out.push(match meta.node { | |
7453a54e | 385 | ast::MetaItemKind::Word(ref lint_name) => Ok((lint_name.clone(), level, meta.span)), |
92a42be0 SL |
386 | _ => Err(meta.span), |
387 | }); | |
1a4d82fc | 388 | } |
92a42be0 | 389 | |
1a4d82fc JJ |
390 | out |
391 | } | |
392 | ||
393 | /// Emit a lint as a warning or an error (or not at all) | |
394 | /// according to `level`. | |
395 | /// | |
396 | /// This lives outside of `Context` so it can be used by checks | |
397 | /// in trans that run after the main lint pass is finished. Most | |
398 | /// lints elsewhere in the compiler should call | |
399 | /// `Session::add_lint()` instead. | |
92a42be0 SL |
400 | pub fn raw_emit_lint(sess: &Session, |
401 | lints: &LintStore, | |
402 | lint: &'static Lint, | |
403 | lvlsrc: LevelSource, | |
404 | span: Option<Span>, | |
405 | msg: &str) { | |
9cc50fc6 SL |
406 | raw_struct_lint(sess, lints, lint, lvlsrc, span, msg).emit(); |
407 | } | |
408 | ||
409 | pub fn raw_struct_lint<'a>(sess: &'a Session, | |
410 | lints: &LintStore, | |
411 | lint: &'static Lint, | |
412 | lvlsrc: LevelSource, | |
413 | span: Option<Span>, | |
414 | msg: &str) | |
415 | -> DiagnosticBuilder<'a> { | |
1a4d82fc | 416 | let (mut level, source) = lvlsrc; |
9cc50fc6 SL |
417 | if level == Allow { |
418 | return sess.diagnostic().struct_dummy(); | |
419 | } | |
1a4d82fc JJ |
420 | |
421 | let name = lint.name_lower(); | |
422 | let mut def = None; | |
1a4d82fc JJ |
423 | let msg = match source { |
424 | Default => { | |
425 | format!("{}, #[{}({})] on by default", msg, | |
426 | level.as_str(), name) | |
427 | }, | |
428 | CommandLine => { | |
429 | format!("{} [-{} {}]", msg, | |
430 | match level { | |
431 | Warn => 'W', Deny => 'D', Forbid => 'F', | |
54a0048b | 432 | Allow => bug!() |
1a4d82fc JJ |
433 | }, name.replace("_", "-")) |
434 | }, | |
435 | Node(src) => { | |
436 | def = Some(src); | |
437 | msg.to_string() | |
438 | } | |
1a4d82fc JJ |
439 | }; |
440 | ||
441 | // For purposes of printing, we can treat forbid as deny. | |
442 | if level == Forbid { level = Deny; } | |
443 | ||
9cc50fc6 SL |
444 | let mut err = match (level, span) { |
445 | (Warn, Some(sp)) => sess.struct_span_warn(sp, &msg[..]), | |
446 | (Warn, None) => sess.struct_warn(&msg[..]), | |
447 | (Deny, Some(sp)) => sess.struct_span_err(sp, &msg[..]), | |
448 | (Deny, None) => sess.struct_err(&msg[..]), | |
54a0048b | 449 | _ => bug!("impossible level in raw_emit_lint"), |
9cc50fc6 | 450 | }; |
1a4d82fc | 451 | |
92a42be0 | 452 | // Check for future incompatibility lints and issue a stronger warning. |
9cc50fc6 SL |
453 | if let Some(future_incompatible) = lints.future_incompatible(LintId::of(lint)) { |
454 | let explanation = format!("this was previously accepted by the compiler \ | |
455 | but is being phased out; \ | |
456 | it will become a hard error in a future release!"); | |
457 | let citation = format!("for more information, see {}", | |
458 | future_incompatible.reference); | |
92a42be0 | 459 | if let Some(sp) = span { |
9cc50fc6 SL |
460 | err.fileline_warn(sp, &explanation); |
461 | err.fileline_note(sp, &citation); | |
92a42be0 | 462 | } else { |
9cc50fc6 SL |
463 | err.warn(&explanation); |
464 | err.note(&citation); | |
92a42be0 SL |
465 | } |
466 | } | |
467 | ||
85aaf69f | 468 | if let Some(span) = def { |
9cc50fc6 | 469 | err.span_note(span, "lint level defined here"); |
1a4d82fc | 470 | } |
9cc50fc6 SL |
471 | |
472 | err | |
1a4d82fc JJ |
473 | } |
474 | ||
b039eaaf SL |
475 | pub trait LintContext: Sized { |
476 | fn sess(&self) -> &Session; | |
477 | fn lints(&self) -> &LintStore; | |
478 | fn mut_lints(&mut self) -> &mut LintStore; | |
479 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)>; | |
480 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]); | |
481 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]); | |
1a4d82fc JJ |
482 | |
483 | /// Get the level of `lint` at the current position of the lint | |
484 | /// traversal. | |
b039eaaf SL |
485 | fn current_level(&self, lint: &'static Lint) -> Level { |
486 | self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl) | |
1a4d82fc JJ |
487 | } |
488 | ||
9cc50fc6 SL |
489 | fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> { |
490 | self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls { | |
54a0048b | 491 | &(Warn, _) => { |
1a4d82fc | 492 | let lint_id = LintId::of(builtin::WARNINGS); |
54a0048b SL |
493 | let warn_src = self.lints().get_level_source(lint_id); |
494 | if warn_src.0 != Warn { | |
495 | warn_src | |
496 | } else { | |
497 | *ls | |
498 | } | |
1a4d82fc | 499 | } |
9cc50fc6 SL |
500 | _ => *ls |
501 | }) | |
502 | } | |
503 | ||
504 | fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) { | |
505 | let (level, src) = match self.level_src(lint) { | |
506 | None => return, | |
507 | Some(pair) => pair, | |
1a4d82fc JJ |
508 | }; |
509 | ||
92a42be0 | 510 | raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg); |
1a4d82fc JJ |
511 | } |
512 | ||
9cc50fc6 SL |
513 | fn lookup(&self, |
514 | lint: &'static Lint, | |
515 | span: Option<Span>, | |
516 | msg: &str) | |
517 | -> DiagnosticBuilder { | |
518 | let (level, src) = match self.level_src(lint) { | |
519 | None => return self.sess().diagnostic().struct_dummy(), | |
520 | Some(pair) => pair, | |
521 | }; | |
522 | ||
523 | raw_struct_lint(&self.sess(), self.lints(), lint, (level, src), span, msg) | |
524 | } | |
525 | ||
1a4d82fc | 526 | /// Emit a lint at the appropriate level, for a particular span. |
b039eaaf | 527 | fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { |
1a4d82fc JJ |
528 | self.lookup_and_emit(lint, Some(span), msg); |
529 | } | |
530 | ||
9cc50fc6 SL |
531 | fn struct_span_lint(&self, |
532 | lint: &'static Lint, | |
533 | span: Span, | |
534 | msg: &str) | |
535 | -> DiagnosticBuilder { | |
536 | self.lookup(lint, Some(span), msg) | |
537 | } | |
538 | ||
b039eaaf SL |
539 | /// Emit a lint and note at the appropriate level, for a particular span. |
540 | fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str, | |
541 | note_span: Span, note: &str) { | |
9cc50fc6 | 542 | let mut err = self.lookup(lint, Some(span), msg); |
b039eaaf SL |
543 | if self.current_level(lint) != Level::Allow { |
544 | if note_span == span { | |
9cc50fc6 | 545 | err.fileline_note(note_span, note); |
b039eaaf | 546 | } else { |
9cc50fc6 | 547 | err.span_note(note_span, note); |
b039eaaf SL |
548 | } |
549 | } | |
9cc50fc6 | 550 | err.emit(); |
b039eaaf SL |
551 | } |
552 | ||
553 | /// Emit a lint and help at the appropriate level, for a particular span. | |
554 | fn span_lint_help(&self, lint: &'static Lint, span: Span, | |
555 | msg: &str, help: &str) { | |
9cc50fc6 | 556 | let mut err = self.lookup(lint, Some(span), msg); |
b039eaaf SL |
557 | self.span_lint(lint, span, msg); |
558 | if self.current_level(lint) != Level::Allow { | |
9cc50fc6 | 559 | err.span_help(span, help); |
b039eaaf | 560 | } |
9cc50fc6 | 561 | err.emit(); |
b039eaaf SL |
562 | } |
563 | ||
564 | /// Emit a lint at the appropriate level, with no associated span. | |
565 | fn lint(&self, lint: &'static Lint, msg: &str) { | |
566 | self.lookup_and_emit(lint, None, msg); | |
567 | } | |
568 | ||
1a4d82fc JJ |
569 | /// Merge the lints specified by any lint attributes into the |
570 | /// current lint context, call the provided function, then reset the | |
571 | /// lints in effect to their previous state. | |
572 | fn with_lint_attrs<F>(&mut self, | |
b039eaaf SL |
573 | attrs: &[ast::Attribute], |
574 | f: F) | |
575 | where F: FnOnce(&mut Self), | |
1a4d82fc JJ |
576 | { |
577 | // Parse all of the lint attributes, and then add them all to the | |
578 | // current dictionary of lint information. Along the way, keep a history | |
579 | // of what we changed so we can roll everything back after invoking the | |
580 | // specified closure | |
85aaf69f | 581 | let mut pushed = 0; |
1a4d82fc | 582 | |
85aaf69f | 583 | for result in gather_attrs(attrs) { |
1a4d82fc JJ |
584 | let v = match result { |
585 | Err(span) => { | |
b039eaaf SL |
586 | span_err!(self.sess(), span, E0452, |
587 | "malformed lint attribute"); | |
1a4d82fc JJ |
588 | continue; |
589 | } | |
590 | Ok((lint_name, level, span)) => { | |
b039eaaf | 591 | match self.lints().find_lint(&lint_name, &self.sess(), Some(span)) { |
c1a9b12d SL |
592 | Ok(lint_id) => vec![(lint_id, level, span)], |
593 | Err(FindLintError::NotFound) => { | |
b039eaaf | 594 | match self.lints().lint_groups.get(&lint_name[..]) { |
1a4d82fc JJ |
595 | Some(&(ref v, _)) => v.iter() |
596 | .map(|lint_id: &LintId| | |
597 | (*lint_id, level, span)) | |
598 | .collect(), | |
599 | None => { | |
92a42be0 SL |
600 | // The lint or lint group doesn't exist. |
601 | // This is an error, but it was handled | |
602 | // by check_lint_name_attribute. | |
1a4d82fc JJ |
603 | continue; |
604 | } | |
605 | } | |
c1a9b12d SL |
606 | }, |
607 | Err(FindLintError::Removed) => { continue; } | |
1a4d82fc JJ |
608 | } |
609 | } | |
610 | }; | |
611 | ||
85aaf69f | 612 | for (lint_id, level, span) in v { |
b039eaaf | 613 | let now = self.lints().get_level_source(lint_id).0; |
1a4d82fc JJ |
614 | if now == Forbid && level != Forbid { |
615 | let lint_name = lint_id.as_str(); | |
b039eaaf SL |
616 | span_err!(self.sess(), span, E0453, |
617 | "{}({}) overruled by outer forbid({})", | |
618 | level.as_str(), lint_name, | |
619 | lint_name); | |
1a4d82fc | 620 | } else if now != level { |
b039eaaf SL |
621 | let src = self.lints().get_level_source(lint_id).1; |
622 | self.level_stack().push((lint_id, (now, src))); | |
1a4d82fc | 623 | pushed += 1; |
b039eaaf | 624 | self.mut_lints().set_level(lint_id, (level, Node(span))); |
1a4d82fc JJ |
625 | } |
626 | } | |
627 | } | |
628 | ||
b039eaaf | 629 | self.enter_attrs(attrs); |
1a4d82fc | 630 | f(self); |
b039eaaf | 631 | self.exit_attrs(attrs); |
1a4d82fc JJ |
632 | |
633 | // rollback | |
85aaf69f | 634 | for _ in 0..pushed { |
b039eaaf SL |
635 | let (lint, lvlsrc) = self.level_stack().pop().unwrap(); |
636 | self.mut_lints().set_level(lint, lvlsrc); | |
637 | } | |
638 | } | |
639 | } | |
640 | ||
641 | ||
642 | impl<'a> EarlyContext<'a> { | |
643 | fn new(sess: &'a Session, | |
644 | krate: &'a ast::Crate) -> EarlyContext<'a> { | |
645 | // We want to own the lint store, so move it out of the session. Remember | |
646 | // to put it back later... | |
647 | let lint_store = mem::replace(&mut *sess.lint_store.borrow_mut(), | |
648 | LintStore::new()); | |
649 | EarlyContext { | |
650 | sess: sess, | |
651 | krate: krate, | |
652 | lints: lint_store, | |
653 | level_stack: vec![], | |
654 | } | |
655 | } | |
b039eaaf SL |
656 | } |
657 | ||
658 | impl<'a, 'tcx> LateContext<'a, 'tcx> { | |
54a0048b | 659 | fn new(tcx: &'a TyCtxt<'tcx>, |
b039eaaf | 660 | krate: &'a hir::Crate, |
92a42be0 | 661 | access_levels: &'a AccessLevels) -> LateContext<'a, 'tcx> { |
b039eaaf SL |
662 | // We want to own the lint store, so move it out of the session. |
663 | let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), | |
664 | LintStore::new()); | |
665 | ||
666 | LateContext { | |
667 | tcx: tcx, | |
668 | krate: krate, | |
92a42be0 | 669 | access_levels: access_levels, |
b039eaaf SL |
670 | lints: lint_store, |
671 | level_stack: vec![], | |
672 | node_levels: RefCell::new(FnvHashMap()), | |
1a4d82fc JJ |
673 | } |
674 | } | |
675 | ||
b039eaaf | 676 | fn visit_ids<F>(&mut self, f: F) |
54a0048b | 677 | where F: FnOnce(&mut IdVisitor<LateContext>) |
1a4d82fc | 678 | { |
54a0048b | 679 | let mut v = IdVisitor::new(self); |
1a4d82fc JJ |
680 | f(&mut v); |
681 | } | |
682 | } | |
683 | ||
b039eaaf SL |
684 | impl<'a, 'tcx> LintContext for LateContext<'a, 'tcx> { |
685 | /// Get the overall compiler `Session` object. | |
686 | fn sess(&self) -> &Session { | |
687 | &self.tcx.sess | |
688 | } | |
689 | ||
690 | fn lints(&self) -> &LintStore { | |
691 | &self.lints | |
692 | } | |
693 | ||
694 | fn mut_lints(&mut self) -> &mut LintStore { | |
695 | &mut self.lints | |
696 | } | |
697 | ||
698 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> { | |
699 | &mut self.level_stack | |
700 | } | |
701 | ||
702 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 703 | debug!("late context: enter_attrs({:?})", attrs); |
b039eaaf SL |
704 | run_lints!(self, enter_lint_attrs, late_passes, attrs); |
705 | } | |
706 | ||
707 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 708 | debug!("late context: exit_attrs({:?})", attrs); |
b039eaaf SL |
709 | run_lints!(self, exit_lint_attrs, late_passes, attrs); |
710 | } | |
711 | } | |
712 | ||
713 | impl<'a> LintContext for EarlyContext<'a> { | |
714 | /// Get the overall compiler `Session` object. | |
715 | fn sess(&self) -> &Session { | |
716 | &self.sess | |
717 | } | |
718 | ||
719 | fn lints(&self) -> &LintStore { | |
720 | &self.lints | |
721 | } | |
722 | ||
723 | fn mut_lints(&mut self) -> &mut LintStore { | |
724 | &mut self.lints | |
725 | } | |
726 | ||
727 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> { | |
728 | &mut self.level_stack | |
729 | } | |
730 | ||
731 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]) { | |
7453a54e | 732 | debug!("early context: enter_attrs({:?})", attrs); |
b039eaaf SL |
733 | run_lints!(self, enter_lint_attrs, early_passes, attrs); |
734 | } | |
735 | ||
736 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 737 | debug!("early context: exit_attrs({:?})", attrs); |
b039eaaf SL |
738 | run_lints!(self, exit_lint_attrs, early_passes, attrs); |
739 | } | |
740 | } | |
741 | ||
742 | impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> { | |
92a42be0 SL |
743 | /// Because lints are scoped lexically, we want to walk nested |
744 | /// items in the context of the outer item, so enable | |
745 | /// deep-walking. | |
746 | fn visit_nested_item(&mut self, item: hir::ItemId) { | |
747 | self.visit_item(self.tcx.map.expect_item(item.id)) | |
748 | } | |
749 | ||
e9174d1e | 750 | fn visit_item(&mut self, it: &hir::Item) { |
c34b1796 | 751 | self.with_lint_attrs(&it.attrs, |cx| { |
b039eaaf | 752 | run_lints!(cx, check_item, late_passes, it); |
1a4d82fc | 753 | cx.visit_ids(|v| v.visit_item(it)); |
b039eaaf | 754 | hir_visit::walk_item(cx, it); |
7453a54e | 755 | run_lints!(cx, check_item_post, late_passes, it); |
1a4d82fc JJ |
756 | }) |
757 | } | |
758 | ||
e9174d1e | 759 | fn visit_foreign_item(&mut self, it: &hir::ForeignItem) { |
c34b1796 | 760 | self.with_lint_attrs(&it.attrs, |cx| { |
b039eaaf SL |
761 | run_lints!(cx, check_foreign_item, late_passes, it); |
762 | hir_visit::walk_foreign_item(cx, it); | |
7453a54e | 763 | run_lints!(cx, check_foreign_item_post, late_passes, it); |
1a4d82fc JJ |
764 | }) |
765 | } | |
766 | ||
e9174d1e | 767 | fn visit_pat(&mut self, p: &hir::Pat) { |
b039eaaf SL |
768 | run_lints!(self, check_pat, late_passes, p); |
769 | hir_visit::walk_pat(self, p); | |
1a4d82fc JJ |
770 | } |
771 | ||
e9174d1e | 772 | fn visit_expr(&mut self, e: &hir::Expr) { |
92a42be0 SL |
773 | self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| { |
774 | run_lints!(cx, check_expr, late_passes, e); | |
775 | hir_visit::walk_expr(cx, e); | |
776 | }) | |
1a4d82fc JJ |
777 | } |
778 | ||
e9174d1e | 779 | fn visit_stmt(&mut self, s: &hir::Stmt) { |
92a42be0 SL |
780 | // statement attributes are actually just attributes on one of |
781 | // - item | |
782 | // - local | |
783 | // - expression | |
784 | // so we keep track of lint levels there | |
b039eaaf SL |
785 | run_lints!(self, check_stmt, late_passes, s); |
786 | hir_visit::walk_stmt(self, s); | |
1a4d82fc JJ |
787 | } |
788 | ||
b039eaaf | 789 | fn visit_fn(&mut self, fk: hir_visit::FnKind<'v>, decl: &'v hir::FnDecl, |
e9174d1e | 790 | body: &'v hir::Block, span: Span, id: ast::NodeId) { |
b039eaaf SL |
791 | run_lints!(self, check_fn, late_passes, fk, decl, body, span, id); |
792 | hir_visit::walk_fn(self, fk, decl, body, span); | |
7453a54e | 793 | run_lints!(self, check_fn_post, late_passes, fk, decl, body, span, id); |
1a4d82fc JJ |
794 | } |
795 | ||
b039eaaf SL |
796 | fn visit_variant_data(&mut self, |
797 | s: &hir::VariantData, | |
798 | name: ast::Name, | |
e9174d1e | 799 | g: &hir::Generics, |
b039eaaf SL |
800 | item_id: ast::NodeId, |
801 | _: Span) { | |
802 | run_lints!(self, check_struct_def, late_passes, s, name, g, item_id); | |
803 | hir_visit::walk_struct_def(self, s); | |
804 | run_lints!(self, check_struct_def_post, late_passes, s, name, g, item_id); | |
1a4d82fc JJ |
805 | } |
806 | ||
e9174d1e | 807 | fn visit_struct_field(&mut self, s: &hir::StructField) { |
54a0048b | 808 | self.with_lint_attrs(&s.attrs, |cx| { |
b039eaaf SL |
809 | run_lints!(cx, check_struct_field, late_passes, s); |
810 | hir_visit::walk_struct_field(cx, s); | |
1a4d82fc JJ |
811 | }) |
812 | } | |
813 | ||
b039eaaf | 814 | fn visit_variant(&mut self, v: &hir::Variant, g: &hir::Generics, item_id: ast::NodeId) { |
c34b1796 | 815 | self.with_lint_attrs(&v.node.attrs, |cx| { |
b039eaaf SL |
816 | run_lints!(cx, check_variant, late_passes, v, g); |
817 | hir_visit::walk_variant(cx, v, g, item_id); | |
818 | run_lints!(cx, check_variant_post, late_passes, v, g); | |
1a4d82fc JJ |
819 | }) |
820 | } | |
821 | ||
e9174d1e | 822 | fn visit_ty(&mut self, t: &hir::Ty) { |
b039eaaf SL |
823 | run_lints!(self, check_ty, late_passes, t); |
824 | hir_visit::walk_ty(self, t); | |
1a4d82fc JJ |
825 | } |
826 | ||
b039eaaf SL |
827 | fn visit_name(&mut self, sp: Span, name: ast::Name) { |
828 | run_lints!(self, check_name, late_passes, sp, name); | |
1a4d82fc JJ |
829 | } |
830 | ||
e9174d1e | 831 | fn visit_mod(&mut self, m: &hir::Mod, s: Span, n: ast::NodeId) { |
b039eaaf SL |
832 | run_lints!(self, check_mod, late_passes, m, s, n); |
833 | hir_visit::walk_mod(self, m); | |
7453a54e | 834 | run_lints!(self, check_mod_post, late_passes, m, s, n); |
1a4d82fc JJ |
835 | } |
836 | ||
e9174d1e | 837 | fn visit_local(&mut self, l: &hir::Local) { |
92a42be0 SL |
838 | self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| { |
839 | run_lints!(cx, check_local, late_passes, l); | |
840 | hir_visit::walk_local(cx, l); | |
841 | }) | |
1a4d82fc JJ |
842 | } |
843 | ||
e9174d1e | 844 | fn visit_block(&mut self, b: &hir::Block) { |
b039eaaf SL |
845 | run_lints!(self, check_block, late_passes, b); |
846 | hir_visit::walk_block(self, b); | |
7453a54e | 847 | run_lints!(self, check_block_post, late_passes, b); |
1a4d82fc JJ |
848 | } |
849 | ||
e9174d1e | 850 | fn visit_arm(&mut self, a: &hir::Arm) { |
b039eaaf SL |
851 | run_lints!(self, check_arm, late_passes, a); |
852 | hir_visit::walk_arm(self, a); | |
1a4d82fc JJ |
853 | } |
854 | ||
e9174d1e | 855 | fn visit_decl(&mut self, d: &hir::Decl) { |
b039eaaf SL |
856 | run_lints!(self, check_decl, late_passes, d); |
857 | hir_visit::walk_decl(self, d); | |
1a4d82fc JJ |
858 | } |
859 | ||
e9174d1e | 860 | fn visit_expr_post(&mut self, e: &hir::Expr) { |
b039eaaf | 861 | run_lints!(self, check_expr_post, late_passes, e); |
1a4d82fc JJ |
862 | } |
863 | ||
e9174d1e | 864 | fn visit_generics(&mut self, g: &hir::Generics) { |
b039eaaf SL |
865 | run_lints!(self, check_generics, late_passes, g); |
866 | hir_visit::walk_generics(self, g); | |
1a4d82fc JJ |
867 | } |
868 | ||
e9174d1e | 869 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { |
c34b1796 | 870 | self.with_lint_attrs(&trait_item.attrs, |cx| { |
b039eaaf | 871 | run_lints!(cx, check_trait_item, late_passes, trait_item); |
c34b1796 | 872 | cx.visit_ids(|v| v.visit_trait_item(trait_item)); |
b039eaaf | 873 | hir_visit::walk_trait_item(cx, trait_item); |
7453a54e | 874 | run_lints!(cx, check_trait_item_post, late_passes, trait_item); |
c34b1796 AL |
875 | }); |
876 | } | |
877 | ||
e9174d1e | 878 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { |
c34b1796 | 879 | self.with_lint_attrs(&impl_item.attrs, |cx| { |
b039eaaf | 880 | run_lints!(cx, check_impl_item, late_passes, impl_item); |
c34b1796 | 881 | cx.visit_ids(|v| v.visit_impl_item(impl_item)); |
b039eaaf | 882 | hir_visit::walk_impl_item(cx, impl_item); |
7453a54e | 883 | run_lints!(cx, check_impl_item_post, late_passes, impl_item); |
c34b1796 | 884 | }); |
1a4d82fc JJ |
885 | } |
886 | ||
b039eaaf SL |
887 | fn visit_lifetime(&mut self, lt: &hir::Lifetime) { |
888 | run_lints!(self, check_lifetime, late_passes, lt); | |
1a4d82fc JJ |
889 | } |
890 | ||
e9174d1e | 891 | fn visit_lifetime_def(&mut self, lt: &hir::LifetimeDef) { |
b039eaaf | 892 | run_lints!(self, check_lifetime_def, late_passes, lt); |
1a4d82fc JJ |
893 | } |
894 | ||
e9174d1e | 895 | fn visit_explicit_self(&mut self, es: &hir::ExplicitSelf) { |
b039eaaf SL |
896 | run_lints!(self, check_explicit_self, late_passes, es); |
897 | hir_visit::walk_explicit_self(self, es); | |
1a4d82fc JJ |
898 | } |
899 | ||
e9174d1e | 900 | fn visit_path(&mut self, p: &hir::Path, id: ast::NodeId) { |
b039eaaf SL |
901 | run_lints!(self, check_path, late_passes, p, id); |
902 | hir_visit::walk_path(self, p); | |
903 | } | |
904 | ||
905 | fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) { | |
906 | run_lints!(self, check_path_list_item, late_passes, item); | |
907 | hir_visit::walk_path_list_item(self, prefix, item); | |
1a4d82fc JJ |
908 | } |
909 | ||
b039eaaf | 910 | fn visit_attribute(&mut self, attr: &ast::Attribute) { |
92a42be0 | 911 | check_lint_name_attribute(self, attr); |
b039eaaf SL |
912 | run_lints!(self, check_attribute, late_passes, attr); |
913 | } | |
914 | } | |
915 | ||
916 | impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> { | |
917 | fn visit_item(&mut self, it: &ast::Item) { | |
918 | self.with_lint_attrs(&it.attrs, |cx| { | |
919 | run_lints!(cx, check_item, early_passes, it); | |
b039eaaf | 920 | ast_visit::walk_item(cx, it); |
7453a54e | 921 | run_lints!(cx, check_item_post, early_passes, it); |
b039eaaf SL |
922 | }) |
923 | } | |
924 | ||
925 | fn visit_foreign_item(&mut self, it: &ast::ForeignItem) { | |
926 | self.with_lint_attrs(&it.attrs, |cx| { | |
927 | run_lints!(cx, check_foreign_item, early_passes, it); | |
928 | ast_visit::walk_foreign_item(cx, it); | |
7453a54e | 929 | run_lints!(cx, check_foreign_item_post, early_passes, it); |
b039eaaf SL |
930 | }) |
931 | } | |
932 | ||
933 | fn visit_pat(&mut self, p: &ast::Pat) { | |
934 | run_lints!(self, check_pat, early_passes, p); | |
935 | ast_visit::walk_pat(self, p); | |
936 | } | |
937 | ||
938 | fn visit_expr(&mut self, e: &ast::Expr) { | |
7453a54e SL |
939 | self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| { |
940 | run_lints!(cx, check_expr, early_passes, e); | |
941 | ast_visit::walk_expr(cx, e); | |
942 | }) | |
b039eaaf SL |
943 | } |
944 | ||
945 | fn visit_stmt(&mut self, s: &ast::Stmt) { | |
946 | run_lints!(self, check_stmt, early_passes, s); | |
947 | ast_visit::walk_stmt(self, s); | |
948 | } | |
949 | ||
950 | fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, decl: &'v ast::FnDecl, | |
951 | body: &'v ast::Block, span: Span, id: ast::NodeId) { | |
952 | run_lints!(self, check_fn, early_passes, fk, decl, body, span, id); | |
953 | ast_visit::walk_fn(self, fk, decl, body, span); | |
7453a54e | 954 | run_lints!(self, check_fn_post, early_passes, fk, decl, body, span, id); |
b039eaaf SL |
955 | } |
956 | ||
957 | fn visit_variant_data(&mut self, | |
958 | s: &ast::VariantData, | |
959 | ident: ast::Ident, | |
960 | g: &ast::Generics, | |
961 | item_id: ast::NodeId, | |
962 | _: Span) { | |
963 | run_lints!(self, check_struct_def, early_passes, s, ident, g, item_id); | |
964 | ast_visit::walk_struct_def(self, s); | |
965 | run_lints!(self, check_struct_def_post, early_passes, s, ident, g, item_id); | |
966 | } | |
967 | ||
968 | fn visit_struct_field(&mut self, s: &ast::StructField) { | |
54a0048b | 969 | self.with_lint_attrs(&s.attrs, |cx| { |
b039eaaf SL |
970 | run_lints!(cx, check_struct_field, early_passes, s); |
971 | ast_visit::walk_struct_field(cx, s); | |
972 | }) | |
973 | } | |
974 | ||
975 | fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, item_id: ast::NodeId) { | |
976 | self.with_lint_attrs(&v.node.attrs, |cx| { | |
977 | run_lints!(cx, check_variant, early_passes, v, g); | |
978 | ast_visit::walk_variant(cx, v, g, item_id); | |
979 | run_lints!(cx, check_variant_post, early_passes, v, g); | |
980 | }) | |
981 | } | |
982 | ||
983 | fn visit_ty(&mut self, t: &ast::Ty) { | |
984 | run_lints!(self, check_ty, early_passes, t); | |
985 | ast_visit::walk_ty(self, t); | |
986 | } | |
987 | ||
988 | fn visit_ident(&mut self, sp: Span, id: ast::Ident) { | |
989 | run_lints!(self, check_ident, early_passes, sp, id); | |
990 | } | |
991 | ||
992 | fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) { | |
993 | run_lints!(self, check_mod, early_passes, m, s, n); | |
994 | ast_visit::walk_mod(self, m); | |
7453a54e | 995 | run_lints!(self, check_mod_post, early_passes, m, s, n); |
b039eaaf SL |
996 | } |
997 | ||
998 | fn visit_local(&mut self, l: &ast::Local) { | |
7453a54e SL |
999 | self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| { |
1000 | run_lints!(cx, check_local, early_passes, l); | |
1001 | ast_visit::walk_local(cx, l); | |
1002 | }) | |
b039eaaf SL |
1003 | } |
1004 | ||
1005 | fn visit_block(&mut self, b: &ast::Block) { | |
1006 | run_lints!(self, check_block, early_passes, b); | |
1007 | ast_visit::walk_block(self, b); | |
7453a54e | 1008 | run_lints!(self, check_block_post, early_passes, b); |
b039eaaf SL |
1009 | } |
1010 | ||
1011 | fn visit_arm(&mut self, a: &ast::Arm) { | |
1012 | run_lints!(self, check_arm, early_passes, a); | |
1013 | ast_visit::walk_arm(self, a); | |
1014 | } | |
1015 | ||
1016 | fn visit_decl(&mut self, d: &ast::Decl) { | |
1017 | run_lints!(self, check_decl, early_passes, d); | |
1018 | ast_visit::walk_decl(self, d); | |
1019 | } | |
1020 | ||
1021 | fn visit_expr_post(&mut self, e: &ast::Expr) { | |
1022 | run_lints!(self, check_expr_post, early_passes, e); | |
1023 | } | |
1024 | ||
1025 | fn visit_generics(&mut self, g: &ast::Generics) { | |
1026 | run_lints!(self, check_generics, early_passes, g); | |
1027 | ast_visit::walk_generics(self, g); | |
1028 | } | |
1029 | ||
1030 | fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { | |
1031 | self.with_lint_attrs(&trait_item.attrs, |cx| { | |
1032 | run_lints!(cx, check_trait_item, early_passes, trait_item); | |
b039eaaf | 1033 | ast_visit::walk_trait_item(cx, trait_item); |
7453a54e | 1034 | run_lints!(cx, check_trait_item_post, early_passes, trait_item); |
b039eaaf SL |
1035 | }); |
1036 | } | |
1037 | ||
1038 | fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { | |
1039 | self.with_lint_attrs(&impl_item.attrs, |cx| { | |
1040 | run_lints!(cx, check_impl_item, early_passes, impl_item); | |
b039eaaf | 1041 | ast_visit::walk_impl_item(cx, impl_item); |
7453a54e | 1042 | run_lints!(cx, check_impl_item_post, early_passes, impl_item); |
b039eaaf SL |
1043 | }); |
1044 | } | |
1045 | ||
1046 | fn visit_lifetime(&mut self, lt: &ast::Lifetime) { | |
1047 | run_lints!(self, check_lifetime, early_passes, lt); | |
1048 | } | |
1049 | ||
1050 | fn visit_lifetime_def(&mut self, lt: &ast::LifetimeDef) { | |
1051 | run_lints!(self, check_lifetime_def, early_passes, lt); | |
1052 | } | |
1053 | ||
1054 | fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) { | |
1055 | run_lints!(self, check_explicit_self, early_passes, es); | |
1056 | ast_visit::walk_explicit_self(self, es); | |
1057 | } | |
1058 | ||
1059 | fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) { | |
1060 | run_lints!(self, check_path, early_passes, p, id); | |
1061 | ast_visit::walk_path(self, p); | |
1062 | } | |
1063 | ||
1064 | fn visit_path_list_item(&mut self, prefix: &ast::Path, item: &ast::PathListItem) { | |
1065 | run_lints!(self, check_path_list_item, early_passes, item); | |
1066 | ast_visit::walk_path_list_item(self, prefix, item); | |
1067 | } | |
1068 | ||
1069 | fn visit_attribute(&mut self, attr: &ast::Attribute) { | |
1070 | run_lints!(self, check_attribute, early_passes, attr); | |
1a4d82fc JJ |
1071 | } |
1072 | } | |
1073 | ||
1074 | // Output any lints that were previously added to the session. | |
b039eaaf SL |
1075 | impl<'a, 'tcx> IdVisitingOperation for LateContext<'a, 'tcx> { |
1076 | fn visit_id(&mut self, id: ast::NodeId) { | |
1077 | match self.sess().lints.borrow_mut().remove(&id) { | |
1078 | None => {} | |
1079 | Some(lints) => { | |
92a42be0 | 1080 | debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints); |
b039eaaf SL |
1081 | for (lint_id, span, msg) in lints { |
1082 | self.span_lint(lint_id.lint, span, &msg[..]) | |
1083 | } | |
1084 | } | |
1085 | } | |
1086 | } | |
1087 | } | |
1a4d82fc | 1088 | |
b039eaaf | 1089 | // This lint pass is defined here because it touches parts of the `LateContext` |
1a4d82fc JJ |
1090 | // that we don't want to expose. It records the lint level at certain AST |
1091 | // nodes, so that the variant size difference check in trans can call | |
1092 | // `raw_emit_lint`. | |
1093 | ||
c34b1796 | 1094 | pub struct GatherNodeLevels; |
1a4d82fc JJ |
1095 | |
1096 | impl LintPass for GatherNodeLevels { | |
1097 | fn get_lints(&self) -> LintArray { | |
1098 | lint_array!() | |
1099 | } | |
b039eaaf | 1100 | } |
1a4d82fc | 1101 | |
b039eaaf SL |
1102 | impl LateLintPass for GatherNodeLevels { |
1103 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { | |
1a4d82fc | 1104 | match it.node { |
e9174d1e | 1105 | hir::ItemEnum(..) => { |
1a4d82fc JJ |
1106 | let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES); |
1107 | let lvlsrc = cx.lints.get_level_source(lint_id); | |
1108 | match lvlsrc { | |
1109 | (lvl, _) if lvl != Allow => { | |
1110 | cx.node_levels.borrow_mut() | |
1111 | .insert((it.id, lint_id), lvlsrc); | |
1112 | }, | |
1113 | _ => { } | |
1114 | } | |
1115 | }, | |
1116 | _ => { } | |
1117 | } | |
1118 | } | |
1119 | } | |
1120 | ||
54a0048b | 1121 | enum CheckLintNameResult { |
92a42be0 SL |
1122 | Ok, |
1123 | // Lint doesn't exist | |
1124 | NoLint, | |
54a0048b SL |
1125 | // The lint is either renamed or removed. This is the warning |
1126 | // message. | |
1127 | Warning(String) | |
92a42be0 SL |
1128 | } |
1129 | ||
1130 | /// Checks the name of a lint for its existence, and whether it was | |
1131 | /// renamed or removed. Generates a DiagnosticBuilder containing a | |
1132 | /// warning for renamed and removed lints. This is over both lint | |
1133 | /// names from attributes and those passed on the command line. Since | |
1134 | /// it emits non-fatal warnings and there are *two* lint passes that | |
1135 | /// inspect attributes, this is only run from the late pass to avoid | |
1136 | /// printing duplicate warnings. | |
54a0048b SL |
1137 | fn check_lint_name(lint_cx: &LintStore, |
1138 | lint_name: &str) -> CheckLintNameResult { | |
92a42be0 SL |
1139 | match lint_cx.by_name.get(lint_name) { |
1140 | Some(&Renamed(ref new_name, _)) => { | |
54a0048b SL |
1141 | CheckLintNameResult::Warning( |
1142 | format!("lint {} has been renamed to {}", lint_name, new_name) | |
1143 | ) | |
92a42be0 SL |
1144 | }, |
1145 | Some(&Removed(ref reason)) => { | |
54a0048b SL |
1146 | CheckLintNameResult::Warning( |
1147 | format!("lint {} has been removed: {}", lint_name, reason) | |
1148 | ) | |
92a42be0 SL |
1149 | }, |
1150 | None => { | |
1151 | match lint_cx.lint_groups.get(lint_name) { | |
1152 | None => { | |
1153 | CheckLintNameResult::NoLint | |
1154 | } | |
1155 | Some(_) => { | |
1156 | /* lint group exists */ | |
1157 | CheckLintNameResult::Ok | |
1158 | } | |
1159 | } | |
1160 | } | |
1161 | Some(_) => { | |
1162 | /* lint exists */ | |
1163 | CheckLintNameResult::Ok | |
1164 | } | |
1165 | } | |
1166 | } | |
1167 | ||
1168 | // Checks the validity of lint names derived from attributes | |
1169 | fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) { | |
1170 | for result in gather_attr(attr) { | |
1171 | match result { | |
1172 | Err(_) => { | |
1173 | // Malformed lint attr. Reported by with_lint_attrs | |
1174 | continue; | |
1175 | } | |
1176 | Ok((lint_name, _, span)) => { | |
54a0048b SL |
1177 | match check_lint_name(&cx.lints, |
1178 | &lint_name[..]) { | |
92a42be0 | 1179 | CheckLintNameResult::Ok => (), |
54a0048b SL |
1180 | CheckLintNameResult::Warning(ref msg) => { |
1181 | cx.span_lint(builtin::RENAMED_AND_REMOVED_LINTS, | |
1182 | span, msg); | |
9cc50fc6 | 1183 | } |
92a42be0 SL |
1184 | CheckLintNameResult::NoLint => { |
1185 | cx.span_lint(builtin::UNKNOWN_LINTS, span, | |
1186 | &format!("unknown lint: `{}`", | |
1187 | lint_name)); | |
1188 | } | |
1189 | } | |
1190 | } | |
1191 | } | |
1192 | } | |
1193 | } | |
1194 | ||
1195 | // Checks the validity of lint names derived from the command line | |
1196 | fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore, | |
1197 | lint_name: &str, level: Level) { | |
54a0048b | 1198 | let db = match check_lint_name(lint_cx, lint_name) { |
9cc50fc6 | 1199 | CheckLintNameResult::Ok => None, |
54a0048b SL |
1200 | CheckLintNameResult::Warning(ref msg) => { |
1201 | Some(sess.struct_warn(msg)) | |
1202 | }, | |
92a42be0 | 1203 | CheckLintNameResult::NoLint => { |
9cc50fc6 | 1204 | Some(sess.struct_err(&format!("unknown lint: `{}`", lint_name))) |
92a42be0 SL |
1205 | } |
1206 | }; | |
1207 | ||
9cc50fc6 | 1208 | if let Some(mut db) = db { |
92a42be0 SL |
1209 | let msg = format!("requested on the command line with `{} {}`", |
1210 | match level { | |
1211 | Level::Allow => "-A", | |
1212 | Level::Warn => "-W", | |
1213 | Level::Deny => "-D", | |
1214 | Level::Forbid => "-F", | |
1215 | }, | |
1216 | lint_name); | |
9cc50fc6 SL |
1217 | db.note(&msg); |
1218 | db.emit(); | |
92a42be0 SL |
1219 | } |
1220 | } | |
1221 | ||
1222 | ||
1a4d82fc JJ |
1223 | /// Perform lint checking on a crate. |
1224 | /// | |
1225 | /// Consumes the `lint_store` field of the `Session`. | |
54a0048b | 1226 | pub fn check_crate(tcx: &TyCtxt, access_levels: &AccessLevels) { |
9cc50fc6 SL |
1227 | let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck); |
1228 | ||
92a42be0 SL |
1229 | let krate = tcx.map.krate(); |
1230 | let mut cx = LateContext::new(tcx, krate, access_levels); | |
1a4d82fc JJ |
1231 | |
1232 | // Visit the whole crate. | |
c34b1796 | 1233 | cx.with_lint_attrs(&krate.attrs, |cx| { |
1a4d82fc JJ |
1234 | cx.visit_id(ast::CRATE_NODE_ID); |
1235 | cx.visit_ids(|v| { | |
b039eaaf | 1236 | hir_visit::walk_crate(v, krate); |
1a4d82fc JJ |
1237 | }); |
1238 | ||
1239 | // since the root module isn't visited as an item (because it isn't an | |
1240 | // item), warn for it here. | |
b039eaaf | 1241 | run_lints!(cx, check_crate, late_passes, krate); |
1a4d82fc | 1242 | |
b039eaaf | 1243 | hir_visit::walk_crate(cx, krate); |
7453a54e SL |
1244 | |
1245 | run_lints!(cx, check_crate_post, late_passes, krate); | |
1a4d82fc JJ |
1246 | }); |
1247 | ||
1248 | // If we missed any lints added to the session, then there's a bug somewhere | |
1249 | // in the iteration code. | |
62682a34 | 1250 | for (id, v) in tcx.sess.lints.borrow().iter() { |
85aaf69f | 1251 | for &(lint, span, ref msg) in v { |
54a0048b SL |
1252 | span_bug!(span, |
1253 | "unprocessed lint {} at {}: {}", | |
1254 | lint.as_str(), tcx.map.node_to_string(*id), *msg) | |
1a4d82fc JJ |
1255 | } |
1256 | } | |
1257 | ||
1a4d82fc | 1258 | *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner(); |
54a0048b SL |
1259 | |
1260 | // Put the lint store back in the session. | |
1261 | mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints); | |
1a4d82fc | 1262 | } |
b039eaaf SL |
1263 | |
1264 | pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { | |
1265 | let mut cx = EarlyContext::new(sess, krate); | |
1266 | ||
1267 | // Visit the whole crate. | |
1268 | cx.with_lint_attrs(&krate.attrs, |cx| { | |
54a0048b SL |
1269 | // Lints may be assigned to the whole crate. |
1270 | if let Some(lints) = cx.sess.lints.borrow_mut().remove(&ast::CRATE_NODE_ID) { | |
1271 | for (lint_id, span, msg) in lints { | |
1272 | cx.span_lint(lint_id.lint, span, &msg[..]) | |
1273 | } | |
1274 | } | |
b039eaaf SL |
1275 | |
1276 | // since the root module isn't visited as an item (because it isn't an | |
1277 | // item), warn for it here. | |
1278 | run_lints!(cx, check_crate, early_passes, krate); | |
1279 | ||
1280 | ast_visit::walk_crate(cx, krate); | |
7453a54e SL |
1281 | |
1282 | run_lints!(cx, check_crate_post, early_passes, krate); | |
b039eaaf SL |
1283 | }); |
1284 | ||
1285 | // Put the lint store back in the session. | |
1286 | mem::replace(&mut *sess.lint_store.borrow_mut(), cx.lints); | |
1287 | ||
1288 | // If we missed any lints added to the session, then there's a bug somewhere | |
1289 | // in the iteration code. | |
1290 | for (_, v) in sess.lints.borrow().iter() { | |
1291 | for &(lint, span, ref msg) in v { | |
54a0048b | 1292 | span_bug!(span, "unprocessed lint {}: {}", lint.as_str(), *msg) |
b039eaaf SL |
1293 | } |
1294 | } | |
1295 | } |