]>
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 | 32 | use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass}; |
a7813a04 | 33 | use lint::{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. |
a7813a04 | 300 | pub tcx: TyCtxt<'a, 'tcx, '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); | |
a7813a04 XL |
459 | err.warn(&explanation); |
460 | err.note(&citation); | |
92a42be0 SL |
461 | } |
462 | ||
85aaf69f | 463 | if let Some(span) = def { |
a7813a04 XL |
464 | let explanation = "lint level defined here"; |
465 | err.span_note(span, &explanation); | |
1a4d82fc | 466 | } |
9cc50fc6 SL |
467 | |
468 | err | |
1a4d82fc JJ |
469 | } |
470 | ||
b039eaaf SL |
471 | pub trait LintContext: Sized { |
472 | fn sess(&self) -> &Session; | |
473 | fn lints(&self) -> &LintStore; | |
474 | fn mut_lints(&mut self) -> &mut LintStore; | |
475 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)>; | |
476 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]); | |
477 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]); | |
1a4d82fc JJ |
478 | |
479 | /// Get the level of `lint` at the current position of the lint | |
480 | /// traversal. | |
b039eaaf SL |
481 | fn current_level(&self, lint: &'static Lint) -> Level { |
482 | self.lints().levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl) | |
1a4d82fc JJ |
483 | } |
484 | ||
9cc50fc6 SL |
485 | fn level_src(&self, lint: &'static Lint) -> Option<LevelSource> { |
486 | self.lints().levels.get(&LintId::of(lint)).map(|ls| match ls { | |
54a0048b | 487 | &(Warn, _) => { |
1a4d82fc | 488 | let lint_id = LintId::of(builtin::WARNINGS); |
54a0048b SL |
489 | let warn_src = self.lints().get_level_source(lint_id); |
490 | if warn_src.0 != Warn { | |
491 | warn_src | |
492 | } else { | |
493 | *ls | |
494 | } | |
1a4d82fc | 495 | } |
9cc50fc6 SL |
496 | _ => *ls |
497 | }) | |
498 | } | |
499 | ||
500 | fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) { | |
501 | let (level, src) = match self.level_src(lint) { | |
502 | None => return, | |
503 | Some(pair) => pair, | |
1a4d82fc JJ |
504 | }; |
505 | ||
92a42be0 | 506 | raw_emit_lint(&self.sess(), self.lints(), lint, (level, src), span, msg); |
1a4d82fc JJ |
507 | } |
508 | ||
9cc50fc6 SL |
509 | fn lookup(&self, |
510 | lint: &'static Lint, | |
511 | span: Option<Span>, | |
512 | msg: &str) | |
513 | -> DiagnosticBuilder { | |
514 | let (level, src) = match self.level_src(lint) { | |
515 | None => return self.sess().diagnostic().struct_dummy(), | |
516 | Some(pair) => pair, | |
517 | }; | |
518 | ||
519 | raw_struct_lint(&self.sess(), self.lints(), lint, (level, src), span, msg) | |
520 | } | |
521 | ||
1a4d82fc | 522 | /// Emit a lint at the appropriate level, for a particular span. |
b039eaaf | 523 | fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { |
1a4d82fc JJ |
524 | self.lookup_and_emit(lint, Some(span), msg); |
525 | } | |
526 | ||
9cc50fc6 SL |
527 | fn struct_span_lint(&self, |
528 | lint: &'static Lint, | |
529 | span: Span, | |
530 | msg: &str) | |
531 | -> DiagnosticBuilder { | |
532 | self.lookup(lint, Some(span), msg) | |
533 | } | |
534 | ||
b039eaaf SL |
535 | /// Emit a lint and note at the appropriate level, for a particular span. |
536 | fn span_lint_note(&self, lint: &'static Lint, span: Span, msg: &str, | |
537 | note_span: Span, note: &str) { | |
9cc50fc6 | 538 | let mut err = self.lookup(lint, Some(span), msg); |
b039eaaf SL |
539 | if self.current_level(lint) != Level::Allow { |
540 | if note_span == span { | |
a7813a04 | 541 | err.note(note); |
b039eaaf | 542 | } else { |
9cc50fc6 | 543 | err.span_note(note_span, note); |
b039eaaf SL |
544 | } |
545 | } | |
9cc50fc6 | 546 | err.emit(); |
b039eaaf SL |
547 | } |
548 | ||
549 | /// Emit a lint and help at the appropriate level, for a particular span. | |
550 | fn span_lint_help(&self, lint: &'static Lint, span: Span, | |
551 | msg: &str, help: &str) { | |
9cc50fc6 | 552 | let mut err = self.lookup(lint, Some(span), msg); |
b039eaaf SL |
553 | self.span_lint(lint, span, msg); |
554 | if self.current_level(lint) != Level::Allow { | |
9cc50fc6 | 555 | err.span_help(span, help); |
b039eaaf | 556 | } |
9cc50fc6 | 557 | err.emit(); |
b039eaaf SL |
558 | } |
559 | ||
560 | /// Emit a lint at the appropriate level, with no associated span. | |
561 | fn lint(&self, lint: &'static Lint, msg: &str) { | |
562 | self.lookup_and_emit(lint, None, msg); | |
563 | } | |
564 | ||
1a4d82fc JJ |
565 | /// Merge the lints specified by any lint attributes into the |
566 | /// current lint context, call the provided function, then reset the | |
567 | /// lints in effect to their previous state. | |
568 | fn with_lint_attrs<F>(&mut self, | |
b039eaaf SL |
569 | attrs: &[ast::Attribute], |
570 | f: F) | |
571 | where F: FnOnce(&mut Self), | |
1a4d82fc JJ |
572 | { |
573 | // Parse all of the lint attributes, and then add them all to the | |
574 | // current dictionary of lint information. Along the way, keep a history | |
575 | // of what we changed so we can roll everything back after invoking the | |
576 | // specified closure | |
85aaf69f | 577 | let mut pushed = 0; |
1a4d82fc | 578 | |
85aaf69f | 579 | for result in gather_attrs(attrs) { |
1a4d82fc JJ |
580 | let v = match result { |
581 | Err(span) => { | |
b039eaaf SL |
582 | span_err!(self.sess(), span, E0452, |
583 | "malformed lint attribute"); | |
1a4d82fc JJ |
584 | continue; |
585 | } | |
586 | Ok((lint_name, level, span)) => { | |
b039eaaf | 587 | match self.lints().find_lint(&lint_name, &self.sess(), Some(span)) { |
c1a9b12d SL |
588 | Ok(lint_id) => vec![(lint_id, level, span)], |
589 | Err(FindLintError::NotFound) => { | |
b039eaaf | 590 | match self.lints().lint_groups.get(&lint_name[..]) { |
1a4d82fc JJ |
591 | Some(&(ref v, _)) => v.iter() |
592 | .map(|lint_id: &LintId| | |
593 | (*lint_id, level, span)) | |
594 | .collect(), | |
595 | None => { | |
92a42be0 SL |
596 | // The lint or lint group doesn't exist. |
597 | // This is an error, but it was handled | |
598 | // by check_lint_name_attribute. | |
1a4d82fc JJ |
599 | continue; |
600 | } | |
601 | } | |
c1a9b12d SL |
602 | }, |
603 | Err(FindLintError::Removed) => { continue; } | |
1a4d82fc JJ |
604 | } |
605 | } | |
606 | }; | |
607 | ||
85aaf69f | 608 | for (lint_id, level, span) in v { |
b039eaaf | 609 | let now = self.lints().get_level_source(lint_id).0; |
1a4d82fc JJ |
610 | if now == Forbid && level != Forbid { |
611 | let lint_name = lint_id.as_str(); | |
b039eaaf SL |
612 | span_err!(self.sess(), span, E0453, |
613 | "{}({}) overruled by outer forbid({})", | |
614 | level.as_str(), lint_name, | |
615 | lint_name); | |
1a4d82fc | 616 | } else if now != level { |
b039eaaf SL |
617 | let src = self.lints().get_level_source(lint_id).1; |
618 | self.level_stack().push((lint_id, (now, src))); | |
1a4d82fc | 619 | pushed += 1; |
b039eaaf | 620 | self.mut_lints().set_level(lint_id, (level, Node(span))); |
1a4d82fc JJ |
621 | } |
622 | } | |
623 | } | |
624 | ||
b039eaaf | 625 | self.enter_attrs(attrs); |
1a4d82fc | 626 | f(self); |
b039eaaf | 627 | self.exit_attrs(attrs); |
1a4d82fc JJ |
628 | |
629 | // rollback | |
85aaf69f | 630 | for _ in 0..pushed { |
b039eaaf SL |
631 | let (lint, lvlsrc) = self.level_stack().pop().unwrap(); |
632 | self.mut_lints().set_level(lint, lvlsrc); | |
633 | } | |
634 | } | |
635 | } | |
636 | ||
637 | ||
638 | impl<'a> EarlyContext<'a> { | |
639 | fn new(sess: &'a Session, | |
640 | krate: &'a ast::Crate) -> EarlyContext<'a> { | |
641 | // We want to own the lint store, so move it out of the session. Remember | |
642 | // to put it back later... | |
643 | let lint_store = mem::replace(&mut *sess.lint_store.borrow_mut(), | |
644 | LintStore::new()); | |
645 | EarlyContext { | |
646 | sess: sess, | |
647 | krate: krate, | |
648 | lints: lint_store, | |
649 | level_stack: vec![], | |
650 | } | |
651 | } | |
b039eaaf SL |
652 | } |
653 | ||
654 | impl<'a, 'tcx> LateContext<'a, 'tcx> { | |
a7813a04 | 655 | fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
b039eaaf | 656 | krate: &'a hir::Crate, |
92a42be0 | 657 | access_levels: &'a AccessLevels) -> LateContext<'a, 'tcx> { |
b039eaaf SL |
658 | // We want to own the lint store, so move it out of the session. |
659 | let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), | |
660 | LintStore::new()); | |
661 | ||
662 | LateContext { | |
663 | tcx: tcx, | |
664 | krate: krate, | |
92a42be0 | 665 | access_levels: access_levels, |
b039eaaf SL |
666 | lints: lint_store, |
667 | level_stack: vec![], | |
668 | node_levels: RefCell::new(FnvHashMap()), | |
1a4d82fc JJ |
669 | } |
670 | } | |
671 | ||
b039eaaf | 672 | fn visit_ids<F>(&mut self, f: F) |
54a0048b | 673 | where F: FnOnce(&mut IdVisitor<LateContext>) |
1a4d82fc | 674 | { |
54a0048b | 675 | let mut v = IdVisitor::new(self); |
1a4d82fc JJ |
676 | f(&mut v); |
677 | } | |
678 | } | |
679 | ||
b039eaaf SL |
680 | impl<'a, 'tcx> LintContext for LateContext<'a, 'tcx> { |
681 | /// Get the overall compiler `Session` object. | |
682 | fn sess(&self) -> &Session { | |
683 | &self.tcx.sess | |
684 | } | |
685 | ||
686 | fn lints(&self) -> &LintStore { | |
687 | &self.lints | |
688 | } | |
689 | ||
690 | fn mut_lints(&mut self) -> &mut LintStore { | |
691 | &mut self.lints | |
692 | } | |
693 | ||
694 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> { | |
695 | &mut self.level_stack | |
696 | } | |
697 | ||
698 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 699 | debug!("late context: enter_attrs({:?})", attrs); |
b039eaaf SL |
700 | run_lints!(self, enter_lint_attrs, late_passes, attrs); |
701 | } | |
702 | ||
703 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 704 | debug!("late context: exit_attrs({:?})", attrs); |
b039eaaf SL |
705 | run_lints!(self, exit_lint_attrs, late_passes, attrs); |
706 | } | |
707 | } | |
708 | ||
709 | impl<'a> LintContext for EarlyContext<'a> { | |
710 | /// Get the overall compiler `Session` object. | |
711 | fn sess(&self) -> &Session { | |
712 | &self.sess | |
713 | } | |
714 | ||
715 | fn lints(&self) -> &LintStore { | |
716 | &self.lints | |
717 | } | |
718 | ||
719 | fn mut_lints(&mut self) -> &mut LintStore { | |
720 | &mut self.lints | |
721 | } | |
722 | ||
723 | fn level_stack(&mut self) -> &mut Vec<(LintId, LevelSource)> { | |
724 | &mut self.level_stack | |
725 | } | |
726 | ||
727 | fn enter_attrs(&mut self, attrs: &[ast::Attribute]) { | |
7453a54e | 728 | debug!("early context: enter_attrs({:?})", attrs); |
b039eaaf SL |
729 | run_lints!(self, enter_lint_attrs, early_passes, attrs); |
730 | } | |
731 | ||
732 | fn exit_attrs(&mut self, attrs: &[ast::Attribute]) { | |
92a42be0 | 733 | debug!("early context: exit_attrs({:?})", attrs); |
b039eaaf SL |
734 | run_lints!(self, exit_lint_attrs, early_passes, attrs); |
735 | } | |
736 | } | |
737 | ||
738 | impl<'a, 'tcx, 'v> hir_visit::Visitor<'v> for LateContext<'a, 'tcx> { | |
92a42be0 SL |
739 | /// Because lints are scoped lexically, we want to walk nested |
740 | /// items in the context of the outer item, so enable | |
741 | /// deep-walking. | |
742 | fn visit_nested_item(&mut self, item: hir::ItemId) { | |
a7813a04 XL |
743 | let tcx = self.tcx; |
744 | self.visit_item(tcx.map.expect_item(item.id)) | |
92a42be0 SL |
745 | } |
746 | ||
e9174d1e | 747 | fn visit_item(&mut self, it: &hir::Item) { |
c34b1796 | 748 | self.with_lint_attrs(&it.attrs, |cx| { |
b039eaaf | 749 | run_lints!(cx, check_item, late_passes, it); |
1a4d82fc | 750 | cx.visit_ids(|v| v.visit_item(it)); |
b039eaaf | 751 | hir_visit::walk_item(cx, it); |
7453a54e | 752 | run_lints!(cx, check_item_post, late_passes, it); |
1a4d82fc JJ |
753 | }) |
754 | } | |
755 | ||
e9174d1e | 756 | fn visit_foreign_item(&mut self, it: &hir::ForeignItem) { |
c34b1796 | 757 | self.with_lint_attrs(&it.attrs, |cx| { |
b039eaaf SL |
758 | run_lints!(cx, check_foreign_item, late_passes, it); |
759 | hir_visit::walk_foreign_item(cx, it); | |
7453a54e | 760 | run_lints!(cx, check_foreign_item_post, late_passes, it); |
1a4d82fc JJ |
761 | }) |
762 | } | |
763 | ||
e9174d1e | 764 | fn visit_pat(&mut self, p: &hir::Pat) { |
b039eaaf SL |
765 | run_lints!(self, check_pat, late_passes, p); |
766 | hir_visit::walk_pat(self, p); | |
1a4d82fc JJ |
767 | } |
768 | ||
e9174d1e | 769 | fn visit_expr(&mut self, e: &hir::Expr) { |
92a42be0 SL |
770 | self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| { |
771 | run_lints!(cx, check_expr, late_passes, e); | |
772 | hir_visit::walk_expr(cx, e); | |
773 | }) | |
1a4d82fc JJ |
774 | } |
775 | ||
e9174d1e | 776 | fn visit_stmt(&mut self, s: &hir::Stmt) { |
92a42be0 SL |
777 | // statement attributes are actually just attributes on one of |
778 | // - item | |
779 | // - local | |
780 | // - expression | |
781 | // so we keep track of lint levels there | |
b039eaaf SL |
782 | run_lints!(self, check_stmt, late_passes, s); |
783 | hir_visit::walk_stmt(self, s); | |
1a4d82fc JJ |
784 | } |
785 | ||
b039eaaf | 786 | fn visit_fn(&mut self, fk: hir_visit::FnKind<'v>, decl: &'v hir::FnDecl, |
e9174d1e | 787 | body: &'v hir::Block, span: Span, id: ast::NodeId) { |
b039eaaf SL |
788 | run_lints!(self, check_fn, late_passes, fk, decl, body, span, id); |
789 | hir_visit::walk_fn(self, fk, decl, body, span); | |
7453a54e | 790 | run_lints!(self, check_fn_post, late_passes, fk, decl, body, span, id); |
1a4d82fc JJ |
791 | } |
792 | ||
b039eaaf SL |
793 | fn visit_variant_data(&mut self, |
794 | s: &hir::VariantData, | |
795 | name: ast::Name, | |
e9174d1e | 796 | g: &hir::Generics, |
b039eaaf SL |
797 | item_id: ast::NodeId, |
798 | _: Span) { | |
799 | run_lints!(self, check_struct_def, late_passes, s, name, g, item_id); | |
800 | hir_visit::walk_struct_def(self, s); | |
801 | run_lints!(self, check_struct_def_post, late_passes, s, name, g, item_id); | |
1a4d82fc JJ |
802 | } |
803 | ||
e9174d1e | 804 | fn visit_struct_field(&mut self, s: &hir::StructField) { |
54a0048b | 805 | self.with_lint_attrs(&s.attrs, |cx| { |
b039eaaf SL |
806 | run_lints!(cx, check_struct_field, late_passes, s); |
807 | hir_visit::walk_struct_field(cx, s); | |
1a4d82fc JJ |
808 | }) |
809 | } | |
810 | ||
b039eaaf | 811 | fn visit_variant(&mut self, v: &hir::Variant, g: &hir::Generics, item_id: ast::NodeId) { |
c34b1796 | 812 | self.with_lint_attrs(&v.node.attrs, |cx| { |
b039eaaf SL |
813 | run_lints!(cx, check_variant, late_passes, v, g); |
814 | hir_visit::walk_variant(cx, v, g, item_id); | |
815 | run_lints!(cx, check_variant_post, late_passes, v, g); | |
1a4d82fc JJ |
816 | }) |
817 | } | |
818 | ||
e9174d1e | 819 | fn visit_ty(&mut self, t: &hir::Ty) { |
b039eaaf SL |
820 | run_lints!(self, check_ty, late_passes, t); |
821 | hir_visit::walk_ty(self, t); | |
1a4d82fc JJ |
822 | } |
823 | ||
b039eaaf SL |
824 | fn visit_name(&mut self, sp: Span, name: ast::Name) { |
825 | run_lints!(self, check_name, late_passes, sp, name); | |
1a4d82fc JJ |
826 | } |
827 | ||
e9174d1e | 828 | fn visit_mod(&mut self, m: &hir::Mod, s: Span, n: ast::NodeId) { |
b039eaaf SL |
829 | run_lints!(self, check_mod, late_passes, m, s, n); |
830 | hir_visit::walk_mod(self, m); | |
7453a54e | 831 | run_lints!(self, check_mod_post, late_passes, m, s, n); |
1a4d82fc JJ |
832 | } |
833 | ||
e9174d1e | 834 | fn visit_local(&mut self, l: &hir::Local) { |
92a42be0 SL |
835 | self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| { |
836 | run_lints!(cx, check_local, late_passes, l); | |
837 | hir_visit::walk_local(cx, l); | |
838 | }) | |
1a4d82fc JJ |
839 | } |
840 | ||
e9174d1e | 841 | fn visit_block(&mut self, b: &hir::Block) { |
b039eaaf SL |
842 | run_lints!(self, check_block, late_passes, b); |
843 | hir_visit::walk_block(self, b); | |
7453a54e | 844 | run_lints!(self, check_block_post, late_passes, b); |
1a4d82fc JJ |
845 | } |
846 | ||
e9174d1e | 847 | fn visit_arm(&mut self, a: &hir::Arm) { |
b039eaaf SL |
848 | run_lints!(self, check_arm, late_passes, a); |
849 | hir_visit::walk_arm(self, a); | |
1a4d82fc JJ |
850 | } |
851 | ||
e9174d1e | 852 | fn visit_decl(&mut self, d: &hir::Decl) { |
b039eaaf SL |
853 | run_lints!(self, check_decl, late_passes, d); |
854 | hir_visit::walk_decl(self, d); | |
1a4d82fc JJ |
855 | } |
856 | ||
e9174d1e | 857 | fn visit_expr_post(&mut self, e: &hir::Expr) { |
b039eaaf | 858 | run_lints!(self, check_expr_post, late_passes, e); |
1a4d82fc JJ |
859 | } |
860 | ||
e9174d1e | 861 | fn visit_generics(&mut self, g: &hir::Generics) { |
b039eaaf SL |
862 | run_lints!(self, check_generics, late_passes, g); |
863 | hir_visit::walk_generics(self, g); | |
1a4d82fc JJ |
864 | } |
865 | ||
e9174d1e | 866 | fn visit_trait_item(&mut self, trait_item: &hir::TraitItem) { |
c34b1796 | 867 | self.with_lint_attrs(&trait_item.attrs, |cx| { |
b039eaaf | 868 | run_lints!(cx, check_trait_item, late_passes, trait_item); |
c34b1796 | 869 | cx.visit_ids(|v| v.visit_trait_item(trait_item)); |
b039eaaf | 870 | hir_visit::walk_trait_item(cx, trait_item); |
7453a54e | 871 | run_lints!(cx, check_trait_item_post, late_passes, trait_item); |
c34b1796 AL |
872 | }); |
873 | } | |
874 | ||
e9174d1e | 875 | fn visit_impl_item(&mut self, impl_item: &hir::ImplItem) { |
c34b1796 | 876 | self.with_lint_attrs(&impl_item.attrs, |cx| { |
b039eaaf | 877 | run_lints!(cx, check_impl_item, late_passes, impl_item); |
c34b1796 | 878 | cx.visit_ids(|v| v.visit_impl_item(impl_item)); |
b039eaaf | 879 | hir_visit::walk_impl_item(cx, impl_item); |
7453a54e | 880 | run_lints!(cx, check_impl_item_post, late_passes, impl_item); |
c34b1796 | 881 | }); |
1a4d82fc JJ |
882 | } |
883 | ||
b039eaaf SL |
884 | fn visit_lifetime(&mut self, lt: &hir::Lifetime) { |
885 | run_lints!(self, check_lifetime, late_passes, lt); | |
1a4d82fc JJ |
886 | } |
887 | ||
e9174d1e | 888 | fn visit_lifetime_def(&mut self, lt: &hir::LifetimeDef) { |
b039eaaf | 889 | run_lints!(self, check_lifetime_def, late_passes, lt); |
1a4d82fc JJ |
890 | } |
891 | ||
e9174d1e | 892 | fn visit_path(&mut self, p: &hir::Path, id: ast::NodeId) { |
b039eaaf SL |
893 | run_lints!(self, check_path, late_passes, p, id); |
894 | hir_visit::walk_path(self, p); | |
895 | } | |
896 | ||
897 | fn visit_path_list_item(&mut self, prefix: &hir::Path, item: &hir::PathListItem) { | |
898 | run_lints!(self, check_path_list_item, late_passes, item); | |
899 | hir_visit::walk_path_list_item(self, prefix, item); | |
1a4d82fc JJ |
900 | } |
901 | ||
b039eaaf | 902 | fn visit_attribute(&mut self, attr: &ast::Attribute) { |
92a42be0 | 903 | check_lint_name_attribute(self, attr); |
b039eaaf SL |
904 | run_lints!(self, check_attribute, late_passes, attr); |
905 | } | |
906 | } | |
907 | ||
908 | impl<'a, 'v> ast_visit::Visitor<'v> for EarlyContext<'a> { | |
909 | fn visit_item(&mut self, it: &ast::Item) { | |
910 | self.with_lint_attrs(&it.attrs, |cx| { | |
911 | run_lints!(cx, check_item, early_passes, it); | |
b039eaaf | 912 | ast_visit::walk_item(cx, it); |
7453a54e | 913 | run_lints!(cx, check_item_post, early_passes, it); |
b039eaaf SL |
914 | }) |
915 | } | |
916 | ||
917 | fn visit_foreign_item(&mut self, it: &ast::ForeignItem) { | |
918 | self.with_lint_attrs(&it.attrs, |cx| { | |
919 | run_lints!(cx, check_foreign_item, early_passes, it); | |
920 | ast_visit::walk_foreign_item(cx, it); | |
7453a54e | 921 | run_lints!(cx, check_foreign_item_post, early_passes, it); |
b039eaaf SL |
922 | }) |
923 | } | |
924 | ||
925 | fn visit_pat(&mut self, p: &ast::Pat) { | |
926 | run_lints!(self, check_pat, early_passes, p); | |
927 | ast_visit::walk_pat(self, p); | |
928 | } | |
929 | ||
930 | fn visit_expr(&mut self, e: &ast::Expr) { | |
7453a54e SL |
931 | self.with_lint_attrs(e.attrs.as_attr_slice(), |cx| { |
932 | run_lints!(cx, check_expr, early_passes, e); | |
933 | ast_visit::walk_expr(cx, e); | |
934 | }) | |
b039eaaf SL |
935 | } |
936 | ||
937 | fn visit_stmt(&mut self, s: &ast::Stmt) { | |
938 | run_lints!(self, check_stmt, early_passes, s); | |
939 | ast_visit::walk_stmt(self, s); | |
940 | } | |
941 | ||
942 | fn visit_fn(&mut self, fk: ast_visit::FnKind<'v>, decl: &'v ast::FnDecl, | |
943 | body: &'v ast::Block, span: Span, id: ast::NodeId) { | |
944 | run_lints!(self, check_fn, early_passes, fk, decl, body, span, id); | |
945 | ast_visit::walk_fn(self, fk, decl, body, span); | |
7453a54e | 946 | run_lints!(self, check_fn_post, early_passes, fk, decl, body, span, id); |
b039eaaf SL |
947 | } |
948 | ||
949 | fn visit_variant_data(&mut self, | |
950 | s: &ast::VariantData, | |
951 | ident: ast::Ident, | |
952 | g: &ast::Generics, | |
953 | item_id: ast::NodeId, | |
954 | _: Span) { | |
955 | run_lints!(self, check_struct_def, early_passes, s, ident, g, item_id); | |
956 | ast_visit::walk_struct_def(self, s); | |
957 | run_lints!(self, check_struct_def_post, early_passes, s, ident, g, item_id); | |
958 | } | |
959 | ||
960 | fn visit_struct_field(&mut self, s: &ast::StructField) { | |
54a0048b | 961 | self.with_lint_attrs(&s.attrs, |cx| { |
b039eaaf SL |
962 | run_lints!(cx, check_struct_field, early_passes, s); |
963 | ast_visit::walk_struct_field(cx, s); | |
964 | }) | |
965 | } | |
966 | ||
967 | fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics, item_id: ast::NodeId) { | |
968 | self.with_lint_attrs(&v.node.attrs, |cx| { | |
969 | run_lints!(cx, check_variant, early_passes, v, g); | |
970 | ast_visit::walk_variant(cx, v, g, item_id); | |
971 | run_lints!(cx, check_variant_post, early_passes, v, g); | |
972 | }) | |
973 | } | |
974 | ||
975 | fn visit_ty(&mut self, t: &ast::Ty) { | |
976 | run_lints!(self, check_ty, early_passes, t); | |
977 | ast_visit::walk_ty(self, t); | |
978 | } | |
979 | ||
980 | fn visit_ident(&mut self, sp: Span, id: ast::Ident) { | |
981 | run_lints!(self, check_ident, early_passes, sp, id); | |
982 | } | |
983 | ||
984 | fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) { | |
985 | run_lints!(self, check_mod, early_passes, m, s, n); | |
986 | ast_visit::walk_mod(self, m); | |
7453a54e | 987 | run_lints!(self, check_mod_post, early_passes, m, s, n); |
b039eaaf SL |
988 | } |
989 | ||
990 | fn visit_local(&mut self, l: &ast::Local) { | |
7453a54e SL |
991 | self.with_lint_attrs(l.attrs.as_attr_slice(), |cx| { |
992 | run_lints!(cx, check_local, early_passes, l); | |
993 | ast_visit::walk_local(cx, l); | |
994 | }) | |
b039eaaf SL |
995 | } |
996 | ||
997 | fn visit_block(&mut self, b: &ast::Block) { | |
998 | run_lints!(self, check_block, early_passes, b); | |
999 | ast_visit::walk_block(self, b); | |
7453a54e | 1000 | run_lints!(self, check_block_post, early_passes, b); |
b039eaaf SL |
1001 | } |
1002 | ||
1003 | fn visit_arm(&mut self, a: &ast::Arm) { | |
1004 | run_lints!(self, check_arm, early_passes, a); | |
1005 | ast_visit::walk_arm(self, a); | |
1006 | } | |
1007 | ||
1008 | fn visit_decl(&mut self, d: &ast::Decl) { | |
1009 | run_lints!(self, check_decl, early_passes, d); | |
1010 | ast_visit::walk_decl(self, d); | |
1011 | } | |
1012 | ||
1013 | fn visit_expr_post(&mut self, e: &ast::Expr) { | |
1014 | run_lints!(self, check_expr_post, early_passes, e); | |
1015 | } | |
1016 | ||
1017 | fn visit_generics(&mut self, g: &ast::Generics) { | |
1018 | run_lints!(self, check_generics, early_passes, g); | |
1019 | ast_visit::walk_generics(self, g); | |
1020 | } | |
1021 | ||
1022 | fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) { | |
1023 | self.with_lint_attrs(&trait_item.attrs, |cx| { | |
1024 | run_lints!(cx, check_trait_item, early_passes, trait_item); | |
b039eaaf | 1025 | ast_visit::walk_trait_item(cx, trait_item); |
7453a54e | 1026 | run_lints!(cx, check_trait_item_post, early_passes, trait_item); |
b039eaaf SL |
1027 | }); |
1028 | } | |
1029 | ||
1030 | fn visit_impl_item(&mut self, impl_item: &ast::ImplItem) { | |
1031 | self.with_lint_attrs(&impl_item.attrs, |cx| { | |
1032 | run_lints!(cx, check_impl_item, early_passes, impl_item); | |
b039eaaf | 1033 | ast_visit::walk_impl_item(cx, impl_item); |
7453a54e | 1034 | run_lints!(cx, check_impl_item_post, early_passes, impl_item); |
b039eaaf SL |
1035 | }); |
1036 | } | |
1037 | ||
1038 | fn visit_lifetime(&mut self, lt: &ast::Lifetime) { | |
1039 | run_lints!(self, check_lifetime, early_passes, lt); | |
1040 | } | |
1041 | ||
1042 | fn visit_lifetime_def(&mut self, lt: &ast::LifetimeDef) { | |
1043 | run_lints!(self, check_lifetime_def, early_passes, lt); | |
1044 | } | |
1045 | ||
1046 | fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) { | |
1047 | run_lints!(self, check_explicit_self, early_passes, es); | |
1048 | ast_visit::walk_explicit_self(self, es); | |
1049 | } | |
1050 | ||
1051 | fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) { | |
1052 | run_lints!(self, check_path, early_passes, p, id); | |
1053 | ast_visit::walk_path(self, p); | |
1054 | } | |
1055 | ||
1056 | fn visit_path_list_item(&mut self, prefix: &ast::Path, item: &ast::PathListItem) { | |
1057 | run_lints!(self, check_path_list_item, early_passes, item); | |
1058 | ast_visit::walk_path_list_item(self, prefix, item); | |
1059 | } | |
1060 | ||
1061 | fn visit_attribute(&mut self, attr: &ast::Attribute) { | |
1062 | run_lints!(self, check_attribute, early_passes, attr); | |
1a4d82fc JJ |
1063 | } |
1064 | } | |
1065 | ||
1066 | // Output any lints that were previously added to the session. | |
b039eaaf SL |
1067 | impl<'a, 'tcx> IdVisitingOperation for LateContext<'a, 'tcx> { |
1068 | fn visit_id(&mut self, id: ast::NodeId) { | |
1069 | match self.sess().lints.borrow_mut().remove(&id) { | |
1070 | None => {} | |
1071 | Some(lints) => { | |
92a42be0 | 1072 | debug!("LateContext::visit_id: id={:?} lints={:?}", id, lints); |
b039eaaf SL |
1073 | for (lint_id, span, msg) in lints { |
1074 | self.span_lint(lint_id.lint, span, &msg[..]) | |
1075 | } | |
1076 | } | |
1077 | } | |
1078 | } | |
1079 | } | |
1a4d82fc | 1080 | |
b039eaaf | 1081 | // This lint pass is defined here because it touches parts of the `LateContext` |
1a4d82fc JJ |
1082 | // that we don't want to expose. It records the lint level at certain AST |
1083 | // nodes, so that the variant size difference check in trans can call | |
1084 | // `raw_emit_lint`. | |
1085 | ||
c34b1796 | 1086 | pub struct GatherNodeLevels; |
1a4d82fc JJ |
1087 | |
1088 | impl LintPass for GatherNodeLevels { | |
1089 | fn get_lints(&self) -> LintArray { | |
1090 | lint_array!() | |
1091 | } | |
b039eaaf | 1092 | } |
1a4d82fc | 1093 | |
b039eaaf SL |
1094 | impl LateLintPass for GatherNodeLevels { |
1095 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { | |
1a4d82fc | 1096 | match it.node { |
e9174d1e | 1097 | hir::ItemEnum(..) => { |
1a4d82fc JJ |
1098 | let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES); |
1099 | let lvlsrc = cx.lints.get_level_source(lint_id); | |
1100 | match lvlsrc { | |
1101 | (lvl, _) if lvl != Allow => { | |
1102 | cx.node_levels.borrow_mut() | |
1103 | .insert((it.id, lint_id), lvlsrc); | |
1104 | }, | |
1105 | _ => { } | |
1106 | } | |
1107 | }, | |
1108 | _ => { } | |
1109 | } | |
1110 | } | |
1111 | } | |
1112 | ||
54a0048b | 1113 | enum CheckLintNameResult { |
92a42be0 SL |
1114 | Ok, |
1115 | // Lint doesn't exist | |
1116 | NoLint, | |
54a0048b SL |
1117 | // The lint is either renamed or removed. This is the warning |
1118 | // message. | |
1119 | Warning(String) | |
92a42be0 SL |
1120 | } |
1121 | ||
1122 | /// Checks the name of a lint for its existence, and whether it was | |
1123 | /// renamed or removed. Generates a DiagnosticBuilder containing a | |
1124 | /// warning for renamed and removed lints. This is over both lint | |
1125 | /// names from attributes and those passed on the command line. Since | |
1126 | /// it emits non-fatal warnings and there are *two* lint passes that | |
1127 | /// inspect attributes, this is only run from the late pass to avoid | |
1128 | /// printing duplicate warnings. | |
54a0048b SL |
1129 | fn check_lint_name(lint_cx: &LintStore, |
1130 | lint_name: &str) -> CheckLintNameResult { | |
92a42be0 SL |
1131 | match lint_cx.by_name.get(lint_name) { |
1132 | Some(&Renamed(ref new_name, _)) => { | |
54a0048b SL |
1133 | CheckLintNameResult::Warning( |
1134 | format!("lint {} has been renamed to {}", lint_name, new_name) | |
1135 | ) | |
92a42be0 SL |
1136 | }, |
1137 | Some(&Removed(ref reason)) => { | |
54a0048b SL |
1138 | CheckLintNameResult::Warning( |
1139 | format!("lint {} has been removed: {}", lint_name, reason) | |
1140 | ) | |
92a42be0 SL |
1141 | }, |
1142 | None => { | |
1143 | match lint_cx.lint_groups.get(lint_name) { | |
1144 | None => { | |
1145 | CheckLintNameResult::NoLint | |
1146 | } | |
1147 | Some(_) => { | |
1148 | /* lint group exists */ | |
1149 | CheckLintNameResult::Ok | |
1150 | } | |
1151 | } | |
1152 | } | |
1153 | Some(_) => { | |
1154 | /* lint exists */ | |
1155 | CheckLintNameResult::Ok | |
1156 | } | |
1157 | } | |
1158 | } | |
1159 | ||
1160 | // Checks the validity of lint names derived from attributes | |
1161 | fn check_lint_name_attribute(cx: &LateContext, attr: &ast::Attribute) { | |
1162 | for result in gather_attr(attr) { | |
1163 | match result { | |
1164 | Err(_) => { | |
1165 | // Malformed lint attr. Reported by with_lint_attrs | |
1166 | continue; | |
1167 | } | |
1168 | Ok((lint_name, _, span)) => { | |
54a0048b SL |
1169 | match check_lint_name(&cx.lints, |
1170 | &lint_name[..]) { | |
92a42be0 | 1171 | CheckLintNameResult::Ok => (), |
54a0048b SL |
1172 | CheckLintNameResult::Warning(ref msg) => { |
1173 | cx.span_lint(builtin::RENAMED_AND_REMOVED_LINTS, | |
1174 | span, msg); | |
9cc50fc6 | 1175 | } |
92a42be0 SL |
1176 | CheckLintNameResult::NoLint => { |
1177 | cx.span_lint(builtin::UNKNOWN_LINTS, span, | |
1178 | &format!("unknown lint: `{}`", | |
1179 | lint_name)); | |
1180 | } | |
1181 | } | |
1182 | } | |
1183 | } | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | // Checks the validity of lint names derived from the command line | |
1188 | fn check_lint_name_cmdline(sess: &Session, lint_cx: &LintStore, | |
1189 | lint_name: &str, level: Level) { | |
54a0048b | 1190 | let db = match check_lint_name(lint_cx, lint_name) { |
9cc50fc6 | 1191 | CheckLintNameResult::Ok => None, |
54a0048b SL |
1192 | CheckLintNameResult::Warning(ref msg) => { |
1193 | Some(sess.struct_warn(msg)) | |
1194 | }, | |
92a42be0 | 1195 | CheckLintNameResult::NoLint => { |
9cc50fc6 | 1196 | Some(sess.struct_err(&format!("unknown lint: `{}`", lint_name))) |
92a42be0 SL |
1197 | } |
1198 | }; | |
1199 | ||
9cc50fc6 | 1200 | if let Some(mut db) = db { |
92a42be0 SL |
1201 | let msg = format!("requested on the command line with `{} {}`", |
1202 | match level { | |
1203 | Level::Allow => "-A", | |
1204 | Level::Warn => "-W", | |
1205 | Level::Deny => "-D", | |
1206 | Level::Forbid => "-F", | |
1207 | }, | |
1208 | lint_name); | |
9cc50fc6 SL |
1209 | db.note(&msg); |
1210 | db.emit(); | |
92a42be0 SL |
1211 | } |
1212 | } | |
1213 | ||
1214 | ||
1a4d82fc JJ |
1215 | /// Perform lint checking on a crate. |
1216 | /// | |
1217 | /// Consumes the `lint_store` field of the `Session`. | |
a7813a04 XL |
1218 | pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
1219 | access_levels: &AccessLevels) { | |
9cc50fc6 SL |
1220 | let _task = tcx.dep_graph.in_task(DepNode::LateLintCheck); |
1221 | ||
92a42be0 SL |
1222 | let krate = tcx.map.krate(); |
1223 | let mut cx = LateContext::new(tcx, krate, access_levels); | |
1a4d82fc JJ |
1224 | |
1225 | // Visit the whole crate. | |
c34b1796 | 1226 | cx.with_lint_attrs(&krate.attrs, |cx| { |
1a4d82fc JJ |
1227 | cx.visit_id(ast::CRATE_NODE_ID); |
1228 | cx.visit_ids(|v| { | |
b039eaaf | 1229 | hir_visit::walk_crate(v, krate); |
1a4d82fc JJ |
1230 | }); |
1231 | ||
1232 | // since the root module isn't visited as an item (because it isn't an | |
1233 | // item), warn for it here. | |
b039eaaf | 1234 | run_lints!(cx, check_crate, late_passes, krate); |
1a4d82fc | 1235 | |
b039eaaf | 1236 | hir_visit::walk_crate(cx, krate); |
7453a54e SL |
1237 | |
1238 | run_lints!(cx, check_crate_post, late_passes, krate); | |
1a4d82fc JJ |
1239 | }); |
1240 | ||
1241 | // If we missed any lints added to the session, then there's a bug somewhere | |
1242 | // in the iteration code. | |
62682a34 | 1243 | for (id, v) in tcx.sess.lints.borrow().iter() { |
85aaf69f | 1244 | for &(lint, span, ref msg) in v { |
54a0048b SL |
1245 | span_bug!(span, |
1246 | "unprocessed lint {} at {}: {}", | |
1247 | lint.as_str(), tcx.map.node_to_string(*id), *msg) | |
1a4d82fc JJ |
1248 | } |
1249 | } | |
1250 | ||
1a4d82fc | 1251 | *tcx.node_lint_levels.borrow_mut() = cx.node_levels.into_inner(); |
54a0048b SL |
1252 | |
1253 | // Put the lint store back in the session. | |
1254 | mem::replace(&mut *tcx.sess.lint_store.borrow_mut(), cx.lints); | |
1a4d82fc | 1255 | } |
b039eaaf SL |
1256 | |
1257 | pub fn check_ast_crate(sess: &Session, krate: &ast::Crate) { | |
1258 | let mut cx = EarlyContext::new(sess, krate); | |
1259 | ||
1260 | // Visit the whole crate. | |
1261 | cx.with_lint_attrs(&krate.attrs, |cx| { | |
54a0048b SL |
1262 | // Lints may be assigned to the whole crate. |
1263 | if let Some(lints) = cx.sess.lints.borrow_mut().remove(&ast::CRATE_NODE_ID) { | |
1264 | for (lint_id, span, msg) in lints { | |
1265 | cx.span_lint(lint_id.lint, span, &msg[..]) | |
1266 | } | |
1267 | } | |
b039eaaf SL |
1268 | |
1269 | // since the root module isn't visited as an item (because it isn't an | |
1270 | // item), warn for it here. | |
1271 | run_lints!(cx, check_crate, early_passes, krate); | |
1272 | ||
1273 | ast_visit::walk_crate(cx, krate); | |
7453a54e SL |
1274 | |
1275 | run_lints!(cx, check_crate_post, early_passes, krate); | |
b039eaaf SL |
1276 | }); |
1277 | ||
1278 | // Put the lint store back in the session. | |
1279 | mem::replace(&mut *sess.lint_store.borrow_mut(), cx.lints); | |
1280 | ||
1281 | // If we missed any lints added to the session, then there's a bug somewhere | |
1282 | // in the iteration code. | |
1283 | for (_, v) in sess.lints.borrow().iter() { | |
1284 | for &(lint, span, ref msg) in v { | |
54a0048b | 1285 | span_bug!(span, "unprocessed lint {}: {}", lint.as_str(), *msg) |
b039eaaf SL |
1286 | } |
1287 | } | |
1288 | } |