1 // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
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.
11 //! Implementation of lint checking.
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.
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
::*;
28 use dep_graph
::DepNode
;
29 use middle
::privacy
::AccessLevels
;
31 use session
::{config, early_error, Session}
;
32 use lint
::{Level, LevelSource, Lint, LintId, LintArray, LintPass}
;
33 use lint
::{EarlyLintPass, EarlyLintPassObject, LateLintPass, LateLintPassObject}
;
34 use lint
::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid}
;
36 use util
::nodemap
::FnvHashMap
;
38 use std
::cell
::RefCell
;
40 use std
::default::Default
as StdDefault
;
42 use syntax
::attr
::{self, AttrMetaMethods}
;
43 use syntax
::codemap
::Span
;
44 use syntax
::errors
::DiagnosticBuilder
;
45 use syntax
::parse
::token
::InternedString
;
47 use syntax
::attr
::ThinAttributesExt
;
49 use hir
::intravisit
as hir_visit
;
50 use hir
::intravisit
::{IdVisitor, IdVisitingOperation}
;
51 use syntax
::visit
as ast_visit
;
53 /// Information about the registered lints.
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
)>,
62 /// Trait objects for each lint pass.
63 /// This is only `None` while iterating over the objects. See the definition
65 early_passes
: Option
<Vec
<EarlyLintPassObject
>>,
66 late_passes
: Option
<Vec
<LateLintPassObject
>>,
68 /// Lints indexed by name.
69 by_name
: FnvHashMap
<String
, TargetLint
>,
71 /// Current levels of each lint, and where they were set.
72 levels
: FnvHashMap
<LintId
, LevelSource
>,
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
)>,
78 /// Extra info for future incompatibility lints, descibing the
79 /// issue or RFC that caused the incompatibility.
80 future_incompatible
: FnvHashMap
<LintId
, FutureIncompatibleInfo
>,
82 /// Maximum level a lint can be
83 lint_cap
: Option
<Level
>,
86 /// Extra information for a future incompatibility lint. See the call
87 /// to `register_future_incompatible` in `librustc_lint/lib.rs` for
89 pub struct FutureIncompatibleInfo
{
91 pub reference
: &'
static str // e.g., a URL for an issue/PR/RFC or error code
94 /// The targed of the `by_name` map, which accounts for renaming/deprecation.
96 /// A direct lint target
99 /// Temporary renaming, used for easing migration pain; see #16545
100 Renamed(String
, LintId
),
102 /// Lint with this name existed previously, but has been removed/deprecated.
103 /// The string argument is the reason for removal.
113 fn get_level_source(&self, lint
: LintId
) -> LevelSource
{
114 match self.levels
.get(&lint
) {
116 None
=> (Allow
, Default
),
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
);
124 if lvlsrc
.0 == Allow
{
125 self.levels
.remove(&lint
);
127 self.levels
.insert(lint
, lvlsrc
);
131 pub fn new() -> LintStore
{
134 early_passes
: Some(vec
!()),
135 late_passes
: Some(vec
!()),
136 by_name
: FnvHashMap(),
137 levels
: FnvHashMap(),
138 future_incompatible
: FnvHashMap(),
139 lint_groups
: FnvHashMap(),
144 pub fn get_lints
<'t
>(&'t
self) -> &'t
[(&'
static Lint
, bool
)] {
148 pub fn get_lint_groups
<'t
>(&'t
self) -> Vec
<(&'
static str, Vec
<LintId
>, bool
)> {
149 self.lint_groups
.iter().map(|(k
, v
)| (*k
,
154 pub fn register_early_pass(&mut self,
155 sess
: Option
<&Session
>,
157 pass
: EarlyLintPassObject
) {
158 self.push_pass(sess
, from_plugin
, &pass
);
159 self.early_passes
.as_mut().unwrap().push(pass
);
162 pub fn register_late_pass(&mut self,
163 sess
: Option
<&Session
>,
165 pass
: LateLintPassObject
) {
166 self.push_pass(sess
, from_plugin
, &pass
);
167 self.late_passes
.as_mut().unwrap().push(pass
);
170 // Helper method for register_early/late_pass
171 fn push_pass
<P
: LintPass
+ ?Sized
+ '
static>(&mut self,
172 sess
: Option
<&Session
>,
175 for &lint
in pass
.get_lints() {
176 self.lints
.push((*lint
, from_plugin
));
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.
184 (None
, _
) => early_error(config
::ErrorOutputType
::default(), &msg
[..]),
185 (Some(_
), false) => bug
!("{}", msg
),
187 // A duplicate name from a plugin is a user error.
188 (Some(sess
), true) => sess
.err(&msg
[..]),
192 if lint
.default_level
!= Allow
{
193 self.levels
.insert(id
, (lint
.default_level
, Default
));
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
);
204 self.future_incompatible
.insert(info
.id
, info
);
208 pub fn future_incompatible(&self, id
: LintId
) -> Option
<&FutureIncompatibleInfo
> {
209 self.future_incompatible
.get(&id
)
212 pub fn register_group(&mut self, sess
: Option
<&Session
>,
213 from_plugin
: bool
, name
: &'
static str,
215 let new
= self.lint_groups
.insert(name
, (to
, from_plugin
)).is_none();
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.
222 (None
, _
) => early_error(config
::ErrorOutputType
::default(), &msg
[..]),
223 (Some(_
), false) => bug
!("{}", msg
),
225 // A duplicate name from a plugin is a user error.
226 (Some(sess
), true) => sess
.err(&msg
[..]),
231 pub fn register_renamed(&mut self, old_name
: &str, new_name
: &str) {
232 let target
= match self.by_name
.get(new_name
) {
233 Some(&Id(lint_id
)) => lint_id
.clone(),
234 _
=> bug
!("invalid lint renaming of {} to {}", old_name
, new_name
)
236 self.by_name
.insert(old_name
.to_string(), Renamed(new_name
.to_string(), target
));
239 pub fn register_removed(&mut self, name
: &str, reason
: &str) {
240 self.by_name
.insert(name
.into(), Removed(reason
.into()));
243 #[allow(unused_variables)]
244 fn find_lint(&self, lint_name
: &str, sess
: &Session
, span
: Option
<Span
>)
245 -> Result
<LintId
, FindLintError
>
247 match self.by_name
.get(lint_name
) {
248 Some(&Id(lint_id
)) => Ok(lint_id
),
249 Some(&Renamed(_
, lint_id
)) => {
252 Some(&Removed(ref reason
)) => {
253 Err(FindLintError
::Removed
)
255 None
=> Err(FindLintError
::NotFound
)
259 pub fn process_command_line(&mut self, sess
: &Session
) {
260 for &(ref lint_name
, level
) in &sess
.opts
.lint_opts
{
261 check_lint_name_cmdline(sess
, self,
262 &lint_name
[..], level
);
264 match self.find_lint(&lint_name
[..], sess
, None
) {
265 Ok(lint_id
) => self.set_level(lint_id
, (level
, CommandLine
)),
266 Err(FindLintError
::Removed
) => { }
268 match self.lint_groups
.iter().map(|(&x
, pair
)| (x
, pair
.0.clone()))
269 .collect
::<FnvHashMap
<&'
static str,
271 .get(&lint_name
[..]) {
274 .map(|lint_id
: &LintId
|
275 self.set_level(*lint_id
, (level
, CommandLine
)))
276 .collect
::<Vec
<()>>();
279 // The lint or lint group doesn't exist.
280 // This is an error, but it was handled
281 // by check_lint_name_cmdline.
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
);
297 /// Context for lint checking after type checking.
298 pub struct LateContext
<'a
, 'tcx
: 'a
> {
299 /// Type context we're checking in.
300 pub tcx
: &'a TyCtxt
<'tcx
>,
302 /// The crate being checked.
303 pub krate
: &'a hir
::Crate
,
305 /// Items accessible from the crate being checked.
306 pub access_levels
: &'a AccessLevels
,
308 /// The store of registered lints.
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
314 level_stack
: Vec
<(LintId
, LevelSource
)>,
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
>>,
321 /// Context for lint checking of the AST, after expansion, before lowering to
323 pub struct EarlyContext
<'a
> {
324 /// Type context we're checking in.
325 pub sess
: &'a Session
,
327 /// The crate being checked.
328 pub krate
: &'a ast
::Crate
,
330 /// The store of registered lints.
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
336 level_stack
: Vec
<(LintId
, LevelSource
)>,
339 /// Convenience macro for calling a `LintPass` method on every pass in the context.
340 macro_rules
! run_lints
{ ($cx
:expr
, $f
:ident
, $ps
:ident
, $
($args
:expr
),*) => ({
341 // Move the vector of passes out of `$cx` so that we can
342 // iterate over it mutably while passing `$cx` to the methods.
343 let mut passes
= $cx
.mut_lints().$ps
.take().unwrap();
344 for obj
in &mut passes
{
345 obj
.$
f($cx
, $
($args
),*);
347 $cx
.mut_lints().$ps
= Some(passes
);
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.
352 // See also the hir version just below.
353 pub fn gather_attrs(attrs
: &[ast
::Attribute
])
354 -> Vec
<Result
<(InternedString
, Level
, Span
), Span
>> {
355 let mut out
= vec
!();
357 let r
= gather_attr(attr
);
358 out
.extend(r
.into_iter());
363 pub fn gather_attr(attr
: &ast
::Attribute
)
364 -> Vec
<Result
<(InternedString
, Level
, Span
), Span
>> {
365 let mut out
= vec
!();
367 let level
= match Level
::from_str(&attr
.name()) {
372 attr
::mark_used(attr
);
374 let meta
= &attr
.node
.value
;
375 let metas
= match meta
.node
{
376 ast
::MetaItemKind
::List(_
, ref metas
) => metas
,
378 out
.push(Err(meta
.span
));
384 out
.push(match meta
.node
{
385 ast
::MetaItemKind
::Word(ref lint_name
) => Ok((lint_name
.clone(), level
, meta
.span
)),
393 /// Emit a lint as a warning or an error (or not at all)
394 /// according to `level`.
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.
400 pub fn raw_emit_lint(sess
: &Session
,
406 raw_struct_lint(sess
, lints
, lint
, lvlsrc
, span
, msg
).emit();
409 pub fn raw_struct_lint
<'a
>(sess
: &'a Session
,
415 -> DiagnosticBuilder
<'a
> {
416 let (mut level
, source
) = lvlsrc
;
418 return sess
.diagnostic().struct_dummy();
421 let name
= lint
.name_lower();
423 let msg
= match source
{
425 format
!("{}, #[{}({})] on by default", msg
,
426 level
.as_str(), name
)
429 format
!("{} [-{} {}]", msg
,
431 Warn
=> 'W'
, Deny
=> 'D'
, Forbid
=> 'F'
,
433 }, name
.replace("_", "-"))
441 // For purposes of printing, we can treat forbid as deny.
442 if level
== Forbid { level = Deny; }
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
[..]),
449 _
=> bug
!("impossible level in raw_emit_lint"),
452 // Check for future incompatibility lints and issue a stronger warning.
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
);
459 if let Some(sp
) = span
{
460 err
.fileline_warn(sp
, &explanation
);
461 err
.fileline_note(sp
, &citation
);
463 err
.warn(&explanation
);
468 if let Some(span
) = def
{
469 err
.span_note(span
, "lint level defined here");
475 pub trait LintContext
: Sized
{
476 fn sess(&self) -> &Session
;
477 fn lints(&self) -> &LintStore
;
478 fn mut_lints(&mut self) -> &mut LintStore
;
479 fn level_stack(&mut self) -> &mut Vec
<(LintId
, LevelSource
)>;
480 fn enter_attrs(&mut self, attrs
: &[ast
::Attribute
]);
481 fn exit_attrs(&mut self, attrs
: &[ast
::Attribute
]);
483 /// Get the level of `lint` at the current position of the lint
485 fn current_level(&self, lint
: &'
static Lint
) -> Level
{
486 self.lints().levels
.get(&LintId
::of(lint
)).map_or(Allow
, |&(lvl
, _
)| lvl
)
489 fn level_src(&self, lint
: &'
static Lint
) -> Option
<LevelSource
> {
490 self.lints().levels
.get(&LintId
::of(lint
)).map(|ls
| match ls
{
492 let lint_id
= LintId
::of(builtin
::WARNINGS
);
493 let warn_src
= self.lints().get_level_source(lint_id
);
494 if warn_src
.0 != Warn
{
504 fn lookup_and_emit(&self, lint
: &'
static Lint
, span
: Option
<Span
>, msg
: &str) {
505 let (level
, src
) = match self.level_src(lint
) {
510 raw_emit_lint(&self.sess(), self.lints(), lint
, (level
, src
), span
, msg
);
517 -> DiagnosticBuilder
{
518 let (level
, src
) = match self.level_src(lint
) {
519 None
=> return self.sess().diagnostic().struct_dummy(),
523 raw_struct_lint(&self.sess(), self.lints(), lint
, (level
, src
), span
, msg
)
526 /// Emit a lint at the appropriate level, for a particular span.
527 fn span_lint(&self, lint
: &'
static Lint
, span
: Span
, msg
: &str) {
528 self.lookup_and_emit(lint
, Some(span
), msg
);
531 fn struct_span_lint(&self,
535 -> DiagnosticBuilder
{
536 self.lookup(lint
, Some(span
), msg
)
539 /// Emit a lint and note at the appropriate level, for a particular span.
540 fn span_lint_note(&self, lint
: &'
static Lint
, span
: Span
, msg
: &str,
541 note_span
: Span
, note
: &str) {
542 let mut err
= self.lookup(lint
, Some(span
), msg
);
543 if self.current_level(lint
) != Level
::Allow
{
544 if note_span
== span
{
545 err
.fileline_note(note_span
, note
);
547 err
.span_note(note_span
, note
);
553 /// Emit a lint and help at the appropriate level, for a particular span.
554 fn span_lint_help(&self, lint
: &'
static Lint
, span
: Span
,
555 msg
: &str, help
: &str) {
556 let mut err
= self.lookup(lint
, Some(span
), msg
);
557 self.span_lint(lint
, span
, msg
);
558 if self.current_level(lint
) != Level
::Allow
{
559 err
.span_help(span
, help
);
564 /// Emit a lint at the appropriate level, with no associated span.
565 fn lint(&self, lint
: &'
static Lint
, msg
: &str) {
566 self.lookup_and_emit(lint
, None
, msg
);
569 /// Merge the lints specified by any lint attributes into the
570 /// current lint context, call the provided function, then reset the
571 /// lints in effect to their previous state.
572 fn with_lint_attrs
<F
>(&mut self,
573 attrs
: &[ast
::Attribute
],
575 where F
: FnOnce(&mut Self),
577 // Parse all of the lint attributes, and then add them all to the
578 // current dictionary of lint information. Along the way, keep a history
579 // of what we changed so we can roll everything back after invoking the
583 for result
in gather_attrs(attrs
) {
584 let v
= match result
{
586 span_err
!(self.sess(), span
, E0452
,
587 "malformed lint attribute");
590 Ok((lint_name
, level
, span
)) => {
591 match self.lints().find_lint(&lint_name
, &self.sess(), Some(span
)) {
592 Ok(lint_id
) => vec
![(lint_id
, level
, span
)],
593 Err(FindLintError
::NotFound
) => {
594 match self.lints().lint_groups
.get(&lint_name
[..]) {
595 Some(&(ref v
, _
)) => v
.iter()
596 .map(|lint_id
: &LintId
|
597 (*lint_id
, level
, span
))
600 // The lint or lint group doesn't exist.
601 // This is an error, but it was handled
602 // by check_lint_name_attribute.
607 Err(FindLintError
::Removed
) => { continue; }
612 for (lint_id
, level
, span
) in v
{
613 let now
= self.lints().get_level_source(lint_id
).0;
614 if now
== Forbid
&& level
!= Forbid
{
615 let lint_name
= lint_id
.as_str();
616 span_err
!(self.sess(), span
, E0453
,
617 "{}({}) overruled by outer forbid({})",
618 level
.as_str(), lint_name
,
620 } else if now
!= level
{
621 let src
= self.lints().get_level_source(lint_id
).1;
622 self.level_stack().push((lint_id
, (now
, src
)));
624 self.mut_lints().set_level(lint_id
, (level
, Node(span
)));
629 self.enter_attrs(attrs
);
631 self.exit_attrs(attrs
);
635 let (lint
, lvlsrc
) = self.level_stack().pop().unwrap();
636 self.mut_lints().set_level(lint
, lvlsrc
);
642 impl<'a
> EarlyContext
<'a
> {
643 fn new(sess
: &'a Session
,
644 krate
: &'a ast
::Crate
) -> EarlyContext
<'a
> {
645 // We want to own the lint store, so move it out of the session. Remember
646 // to put it back later...
647 let lint_store
= mem
::replace(&mut *sess
.lint_store
.borrow_mut(),
658 impl<'a
, 'tcx
> LateContext
<'a
, 'tcx
> {
659 fn new(tcx
: &'a TyCtxt
<'tcx
>,
660 krate
: &'a hir
::Crate
,
661 access_levels
: &'a AccessLevels
) -> LateContext
<'a
, 'tcx
> {
662 // We want to own the lint store, so move it out of the session.
663 let lint_store
= mem
::replace(&mut *tcx
.sess
.lint_store
.borrow_mut(),
669 access_levels
: access_levels
,
672 node_levels
: RefCell
::new(FnvHashMap()),
676 fn visit_ids
<F
>(&mut self, f
: F
)
677 where F
: FnOnce(&mut IdVisitor
<LateContext
>)
679 let mut v
= IdVisitor
::new(self);
684 impl<'a
, 'tcx
> LintContext
for LateContext
<'a
, 'tcx
> {
685 /// Get the overall compiler `Session` object.
686 fn sess(&self) -> &Session
{
690 fn lints(&self) -> &LintStore
{
694 fn mut_lints(&mut self) -> &mut LintStore
{
698 fn level_stack(&mut self) -> &mut Vec
<(LintId
, LevelSource
)> {
699 &mut self.level_stack
702 fn enter_attrs(&mut self, attrs
: &[ast
::Attribute
]) {
703 debug
!("late context: enter_attrs({:?})", attrs
);
704 run_lints
!(self, enter_lint_attrs
, late_passes
, attrs
);
707 fn exit_attrs(&mut self, attrs
: &[ast
::Attribute
]) {
708 debug
!("late context: exit_attrs({:?})", attrs
);
709 run_lints
!(self, exit_lint_attrs
, late_passes
, attrs
);
713 impl<'a
> LintContext
for EarlyContext
<'a
> {
714 /// Get the overall compiler `Session` object.
715 fn sess(&self) -> &Session
{
719 fn lints(&self) -> &LintStore
{
723 fn mut_lints(&mut self) -> &mut LintStore
{
727 fn level_stack(&mut self) -> &mut Vec
<(LintId
, LevelSource
)> {
728 &mut self.level_stack
731 fn enter_attrs(&mut self, attrs
: &[ast
::Attribute
]) {
732 debug
!("early context: enter_attrs({:?})", attrs
);
733 run_lints
!(self, enter_lint_attrs
, early_passes
, attrs
);
736 fn exit_attrs(&mut self, attrs
: &[ast
::Attribute
]) {
737 debug
!("early context: exit_attrs({:?})", attrs
);
738 run_lints
!(self, exit_lint_attrs
, early_passes
, attrs
);
742 impl<'a
, 'tcx
, 'v
> hir_visit
::Visitor
<'v
> for LateContext
<'a
, 'tcx
> {
743 /// Because lints are scoped lexically, we want to walk nested
744 /// items in the context of the outer item, so enable
746 fn visit_nested_item(&mut self, item
: hir
::ItemId
) {
747 self.visit_item(self.tcx
.map
.expect_item(item
.id
))
750 fn visit_item(&mut self, it
: &hir
::Item
) {
751 self.with_lint_attrs(&it
.attrs
, |cx
| {
752 run_lints
!(cx
, check_item
, late_passes
, it
);
753 cx
.visit_ids(|v
| v
.visit_item(it
));
754 hir_visit
::walk_item(cx
, it
);
755 run_lints
!(cx
, check_item_post
, late_passes
, it
);
759 fn visit_foreign_item(&mut self, it
: &hir
::ForeignItem
) {
760 self.with_lint_attrs(&it
.attrs
, |cx
| {
761 run_lints
!(cx
, check_foreign_item
, late_passes
, it
);
762 hir_visit
::walk_foreign_item(cx
, it
);
763 run_lints
!(cx
, check_foreign_item_post
, late_passes
, it
);
767 fn visit_pat(&mut self, p
: &hir
::Pat
) {
768 run_lints
!(self, check_pat
, late_passes
, p
);
769 hir_visit
::walk_pat(self, p
);
772 fn visit_expr(&mut self, e
: &hir
::Expr
) {
773 self.with_lint_attrs(e
.attrs
.as_attr_slice(), |cx
| {
774 run_lints
!(cx
, check_expr
, late_passes
, e
);
775 hir_visit
::walk_expr(cx
, e
);
779 fn visit_stmt(&mut self, s
: &hir
::Stmt
) {
780 // statement attributes are actually just attributes on one of
784 // so we keep track of lint levels there
785 run_lints
!(self, check_stmt
, late_passes
, s
);
786 hir_visit
::walk_stmt(self, s
);
789 fn visit_fn(&mut self, fk
: hir_visit
::FnKind
<'v
>, decl
: &'v hir
::FnDecl
,
790 body
: &'v hir
::Block
, span
: Span
, id
: ast
::NodeId
) {
791 run_lints
!(self, check_fn
, late_passes
, fk
, decl
, body
, span
, id
);
792 hir_visit
::walk_fn(self, fk
, decl
, body
, span
);
793 run_lints
!(self, check_fn_post
, late_passes
, fk
, decl
, body
, span
, id
);
796 fn visit_variant_data(&mut self,
797 s
: &hir
::VariantData
,
800 item_id
: ast
::NodeId
,
802 run_lints
!(self, check_struct_def
, late_passes
, s
, name
, g
, item_id
);
803 hir_visit
::walk_struct_def(self, s
);
804 run_lints
!(self, check_struct_def_post
, late_passes
, s
, name
, g
, item_id
);
807 fn visit_struct_field(&mut self, s
: &hir
::StructField
) {
808 self.with_lint_attrs(&s
.attrs
, |cx
| {
809 run_lints
!(cx
, check_struct_field
, late_passes
, s
);
810 hir_visit
::walk_struct_field(cx
, s
);
814 fn visit_variant(&mut self, v
: &hir
::Variant
, g
: &hir
::Generics
, item_id
: ast
::NodeId
) {
815 self.with_lint_attrs(&v
.node
.attrs
, |cx
| {
816 run_lints
!(cx
, check_variant
, late_passes
, v
, g
);
817 hir_visit
::walk_variant(cx
, v
, g
, item_id
);
818 run_lints
!(cx
, check_variant_post
, late_passes
, v
, g
);
822 fn visit_ty(&mut self, t
: &hir
::Ty
) {
823 run_lints
!(self, check_ty
, late_passes
, t
);
824 hir_visit
::walk_ty(self, t
);
827 fn visit_name(&mut self, sp
: Span
, name
: ast
::Name
) {
828 run_lints
!(self, check_name
, late_passes
, sp
, name
);
831 fn visit_mod(&mut self, m
: &hir
::Mod
, s
: Span
, n
: ast
::NodeId
) {
832 run_lints
!(self, check_mod
, late_passes
, m
, s
, n
);
833 hir_visit
::walk_mod(self, m
);
834 run_lints
!(self, check_mod_post
, late_passes
, m
, s
, n
);
837 fn visit_local(&mut self, l
: &hir
::Local
) {
838 self.with_lint_attrs(l
.attrs
.as_attr_slice(), |cx
| {
839 run_lints
!(cx
, check_local
, late_passes
, l
);
840 hir_visit
::walk_local(cx
, l
);
844 fn visit_block(&mut self, b
: &hir
::Block
) {
845 run_lints
!(self, check_block
, late_passes
, b
);
846 hir_visit
::walk_block(self, b
);
847 run_lints
!(self, check_block_post
, late_passes
, b
);
850 fn visit_arm(&mut self, a
: &hir
::Arm
) {
851 run_lints
!(self, check_arm
, late_passes
, a
);
852 hir_visit
::walk_arm(self, a
);
855 fn visit_decl(&mut self, d
: &hir
::Decl
) {
856 run_lints
!(self, check_decl
, late_passes
, d
);
857 hir_visit
::walk_decl(self, d
);
860 fn visit_expr_post(&mut self, e
: &hir
::Expr
) {
861 run_lints
!(self, check_expr_post
, late_passes
, e
);
864 fn visit_generics(&mut self, g
: &hir
::Generics
) {
865 run_lints
!(self, check_generics
, late_passes
, g
);
866 hir_visit
::walk_generics(self, g
);
869 fn visit_trait_item(&mut self, trait_item
: &hir
::TraitItem
) {
870 self.with_lint_attrs(&trait_item
.attrs
, |cx
| {
871 run_lints
!(cx
, check_trait_item
, late_passes
, trait_item
);
872 cx
.visit_ids(|v
| v
.visit_trait_item(trait_item
));
873 hir_visit
::walk_trait_item(cx
, trait_item
);
874 run_lints
!(cx
, check_trait_item_post
, late_passes
, trait_item
);
878 fn visit_impl_item(&mut self, impl_item
: &hir
::ImplItem
) {
879 self.with_lint_attrs(&impl_item
.attrs
, |cx
| {
880 run_lints
!(cx
, check_impl_item
, late_passes
, impl_item
);
881 cx
.visit_ids(|v
| v
.visit_impl_item(impl_item
));
882 hir_visit
::walk_impl_item(cx
, impl_item
);
883 run_lints
!(cx
, check_impl_item_post
, late_passes
, impl_item
);
887 fn visit_lifetime(&mut self, lt
: &hir
::Lifetime
) {
888 run_lints
!(self, check_lifetime
, late_passes
, lt
);
891 fn visit_lifetime_def(&mut self, lt
: &hir
::LifetimeDef
) {
892 run_lints
!(self, check_lifetime_def
, late_passes
, lt
);
895 fn visit_explicit_self(&mut self, es
: &hir
::ExplicitSelf
) {
896 run_lints
!(self, check_explicit_self
, late_passes
, es
);
897 hir_visit
::walk_explicit_self(self, es
);
900 fn visit_path(&mut self, p
: &hir
::Path
, id
: ast
::NodeId
) {
901 run_lints
!(self, check_path
, late_passes
, p
, id
);
902 hir_visit
::walk_path(self, p
);
905 fn visit_path_list_item(&mut self, prefix
: &hir
::Path
, item
: &hir
::PathListItem
) {
906 run_lints
!(self, check_path_list_item
, late_passes
, item
);
907 hir_visit
::walk_path_list_item(self, prefix
, item
);
910 fn visit_attribute(&mut self, attr
: &ast
::Attribute
) {
911 check_lint_name_attribute(self, attr
);
912 run_lints
!(self, check_attribute
, late_passes
, attr
);
916 impl<'a
, 'v
> ast_visit
::Visitor
<'v
> for EarlyContext
<'a
> {
917 fn visit_item(&mut self, it
: &ast
::Item
) {
918 self.with_lint_attrs(&it
.attrs
, |cx
| {
919 run_lints
!(cx
, check_item
, early_passes
, it
);
920 ast_visit
::walk_item(cx
, it
);
921 run_lints
!(cx
, check_item_post
, early_passes
, it
);
925 fn visit_foreign_item(&mut self, it
: &ast
::ForeignItem
) {
926 self.with_lint_attrs(&it
.attrs
, |cx
| {
927 run_lints
!(cx
, check_foreign_item
, early_passes
, it
);
928 ast_visit
::walk_foreign_item(cx
, it
);
929 run_lints
!(cx
, check_foreign_item_post
, early_passes
, it
);
933 fn visit_pat(&mut self, p
: &ast
::Pat
) {
934 run_lints
!(self, check_pat
, early_passes
, p
);
935 ast_visit
::walk_pat(self, p
);
938 fn visit_expr(&mut self, e
: &ast
::Expr
) {
939 self.with_lint_attrs(e
.attrs
.as_attr_slice(), |cx
| {
940 run_lints
!(cx
, check_expr
, early_passes
, e
);
941 ast_visit
::walk_expr(cx
, e
);
945 fn visit_stmt(&mut self, s
: &ast
::Stmt
) {
946 run_lints
!(self, check_stmt
, early_passes
, s
);
947 ast_visit
::walk_stmt(self, s
);
950 fn visit_fn(&mut self, fk
: ast_visit
::FnKind
<'v
>, decl
: &'v ast
::FnDecl
,
951 body
: &'v ast
::Block
, span
: Span
, id
: ast
::NodeId
) {
952 run_lints
!(self, check_fn
, early_passes
, fk
, decl
, body
, span
, id
);
953 ast_visit
::walk_fn(self, fk
, decl
, body
, span
);
954 run_lints
!(self, check_fn_post
, early_passes
, fk
, decl
, body
, span
, id
);
957 fn visit_variant_data(&mut self,
958 s
: &ast
::VariantData
,
961 item_id
: ast
::NodeId
,
963 run_lints
!(self, check_struct_def
, early_passes
, s
, ident
, g
, item_id
);
964 ast_visit
::walk_struct_def(self, s
);
965 run_lints
!(self, check_struct_def_post
, early_passes
, s
, ident
, g
, item_id
);
968 fn visit_struct_field(&mut self, s
: &ast
::StructField
) {
969 self.with_lint_attrs(&s
.attrs
, |cx
| {
970 run_lints
!(cx
, check_struct_field
, early_passes
, s
);
971 ast_visit
::walk_struct_field(cx
, s
);
975 fn visit_variant(&mut self, v
: &ast
::Variant
, g
: &ast
::Generics
, item_id
: ast
::NodeId
) {
976 self.with_lint_attrs(&v
.node
.attrs
, |cx
| {
977 run_lints
!(cx
, check_variant
, early_passes
, v
, g
);
978 ast_visit
::walk_variant(cx
, v
, g
, item_id
);
979 run_lints
!(cx
, check_variant_post
, early_passes
, v
, g
);
983 fn visit_ty(&mut self, t
: &ast
::Ty
) {
984 run_lints
!(self, check_ty
, early_passes
, t
);
985 ast_visit
::walk_ty(self, t
);
988 fn visit_ident(&mut self, sp
: Span
, id
: ast
::Ident
) {
989 run_lints
!(self, check_ident
, early_passes
, sp
, id
);
992 fn visit_mod(&mut self, m
: &ast
::Mod
, s
: Span
, n
: ast
::NodeId
) {
993 run_lints
!(self, check_mod
, early_passes
, m
, s
, n
);
994 ast_visit
::walk_mod(self, m
);
995 run_lints
!(self, check_mod_post
, early_passes
, m
, s
, n
);
998 fn visit_local(&mut self, l
: &ast
::Local
) {
999 self.with_lint_attrs(l
.attrs
.as_attr_slice(), |cx
| {
1000 run_lints
!(cx
, check_local
, early_passes
, l
);
1001 ast_visit
::walk_local(cx
, l
);
1005 fn visit_block(&mut self, b
: &ast
::Block
) {
1006 run_lints
!(self, check_block
, early_passes
, b
);
1007 ast_visit
::walk_block(self, b
);
1008 run_lints
!(self, check_block_post
, early_passes
, b
);
1011 fn visit_arm(&mut self, a
: &ast
::Arm
) {
1012 run_lints
!(self, check_arm
, early_passes
, a
);
1013 ast_visit
::walk_arm(self, a
);
1016 fn visit_decl(&mut self, d
: &ast
::Decl
) {
1017 run_lints
!(self, check_decl
, early_passes
, d
);
1018 ast_visit
::walk_decl(self, d
);
1021 fn visit_expr_post(&mut self, e
: &ast
::Expr
) {
1022 run_lints
!(self, check_expr_post
, early_passes
, e
);
1025 fn visit_generics(&mut self, g
: &ast
::Generics
) {
1026 run_lints
!(self, check_generics
, early_passes
, g
);
1027 ast_visit
::walk_generics(self, g
);
1030 fn visit_trait_item(&mut self, trait_item
: &ast
::TraitItem
) {
1031 self.with_lint_attrs(&trait_item
.attrs
, |cx
| {
1032 run_lints
!(cx
, check_trait_item
, early_passes
, trait_item
);
1033 ast_visit
::walk_trait_item(cx
, trait_item
);
1034 run_lints
!(cx
, check_trait_item_post
, early_passes
, trait_item
);
1038 fn visit_impl_item(&mut self, impl_item
: &ast
::ImplItem
) {
1039 self.with_lint_attrs(&impl_item
.attrs
, |cx
| {
1040 run_lints
!(cx
, check_impl_item
, early_passes
, impl_item
);
1041 ast_visit
::walk_impl_item(cx
, impl_item
);
1042 run_lints
!(cx
, check_impl_item_post
, early_passes
, impl_item
);
1046 fn visit_lifetime(&mut self, lt
: &ast
::Lifetime
) {
1047 run_lints
!(self, check_lifetime
, early_passes
, lt
);
1050 fn visit_lifetime_def(&mut self, lt
: &ast
::LifetimeDef
) {
1051 run_lints
!(self, check_lifetime_def
, early_passes
, lt
);
1054 fn visit_explicit_self(&mut self, es
: &ast
::ExplicitSelf
) {
1055 run_lints
!(self, check_explicit_self
, early_passes
, es
);
1056 ast_visit
::walk_explicit_self(self, es
);
1059 fn visit_path(&mut self, p
: &ast
::Path
, id
: ast
::NodeId
) {
1060 run_lints
!(self, check_path
, early_passes
, p
, id
);
1061 ast_visit
::walk_path(self, p
);
1064 fn visit_path_list_item(&mut self, prefix
: &ast
::Path
, item
: &ast
::PathListItem
) {
1065 run_lints
!(self, check_path_list_item
, early_passes
, item
);
1066 ast_visit
::walk_path_list_item(self, prefix
, item
);
1069 fn visit_attribute(&mut self, attr
: &ast
::Attribute
) {
1070 run_lints
!(self, check_attribute
, early_passes
, attr
);
1074 // Output any lints that were previously added to the session.
1075 impl<'a
, 'tcx
> IdVisitingOperation
for LateContext
<'a
, 'tcx
> {
1076 fn visit_id(&mut self, id
: ast
::NodeId
) {
1077 match self.sess().lints
.borrow_mut().remove(&id
) {
1080 debug
!("LateContext::visit_id: id={:?} lints={:?}", id
, lints
);
1081 for (lint_id
, span
, msg
) in lints
{
1082 self.span_lint(lint_id
.lint
, span
, &msg
[..])
1089 // This lint pass is defined here because it touches parts of the `LateContext`
1090 // that we don't want to expose. It records the lint level at certain AST
1091 // nodes, so that the variant size difference check in trans can call
1094 pub struct GatherNodeLevels
;
1096 impl LintPass
for GatherNodeLevels
{
1097 fn get_lints(&self) -> LintArray
{
1102 impl LateLintPass
for GatherNodeLevels
{
1103 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
1105 hir
::ItemEnum(..) => {
1106 let lint_id
= LintId
::of(builtin
::VARIANT_SIZE_DIFFERENCES
);
1107 let lvlsrc
= cx
.lints
.get_level_source(lint_id
);
1109 (lvl
, _
) if lvl
!= Allow
=> {
1110 cx
.node_levels
.borrow_mut()
1111 .insert((it
.id
, lint_id
), lvlsrc
);
1121 enum CheckLintNameResult
{
1123 // Lint doesn't exist
1125 // The lint is either renamed or removed. This is the warning
1130 /// Checks the name of a lint for its existence, and whether it was
1131 /// renamed or removed. Generates a DiagnosticBuilder containing a
1132 /// warning for renamed and removed lints. This is over both lint
1133 /// names from attributes and those passed on the command line. Since
1134 /// it emits non-fatal warnings and there are *two* lint passes that
1135 /// inspect attributes, this is only run from the late pass to avoid
1136 /// printing duplicate warnings.
1137 fn check_lint_name(lint_cx
: &LintStore
,
1138 lint_name
: &str) -> CheckLintNameResult
{
1139 match lint_cx
.by_name
.get(lint_name
) {
1140 Some(&Renamed(ref new_name
, _
)) => {
1141 CheckLintNameResult
::Warning(
1142 format
!("lint {} has been renamed to {}", lint_name
, new_name
)
1145 Some(&Removed(ref reason
)) => {
1146 CheckLintNameResult
::Warning(
1147 format
!("lint {} has been removed: {}", lint_name
, reason
)
1151 match lint_cx
.lint_groups
.get(lint_name
) {
1153 CheckLintNameResult
::NoLint
1156 /* lint group exists */
1157 CheckLintNameResult
::Ok
1163 CheckLintNameResult
::Ok
1168 // Checks the validity of lint names derived from attributes
1169 fn check_lint_name_attribute(cx
: &LateContext
, attr
: &ast
::Attribute
) {
1170 for result
in gather_attr(attr
) {
1173 // Malformed lint attr. Reported by with_lint_attrs
1176 Ok((lint_name
, _
, span
)) => {
1177 match check_lint_name(&cx
.lints
,
1179 CheckLintNameResult
::Ok
=> (),
1180 CheckLintNameResult
::Warning(ref msg
) => {
1181 cx
.span_lint(builtin
::RENAMED_AND_REMOVED_LINTS
,
1184 CheckLintNameResult
::NoLint
=> {
1185 cx
.span_lint(builtin
::UNKNOWN_LINTS
, span
,
1186 &format
!("unknown lint: `{}`",
1195 // Checks the validity of lint names derived from the command line
1196 fn check_lint_name_cmdline(sess
: &Session
, lint_cx
: &LintStore
,
1197 lint_name
: &str, level
: Level
) {
1198 let db
= match check_lint_name(lint_cx
, lint_name
) {
1199 CheckLintNameResult
::Ok
=> None
,
1200 CheckLintNameResult
::Warning(ref msg
) => {
1201 Some(sess
.struct_warn(msg
))
1203 CheckLintNameResult
::NoLint
=> {
1204 Some(sess
.struct_err(&format
!("unknown lint: `{}`", lint_name
)))
1208 if let Some(mut db
) = db
{
1209 let msg
= format
!("requested on the command line with `{} {}`",
1211 Level
::Allow
=> "-A",
1212 Level
::Warn
=> "-W",
1213 Level
::Deny
=> "-D",
1214 Level
::Forbid
=> "-F",
1223 /// Perform lint checking on a crate.
1225 /// Consumes the `lint_store` field of the `Session`.
1226 pub fn check_crate(tcx
: &TyCtxt
, access_levels
: &AccessLevels
) {
1227 let _task
= tcx
.dep_graph
.in_task(DepNode
::LateLintCheck
);
1229 let krate
= tcx
.map
.krate();
1230 let mut cx
= LateContext
::new(tcx
, krate
, access_levels
);
1232 // Visit the whole crate.
1233 cx
.with_lint_attrs(&krate
.attrs
, |cx
| {
1234 cx
.visit_id(ast
::CRATE_NODE_ID
);
1236 hir_visit
::walk_crate(v
, krate
);
1239 // since the root module isn't visited as an item (because it isn't an
1240 // item), warn for it here.
1241 run_lints
!(cx
, check_crate
, late_passes
, krate
);
1243 hir_visit
::walk_crate(cx
, krate
);
1245 run_lints
!(cx
, check_crate_post
, late_passes
, krate
);
1248 // If we missed any lints added to the session, then there's a bug somewhere
1249 // in the iteration code.
1250 for (id
, v
) in tcx
.sess
.lints
.borrow().iter() {
1251 for &(lint
, span
, ref msg
) in v
{
1253 "unprocessed lint {} at {}: {}",
1254 lint
.as_str(), tcx
.map
.node_to_string(*id
), *msg
)
1258 *tcx
.node_lint_levels
.borrow_mut() = cx
.node_levels
.into_inner();
1260 // Put the lint store back in the session.
1261 mem
::replace(&mut *tcx
.sess
.lint_store
.borrow_mut(), cx
.lints
);
1264 pub fn check_ast_crate(sess
: &Session
, krate
: &ast
::Crate
) {
1265 let mut cx
= EarlyContext
::new(sess
, krate
);
1267 // Visit the whole crate.
1268 cx
.with_lint_attrs(&krate
.attrs
, |cx
| {
1269 // Lints may be assigned to the whole crate.
1270 if let Some(lints
) = cx
.sess
.lints
.borrow_mut().remove(&ast
::CRATE_NODE_ID
) {
1271 for (lint_id
, span
, msg
) in lints
{
1272 cx
.span_lint(lint_id
.lint
, span
, &msg
[..])
1276 // since the root module isn't visited as an item (because it isn't an
1277 // item), warn for it here.
1278 run_lints
!(cx
, check_crate
, early_passes
, krate
);
1280 ast_visit
::walk_crate(cx
, krate
);
1282 run_lints
!(cx
, check_crate_post
, early_passes
, krate
);
1285 // Put the lint store back in the session.
1286 mem
::replace(&mut *sess
.lint_store
.borrow_mut(), cx
.lints
);
1288 // If we missed any lints added to the session, then there's a bug somewhere
1289 // in the iteration code.
1290 for (_
, v
) in sess
.lints
.borrow().iter() {
1291 for &(lint
, span
, ref msg
) in v
{
1292 span_bug
!(span
, "unprocessed lint {}: {}", lint
.as_str(), *msg
)