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