context.rs 30.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of lint checking.
//!
//! The lint checking is mostly consolidated into one pass which runs just
//! before translation to LLVM bytecode. Throughout compilation, lint warnings
//! can be added via the `add_lint` method on the Session structure. This
//! requires a span and an id of the node that the lint is being added to. The
//! lint isn't actually emitted at that time because it is unknown what the
//! actual lint level at that location is.
//!
//! To actually emit lint warnings/errors, a separate pass is used just before
//! translation. A context keeps track of the current state of all lint levels.
//! Upon entering a node of the ast which can modify the lint settings, the
//! previous lint state is pushed onto a stack and the ast is then recursed
//! upon.  As the ast is traversed, this keeps track of the current lint level
//! for all lint attributes.
S
Steven Fackler 已提交
26
use self::TargetLint::*;
27 28

use middle::privacy::ExportedItems;
29
use middle::subst;
30 31 32 33 34 35 36 37
use middle::ty;
use middle::typeck::astconv::AstConv;
use middle::typeck::infer;
use driver::session::Session;
use driver::early_error;
use lint::{Level, LevelSource, Lint, LintId, LintArray, LintPass, LintPassObject};
use lint::{Default, CommandLine, Node, Allow, Warn, Deny, Forbid};
use lint::builtin;
38
use util::nodemap::FnvHashMap;
39 40 41 42 43 44 45 46 47 48

use std::rc::Rc;
use std::cell::RefCell;
use std::tuple::Tuple2;
use std::mem;
use syntax::ast_util::IdVisitingOperation;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
use syntax::codemap::Span;
use syntax::visit::{Visitor, FnKind};
49
use syntax::parse::token::InternedString;
50 51 52 53 54 55 56 57 58 59 60 61
use syntax::{ast, ast_util, visit};

/// Information about the registered lints.
///
/// This is basically the subset of `Context` that we can
/// build early in the compile pipeline.
pub struct LintStore {
    /// Registered lints. The bool is true if the lint was
    /// added by a plugin.
    lints: Vec<(&'static Lint, bool)>,

    /// Trait objects for each lint pass.
62 63 64
    /// This is only `None` while iterating over the objects. See the definition
    /// of run_lints.
    passes: Option<Vec<LintPassObject>>,
65 66

    /// Lints indexed by name.
67
    by_name: FnvHashMap<String, TargetLint>,
68 69

    /// Current levels of each lint, and where they were set.
70
    levels: FnvHashMap<LintId, LevelSource>,
71 72 73

    /// Map of registered lint groups to what lints they expand to. The bool
    /// is true if the lint group was added by a plugin.
74
    lint_groups: FnvHashMap<&'static str, (Vec<LintId>, bool)>,
75 76
}

77 78 79 80 81 82 83 84 85
/// The targed of the `by_name` map, which accounts for renaming/deprecation.
enum TargetLint {
    /// A direct lint target
    Id(LintId),

    /// Temporary renaming, used for easing migration pain; see #16545
    Renamed(String, LintId),
}

86 87
impl LintStore {
    fn get_level_source(&self, lint: LintId) -> LevelSource {
88
        match self.levels.get(&lint) {
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
            Some(&s) => s,
            None => (Allow, Default),
        }
    }

    fn set_level(&mut self, lint: LintId, lvlsrc: LevelSource) {
        if lvlsrc.val0() == Allow {
            self.levels.remove(&lint);
        } else {
            self.levels.insert(lint, lvlsrc);
        }
    }

    pub fn new() -> LintStore {
        LintStore {
            lints: vec!(),
105
            passes: Some(vec!()),
106 107 108
            by_name: FnvHashMap::new(),
            levels: FnvHashMap::new(),
            lint_groups: FnvHashMap::new(),
109 110 111 112 113 114 115
        }
    }

    pub fn get_lints<'t>(&'t self) -> &'t [(&'static Lint, bool)] {
        self.lints.as_slice()
    }

116
    pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
117 118 119
        self.lint_groups.iter().map(|(k, v)| (*k,
                                              v.ref0().clone(),
                                              *v.ref1())).collect()
120 121
    }

122 123 124
    pub fn register_pass(&mut self, sess: Option<&Session>,
                         from_plugin: bool, pass: LintPassObject) {
        for &lint in pass.get_lints().iter() {
125
            self.lints.push((*lint, from_plugin));
126

127
            let id = LintId::of(*lint);
128
            if self.by_name.insert(lint.name_lower(), Id(id)).is_some() {
129
                let msg = format!("duplicate specification of lint {}", lint.name_lower());
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
                match (sess, from_plugin) {
                    // We load builtin lints first, so a duplicate is a compiler bug.
                    // Use early_error when handling -W help with no crate.
                    (None, _) => early_error(msg.as_slice()),
                    (Some(sess), false) => sess.bug(msg.as_slice()),

                    // A duplicate name from a plugin is a user error.
                    (Some(sess), true)  => sess.err(msg.as_slice()),
                }
            }

            if lint.default_level != Allow {
                self.levels.insert(id, (lint.default_level, Default));
            }
        }
145
        self.passes.as_mut().unwrap().push(pass);
146 147
    }

148 149 150
    pub fn register_group(&mut self, sess: Option<&Session>,
                          from_plugin: bool, name: &'static str,
                          to: Vec<LintId>) {
151
        let new = self.lint_groups.insert(name, (to, from_plugin)).is_none();
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166

        if !new {
            let msg = format!("duplicate specification of lint group {}", name);
            match (sess, from_plugin) {
                // We load builtin lints first, so a duplicate is a compiler bug.
                // Use early_error when handling -W help with no crate.
                (None, _) => early_error(msg.as_slice()),
                (Some(sess), false) => sess.bug(msg.as_slice()),

                // A duplicate name from a plugin is a user error.
                (Some(sess), true)  => sess.err(msg.as_slice()),
            }
        }
    }

167
    fn register_renamed(&mut self, old_name: &str, new_name: &str) {
J
Jorge Aparicio 已提交
168
        let target = match self.by_name.find_equiv(new_name) {
169
            Some(&Id(lint_id)) => lint_id.clone(),
S
Steve Klabnik 已提交
170
            _ => panic!("invalid lint renaming of {} to {}", old_name, new_name)
171 172 173 174
        };
        self.by_name.insert(old_name.to_string(), Renamed(new_name.to_string(), target));
    }

175
    pub fn register_builtin(&mut self, sess: Option<&Session>) {
176
        macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => (
177
            {$(
178
                self.register_pass($sess, false, box builtin::$name as LintPassObject);
179 180 181
            )*}
        ))

182 183 184 185 186 187
        macro_rules! add_builtin_with_new ( ( $sess:ident, $($name:ident),*, ) => (
            {$(
                self.register_pass($sess, false, box builtin::$name::new() as LintPassObject);
            )*}
        ))

188 189 190 191
        macro_rules! add_lint_group ( ( $sess:ident, $name:expr, $($lint:ident),* ) => (
            self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]);
        ))

192 193 194 195
        add_builtin!(sess,
                     HardwiredLints,
                     WhileTrue,
                     UnusedCasts,
A
Aaron Turon 已提交
196 197 198 199 200
                     ImproperCTypes,
                     BoxPointers,
                     UnusedAttributes,
                     PathStatements,
                     UnusedResults,
201
                     NonCamelCaseTypes,
202
                     NonSnakeCase,
A
Aaron Turon 已提交
203 204 205
                     NonUpperCaseGlobals,
                     UnusedParens,
                     UnusedImportBraces,
206
                     NonShorthandFieldPatterns,
207
                     UnusedUnsafe,
A
Aaron Turon 已提交
208
                     UnsafeBlocks,
209
                     UnusedMut,
A
Aaron Turon 已提交
210
                     UnusedAllocation,
211
                     Stability,
212 213 214
        )

        add_builtin_with_new!(sess,
215 216 217
                              TypeLimits,
                              RawPointerDeriving,
                              MissingDoc,
218 219
        )

220
        add_lint_group!(sess, "bad_style",
A
Aaron Turon 已提交
221
                        NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS)
222 223

        add_lint_group!(sess, "unused",
A
Aaron Turon 已提交
224
                        UNUSED_IMPORTS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE,
225 226
                        UNUSED_MUT, UNREACHABLE_CODE, UNUSED_MUST_USE,
                        UNUSED_UNSAFE, PATH_STATEMENTS)
227

228 229
        // We have one lint pass defined in this module.
        self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

        // Insert temporary renamings for a one-time deprecation (#16545)
        self.register_renamed("unnecessary_typecast", "unused_typecasts");
        self.register_renamed("unsigned_negate", "unsigned_negation");
        self.register_renamed("type_limits", "unused_comparisons");
        self.register_renamed("type_overflow", "overflowing_literals");
        self.register_renamed("ctypes", "improper_ctypes");
        self.register_renamed("owned_heap_memory", "box_pointers");
        self.register_renamed("unused_attribute", "unused_attributes");
        self.register_renamed("path_statement", "path_statements");
        self.register_renamed("unused_result", "unused_results");
        self.register_renamed("non_uppercase_statics", "non_upper_case_globals");
        self.register_renamed("unnecessary_parens", "unused_parens");
        self.register_renamed("unnecessary_import_braces", "unused_import_braces");
        self.register_renamed("unsafe_block", "unsafe_blocks");
        self.register_renamed("unnecessary_allocation", "unused_allocation");
        self.register_renamed("missing_doc", "missing_docs");
        self.register_renamed("unused_extern_crate", "unused_extern_crates");
        self.register_renamed("unnecessary_qualification", "unused_qualifications");
        self.register_renamed("unrecognized_lint", "unknown_lints");
        self.register_renamed("unused_variable", "unused_variables");
        self.register_renamed("dead_assignment", "unused_assignments");
        self.register_renamed("unknown_crate_type", "unknown_crate_types");
        self.register_renamed("variant_size_difference", "variant_size_differences");
        self.register_renamed("transmute_fat_ptr", "fat_ptr_transmutes");

    }

A
Alex Crichton 已提交
258
    #[allow(unused_variables)]
259 260 261
    fn find_lint(&self, lint_name: &str, sess: &Session, span: Option<Span>)
                 -> Option<LintId>
    {
J
Jorge Aparicio 已提交
262
        match self.by_name.find_equiv(lint_name) {
263 264
            Some(&Id(lint_id)) => Some(lint_id),
            Some(&Renamed(ref new_name, lint_id)) => {
265 266 267 268 269 270
                let warning = format!("lint {} has been renamed to {}",
                                      lint_name, new_name);
                match span {
                    Some(span) => sess.span_warn(span, warning.as_slice()),
                    None => sess.warn(warning.as_slice()),
                };
271 272 273 274
                Some(lint_id)
            }
            None => None
        }
275 276 277 278
    }

    pub fn process_command_line(&mut self, sess: &Session) {
        for &(ref lint_name, level) in sess.opts.lint_opts.iter() {
279 280
            match self.find_lint(lint_name.as_slice(), sess, None) {
                Some(lint_id) => self.set_level(lint_id, (level, CommandLine)),
281
                None => {
282
                    match self.lint_groups.iter().map(|(&x, pair)| (x, pair.ref0().clone()))
283 284
                                                 .collect::<FnvHashMap<&'static str,
                                                                       Vec<LintId>>>()
J
Jorge Aparicio 已提交
285
                                                 .find_equiv(lint_name.as_slice()) {
286 287 288 289 290 291 292 293 294 295
                        Some(v) => {
                            v.iter()
                             .map(|lint_id: &LintId|
                                     self.set_level(*lint_id, (level, CommandLine)))
                             .collect::<Vec<()>>();
                        }
                        None => sess.err(format!("unknown {} flag: {}",
                                                 level.as_str(), lint_name).as_slice()),
                    }
                }
296 297 298 299 300 301
            }
        }
    }
}

/// Context for lint checking.
302
pub struct Context<'a, 'tcx: 'a> {
303
    /// Type context we're checking in.
304
    pub tcx: &'a ty::ctxt<'tcx>,
305

306 307 308 309 310 311
    /// The crate being checked.
    pub krate: &'a ast::Crate,

    /// Items exported from the crate being checked.
    pub exported_items: &'a ExportedItems,

312 313 314 315 316 317 318 319 320 321
    /// The store of registered lints.
    lints: LintStore,

    /// When recursing into an attributed node of the ast which modifies lint
    /// levels, this stack keeps track of the previous lint levels of whatever
    /// was modified.
    level_stack: Vec<(LintId, LevelSource)>,

    /// Level of lints for certain NodeIds, stored here because the body of
    /// the lint needs to run in trans.
322
    node_levels: RefCell<FnvHashMap<(ast::NodeId, LintId), LevelSource>>,
323 324 325
}

/// Convenience macro for calling a `LintPass` method on every pass in the context.
326 327 328
macro_rules! run_lints ( ($cx:expr, $f:ident, $($args:expr),*) => ({
    // Move the vector of passes out of `$cx` so that we can
    // iterate over it mutably while passing `$cx` to the methods.
329
    let mut passes = $cx.lints.passes.take().unwrap();
A
Aaron Turon 已提交
330
    for obj in passes.iter_mut() {
331
        obj.$f($cx, $($args),*);
332
    }
333 334
    $cx.lints.passes = Some(passes);
}))
335

336 337 338 339 340 341 342 343 344 345 346 347 348
/// Parse the lint attributes into a vector, with `Err`s for malformed lint
/// attributes. Writing this as an iterator is an enormous mess.
pub fn gather_attrs(attrs: &[ast::Attribute])
                    -> Vec<Result<(InternedString, Level, Span), Span>> {
    let mut out = vec!();
    for attr in attrs.iter() {
        let level = match Level::from_str(attr.name().get()) {
            None => continue,
            Some(lvl) => lvl,
        };

        attr::mark_used(attr);

349
        let meta = &attr.node.value;
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
        let metas = match meta.node {
            ast::MetaList(_, ref metas) => metas,
            _ => {
                out.push(Err(meta.span));
                continue;
            }
        };

        for meta in metas.iter() {
            out.push(match meta.node {
                ast::MetaWord(ref lint_name) => Ok((lint_name.clone(), level, meta.span)),
                _ => Err(meta.span),
            });
        }
    }
    out
}

368 369 370 371 372 373 374 375 376 377 378 379
/// Emit a lint as a warning or an error (or not at all)
/// according to `level`.
///
/// This lives outside of `Context` so it can be used by checks
/// in trans that run after the main lint pass is finished. Most
/// lints elsewhere in the compiler should call
/// `Session::add_lint()` instead.
pub fn raw_emit_lint(sess: &Session, lint: &'static Lint,
                     lvlsrc: LevelSource, span: Option<Span>, msg: &str) {
    let (mut level, source) = lvlsrc;
    if level == Allow { return }

380
    let name = lint.name_lower();
381 382 383 384
    let mut note = None;
    let msg = match source {
        Default => {
            format!("{}, #[{}({})] on by default", msg,
385
                    level.as_str(), name)
386 387 388
        },
        CommandLine => {
            format!("{} [-{} {}]", msg,
389 390
                    match level {
                        Warn => 'W', Deny => 'D', Forbid => 'F',
S
Steve Klabnik 已提交
391
                        Allow => panic!()
392
                    }, name.replace("_", "-"))
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410
        },
        Node(src) => {
            note = Some(src);
            msg.to_string()
        }
    };

    // For purposes of printing, we can treat forbid as deny.
    if level == Forbid { level = Deny; }

    match (level, span) {
        (Warn, Some(sp)) => sess.span_warn(sp, msg.as_slice()),
        (Warn, None)     => sess.warn(msg.as_slice()),
        (Deny, Some(sp)) => sess.span_err(sp, msg.as_slice()),
        (Deny, None)     => sess.err(msg.as_slice()),
        _ => sess.bug("impossible level in raw_emit_lint"),
    }

A
Aaron Turon 已提交
411
    for span in note.into_iter() {
412 413 414 415
        sess.span_note(span, "lint level defined here");
    }
}

416 417
impl<'a, 'tcx> Context<'a, 'tcx> {
    fn new(tcx: &'a ty::ctxt<'tcx>,
418
           krate: &'a ast::Crate,
419
           exported_items: &'a ExportedItems) -> Context<'a, 'tcx> {
420 421
        // We want to own the lint store, so move it out of the session.
        let lint_store = mem::replace(&mut *tcx.sess.lint_store.borrow_mut(),
422
                                      LintStore::new());
423 424 425

        Context {
            tcx: tcx,
426 427 428
            krate: krate,
            exported_items: exported_items,
            lints: lint_store,
429
            level_stack: vec![],
430
            node_levels: RefCell::new(FnvHashMap::new()),
431 432 433 434 435 436 437 438
        }
    }

    /// Get the overall compiler `Session` object.
    pub fn sess(&'a self) -> &'a Session {
        &self.tcx.sess
    }

439 440 441
    /// Get the level of `lint` at the current position of the lint
    /// traversal.
    pub fn current_level(&self, lint: &'static Lint) -> Level {
442
        self.lints.levels.get(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
443 444
    }

445
    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
446
        let (level, src) = match self.lints.levels.get(&LintId::of(lint)) {
447 448
            None => return,
            Some(&(Warn, src)) => {
449
                let lint_id = LintId::of(builtin::WARNINGS);
450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
                (self.lints.get_level_source(lint_id).val0(), src)
            }
            Some(&pair) => pair,
        };

        raw_emit_lint(&self.tcx.sess, lint, (level, src), span, msg);
    }

    /// Emit a lint at the appropriate level, with no associated span.
    pub fn lint(&self, lint: &'static Lint, msg: &str) {
        self.lookup_and_emit(lint, None, msg);
    }

    /// Emit a lint at the appropriate level, for a particular span.
    pub fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) {
        self.lookup_and_emit(lint, Some(span), msg);
    }

    /**
     * Merge the lints specified by any lint attributes into the
     * current lint context, call the provided function, then reset the
     * lints in effect to their previous state.
     */
    fn with_lint_attrs(&mut self,
                       attrs: &[ast::Attribute],
                       f: |&mut Context|) {
        // Parse all of the lint attributes, and then add them all to the
        // current dictionary of lint information. Along the way, keep a history
        // of what we changed so we can roll everything back after invoking the
        // specified closure
        let mut pushed = 0u;
481

A
Aaron Turon 已提交
482
        for result in gather_attrs(attrs).into_iter() {
483
            let v = match result {
484 485 486 487 488
                Err(span) => {
                    self.tcx.sess.span_err(span, "malformed lint attribute");
                    continue;
                }
                Ok((lint_name, level, span)) => {
489 490
                    match self.lints.find_lint(lint_name.get(), &self.tcx.sess, Some(span)) {
                        Some(lint_id) => vec![(lint_id, level, span)],
491
                        None => {
J
Jorge Aparicio 已提交
492
                            match self.lints.lint_groups.find_equiv(lint_name.get()) {
493 494 495 496 497
                                Some(&(ref v, _)) => v.iter()
                                                      .map(|lint_id: &LintId|
                                                           (*lint_id, level, span))
                                                      .collect(),
                                None => {
A
Aaron Turon 已提交
498
                                    self.span_lint(builtin::UNKNOWN_LINTS, span,
499 500 501 502 503
                                               format!("unknown `{}` attribute: `{}`",
                                                       level.as_str(), lint_name).as_slice());
                                    continue;
                                }
                            }
504 505 506 507 508
                        }
                    }
                }
            };

A
Aaron Turon 已提交
509
            for (lint_id, level, span) in v.into_iter() {
510 511 512 513 514 515 516 517 518 519 520 521 522
                let now = self.lints.get_level_source(lint_id).val0();
                if now == Forbid && level != Forbid {
                    let lint_name = lint_id.as_str();
                    self.tcx.sess.span_err(span,
                                           format!("{}({}) overruled by outer forbid({})",
                                                   level.as_str(), lint_name,
                                                   lint_name).as_slice());
                } else if now != level {
                    let src = self.lints.get_level_source(lint_id).val1();
                    self.level_stack.push((lint_id, (now, src)));
                    pushed += 1;
                    self.lints.set_level(lint_id, (level, Node(span)));
                }
523 524 525 526 527 528 529 530 531 532 533 534 535 536
            }
        }

        run_lints!(self, enter_lint_attrs, attrs);
        f(self);
        run_lints!(self, exit_lint_attrs, attrs);

        // rollback
        for _ in range(0, pushed) {
            let (lint, lvlsrc) = self.level_stack.pop().unwrap();
            self.lints.set_level(lint, lvlsrc);
        }
    }

A
Ariel Ben-Yehuda 已提交
537
    fn visit_ids(&mut self, f: |&mut ast_util::IdVisitor<Context>|) {
538 539 540 541 542 543 544 545 546
        let mut v = ast_util::IdVisitor {
            operation: self,
            pass_through_items: false,
            visited_outermost: false,
        };
        f(&mut v);
    }
}

547 548
impl<'a, 'tcx> AstConv<'tcx> for Context<'a, 'tcx>{
    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
549

K
Keegan McAllister 已提交
550
    fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype {
551 552 553 554 555 556 557 558 559 560
        ty::lookup_item_type(self.tcx, id)
    }

    fn get_trait_def(&self, id: ast::DefId) -> Rc<ty::TraitDef> {
        ty::lookup_trait_def(self.tcx, id)
    }

    fn ty_infer(&self, _span: Span) -> ty::t {
        infer::new_infer_ctxt(self.tcx).next_ty_var()
    }
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580

    fn associated_types_of_trait_are_valid(&self, _: ty::t, _: ast::DefId)
                                           -> bool {
        // FIXME(pcwalton): This is wrong.
        true
    }

    fn associated_type_binding(&self,
                               _: Span,
                               _: Option<ty::t>,
                               trait_id: ast::DefId,
                               associated_type_id: ast::DefId)
                               -> ty::t {
        // FIXME(pcwalton): This is wrong.
        let trait_def = self.get_trait_def(trait_id);
        let index = ty::associated_type_parameter_index(self.tcx,
                                                        &*trait_def,
                                                        associated_type_id);
        ty::mk_param(self.tcx, subst::TypeSpace, index, associated_type_id)
    }
581 582
}

583
impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
584
    fn visit_item(&mut self, it: &ast::Item) {
585 586
        self.with_lint_attrs(it.attrs.as_slice(), |cx| {
            run_lints!(cx, check_item, it);
587 588
            cx.visit_ids(|v| v.visit_item(it));
            visit::walk_item(cx, it);
589 590 591
        })
    }

592
    fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
593 594
        self.with_lint_attrs(it.attrs.as_slice(), |cx| {
            run_lints!(cx, check_foreign_item, it);
595
            visit::walk_foreign_item(cx, it);
596 597 598
        })
    }

599
    fn visit_view_item(&mut self, i: &ast::ViewItem) {
600 601
        self.with_lint_attrs(i.attrs.as_slice(), |cx| {
            run_lints!(cx, check_view_item, i);
602 603
            cx.visit_ids(|v| v.visit_view_item(i));
            visit::walk_view_item(cx, i);
604 605 606
        })
    }

607
    fn visit_pat(&mut self, p: &ast::Pat) {
608
        run_lints!(self, check_pat, p);
609
        visit::walk_pat(self, p);
610 611
    }

612
    fn visit_expr(&mut self, e: &ast::Expr) {
613
        run_lints!(self, check_expr, e);
614
        visit::walk_expr(self, e);
615 616
    }

617
    fn visit_stmt(&mut self, s: &ast::Stmt) {
618
        run_lints!(self, check_stmt, s);
619
        visit::walk_stmt(self, s);
620 621
    }

622 623 624
    fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v ast::FnDecl,
                body: &'v ast::Block, span: Span, id: ast::NodeId) {
        match fk {
625 626 627 628
            visit::FkMethod(_, _, m) => {
                self.with_lint_attrs(m.attrs.as_slice(), |cx| {
                    run_lints!(cx, check_fn, fk, decl, body, span, id);
                    cx.visit_ids(|v| {
629
                        v.visit_fn(fk, decl, body, span, id);
630
                    });
631
                    visit::walk_fn(cx, fk, decl, body, span);
632 633 634 635
                })
            },
            _ => {
                run_lints!(self, check_fn, fk, decl, body, span, id);
636
                visit::walk_fn(self, fk, decl, body, span);
637 638 639 640
            }
        }
    }

641
    fn visit_ty_method(&mut self, t: &ast::TypeMethod) {
642 643
        self.with_lint_attrs(t.attrs.as_slice(), |cx| {
            run_lints!(cx, check_ty_method, t);
644
            visit::walk_ty_method(cx, t);
645 646 647 648 649 650 651
        })
    }

    fn visit_struct_def(&mut self,
                        s: &ast::StructDef,
                        ident: ast::Ident,
                        g: &ast::Generics,
652
                        id: ast::NodeId) {
653
        run_lints!(self, check_struct_def, s, ident, g, id);
654
        visit::walk_struct_def(self, s);
655 656 657
        run_lints!(self, check_struct_def_post, s, ident, g, id);
    }

658
    fn visit_struct_field(&mut self, s: &ast::StructField) {
659 660
        self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
            run_lints!(cx, check_struct_field, s);
661
            visit::walk_struct_field(cx, s);
662 663 664
        })
    }

665
    fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) {
666 667
        self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
            run_lints!(cx, check_variant, v, g);
668
            visit::walk_variant(cx, v, g);
S
Steven Fackler 已提交
669
            run_lints!(cx, check_variant_post, v, g);
670 671 672 673
        })
    }

    // FIXME(#10894) should continue recursing
674
    fn visit_ty(&mut self, t: &ast::Ty) {
675 676 677
        run_lints!(self, check_ty, t);
    }

678
    fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
679 680 681
        run_lints!(self, check_ident, sp, id);
    }

682
    fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
683
        run_lints!(self, check_mod, m, s, n);
684
        visit::walk_mod(self, m);
685 686
    }

687
    fn visit_local(&mut self, l: &ast::Local) {
688
        run_lints!(self, check_local, l);
689
        visit::walk_local(self, l);
690 691
    }

692
    fn visit_block(&mut self, b: &ast::Block) {
693
        run_lints!(self, check_block, b);
694
        visit::walk_block(self, b);
695 696
    }

697
    fn visit_arm(&mut self, a: &ast::Arm) {
698
        run_lints!(self, check_arm, a);
699
        visit::walk_arm(self, a);
700 701
    }

702
    fn visit_decl(&mut self, d: &ast::Decl) {
703
        run_lints!(self, check_decl, d);
704
        visit::walk_decl(self, d);
705 706
    }

707
    fn visit_expr_post(&mut self, e: &ast::Expr) {
708 709 710
        run_lints!(self, check_expr_post, e);
    }

711
    fn visit_generics(&mut self, g: &ast::Generics) {
712
        run_lints!(self, check_generics, g);
713
        visit::walk_generics(self, g);
714 715
    }

716
    fn visit_trait_item(&mut self, m: &ast::TraitItem) {
717
        run_lints!(self, check_trait_method, m);
718
        visit::walk_trait_item(self, m);
719 720
    }

721
    fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>) {
722 723 724
        run_lints!(self, check_opt_lifetime_ref, sp, lt);
    }

725
    fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime) {
726 727 728
        run_lints!(self, check_lifetime_ref, lt);
    }

729
    fn visit_lifetime_decl(&mut self, lt: &ast::LifetimeDef) {
730 731 732
        run_lints!(self, check_lifetime_decl, lt);
    }

733
    fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
734
        run_lints!(self, check_explicit_self, es);
735
        visit::walk_explicit_self(self, es);
736 737
    }

738
    fn visit_mac(&mut self, mac: &ast::Mac) {
739
        run_lints!(self, check_mac, mac);
740
        visit::walk_mac(self, mac);
741 742
    }

743
    fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
744
        run_lints!(self, check_path, p, id);
745
        visit::walk_path(self, p);
746 747
    }

748
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
749 750 751 752 753
        run_lints!(self, check_attribute, attr);
    }
}

// Output any lints that were previously added to the session.
754
impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> {
A
Ariel Ben-Yehuda 已提交
755
    fn visit_id(&mut self, id: ast::NodeId) {
756
        match self.tcx.sess.lints.borrow_mut().remove(&id) {
757 758
            None => {}
            Some(lints) => {
A
Aaron Turon 已提交
759
                for (lint_id, span, msg) in lints.into_iter() {
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
                    self.span_lint(lint_id.lint, span, msg.as_slice())
                }
            }
        }
    }
}

// This lint pass is defined here because it touches parts of the `Context`
// that we don't want to expose. It records the lint level at certain AST
// nodes, so that the variant size difference check in trans can call
// `raw_emit_lint`.

struct GatherNodeLevels;

impl LintPass for GatherNodeLevels {
    fn get_lints(&self) -> LintArray {
        lint_array!()
    }

    fn check_item(&mut self, cx: &Context, it: &ast::Item) {
        match it.node {
            ast::ItemEnum(..) => {
A
Aaron Turon 已提交
782
                let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES);
783 784 785
                let lvlsrc = cx.lints.get_level_source(lint_id);
                match lvlsrc {
                    (lvl, _) if lvl != Allow => {
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
                        cx.node_levels.borrow_mut()
                            .insert((it.id, lint_id), lvlsrc);
                    },
                    _ => { }
                }
            },
            _ => { }
        }
    }
}

/// Perform lint checking on a crate.
///
/// Consumes the `lint_store` field of the `Session`.
pub fn check_crate(tcx: &ty::ctxt,
                   exported_items: &ExportedItems) {
802
    let krate = tcx.map.krate();
803
    let mut cx = Context::new(tcx, krate, exported_items);
804 805 806 807 808 809

    // Visit the whole crate.
    cx.with_lint_attrs(krate.attrs.as_slice(), |cx| {
        cx.visit_id(ast::CRATE_NODE_ID);
        cx.visit_ids(|v| {
            v.visited_outermost = true;
810
            visit::walk_crate(v, krate);
811 812 813 814
        });

        // since the root module isn't visited as an item (because it isn't an
        // item), warn for it here.
815
        run_lints!(cx, check_crate, krate);
816

817
        visit::walk_crate(cx, krate);
818 819 820 821 822 823 824
    });

    // If we missed any lints added to the session, then there's a bug somewhere
    // in the iteration code.
    for (id, v) in tcx.sess.lints.borrow().iter() {
        for &(lint, span, ref msg) in v.iter() {
            tcx.sess.span_bug(span,
825
                              format!("unprocessed lint {} at {}: {}",
826
                                      lint.as_str(), tcx.map.node_to_string(*id), *msg).as_slice())
827 828 829 830 831 832
        }
    }

    tcx.sess.abort_if_errors();
    *tcx.node_lint_levels.borrow_mut() = cx.node_levels.unwrap();
}