]>
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 | ||
28 | use middle::privacy::ExportedItems; | |
29 | use middle::ty::{self, Ty}; | |
30 | use session::{early_error, Session}; | |
1a4d82fc | 31 | use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject}; |
62682a34 | 32 | use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}; |
1a4d82fc JJ |
33 | use lint::builtin; |
34 | use util::nodemap::FnvHashMap; | |
35 | ||
36 | use std::cell::RefCell; | |
c1a9b12d | 37 | use std::cmp; |
1a4d82fc JJ |
38 | use std::mem; |
39 | use syntax::ast_util::IdVisitingOperation; | |
e9174d1e SL |
40 | use rustc_front::attr::{self, AttrMetaMethods}; |
41 | use rustc_front::util; | |
1a4d82fc | 42 | use syntax::codemap::Span; |
1a4d82fc | 43 | use syntax::parse::token::InternedString; |
e9174d1e SL |
44 | use syntax::ast; |
45 | use rustc_front::hir; | |
46 | use rustc_front::visit::{self, Visitor, FnKind}; | |
47 | use syntax::visit::Visitor as SyntaxVisitor; | |
48 | use syntax::diagnostic; | |
1a4d82fc JJ |
49 | |
50 | /// Information about the registered lints. | |
51 | /// | |
52 | /// This is basically the subset of `Context` that we can | |
53 | /// build early in the compile pipeline. | |
54 | pub struct LintStore { | |
55 | /// Registered lints. The bool is true if the lint was | |
56 | /// added by a plugin. | |
57 | lints: Vec<(&'static Lint, bool)>, | |
58 | ||
59 | /// Trait objects for each lint pass. | |
60 | /// This is only `None` while iterating over the objects. See the definition | |
61 | /// of run_lints. | |
62 | passes: Option<Vec<LintPassObject>>, | |
63 | ||
64 | /// Lints indexed by name. | |
65 | by_name: FnvHashMap<String, TargetLint>, | |
66 | ||
67 | /// Current levels of each lint, and where they were set. | |
68 | levels: FnvHashMap<LintId, LevelSource>, | |
69 | ||
70 | /// Map of registered lint groups to what lints they expand to. The bool | |
71 | /// is true if the lint group was added by a plugin. | |
72 | lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>, | |
c1a9b12d SL |
73 | |
74 | /// Maximum level a lint can be | |
75 | lint_cap: Option<Level>, | |
1a4d82fc JJ |
76 | } |
77 | ||
78 | /// The targed of the `by_name` map, which accounts for renaming/deprecation. | |
79 | enum TargetLint { | |
80 | /// A direct lint target | |
81 | Id(LintId), | |
82 | ||
83 | /// Temporary renaming, used for easing migration pain; see #16545 | |
84 | Renamed(String, LintId), | |
c1a9b12d SL |
85 | |
86 | /// Lint with this name existed previously, but has been removed/deprecated. | |
87 | /// The string argument is the reason for removal. | |
88 | Removed(String), | |
89 | } | |
90 | ||
91 | enum FindLintError { | |
92 | NotFound, | |
93 | Removed | |
1a4d82fc JJ |
94 | } |
95 | ||
96 | impl LintStore { | |
97 | fn get_level_source(&self, lint: LintId) -> LevelSource { | |
98 | match self.levels.get(&lint) { | |
99 | Some(&s) => s, | |
100 | None => (Allow, Default), | |
101 | } | |
102 | } | |
103 | ||
c1a9b12d SL |
104 | fn set_level(&mut self, lint: LintId, mut lvlsrc: LevelSource) { |
105 | if let Some(cap) = self.lint_cap { | |
106 | lvlsrc.0 = cmp::min(lvlsrc.0, cap); | |
107 | } | |
1a4d82fc JJ |
108 | if lvlsrc.0 == Allow { |
109 | self.levels.remove(&lint); | |
110 | } else { | |
111 | self.levels.insert(lint, lvlsrc); | |
112 | } | |
113 | } | |
114 | ||
115 | pub fn new() -> LintStore { | |
116 | LintStore { | |
117 | lints: vec!(), | |
118 | passes: Some(vec!()), | |
85aaf69f SL |
119 | by_name: FnvHashMap(), |
120 | levels: FnvHashMap(), | |
121 | lint_groups: FnvHashMap(), | |
c1a9b12d | 122 | lint_cap: None, |
1a4d82fc JJ |
123 | } |
124 | } | |
125 | ||
126 | pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] { | |
c34b1796 | 127 | &self.lints |
1a4d82fc JJ |
128 | } |
129 | ||
130 | pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> { | |
131 | self.lint_groups.iter().map(|(k, v)| (*k, | |
132 | v.0.clone(), | |
133 | v.1)).collect() | |
134 | } | |
135 | ||
136 | pub fn register_pass(&mut self, sess: Option<&Session>, | |
137 | from_plugin: bool, pass: LintPassObject) { | |
85aaf69f | 138 | for &lint in pass.get_lints() { |
1a4d82fc JJ |
139 | self.lints.push((*lint, from_plugin)); |
140 | ||
141 | let id = LintId::of(*lint); | |
142 | if self.by_name.insert(lint.name_lower(), Id(id)).is_some() { | |
143 | let msg = format!("duplicate specification of lint {}", lint.name_lower()); | |
144 | match (sess, from_plugin) { | |
145 | // We load builtin lints first, so a duplicate is a compiler bug. | |
146 | // Use early_error when handling -W help with no crate. | |
e9174d1e | 147 | (None, _) => early_error(diagnostic::Auto, &msg[..]), |
85aaf69f | 148 | (Some(sess), false) => sess.bug(&msg[..]), |
1a4d82fc JJ |
149 | |
150 | // A duplicate name from a plugin is a user error. | |
85aaf69f | 151 | (Some(sess), true) => sess.err(&msg[..]), |
1a4d82fc JJ |
152 | } |
153 | } | |
154 | ||
155 | if lint.default_level != Allow { | |
156 | self.levels.insert(id, (lint.default_level, Default)); | |
157 | } | |
158 | } | |
159 | self.passes.as_mut().unwrap().push(pass); | |
160 | } | |
161 | ||
162 | pub fn register_group(&mut self, sess: Option<&Session>, | |
163 | from_plugin: bool, name: &'static str, | |
164 | to: Vec<LintId>) { | |
165 | let new = self.lint_groups.insert(name, (to, from_plugin)).is_none(); | |
166 | ||
167 | if !new { | |
168 | let msg = format!("duplicate specification of lint group {}", name); | |
169 | match (sess, from_plugin) { | |
170 | // We load builtin lints first, so a duplicate is a compiler bug. | |
171 | // Use early_error when handling -W help with no crate. | |
e9174d1e | 172 | (None, _) => early_error(diagnostic::Auto, &msg[..]), |
85aaf69f | 173 | (Some(sess), false) => sess.bug(&msg[..]), |
1a4d82fc JJ |
174 | |
175 | // A duplicate name from a plugin is a user error. | |
85aaf69f | 176 | (Some(sess), true) => sess.err(&msg[..]), |
1a4d82fc JJ |
177 | } |
178 | } | |
179 | } | |
180 | ||
c34b1796 | 181 | pub fn register_renamed(&mut self, old_name: &str, new_name: &str) { |
1a4d82fc JJ |
182 | let target = match self.by_name.get(new_name) { |
183 | Some(&Id(lint_id)) => lint_id.clone(), | |
184 | _ => panic!("invalid lint renaming of {} to {}", old_name, new_name) | |
185 | }; | |
186 | self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target)); | |
187 | } | |
188 | ||
c1a9b12d SL |
189 | pub fn register_removed(&mut self, name: &str, reason: &str) { |
190 | self.by_name.insert(name.into(), Removed(reason.into())); | |
191 | } | |
192 | ||
1a4d82fc JJ |
193 | #[allow(unused_variables)] |
194 | fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>) | |
c1a9b12d | 195 | -> Result<LintId, FindLintError> |
1a4d82fc JJ |
196 | { |
197 | match self.by_name.get(lint_name) { | |
c1a9b12d | 198 | Some(&Id(lint_id)) => Ok(lint_id), |
1a4d82fc JJ |
199 | Some(&Renamed(ref new_name, lint_id)) => { |
200 | let warning = format!("lint {} has been renamed to {}", | |
201 | lint_name, new_name); | |
202 | match span { | |
85aaf69f SL |
203 | Some(span) => sess.span_warn(span, &warning[..]), |
204 | None => sess.warn(&warning[..]), | |
1a4d82fc | 205 | }; |
c1a9b12d SL |
206 | Ok(lint_id) |
207 | }, | |
208 | Some(&Removed(ref reason)) => { | |
209 | let warning = format!("lint {} has been removed: {}", lint_name, reason); | |
210 | match span { | |
211 | Some(span) => sess.span_warn(span, &warning[..]), | |
212 | None => sess.warn(&warning[..]) | |
213 | } | |
214 | Err(FindLintError::Removed) | |
215 | }, | |
216 | None => Err(FindLintError::NotFound) | |
1a4d82fc JJ |
217 | } |
218 | } | |
219 | ||
220 | pub fn process_command_line(&mut self, sess: &Session) { | |
85aaf69f SL |
221 | for &(ref lint_name, level) in &sess.opts.lint_opts { |
222 | match self.find_lint(&lint_name[..], sess, None) { | |
c1a9b12d SL |
223 | Ok(lint_id) => self.set_level(lint_id, (level, CommandLine)), |
224 | Err(_) => { | |
1a4d82fc JJ |
225 | match self.lint_groups.iter().map(|(&x, pair)| (x, pair.0.clone())) |
226 | .collect::<FnvHashMap<&'static str, | |
227 | Vec<LintId>>>() | |
85aaf69f | 228 | .get(&lint_name[..]) { |
1a4d82fc JJ |
229 | Some(v) => { |
230 | v.iter() | |
231 | .map(|lint_id: &LintId| | |
232 | self.set_level(*lint_id, (level, CommandLine))) | |
233 | .collect::<Vec<()>>(); | |
234 | } | |
235 | None => sess.err(&format!("unknown {} flag: {}", | |
c34b1796 | 236 | level.as_str(), lint_name)), |
1a4d82fc JJ |
237 | } |
238 | } | |
239 | } | |
240 | } | |
c1a9b12d SL |
241 | |
242 | self.lint_cap = sess.opts.lint_cap; | |
243 | if let Some(cap) = self.lint_cap { | |
244 | for level in self.levels.iter_mut().map(|p| &mut (p.1).0) { | |
245 | *level = cmp::min(*level, cap); | |
246 | } | |
247 | } | |
1a4d82fc | 248 | } |
1a4d82fc JJ |
249 | } |
250 | ||
251 | /// Context for lint checking. | |
252 | pub struct Context<'a, 'tcx: 'a> { | |
253 | /// Type context we're checking in. | |
254 | pub tcx: &'a ty::ctxt<'tcx>, | |
255 | ||
256 | /// The crate being checked. | |
e9174d1e | 257 | pub krate: &'a hir::Crate, |
1a4d82fc JJ |
258 | |
259 | /// Items exported from the crate being checked. | |
260 | pub exported_items: &'a ExportedItems, | |
261 | ||
262 | /// The store of registered lints. | |
263 | lints: LintStore, | |
264 | ||
265 | /// When recursing into an attributed node of the ast which modifies lint | |
266 | /// levels, this stack keeps track of the previous lint levels of whatever | |
267 | /// was modified. | |
268 | level_stack: Vec<(LintId, LevelSource)>, | |
269 | ||
270 | /// Level of lints for certain NodeIds, stored here because the body of | |
271 | /// the lint needs to run in trans. | |
272 | node_levels: RefCell<FnvHashMap<(ast::NodeId, LintId), LevelSource>>, | |
273 | } | |
274 | ||
275 | /// Convenience macro for calling a `LintPass` method on every pass in the context. | |
276 | macro_rules! run_lints { ($cx:expr, $f:ident, $($args:expr),*) => ({ | |
277 | // Move the vector of passes out of `$cx` so that we can | |
278 | // iterate over it mutably while passing `$cx` to the methods. | |
279 | let mut passes = $cx.lints.passes.take().unwrap(); | |
85aaf69f | 280 | for obj in &mut passes { |
1a4d82fc JJ |
281 | obj.$f($cx, $($args),*); |
282 | } | |
283 | $cx.lints.passes = Some(passes); | |
284 | }) } | |
285 | ||
286 | /// Parse the lint attributes into a vector, with `Err`s for malformed lint | |
287 | /// attributes. Writing this as an iterator is an enormous mess. | |
e9174d1e SL |
288 | // See also the hir version just below. |
289 | pub fn gather_attrs(attrs: &[hir::Attribute]) | |
1a4d82fc JJ |
290 | -> Vec<Result<(InternedString, Level, Span), Span>> { |
291 | let mut out = vec!(); | |
85aaf69f SL |
292 | for attr in attrs { |
293 | let level = match Level::from_str(&attr.name()) { | |
1a4d82fc JJ |
294 | None => continue, |
295 | Some(lvl) => lvl, | |
296 | }; | |
297 | ||
298 | attr::mark_used(attr); | |
299 | ||
300 | let meta = &attr.node.value; | |
301 | let metas = match meta.node { | |
e9174d1e | 302 | hir::MetaList(_, ref metas) => metas, |
1a4d82fc JJ |
303 | _ => { |
304 | out.push(Err(meta.span)); | |
305 | continue; | |
306 | } | |
307 | }; | |
308 | ||
85aaf69f | 309 | for meta in metas { |
1a4d82fc | 310 | out.push(match meta.node { |
e9174d1e SL |
311 | hir::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)), |
312 | _ => Err(meta.span), | |
313 | }); | |
314 | } | |
315 | } | |
316 | out | |
317 | } | |
318 | // Copy-pasted from the above function :-( | |
319 | pub fn gather_attrs_from_hir(attrs: &[::rustc_front::hir::Attribute]) | |
320 | -> Vec<Result<(InternedString, Level, Span), Span>> { | |
321 | use ::rustc_front::attr::AttrMetaMethods; | |
322 | ||
323 | let mut out = vec!(); | |
324 | for attr in attrs { | |
325 | let level = match Level::from_str(&attr.name()) { | |
326 | None => continue, | |
327 | Some(lvl) => lvl, | |
328 | }; | |
329 | ||
330 | ::rustc_front::attr::mark_used(attr); | |
331 | ||
332 | let meta = &attr.node.value; | |
333 | let metas = match meta.node { | |
334 | ::rustc_front::hir::MetaList(_, ref metas) => metas, | |
335 | _ => { | |
336 | out.push(Err(meta.span)); | |
337 | continue; | |
338 | } | |
339 | }; | |
340 | ||
341 | for meta in metas { | |
342 | out.push(match meta.node { | |
343 | ::rustc_front::hir::MetaWord(ref lint_name) => { | |
344 | Ok((lint_name.clone(), level, meta.span)) | |
345 | } | |
1a4d82fc JJ |
346 | _ => Err(meta.span), |
347 | }); | |
348 | } | |
349 | } | |
350 | out | |
351 | } | |
352 | ||
353 | /// Emit a lint as a warning or an error (or not at all) | |
354 | /// according to `level`. | |
355 | /// | |
356 | /// This lives outside of `Context` so it can be used by checks | |
357 | /// in trans that run after the main lint pass is finished. Most | |
358 | /// lints elsewhere in the compiler should call | |
359 | /// `Session::add_lint()` instead. | |
360 | pub fn raw_emit_lint(sess: &Session, lint: &'static Lint, | |
361 | lvlsrc: LevelSource, span: Option<Span>, msg: &str) { | |
362 | let (mut level, source) = lvlsrc; | |
363 | if level == Allow { return } | |
364 | ||
365 | let name = lint.name_lower(); | |
366 | let mut def = None; | |
1a4d82fc JJ |
367 | let msg = match source { |
368 | Default => { | |
369 | format!("{}, #[{}({})] on by default", msg, | |
370 | level.as_str(), name) | |
371 | }, | |
372 | CommandLine => { | |
373 | format!("{} [-{} {}]", msg, | |
374 | match level { | |
375 | Warn => 'W', Deny => 'D', Forbid => 'F', | |
376 | Allow => panic!() | |
377 | }, name.replace("_", "-")) | |
378 | }, | |
379 | Node(src) => { | |
380 | def = Some(src); | |
381 | msg.to_string() | |
382 | } | |
1a4d82fc JJ |
383 | }; |
384 | ||
385 | // For purposes of printing, we can treat forbid as deny. | |
386 | if level == Forbid { level = Deny; } | |
387 | ||
388 | match (level, span) { | |
85aaf69f SL |
389 | (Warn, Some(sp)) => sess.span_warn(sp, &msg[..]), |
390 | (Warn, None) => sess.warn(&msg[..]), | |
391 | (Deny, Some(sp)) => sess.span_err(sp, &msg[..]), | |
392 | (Deny, None) => sess.err(&msg[..]), | |
1a4d82fc JJ |
393 | _ => sess.bug("impossible level in raw_emit_lint"), |
394 | } | |
395 | ||
85aaf69f | 396 | if let Some(span) = def { |
1a4d82fc JJ |
397 | sess.span_note(span, "lint level defined here"); |
398 | } | |
399 | } | |
400 | ||
401 | impl<'a, 'tcx> Context<'a, 'tcx> { | |
402 | fn new(tcx: &'a ty::ctxt<'tcx>, | |
e9174d1e | 403 | krate: &'a hir::Crate, |
1a4d82fc JJ |
404 | exported_items: &'a ExportedItems) -> Context<'a, 'tcx> { |
405 | // We want to own the lint store, so move it out of the session. | |
406 | let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), | |
407 | LintStore::new()); | |
408 | ||
409 | Context { | |
410 | tcx: tcx, | |
411 | krate: krate, | |
412 | exported_items: exported_items, | |
413 | lints: lint_store, | |
414 | level_stack: vec![], | |
85aaf69f | 415 | node_levels: RefCell::new(FnvHashMap()), |
1a4d82fc JJ |
416 | } |
417 | } | |
418 | ||
419 | /// Get the overall compiler `Session` object. | |
420 | pub fn sess(&'a self) -> &'a Session { | |
421 | &self.tcx.sess | |
422 | } | |
423 | ||
424 | /// Get the level of `lint` at the current position of the lint | |
425 | /// traversal. | |
426 | pub fn current_level(&self, lint: &'static Lint) -> Level { | |
427 | self.lints.levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl) | |
428 | } | |
429 | ||
430 | fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) { | |
431 | let (level, src) = match self.lints.levels.get(&LintId::of(lint)) { | |
432 | None => return, | |
433 | Some(&(Warn, src)) => { | |
434 | let lint_id = LintId::of(builtin::WARNINGS); | |
435 | (self.lints.get_level_source(lint_id).0, src) | |
436 | } | |
437 | Some(&pair) => pair, | |
438 | }; | |
439 | ||
440 | raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg); | |
441 | } | |
442 | ||
443 | /// Emit a lint at the appropriate level, with no associated span. | |
444 | pub fn lint(&self, lint: &'static Lint, msg: &str) { | |
445 | self.lookup_and_emit(lint, None, msg); | |
446 | } | |
447 | ||
448 | /// Emit a lint at the appropriate level, for a particular span. | |
449 | pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { | |
450 | self.lookup_and_emit(lint, Some(span), msg); | |
451 | } | |
452 | ||
453 | /// Merge the lints specified by any lint attributes into the | |
454 | /// current lint context, call the provided function, then reset the | |
455 | /// lints in effect to their previous state. | |
456 | fn with_lint_attrs<F>(&mut self, | |
e9174d1e | 457 | attrs: &[hir::Attribute], |
1a4d82fc JJ |
458 | f: F) where |
459 | F: FnOnce(&mut Context), | |
460 | { | |
461 | // Parse all of the lint attributes, and then add them all to the | |
462 | // current dictionary of lint information. Along the way, keep a history | |
463 | // of what we changed so we can roll everything back after invoking the | |
464 | // specified closure | |
85aaf69f | 465 | let mut pushed = 0; |
1a4d82fc | 466 | |
85aaf69f | 467 | for result in gather_attrs(attrs) { |
1a4d82fc JJ |
468 | let v = match result { |
469 | Err(span) => { | |
470 | self.tcx.sess.span_err(span, "malformed lint attribute"); | |
471 | continue; | |
472 | } | |
473 | Ok((lint_name, level, span)) => { | |
85aaf69f | 474 | match self.lints.find_lint(&lint_name, &self.tcx.sess, Some(span)) { |
c1a9b12d SL |
475 | Ok(lint_id) => vec![(lint_id, level, span)], |
476 | Err(FindLintError::NotFound) => { | |
85aaf69f | 477 | match self.lints.lint_groups.get(&lint_name[..]) { |
1a4d82fc JJ |
478 | Some(&(ref v, _)) => v.iter() |
479 | .map(|lint_id: &LintId| | |
480 | (*lint_id, level, span)) | |
481 | .collect(), | |
482 | None => { | |
483 | self.span_lint(builtin::UNKNOWN_LINTS, span, | |
85aaf69f SL |
484 | &format!("unknown `{}` attribute: `{}`", |
485 | level.as_str(), lint_name)); | |
1a4d82fc JJ |
486 | continue; |
487 | } | |
488 | } | |
c1a9b12d SL |
489 | }, |
490 | Err(FindLintError::Removed) => { continue; } | |
1a4d82fc JJ |
491 | } |
492 | } | |
493 | }; | |
494 | ||
85aaf69f | 495 | for (lint_id, level, span) in v { |
1a4d82fc JJ |
496 | let now = self.lints.get_level_source(lint_id).0; |
497 | if now == Forbid && level != Forbid { | |
498 | let lint_name = lint_id.as_str(); | |
499 | self.tcx.sess.span_err(span, | |
500 | &format!("{}({}) overruled by outer forbid({})", | |
501 | level.as_str(), lint_name, | |
c34b1796 | 502 | lint_name)); |
1a4d82fc JJ |
503 | } else if now != level { |
504 | let src = self.lints.get_level_source(lint_id).1; | |
505 | self.level_stack.push((lint_id, (now, src))); | |
506 | pushed += 1; | |
507 | self.lints.set_level(lint_id, (level, Node(span))); | |
508 | } | |
509 | } | |
510 | } | |
511 | ||
512 | run_lints!(self, enter_lint_attrs, attrs); | |
513 | f(self); | |
514 | run_lints!(self, exit_lint_attrs, attrs); | |
515 | ||
516 | // rollback | |
85aaf69f | 517 | for _ in 0..pushed { |
1a4d82fc JJ |
518 | let (lint, lvlsrc) = self.level_stack.pop().unwrap(); |
519 | self.lints.set_level(lint, lvlsrc); | |
520 | } | |
521 | } | |
522 | ||
523 | fn visit_ids<F>(&mut self, f: F) where | |
e9174d1e | 524 | F: FnOnce(&mut util::IdVisitor<Context>) |
1a4d82fc | 525 | { |
e9174d1e | 526 | let mut v = util::IdVisitor { |
1a4d82fc JJ |
527 | operation: self, |
528 | pass_through_items: false, | |
529 | visited_outermost: false, | |
530 | }; | |
531 | f(&mut v); | |
532 | } | |
533 | } | |
534 | ||
535 | impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { | |
e9174d1e | 536 | fn visit_item(&mut self, it: &hir::Item) { |
c34b1796 | 537 | self.with_lint_attrs(&it.attrs, |cx| { |
1a4d82fc JJ |
538 | run_lints!(cx, check_item, it); |
539 | cx.visit_ids(|v| v.visit_item(it)); | |
540 | visit::walk_item(cx, it); | |
541 | }) | |
542 | } | |
543 | ||
e9174d1e | 544 | fn visit_foreign_item(&mut self, it: &hir::ForeignItem) { |
c34b1796 | 545 | self.with_lint_attrs(&it.attrs, |cx| { |
1a4d82fc JJ |
546 | run_lints!(cx, check_foreign_item, it); |
547 | visit::walk_foreign_item(cx, it); | |
548 | }) | |
549 | } | |
550 | ||
e9174d1e | 551 | fn visit_pat(&mut self, p: &hir::Pat) { |
1a4d82fc JJ |
552 | run_lints!(self, check_pat, p); |
553 | visit::walk_pat(self, p); | |
554 | } | |
555 | ||
e9174d1e | 556 | fn visit_expr(&mut self, e: &hir::Expr) { |
1a4d82fc JJ |
557 | run_lints!(self, check_expr, e); |
558 | visit::walk_expr(self, e); | |
559 | } | |
560 | ||
e9174d1e | 561 | fn visit_stmt(&mut self, s: &hir::Stmt) { |
1a4d82fc JJ |
562 | run_lints!(self, check_stmt, s); |
563 | visit::walk_stmt(self, s); | |
564 | } | |
565 | ||
e9174d1e SL |
566 | fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v hir::FnDecl, |
567 | body: &'v hir::Block, span: Span, id: ast::NodeId) { | |
c34b1796 AL |
568 | run_lints!(self, check_fn, fk, decl, body, span, id); |
569 | visit::walk_fn(self, fk, decl, body, span); | |
1a4d82fc JJ |
570 | } |
571 | ||
572 | fn visit_struct_def(&mut self, | |
e9174d1e | 573 | s: &hir::StructDef, |
1a4d82fc | 574 | ident: ast::Ident, |
e9174d1e | 575 | g: &hir::Generics, |
1a4d82fc JJ |
576 | id: ast::NodeId) { |
577 | run_lints!(self, check_struct_def, s, ident, g, id); | |
578 | visit::walk_struct_def(self, s); | |
579 | run_lints!(self, check_struct_def_post, s, ident, g, id); | |
580 | } | |
581 | ||
e9174d1e | 582 | fn visit_struct_field(&mut self, s: &hir::StructField) { |
c34b1796 | 583 | self.with_lint_attrs(&s.node.attrs, |cx| { |
1a4d82fc JJ |
584 | run_lints!(cx, check_struct_field, s); |
585 | visit::walk_struct_field(cx, s); | |
586 | }) | |
587 | } | |
588 | ||
e9174d1e | 589 | fn visit_variant(&mut self, v: &hir::Variant, g: &hir::Generics) { |
c34b1796 | 590 | self.with_lint_attrs(&v.node.attrs, |cx| { |
1a4d82fc JJ |
591 | run_lints!(cx, check_variant, v, g); |
592 | visit::walk_variant(cx, v, g); | |
593 | run_lints!(cx, check_variant_post, v, g); | |
594 | }) | |
595 | } | |
596 | ||
e9174d1e | 597 | fn visit_ty(&mut self, t: &hir::Ty) { |
1a4d82fc | 598 | run_lints!(self, check_ty, t); |
c34b1796 | 599 | visit::walk_ty(self, t); |
1a4d82fc JJ |
600 | } |
601 | ||
602 | fn visit_ident(&mut self, sp: Span, id: ast::Ident) { | |
603 | run_lints!(self, check_ident, sp, id); | |
604 | } | |
605 | ||
e9174d1e | 606 | fn visit_mod(&mut self, m: &hir::Mod, s: Span, n: ast::NodeId) { |
1a4d82fc JJ |
607 | run_lints!(self, check_mod, m, s, n); |
608 | visit::walk_mod(self, m); | |
609 | } | |
610 | ||
e9174d1e | 611 | fn visit_local(&mut self, l: &hir::Local) { |
1a4d82fc JJ |
612 | run_lints!(self, check_local, l); |
613 | visit::walk_local(self, l); | |
614 | } | |
615 | ||
e9174d1e | 616 | fn visit_block(&mut self, b: &hir::Block) { |
1a4d82fc JJ |
617 | run_lints!(self, check_block, b); |
618 | visit::walk_block(self, b); | |
619 | } | |
620 | ||
e9174d1e | 621 | fn visit_arm(&mut self, a: &hir::Arm) { |
1a4d82fc JJ |
622 | run_lints!(self, check_arm, a); |
623 | visit::walk_arm(self, a); | |
624 | } | |
625 | ||
e9174d1e | 626 | fn visit_decl(&mut self, d: &hir::Decl) { |
1a4d82fc JJ |
627 | run_lints!(self, check_decl, d); |
628 | visit::walk_decl(self, d); | |
629 | } | |
630 | ||
e9174d1e | 631 | fn visit_expr_post(&mut self, e: &hir::Expr) { |
1a4d82fc JJ |
632 | run_lints!(self, check_expr_post, e); |
633 | } | |
634 | ||
e9174d1e | 635 | fn visit_generics(&mut self, g: &hir::Generics) { |
1a4d82fc JJ |
636 | run_lints!(self, check_generics, g); |
637 | visit::walk_generics(self, g); | |
638 | } | |
639 | ||
e9174d1e | 640 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { |
c34b1796 AL |
641 | self.with_lint_attrs(&trait_item.attrs, |cx| { |
642 | run_lints!(cx, check_trait_item, trait_item); | |
643 | cx.visit_ids(|v| v.visit_trait_item(trait_item)); | |
644 | visit::walk_trait_item(cx, trait_item); | |
645 | }); | |
646 | } | |
647 | ||
e9174d1e | 648 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { |
c34b1796 AL |
649 | self.with_lint_attrs(&impl_item.attrs, |cx| { |
650 | run_lints!(cx, check_impl_item, impl_item); | |
651 | cx.visit_ids(|v| v.visit_impl_item(impl_item)); | |
652 | visit::walk_impl_item(cx, impl_item); | |
653 | }); | |
1a4d82fc JJ |
654 | } |
655 | ||
e9174d1e | 656 | fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<hir::Lifetime>) { |
1a4d82fc JJ |
657 | run_lints!(self, check_opt_lifetime_ref, sp, lt); |
658 | } | |
659 | ||
e9174d1e | 660 | fn visit_lifetime_ref(&mut self, lt: &hir::Lifetime) { |
1a4d82fc JJ |
661 | run_lints!(self, check_lifetime_ref, lt); |
662 | } | |
663 | ||
e9174d1e | 664 | fn visit_lifetime_def(&mut self, lt: &hir::LifetimeDef) { |
1a4d82fc JJ |
665 | run_lints!(self, check_lifetime_def, lt); |
666 | } | |
667 | ||
e9174d1e | 668 | fn visit_explicit_self(&mut self, es: &hir::ExplicitSelf) { |
1a4d82fc JJ |
669 | run_lints!(self, check_explicit_self, es); |
670 | visit::walk_explicit_self(self, es); | |
671 | } | |
672 | ||
e9174d1e | 673 | fn visit_path(&mut self, p: &hir::Path, id: ast::NodeId) { |
1a4d82fc JJ |
674 | run_lints!(self, check_path, p, id); |
675 | visit::walk_path(self, p); | |
676 | } | |
677 | ||
e9174d1e | 678 | fn visit_attribute(&mut self, attr: &hir::Attribute) { |
1a4d82fc JJ |
679 | run_lints!(self, check_attribute, attr); |
680 | } | |
681 | } | |
682 | ||
683 | // Output any lints that were previously added to the session. | |
684 | impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> { | |
685 | fn visit_id(&mut self, id: ast::NodeId) { | |
686 | match self.tcx.sess.lints.borrow_mut().remove(&id) { | |
687 | None => {} | |
688 | Some(lints) => { | |
85aaf69f SL |
689 | for (lint_id, span, msg) in lints { |
690 | self.span_lint(lint_id.lint, span, &msg[..]) | |
1a4d82fc JJ |
691 | } |
692 | } | |
693 | } | |
694 | } | |
695 | } | |
696 | ||
697 | // This lint pass is defined here because it touches parts of the `Context` | |
698 | // that we don't want to expose. It records the lint level at certain AST | |
699 | // nodes, so that the variant size difference check in trans can call | |
700 | // `raw_emit_lint`. | |
701 | ||
c34b1796 | 702 | pub struct GatherNodeLevels; |
1a4d82fc JJ |
703 | |
704 | impl LintPass for GatherNodeLevels { | |
705 | fn get_lints(&self) -> LintArray { | |
706 | lint_array!() | |
707 | } | |
708 | ||
e9174d1e | 709 | fn check_item(&mut self, cx: &Context, it: &hir::Item) { |
1a4d82fc | 710 | match it.node { |
e9174d1e | 711 | hir::ItemEnum(..) => { |
1a4d82fc JJ |
712 | let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES); |
713 | let lvlsrc = cx.lints.get_level_source(lint_id); | |
714 | match lvlsrc { | |
715 | (lvl, _) if lvl != Allow => { | |
716 | cx.node_levels.borrow_mut() | |
717 | .insert((it.id, lint_id), lvlsrc); | |
718 | }, | |
719 | _ => { } | |
720 | } | |
721 | }, | |
722 | _ => { } | |
723 | } | |
724 | } | |
725 | } | |
726 | ||
727 | /// Perform lint checking on a crate. | |
728 | /// | |
729 | /// Consumes the `lint_store` field of the `Session`. | |
730 | pub fn check_crate(tcx: &ty::ctxt, | |
e9174d1e | 731 | krate: &hir::Crate, |
1a4d82fc JJ |
732 | exported_items: &ExportedItems) { |
733 | ||
1a4d82fc JJ |
734 | let mut cx = Context::new(tcx, krate, exported_items); |
735 | ||
736 | // Visit the whole crate. | |
c34b1796 | 737 | cx.with_lint_attrs(&krate.attrs, |cx| { |
1a4d82fc JJ |
738 | cx.visit_id(ast::CRATE_NODE_ID); |
739 | cx.visit_ids(|v| { | |
740 | v.visited_outermost = true; | |
741 | visit::walk_crate(v, krate); | |
742 | }); | |
743 | ||
744 | // since the root module isn't visited as an item (because it isn't an | |
745 | // item), warn for it here. | |
746 | run_lints!(cx, check_crate, krate); | |
747 | ||
748 | visit::walk_crate(cx, krate); | |
749 | }); | |
750 | ||
751 | // If we missed any lints added to the session, then there's a bug somewhere | |
752 | // in the iteration code. | |
62682a34 | 753 | for (id, v) in tcx.sess.lints.borrow().iter() { |
85aaf69f | 754 | for &(lint, span, ref msg) in v { |
1a4d82fc | 755 | tcx.sess.span_bug(span, |
85aaf69f SL |
756 | &format!("unprocessed lint {} at {}: {}", |
757 | lint.as_str(), tcx.map.node_to_string(*id), *msg)) | |
1a4d82fc JJ |
758 | } |
759 | } | |
760 | ||
1a4d82fc JJ |
761 | *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner(); |
762 | } |