context.rs 26.9 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 26 27
// 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.

use middle::privacy::ExportedItems;
28
use middle::subst;
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
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;

use std::collections::HashMap;
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};
48
use syntax::parse::token::InternedString;
49 50 51 52 53 54 55 56 57 58 59 60
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.
61 62 63
    /// This is only `None` while iterating over the objects. See the definition
    /// of run_lints.
    passes: Option<Vec<LintPassObject>>,
64 65

    /// Lints indexed by name.
66
    by_name: HashMap<String, LintId>,
67 68 69

    /// Current levels of each lint, and where they were set.
    levels: HashMap<LintId, LevelSource>,
70 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.
    lint_groups: HashMap<&'static str, (Vec<LintId>, bool)>,
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
}

impl LintStore {
    fn get_level_source(&self, lint: LintId) -> LevelSource {
        match self.levels.find(&lint) {
            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!(),
95
            passes: Some(vec!()),
96 97
            by_name: HashMap::new(),
            levels: HashMap::new(),
98
            lint_groups: HashMap::new(),
99 100 101 102 103 104 105
        }
    }

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

106
    pub fn get_lint_groups<'t>(&'t self) -> Vec<(&'static str, Vec<LintId>, bool)> {
107 108 109
        self.lint_groups.iter().map(|(k, v)| (*k,
                                              v.ref0().clone(),
                                              *v.ref1())).collect()
110 111
    }

112 113 114
    pub fn register_pass(&mut self, sess: Option<&Session>,
                         from_plugin: bool, pass: LintPassObject) {
        for &lint in pass.get_lints().iter() {
115
            self.lints.push((*lint, from_plugin));
116

117
            let id = LintId::of(*lint);
118 119
            if !self.by_name.insert(lint.name_lower(), id) {
                let msg = format!("duplicate specification of lint {}", lint.name_lower());
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
                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));
            }
        }
135
        self.passes.get_mut_ref().push(pass);
136 137
    }

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
    pub fn register_group(&mut self, sess: Option<&Session>,
                          from_plugin: bool, name: &'static str,
                          to: Vec<LintId>) {
        let new = self.lint_groups.insert(name, (to, from_plugin));

        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()),
            }
        }
    }

157
    pub fn register_builtin(&mut self, sess: Option<&Session>) {
158
        macro_rules! add_builtin ( ( $sess:ident, $($name:ident),*, ) => (
159
            {$(
160
                self.register_pass($sess, false, box builtin::$name as LintPassObject);
161 162 163
            )*}
        ))

164 165 166 167 168 169
        macro_rules! add_builtin_with_new ( ( $sess:ident, $($name:ident),*, ) => (
            {$(
                self.register_pass($sess, false, box builtin::$name::new() as LintPassObject);
            )*}
        ))

170 171 172 173
        macro_rules! add_lint_group ( ( $sess:ident, $name:expr, $($lint:ident),* ) => (
            self.register_group($sess, false, $name, vec![$(LintId::of(builtin::$lint)),*]);
        ))

174 175 176 177
        add_builtin!(sess,
                     HardwiredLints,
                     WhileTrue,
                     UnusedCasts,
A
Aaron Turon 已提交
178 179 180 181 182
                     ImproperCTypes,
                     BoxPointers,
                     UnusedAttributes,
                     PathStatements,
                     UnusedResults,
183
                     NonCamelCaseTypes,
184
                     NonSnakeCase,
A
Aaron Turon 已提交
185 186 187
                     NonUpperCaseGlobals,
                     UnusedParens,
                     UnusedImportBraces,
188
                     UnusedUnsafe,
A
Aaron Turon 已提交
189
                     UnsafeBlocks,
190
                     UnusedMut,
A
Aaron Turon 已提交
191
                     UnusedAllocation,
192
                     Stability,
193 194 195
        )

        add_builtin_with_new!(sess,
196 197 198
                              TypeLimits,
                              RawPointerDeriving,
                              MissingDoc,
199 200
        )

201
        add_lint_group!(sess, "bad_style",
A
Aaron Turon 已提交
202
                        NON_CAMEL_CASE_TYPES, NON_SNAKE_CASE, NON_UPPER_CASE_GLOBALS)
203 204

        add_lint_group!(sess, "unused",
A
Aaron Turon 已提交
205 206 207
                        UNUSED_IMPORTS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE,
                        UNUSED_MUT, UNREACHABLE_CODE, UNUSED_EXTERN_CRATES, UNUSED_MUST_USE,
                        UNUSED_UNSAFE, UNUSED_RESULTS, PATH_STATEMENTS)
208

209 210 211 212 213 214 215 216
        // We have one lint pass defined in this module.
        self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
    }

    pub fn process_command_line(&mut self, sess: &Session) {
        for &(ref lint_name, level) in sess.opts.lint_opts.iter() {
            match self.by_name.find_equiv(&lint_name.as_slice()) {
                Some(&lint_id) => self.set_level(lint_id, (level, CommandLine)),
217
                None => {
218
                    match self.lint_groups.iter().map(|(&x, pair)| (x, pair.ref0().clone()))
219 220 221 222 223 224 225 226 227 228 229 230
                                                 .collect::<HashMap<&'static str, Vec<LintId>>>()
                                                 .find_equiv(&lint_name.as_slice()) {
                        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()),
                    }
                }
231 232 233 234 235 236
            }
        }
    }
}

/// Context for lint checking.
237
pub struct Context<'a, 'tcx: 'a> {
238
    /// Type context we're checking in.
239
    pub tcx: &'a ty::ctxt<'tcx>,
240

241 242 243 244 245 246
    /// The crate being checked.
    pub krate: &'a ast::Crate,

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

247 248 249 250 251 252 253 254 255 256 257 258 259 260
    /// 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.
    node_levels: RefCell<HashMap<(ast::NodeId, LintId), LevelSource>>,
}

/// Convenience macro for calling a `LintPass` method on every pass in the context.
261 262 263
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.
264
    let mut passes = $cx.lints.passes.take().unwrap();
A
Aaron Turon 已提交
265
    for obj in passes.iter_mut() {
266
        obj.$f($cx, $($args),*);
267
    }
268 269
    $cx.lints.passes = Some(passes);
}))
270

271 272 273 274 275 276 277 278 279 280 281 282 283
/// 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);

284
        let meta = &attr.node.value;
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
        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
}

303 304 305 306 307 308 309 310 311 312 313 314
/// 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 }

315
    let name = lint.name_lower();
316 317 318 319
    let mut note = None;
    let msg = match source {
        Default => {
            format!("{}, #[{}({})] on by default", msg,
320
                    level.as_str(), name)
321 322 323
        },
        CommandLine => {
            format!("{} [-{} {}]", msg,
324 325 326 327
                    match level {
                        Warn => 'W', Deny => 'D', Forbid => 'F',
                        Allow => fail!()
                    }, name.replace("_", "-"))
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
        },
        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 已提交
346
    for span in note.into_iter() {
347 348 349 350
        sess.span_note(span, "lint level defined here");
    }
}

351 352
impl<'a, 'tcx> Context<'a, 'tcx> {
    fn new(tcx: &'a ty::ctxt<'tcx>,
353
           krate: &'a ast::Crate,
354
           exported_items: &'a ExportedItems) -> Context<'a, 'tcx> {
355 356
        // 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(),
357
                                      LintStore::new());
358 359 360

        Context {
            tcx: tcx,
361 362 363
            krate: krate,
            exported_items: exported_items,
            lints: lint_store,
364
            level_stack: vec![],
365 366 367 368 369 370 371 372 373
            node_levels: RefCell::new(HashMap::new()),
        }
    }

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

374 375 376 377 378 379
    /// Get the level of `lint` at the current position of the lint
    /// traversal.
    pub fn current_level(&self, lint: &'static Lint) -> Level {
        self.lints.levels.find(&LintId::of(lint)).map_or(Allow, |&(lvl, _)| lvl)
    }

380 381 382 383
    fn lookup_and_emit(&self, lint: &'static Lint, span: Option<Span>, msg: &str) {
        let (level, src) = match self.lints.levels.find(&LintId::of(lint)) {
            None => return,
            Some(&(Warn, src)) => {
384
                let lint_id = LintId::of(builtin::WARNINGS);
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415
                (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;
416

A
Aaron Turon 已提交
417
        for result in gather_attrs(attrs).into_iter() {
418
            let v = match result {
419 420 421 422 423 424
                Err(span) => {
                    self.tcx.sess.span_err(span, "malformed lint attribute");
                    continue;
                }
                Ok((lint_name, level, span)) => {
                    match self.lints.by_name.find_equiv(&lint_name.get()) {
425
                        Some(&lint_id) => vec![(lint_id, level, span)],
426
                        None => {
427 428 429 430 431 432
                            match self.lints.lint_groups.find_equiv(&lint_name.get()) {
                                Some(&(ref v, _)) => v.iter()
                                                      .map(|lint_id: &LintId|
                                                           (*lint_id, level, span))
                                                      .collect(),
                                None => {
A
Aaron Turon 已提交
433
                                    self.span_lint(builtin::UNKNOWN_LINTS, span,
434 435 436 437 438
                                               format!("unknown `{}` attribute: `{}`",
                                                       level.as_str(), lint_name).as_slice());
                                    continue;
                                }
                            }
439 440 441 442 443
                        }
                    }
                }
            };

A
Aaron Turon 已提交
444
            for (lint_id, level, span) in v.into_iter() {
445 446 447 448 449 450 451 452 453 454 455 456 457
                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)));
                }
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
            }
        }

        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);
        }
    }

    fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
        let mut v = ast_util::IdVisitor {
            operation: self,
            pass_through_items: false,
            visited_outermost: false,
        };
        f(&mut v);
    }
}

482 483
impl<'a, 'tcx> AstConv<'tcx> for Context<'a, 'tcx>{
    fn tcx<'a>(&'a self) -> &'a ty::ctxt<'tcx> { self.tcx }
484

K
Keegan McAllister 已提交
485
    fn get_item_ty(&self, id: ast::DefId) -> ty::Polytype {
486 487 488 489 490 491 492 493 494 495
        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()
    }
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515

    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)
    }
516 517
}

518
impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
519
    fn visit_item(&mut self, it: &ast::Item) {
520 521
        self.with_lint_attrs(it.attrs.as_slice(), |cx| {
            run_lints!(cx, check_item, it);
522 523
            cx.visit_ids(|v| v.visit_item(it));
            visit::walk_item(cx, it);
524 525 526
        })
    }

527
    fn visit_foreign_item(&mut self, it: &ast::ForeignItem) {
528 529
        self.with_lint_attrs(it.attrs.as_slice(), |cx| {
            run_lints!(cx, check_foreign_item, it);
530
            visit::walk_foreign_item(cx, it);
531 532 533
        })
    }

534
    fn visit_view_item(&mut self, i: &ast::ViewItem) {
535 536
        self.with_lint_attrs(i.attrs.as_slice(), |cx| {
            run_lints!(cx, check_view_item, i);
537 538
            cx.visit_ids(|v| v.visit_view_item(i));
            visit::walk_view_item(cx, i);
539 540 541
        })
    }

542
    fn visit_pat(&mut self, p: &ast::Pat) {
543
        run_lints!(self, check_pat, p);
544
        visit::walk_pat(self, p);
545 546
    }

547
    fn visit_expr(&mut self, e: &ast::Expr) {
548
        run_lints!(self, check_expr, e);
549
        visit::walk_expr(self, e);
550 551
    }

552
    fn visit_stmt(&mut self, s: &ast::Stmt) {
553
        run_lints!(self, check_stmt, s);
554
        visit::walk_stmt(self, s);
555 556
    }

557 558 559
    fn visit_fn(&mut self, fk: FnKind<'v>, decl: &'v ast::FnDecl,
                body: &'v ast::Block, span: Span, id: ast::NodeId) {
        match fk {
560 561 562 563
            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| {
564
                        v.visit_fn(fk, decl, body, span, id);
565
                    });
566
                    visit::walk_fn(cx, fk, decl, body, span);
567 568 569 570
                })
            },
            _ => {
                run_lints!(self, check_fn, fk, decl, body, span, id);
571
                visit::walk_fn(self, fk, decl, body, span);
572 573 574 575
            }
        }
    }

576
    fn visit_ty_method(&mut self, t: &ast::TypeMethod) {
577 578
        self.with_lint_attrs(t.attrs.as_slice(), |cx| {
            run_lints!(cx, check_ty_method, t);
579
            visit::walk_ty_method(cx, t);
580 581 582 583 584 585 586
        })
    }

    fn visit_struct_def(&mut self,
                        s: &ast::StructDef,
                        ident: ast::Ident,
                        g: &ast::Generics,
587
                        id: ast::NodeId) {
588
        run_lints!(self, check_struct_def, s, ident, g, id);
589
        visit::walk_struct_def(self, s);
590 591 592
        run_lints!(self, check_struct_def_post, s, ident, g, id);
    }

593
    fn visit_struct_field(&mut self, s: &ast::StructField) {
594 595
        self.with_lint_attrs(s.node.attrs.as_slice(), |cx| {
            run_lints!(cx, check_struct_field, s);
596
            visit::walk_struct_field(cx, s);
597 598 599
        })
    }

600
    fn visit_variant(&mut self, v: &ast::Variant, g: &ast::Generics) {
601 602
        self.with_lint_attrs(v.node.attrs.as_slice(), |cx| {
            run_lints!(cx, check_variant, v, g);
603
            visit::walk_variant(cx, v, g);
604 605 606 607
        })
    }

    // FIXME(#10894) should continue recursing
608
    fn visit_ty(&mut self, t: &ast::Ty) {
609 610 611
        run_lints!(self, check_ty, t);
    }

612
    fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
613 614 615
        run_lints!(self, check_ident, sp, id);
    }

616
    fn visit_mod(&mut self, m: &ast::Mod, s: Span, n: ast::NodeId) {
617
        run_lints!(self, check_mod, m, s, n);
618
        visit::walk_mod(self, m);
619 620
    }

621
    fn visit_local(&mut self, l: &ast::Local) {
622
        run_lints!(self, check_local, l);
623
        visit::walk_local(self, l);
624 625
    }

626
    fn visit_block(&mut self, b: &ast::Block) {
627
        run_lints!(self, check_block, b);
628
        visit::walk_block(self, b);
629 630
    }

631
    fn visit_arm(&mut self, a: &ast::Arm) {
632
        run_lints!(self, check_arm, a);
633
        visit::walk_arm(self, a);
634 635
    }

636
    fn visit_decl(&mut self, d: &ast::Decl) {
637
        run_lints!(self, check_decl, d);
638
        visit::walk_decl(self, d);
639 640
    }

641
    fn visit_expr_post(&mut self, e: &ast::Expr) {
642 643 644
        run_lints!(self, check_expr_post, e);
    }

645
    fn visit_generics(&mut self, g: &ast::Generics) {
646
        run_lints!(self, check_generics, g);
647
        visit::walk_generics(self, g);
648 649
    }

650
    fn visit_trait_item(&mut self, m: &ast::TraitItem) {
651
        run_lints!(self, check_trait_method, m);
652
        visit::walk_trait_item(self, m);
653 654
    }

655
    fn visit_opt_lifetime_ref(&mut self, sp: Span, lt: &Option<ast::Lifetime>) {
656 657 658
        run_lints!(self, check_opt_lifetime_ref, sp, lt);
    }

659
    fn visit_lifetime_ref(&mut self, lt: &ast::Lifetime) {
660 661 662
        run_lints!(self, check_lifetime_ref, lt);
    }

663
    fn visit_lifetime_decl(&mut self, lt: &ast::LifetimeDef) {
664 665 666
        run_lints!(self, check_lifetime_decl, lt);
    }

667
    fn visit_explicit_self(&mut self, es: &ast::ExplicitSelf) {
668
        run_lints!(self, check_explicit_self, es);
669
        visit::walk_explicit_self(self, es);
670 671
    }

672
    fn visit_mac(&mut self, mac: &ast::Mac) {
673
        run_lints!(self, check_mac, mac);
674
        visit::walk_mac(self, mac);
675 676
    }

677
    fn visit_path(&mut self, p: &ast::Path, id: ast::NodeId) {
678
        run_lints!(self, check_path, p, id);
679
        visit::walk_path(self, p);
680 681
    }

682
    fn visit_attribute(&mut self, attr: &ast::Attribute) {
683 684 685 686 687
        run_lints!(self, check_attribute, attr);
    }
}

// Output any lints that were previously added to the session.
688
impl<'a, 'tcx> IdVisitingOperation for Context<'a, 'tcx> {
689 690 691 692
    fn visit_id(&self, id: ast::NodeId) {
        match self.tcx.sess.lints.borrow_mut().pop(&id) {
            None => {}
            Some(lints) => {
A
Aaron Turon 已提交
693
                for (lint_id, span, msg) in lints.into_iter() {
694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
                    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 已提交
716
                let lint_id = LintId::of(builtin::VARIANT_SIZE_DIFFERENCES);
717 718 719
                let lvlsrc = cx.lints.get_level_source(lint_id);
                match lvlsrc {
                    (lvl, _) if lvl != Allow => {
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
                        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) {
736
    let krate = tcx.map.krate();
737
    let mut cx = Context::new(tcx, krate, exported_items);
738 739 740 741 742 743

    // 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;
744
            visit::walk_crate(v, krate);
745 746 747 748
        });

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

751
        visit::walk_crate(cx, krate);
752 753 754 755 756 757 758
    });

    // 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,
759
                              format!("unprocessed lint {} at {}: {}",
760
                                      lint.as_str(), tcx.map.node_to_string(*id), *msg).as_slice())
761 762 763 764 765 766
        }
    }

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