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