lint.rs 44.4 KB
Newer Older
G
Geoff Hill 已提交
1
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// 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.

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
//! A 'lint' check is a kind of miscellaneous constraint that a user _might_
//! want to enforce, but might reasonably want to permit as well, on a
//! module-by-module basis. They contrast with static constraints enforced by
//! other phases of the compiler, which are generally required to hold in order
//! to compile the program at all.
//!
//! The lint checking is all 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.
//!
//! To add a new lint warning, all you need to do is to either invoke `add_lint`
//! on the session at the appropriate time, or write a few linting functions and
//! modify the Context visitor appropriately. If you're adding lints from the
//! Context itself, span_lint should be used instead of add_lint.
35

36
use driver::session;
37
use middle::privacy;
J
Jed Davis 已提交
38
use middle::trans::adt; // for `adt::is_ffi_safe`
P
Patrick Walton 已提交
39
use middle::ty;
40
use middle::pat_util;
41
use metadata::csearch;
P
Patrick Walton 已提交
42
use util::ppaux::{ty_to_str};
43

44 45 46 47 48 49 50 51 52 53
use std::cmp;
use std::hashmap::HashMap;
use std::i16;
use std::i32;
use std::i64;
use std::i8;
use std::u16;
use std::u32;
use std::u64;
use std::u8;
54
use extra::smallintmap::SmallIntMap;
55
use syntax::ast_map;
56
use syntax::attr;
57
use syntax::attr::{AttrMetaMethods, AttributeMethods};
58
use syntax::codemap::Span;
J
John Clements 已提交
59
use syntax::codemap;
60
use syntax::parse::token;
61
use syntax::{ast, ast_util, visit};
62
use syntax::ast_util::IdVisitingOperation;
63
use syntax::visit::Visitor;
64

65
#[deriving(Clone, Eq)]
66
pub enum lint {
P
Patrick Walton 已提交
67
    ctypes,
68
    unused_imports,
69
    unnecessary_qualification,
70 71
    while_true,
    path_statement,
72
    unrecognized_lint,
73
    non_camel_case_types,
74
    non_uppercase_statics,
75
    non_uppercase_pattern_statics,
76
    type_limits,
77
    type_overflow,
78
    unused_unsafe,
D
Daniel Micay 已提交
79
    unsafe_block,
80
    attribute_usage,
81
    unknown_features,
82

83 84 85 86
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

87 88
    unused_variable,
    dead_assignment,
89
    unused_mut,
90
    unnecessary_allocation,
91

92
    missing_doc,
93
    unreachable_code,
94

95 96 97 98
    deprecated,
    experimental,
    unstable,

99
    warnings,
100 101
}

102
pub fn level_to_str(lv: level) -> &'static str {
103
    match lv {
104 105 106 107
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
108 109 110
    }
}

111
#[deriving(Clone, Eq, Ord)]
112
pub enum level {
113
    allow, warn, deny, forbid
114 115
}

116
#[deriving(Clone, Eq)]
117
pub struct LintSpec {
118
    lint: lint,
119
    desc: &'static str,
120
    default: level
121
}
122

123 124 125 126
impl Ord for LintSpec {
    fn lt(&self, other: &LintSpec) -> bool { self.default < other.default }
}

127
pub type LintDict = HashMap<&'static str, LintSpec>;
128

129 130
#[deriving(Eq)]
enum LintSource {
131
    Node(Span),
132 133 134 135
    Default,
    CommandLine
}

S
Sangeun Kim 已提交
136 137 138 139
static lint_table: &'static [(&'static str, LintSpec)] = &[
    ("ctypes",
     LintSpec {
        lint: ctypes,
140
        desc: "proper use of std::libc types in foreign modules",
S
Sangeun Kim 已提交
141 142 143 144 145 146 147 148 149 150
        default: warn
     }),

    ("unused_imports",
     LintSpec {
        lint: unused_imports,
        desc: "imports that are never used",
        default: warn
     }),

151 152 153 154 155 156 157
    ("unnecessary_qualification",
     LintSpec {
        lint: unnecessary_qualification,
        desc: "detects unnecessarily qualified names",
        default: allow
     }),

S
Sangeun Kim 已提交
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
    ("while_true",
     LintSpec {
        lint: while_true,
        desc: "suggest using loop { } instead of while(true) { }",
        default: warn
     }),

    ("path_statement",
     LintSpec {
        lint: path_statement,
        desc: "path statements with no effect",
        default: warn
     }),

    ("unrecognized_lint",
     LintSpec {
        lint: unrecognized_lint,
        desc: "unrecognized lint attribute",
        default: warn
     }),

    ("non_camel_case_types",
     LintSpec {
        lint: non_camel_case_types,
        desc: "types, variants and traits should have camel case names",
        default: allow
     }),

186 187 188 189
    ("non_uppercase_statics",
     LintSpec {
         lint: non_uppercase_statics,
         desc: "static constants should have uppercase identifiers",
190
         default: allow
191 192
     }),

193 194 195
    ("non_uppercase_pattern_statics",
     LintSpec {
         lint: non_uppercase_pattern_statics,
196
         desc: "static constants in match patterns should be all caps",
197 198 199
         default: warn
     }),

S
Sangeun Kim 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    ("managed_heap_memory",
     LintSpec {
        lint: managed_heap_memory,
        desc: "use of managed (@ type) heap memory",
        default: allow
     }),

    ("owned_heap_memory",
     LintSpec {
        lint: owned_heap_memory,
        desc: "use of owned (~ type) heap memory",
        default: allow
     }),

    ("heap_memory",
     LintSpec {
        lint: heap_memory,
        desc: "use of any (~ type or @ type) heap memory",
        default: allow
     }),

    ("type_limits",
     LintSpec {
        lint: type_limits,
        desc: "comparisons made useless by limits of the types involved",
        default: warn
     }),

228 229 230 231 232 233 234 235
    ("type_overflow",
     LintSpec {
        lint: type_overflow,
        desc: "literal out of range for its type",
        default: warn
     }),


S
Sangeun Kim 已提交
236 237 238 239 240 241 242
    ("unused_unsafe",
     LintSpec {
        lint: unused_unsafe,
        desc: "unnecessary use of an `unsafe` block",
        default: warn
    }),

D
Daniel Micay 已提交
243 244 245 246 247 248 249
    ("unsafe_block",
     LintSpec {
        lint: unsafe_block,
        desc: "usage of an `unsafe` block",
        default: allow
    }),

250 251 252 253 254 255 256
    ("attribute_usage",
     LintSpec {
        lint: attribute_usage,
        desc: "detects bad use of attributes",
        default: warn
    }),

S
Sangeun Kim 已提交
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
    ("unused_variable",
     LintSpec {
        lint: unused_variable,
        desc: "detect variables which are not used in any way",
        default: warn
    }),

    ("dead_assignment",
     LintSpec {
        lint: dead_assignment,
        desc: "detect assignments that will never be read",
        default: warn
    }),

    ("unused_mut",
     LintSpec {
        lint: unused_mut,
        desc: "detect mut variables which don't need to be mutable",
        default: warn
    }),
277 278 279 280 281 282 283

    ("unnecessary_allocation",
     LintSpec {
        lint: unnecessary_allocation,
        desc: "detects unnecessary allocations that can be eliminated",
        default: warn
    }),
284

285
    ("missing_doc",
286
     LintSpec {
287 288
        lint: missing_doc,
        desc: "detects missing documentation for public members",
289 290
        default: allow
    }),
291 292 293 294 295 296 297

    ("unreachable_code",
     LintSpec {
        lint: unreachable_code,
        desc: "detects unreachable code",
        default: warn
    }),
298

299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
    ("deprecated",
     LintSpec {
        lint: deprecated,
        desc: "detects use of #[deprecated] items",
        default: warn
    }),

    ("experimental",
     LintSpec {
        lint: experimental,
        desc: "detects use of #[experimental] items",
        default: warn
    }),

    ("unstable",
     LintSpec {
        lint: unstable,
        desc: "detects use of #[unstable] items (incl. items with no stability attribute)",
        default: allow
    }),

320 321 322 323 324 325
    ("warnings",
     LintSpec {
        lint: warnings,
        desc: "mass-change the level for lints which produce warnings",
        default: warn
    }),
326 327 328 329 330 331 332

    ("unknown_features",
     LintSpec {
        lint: unknown_features,
        desc: "unknown features found in create-level #[feature] directives",
        default: deny,
    }),
S
Sangeun Kim 已提交
333 334
];

335 336 337 338
/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
339
pub fn get_lint_dict() -> LintDict {
340
    let mut map = HashMap::new();
D
Daniel Micay 已提交
341
    for &(k, v) in lint_table.iter() {
342
        map.insert(k, v);
343
    }
344
    return map;
345 346
}

347
struct Context<'self> {
348 349 350
    // All known lint modes (string versions)
    dict: @LintDict,
    // Current levels of each lint warning
351
    cur: SmallIntMap<(level, LintSource)>,
352 353
    // context we're checking in (used to access fields like sess)
    tcx: ty::ctxt,
354 355 356 357 358 359 360
    // Items exported by the crate; used by the missing_doc lint.
    exported_items: &'self privacy::ExportedItems,
    // The id of the current `ast::struct_def` being walked.
    cur_struct_def_id: ast::NodeId,
    // Whether some ancestor of the current node was marked
    // #[doc(hidden)].
    is_doc_hidden: bool,
361

362 363 364
    // 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.
365
    lint_stack: ~[(lint, level, LintSource)],
366 367 368

    // id of the last visited negated expression
    negated_expr_id: ast::NodeId
369
}
370

371
impl<'self> Context<'self> {
372
    fn get_level(&self, lint: lint) -> level {
373
        match self.cur.find(&(lint as uint)) {
374
          Some(&(lvl, _)) => lvl,
375 376
          None => allow
        }
377 378
    }

379
    fn get_source(&self, lint: lint) -> LintSource {
380
        match self.cur.find(&(lint as uint)) {
381 382 383 384 385 386
          Some(&(_, src)) => src,
          None => Default
        }
    }

    fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
387
        if level == allow {
388
            self.cur.remove(&(lint as uint));
389
        } else {
390
            self.cur.insert(lint as uint, (level, src));
391 392 393
        }
    }

394
    fn lint_to_str(&self, lint: lint) -> &'static str {
D
Daniel Micay 已提交
395
        for (k, v) in self.dict.iter() {
396
            if v.lint == lint {
397
                return *k;
398
            }
399
        }
400
        fail!("unregistered lint {:?}", lint);
401 402
    }

403
    fn span_lint(&self, lint: lint, span: Span, msg: &str) {
404
        let (level, src) = match self.cur.find(&(lint as uint)) {
405 406
            None => { return }
            Some(&(warn, src)) => (self.get_level(warnings), src),
407 408
            Some(&pair) => pair,
        };
409
        if level == allow { return }
410 411 412

        let mut note = None;
        let msg = match src {
G
Geoff Hill 已提交
413 414 415 416 417 418 419
            Default => {
                format!("{}, \\#[{}({})] on by default", msg,
                    level_to_str(level), self.lint_to_str(lint))
            },
            CommandLine => {
                format!("{} [-{} {}]", msg,
                    match level {
420
                        warn => 'W', deny => 'D', forbid => 'F',
421
                        allow => fail!()
G
Geoff Hill 已提交
422
                    }, self.lint_to_str(lint).replace("_", "-"))
423 424 425 426 427 428 429 430 431
            },
            Node(src) => {
                note = Some(src);
                msg.to_str()
            }
        };
        match level {
            warn =>          { self.tcx.sess.span_warn(span, msg); }
            deny | forbid => { self.tcx.sess.span_err(span, msg);  }
432
            allow => fail!(),
433 434
        }

D
Daniel Micay 已提交
435
        for &span in note.iter() {
436 437
            self.tcx.sess.span_note(span, "lint level defined here");
        }
438 439
    }

440
    /**
441
     * Merge the lints specified by any lint attributes into the
442
     * current lint context, call the provided function, then reset the
443
     * lints in effect to their previous state.
444
     */
445 446 447
    fn with_lint_attrs(&mut self,
                       attrs: &[ast::Attribute],
                       f: |&mut Context|) {
448 449 450 451 452
        // 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;
453
        each_lint(self.tcx.sess, attrs, |meta, level, lintname| {
454 455 456 457 458
            match self.dict.find_equiv(&lintname) {
                None => {
                    self.span_lint(
                        unrecognized_lint,
                        meta.span,
A
Alex Crichton 已提交
459
                        format!("unknown `{}` attribute: `{}`",
460 461 462 463 464 465 466
                        level_to_str(level), lintname));
                }
                Some(lint) => {
                    let lint = lint.lint;
                    let now = self.get_level(lint);
                    if now == forbid && level != forbid {
                        self.tcx.sess.span_err(meta.span,
A
Alex Crichton 已提交
467
                        format!("{}({}) overruled by outer forbid({})",
468 469 470 471 472 473 474 475 476
                        level_to_str(level),
                        lintname, lintname));
                    } else if now != level {
                        let src = self.get_source(lint);
                        self.lint_stack.push((lint, now, src));
                        pushed += 1;
                        self.set_level(lint, level, Node(meta.span));
                    }
                }
477
            }
478
            true
479
        });
480

481 482 483 484 485 486
        let old_is_doc_hidden = self.is_doc_hidden;
        self.is_doc_hidden = self.is_doc_hidden ||
            attrs.iter().any(|attr| ("doc" == attr.name() && match attr.meta_item_list()
                                     { None => false,
                                       Some(l) => attr::contains_name(l, "hidden") }));

487
        f(self);
488

489
        // rollback
490
        self.is_doc_hidden = old_is_doc_hidden;
491
        pushed.times(|| {
492 493
            let (lint, lvl, src) = self.lint_stack.pop();
            self.set_level(lint, lvl, src);
494
        })
495 496
    }

497
    fn visit_ids(&self, f: |&mut ast_util::IdVisitor<Context>|) {
498 499 500 501 502 503
        let mut v = ast_util::IdVisitor {
            operation: self,
            pass_through_items: false,
            visited_outermost: false,
        };
        f(&mut v);
504 505 506
    }
}

507
pub fn each_lint(sess: session::Session,
508
                 attrs: &[ast::Attribute],
509 510
                 f: |@ast::MetaItem, level, @str| -> bool)
                 -> bool {
511
    let xs = [allow, warn, deny, forbid];
D
Daniel Micay 已提交
512
    for &level in xs.iter() {
513
        let level_name = level_to_str(level);
D
Daniel Micay 已提交
514
        for attr in attrs.iter().filter(|m| level_name == m.name()) {
515 516
            let meta = attr.node.value;
            let metas = match meta.node {
517
                ast::MetaList(_, ref metas) => metas,
518
                _ => {
519
                    sess.span_err(meta.span, "malformed lint attribute");
520
                    continue;
521 522
                }
            };
D
Daniel Micay 已提交
523
            for meta in metas.iter() {
524
                match meta.node {
525
                    ast::MetaWord(lintname) => {
526 527 528 529 530
                        if !f(*meta, level, lintname) {
                            return false;
                        }
                    }
                    _ => {
531
                        sess.span_err(meta.span, "malformed lint attribute");
532 533 534 535 536
                    }
                }
            }
        }
    }
537
    true
538 539
}

540 541 542 543 544
fn check_while_true_expr(cx: &Context, e: &ast::Expr) {
    match e.node {
        ast::ExprWhile(cond, _) => {
            match cond.node {
                ast::ExprLit(@codemap::Spanned {
A
Alex Crichton 已提交
545
                    node: ast::lit_bool(true), ..}) =>
546 547 548
                {
                    cx.span_lint(while_true, e.span,
                                 "denote infinite loops with loop { ... }");
549
                }
550 551
                _ => ()
            }
552 553
        }
        _ => ()
554 555 556
    }
}

557 558 559 560 561 562
fn check_type_limits(cx: &Context, e: &ast::Expr) {
    return match e.node {
        ast::ExprBinary(_, binop, l, r) => {
            if is_comparison(binop) && !check_limits(cx.tcx, binop, l, r) {
                cx.span_lint(type_limits, e.span,
                             "comparison is useless due to type limits");
563
            }
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
        },
        ast::ExprLit(lit) => {
            match ty::get(ty::expr_ty(cx.tcx, e)).sty {
                ty::ty_int(t) => {
                    let int_type = if t == ast::ty_i {
                        cx.tcx.sess.targ_cfg.int_type
                    } else { t };
                    let (min, max) = int_ty_range(int_type);
                    let mut lit_val: i64 = match lit.node {
                        ast::lit_int(v, _) => v,
                        ast::lit_uint(v, _) => v as i64,
                        ast::lit_int_unsuffixed(v) => v,
                        _ => fail!()
                    };
                    if cx.negated_expr_id == e.id {
                        lit_val *= -1;
                    }
                    if  lit_val < min || lit_val > max {
                        cx.span_lint(type_overflow, e.span,
                                     "literal out of range for its type");
                    }
                },
                ty::ty_uint(t) => {
                    let uint_type = if t == ast::ty_u {
                        cx.tcx.sess.targ_cfg.uint_type
                    } else { t };
                    let (min, max) = uint_ty_range(uint_type);
                    let lit_val: u64 = match lit.node {
                        ast::lit_int(v, _) => v as u64,
                        ast::lit_uint(v, _) => v,
                        ast::lit_int_unsuffixed(v) => v as u64,
                        _ => fail!()
                    };
                    if  lit_val < min || lit_val > max {
                        cx.span_lint(type_overflow, e.span,
                                     "literal out of range for its type");
                    }
                },

                _ => ()
            };
        },
606 607
        _ => ()
    };
608

609 610
    fn is_valid<T:cmp::Ord>(binop: ast::BinOp, v: T,
                            min: T, max: T) -> bool {
611
        match binop {
612 613 614 615 616
            ast::BiLt => v <= max,
            ast::BiLe => v < max,
            ast::BiGt => v >= min,
            ast::BiGe => v > min,
            ast::BiEq | ast::BiNe => v >= min && v <= max,
617
            _ => fail!()
618 619 620
        }
    }

621
    fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
622
        match binop {
623 624 625 626
            ast::BiLt => ast::BiGt,
            ast::BiLe => ast::BiGe,
            ast::BiGt => ast::BiLt,
            ast::BiGe => ast::BiLe,
627 628 629 630
            _ => binop
        }
    }

631 632
    // for int & uint, be conservative with the warnings, so that the
    // warnings are consistent between 32- and 64-bit platforms
633
    fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
634
        match int_ty {
635
            ast::ty_i =>    (i64::min_value,        i64::max_value),
636 637 638 639 640 641 642
            ast::ty_i8 =>   (i8::min_value  as i64, i8::max_value  as i64),
            ast::ty_i16 =>  (i16::min_value as i64, i16::max_value as i64),
            ast::ty_i32 =>  (i32::min_value as i64, i32::max_value as i64),
            ast::ty_i64 =>  (i64::min_value,        i64::max_value)
        }
    }

643
    fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
644
        match uint_ty {
645
            ast::ty_u =>   (u64::min_value,         u64::max_value),
646 647 648 649 650 651 652
            ast::ty_u8 =>  (u8::min_value   as u64, u8::max_value   as u64),
            ast::ty_u16 => (u16::min_value  as u64, u16::max_value  as u64),
            ast::ty_u32 => (u32::min_value  as u64, u32::max_value  as u64),
            ast::ty_u64 => (u64::min_value,         u64::max_value)
        }
    }

653 654
    fn check_limits(tcx: ty::ctxt, binop: ast::BinOp,
                    l: &ast::Expr, r: &ast::Expr) -> bool {
655
        let (lit, expr, swap) = match (&l.node, &r.node) {
656 657
            (&ast::ExprLit(_), _) => (l, r, true),
            (_, &ast::ExprLit(_)) => (r, l, false),
658 659 660 661
            _ => return true
        };
        // Normalize the binop so that the literal is always on the RHS in
        // the comparison
662 663
        let norm_binop = if swap { rev_binop(binop) } else { binop };
        match ty::get(ty::expr_ty(tcx, expr)).sty {
664
            ty::ty_int(int_ty) => {
665
                let (min, max) = int_ty_range(int_ty);
666
                let lit_val: i64 = match lit.node {
667
                    ast::ExprLit(li) => match li.node {
668 669 670 671 672
                        ast::lit_int(v, _) => v,
                        ast::lit_uint(v, _) => v as i64,
                        ast::lit_int_unsuffixed(v) => v,
                        _ => return true
                    },
673
                    _ => fail!()
674
                };
675
                is_valid(norm_binop, lit_val, min, max)
676 677
            }
            ty::ty_uint(uint_ty) => {
678
                let (min, max): (u64, u64) = uint_ty_range(uint_ty);
679
                let lit_val: u64 = match lit.node {
680
                    ast::ExprLit(li) => match li.node {
681 682 683 684 685
                        ast::lit_int(v, _) => v as u64,
                        ast::lit_uint(v, _) => v,
                        ast::lit_int_unsuffixed(v) => v as u64,
                        _ => return true
                    },
686
                    _ => fail!()
687
                };
688
                is_valid(norm_binop, lit_val, min, max)
689 690 691 692 693
            }
            _ => true
        }
    }

694
    fn is_comparison(binop: ast::BinOp) -> bool {
695
        match binop {
696 697
            ast::BiEq | ast::BiLt | ast::BiLe |
            ast::BiNe | ast::BiGe | ast::BiGt => true,
698 699 700
            _ => false
        }
    }
701 702
}

703 704
fn check_item_ctypes(cx: &Context, it: &ast::item) {
    fn check_ty(cx: &Context, ty: &ast::Ty) {
705 706 707
        match ty.node {
            ast::ty_path(_, _, id) => {
                match cx.tcx.def_map.get_copy(&id) {
708
                    ast::DefPrimTy(ast::ty_int(ast::ty_i)) => {
709 710 711 712
                        cx.span_lint(ctypes, ty.span,
                                "found rust type `int` in foreign module, while \
                                libc::c_int or libc::c_long should be used");
                    }
713
                    ast::DefPrimTy(ast::ty_uint(ast::ty_u)) => {
714 715 716 717
                        cx.span_lint(ctypes, ty.span,
                                "found rust type `uint` in foreign module, while \
                                libc::c_uint or libc::c_ulong should be used");
                    }
J
Jed Davis 已提交
718 719 720 721 722
                    ast::DefTy(def_id) => {
                        if !adt::is_ffi_safe(cx.tcx, def_id) {
                            cx.span_lint(ctypes, ty.span,
                                         "found enum type without foreign-function-safe \
                                          representation annotation in foreign module");
A
Alex Crichton 已提交
723
                            // hmm... this message could be more helpful
J
Jed Davis 已提交
724 725
                        }
                    }
726 727 728
                    _ => ()
                }
            }
729
            ast::ty_ptr(ref mt) => { check_ty(cx, mt.ty) }
730 731 732
            _ => ()
        }
    }
733

734
    fn check_foreign_fn(cx: &Context, decl: &ast::fn_decl) {
D
Daniel Micay 已提交
735
        for input in decl.inputs.iter() {
736
            check_ty(cx, &input.ty);
737
        }
J
James Miller 已提交
738
        check_ty(cx, &decl.output)
739 740
    }

741
    match it.node {
742
      ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
D
Daniel Micay 已提交
743
        for ni in nmod.items.iter() {
744
            match ni.node {
745
                ast::foreign_item_fn(ref decl, _) => {
746 747
                    check_foreign_fn(cx, decl);
                }
J
James Miller 已提交
748
                ast::foreign_item_static(ref t, _) => { check_ty(cx, t); }
749 750
            }
        }
751
      }
B
Brian Anderson 已提交
752
      _ => {/* nothing to do */ }
753 754
    }
}
755

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
fn check_heap_type(cx: &Context, span: Span, ty: ty::t) {
    let xs = [managed_heap_memory, owned_heap_memory, heap_memory];
    for &lint in xs.iter() {
        if cx.get_level(lint) == allow { continue }

        let mut n_box = 0;
        let mut n_uniq = 0;
        ty::fold_ty(cx.tcx, ty, |t| {
            match ty::get(t).sty {
              ty::ty_box(_) => n_box += 1,
              ty::ty_uniq(_) => n_uniq += 1,
              _ => ()
            };
            t
        });
771

772 773 774 775 776
        if n_uniq > 0 && lint != managed_heap_memory {
            let s = ty_to_str(cx.tcx, ty);
            let m = format!("type uses owned (~ type) pointers: {}", s);
            cx.span_lint(lint, span, m);
        }
777

778 779 780 781 782
        if n_box > 0 && lint != owned_heap_memory {
            let s = ty_to_str(cx.tcx, ty);
            let m = format!("type uses managed (@ type) pointers: {}", s);
            cx.span_lint(lint, span, m);
        }
783
    }
784
}
785

786
fn check_heap_item(cx: &Context, it: &ast::item) {
787
    match it.node {
A
Alex Crichton 已提交
788 789 790 791
        ast::item_fn(..) |
        ast::item_ty(..) |
        ast::item_enum(..) |
        ast::item_struct(..) => check_heap_type(cx, it.span,
792 793 794
                                               ty::node_id_to_type(cx.tcx,
                                                                   it.id)),
        _ => ()
795
    }
796

797 798 799
    // If it's a struct, we also have to check the fields' types
    match it.node {
        ast::item_struct(struct_def, _) => {
D
Daniel Micay 已提交
800
            for struct_field in struct_def.fields.iter() {
801 802 803
                check_heap_type(cx, struct_field.span,
                                ty::node_id_to_type(cx.tcx,
                                                    struct_field.node.id));
804 805 806 807
            }
        }
        _ => ()
    }
808
}
809

K
klutzy 已提交
810
static crate_attrs: &'static [&'static str] = &[
A
Alex Crichton 已提交
811
    "crate_type", "feature", "no_uv", "no_main", "no_std",
K
klutzy 已提交
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
    "desc", "comment", "license", "copyright", // not used in rustc now
];


static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
    ("abi", "Use `extern \"abi\" fn` instead"),
    ("auto_encode", "Use `#[deriving(Encodable)]` instead"),
    ("auto_decode", "Use `#[deriving(Decodable)]` instead"),
    ("fast_ffi", "Remove it"),
    ("fixed_stack_segment", "Remove it"),
    ("rust_stack", "Remove it"),
];

static other_attrs: &'static [&'static str] = &[
    // item-level
    "address_insignificant", // can be crate-level too
D
Daniel Micay 已提交
828
    "thread_local", // for statics
K
klutzy 已提交
829 830 831 832
    "allow", "deny", "forbid", "warn", // lint options
    "deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
    "crate_map", "cfg", "doc", "export_name", "link_section", "no_freeze",
    "no_mangle", "no_send", "static_assert", "unsafe_no_drop_flag",
A
Alex Crichton 已提交
833
    "packed", "simd", "repr", "deriving", "unsafe_destructor", "link",
K
klutzy 已提交
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856

    //mod-level
    "path", "link_name", "link_args", "nolink", "macro_escape", "no_implicit_prelude",

    // fn-level
    "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start",
    "no_split_stack", "cold",

    // internal attribute: bypass privacy inside items
    "!resolve_unexported",
];

fn check_crate_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {

    for attr in attrs.iter() {
        let name = attr.node.value.name();
        let mut iter = crate_attrs.iter().chain(other_attrs.iter());
        if !iter.any(|other_attr| { name.equiv(other_attr) }) {
            cx.span_lint(attribute_usage, attr.span, "unknown crate attribute");
        }
    }
}

K
klutzy 已提交
857 858 859 860
fn check_attrs_usage(cx: &Context, attrs: &[ast::Attribute]) {
    // check if element has crate-level, obsolete, or any unknown attributes.

    for attr in attrs.iter() {
861 862 863 864
        let name = attr.node.value.name();
        for crate_attr in crate_attrs.iter() {
            if name.equiv(crate_attr) {
                let msg = match attr.node.style {
K
klutzy 已提交
865 866
                    ast::AttrOuter => "crate-level attribute should be an inner attribute: \
                                       add semicolon at end",
867 868 869
                    ast::AttrInner => "crate-level attribute should be in the root module",
                };
                cx.span_lint(attribute_usage, attr.span, msg);
K
klutzy 已提交
870
                return;
871 872
            }
        }
K
klutzy 已提交
873 874 875 876

        for &(obs_attr, obs_alter) in obsolete_attrs.iter() {
            if name.equiv(&obs_attr) {
                cx.span_lint(attribute_usage, attr.span,
K
klutzy 已提交
877
                             format!("obsolete attribute: {:s}", obs_alter));
K
klutzy 已提交
878
                return;
K
klutzy 已提交
879 880
            }
        }
K
klutzy 已提交
881 882 883 884

        if !other_attrs.iter().any(|other_attr| { name.equiv(other_attr) }) {
            cx.span_lint(attribute_usage, attr.span, "unknown attribute");
        }
885 886 887
    }
}

888 889 890
fn check_heap_expr(cx: &Context, e: &ast::Expr) {
    let ty = ty::expr_ty(cx.tcx, e);
    check_heap_type(cx, e.span, ty);
891 892
}

893 894
fn check_path_statement(cx: &Context, s: &ast::Stmt) {
    match s.node {
A
Alex Crichton 已提交
895
        ast::StmtSemi(@ast::Expr { node: ast::ExprPath(_), .. }, _) => {
896 897 898 899
            cx.span_lint(path_statement, s.span,
                         "path statement with no effect");
        }
        _ => ()
900 901 902
    }
}

903
fn check_item_non_camel_case_types(cx: &Context, it: &ast::item) {
904
    fn is_camel_case(cx: ty::ctxt, ident: ast::Ident) -> bool {
P
Paul Stansifer 已提交
905
        let ident = cx.sess.str_of(ident);
P
Patrick Walton 已提交
906
        assert!(!ident.is_empty());
907
        let ident = ident.trim_chars(&'_');
908 909 910 911

        // start with a non-lowercase letter rather than non-uppercase
        // ones (some scripts don't have a concept of upper/lowercase)
        !ident.char_at(0).is_lowercase() &&
912 913 914
            !ident.contains_char('_')
    }

915
    fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
916
        if !is_camel_case(cx.tcx, ident) {
917 918
            cx.span_lint(
                non_camel_case_types, span,
A
Alex Crichton 已提交
919
                format!("{} `{}` should have a camel case identifier",
920
                    sort, cx.tcx.sess.str_of(ident)));
921 922 923
        }
    }

924
    match it.node {
A
Alex Crichton 已提交
925
        ast::item_ty(..) | ast::item_struct(..) => {
926 927
            check_case(cx, "type", it.ident, it.span)
        }
A
Alex Crichton 已提交
928
        ast::item_trait(..) => {
929
            check_case(cx, "trait", it.ident, it.span)
930
        }
E
Erick Tryzelaar 已提交
931
        ast::item_enum(ref enum_definition, _) => {
932
            check_case(cx, "type", it.ident, it.span);
D
Daniel Micay 已提交
933
            for variant in enum_definition.variants.iter() {
934
                check_case(cx, "variant", variant.node.name, variant.span);
E
Erick Tryzelaar 已提交
935 936 937
            }
        }
        _ => ()
938 939 940
    }
}

941 942 943
fn check_item_non_uppercase_statics(cx: &Context, it: &ast::item) {
    match it.node {
        // only check static constants
944
        ast::item_static(_, ast::MutImmutable, _) => {
945 946 947 948
            let s = cx.tcx.sess.str_of(it.ident);
            // check for lowercase letters rather than non-uppercase
            // ones (some scripts don't have a concept of
            // upper/lowercase)
949
            if s.chars().any(|c| c.is_lowercase()) {
950 951 952 953 954 955 956 957
                cx.span_lint(non_uppercase_statics, it.span,
                             "static constant should have an uppercase identifier");
            }
        }
        _ => {}
    }
}

958 959 960 961 962 963 964
fn check_pat_non_uppercase_statics(cx: &Context, p: &ast::Pat) {
    // Lint for constants that look like binding identifiers (#7526)
    match (&p.node, cx.tcx.def_map.find(&p.id)) {
        (&ast::PatIdent(_, ref path, _), Some(&ast::DefStatic(_, false))) => {
            // last identifier alone is right choice for this lint.
            let ident = path.segments.last().identifier;
            let s = cx.tcx.sess.str_of(ident);
965
            if s.chars().any(|c| c.is_lowercase()) {
966 967 968 969 970 971 972 973
                cx.span_lint(non_uppercase_pattern_statics, path.span,
                             "static constant in pattern should be all caps");
            }
        }
        _ => {}
    }
}

974 975
fn check_unused_unsafe(cx: &Context, e: &ast::Expr) {
    match e.node {
D
Daniel Micay 已提交
976
        // Don't warn about generated blocks, that'll just pollute the output.
977 978 979 980 981
        ast::ExprBlock(ref blk) => {
            if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
                !cx.tcx.used_unsafe.contains(&blk.id) {
                cx.span_lint(unused_unsafe, blk.span,
                             "unnecessary `unsafe` block");
982 983
            }
        }
984
        _ => ()
985
    }
986 987
}

D
Daniel Micay 已提交
988 989 990 991 992 993 994 995 996 997
fn check_unsafe_block(cx: &Context, e: &ast::Expr) {
    match e.node {
        // Don't warn about generated blocks, that'll just pollute the output.
        ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
            cx.span_lint(unsafe_block, blk.span, "usage of an `unsafe` block");
        }
        _ => ()
    }
}

S
Seo Sanghyeon 已提交
998
fn check_unused_mut_pat(cx: &Context, p: &ast::Pat) {
999
    match p.node {
1000 1001 1002 1003
        ast::PatIdent(ast::BindByValue(ast::MutMutable),
                      ref path, _) if pat_util::pat_is_binding(cx.tcx.def_map, p)=> {
            // `let mut _a = 1;` doesn't need a warning.
            let initial_underscore = match path.segments {
A
Alex Crichton 已提交
1004
                [ast::PathSegment { identifier: id, .. }] => {
1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016
                    cx.tcx.sess.str_of(id).starts_with("_")
                }
                _ => {
                    cx.tcx.sess.span_bug(p.span,
                                         "mutable binding that doesn't \
                                         consist of exactly one segment");
                }
            };

            if !initial_underscore && !cx.tcx.used_mut_nodes.contains(&p.id) {
                cx.span_lint(unused_mut, p.span,
                             "variable does not need to be mutable");
1017 1018
            }
        }
1019 1020
        _ => ()
    }
1021 1022
}

1023
fn check_unnecessary_allocation(cx: &Context, e: &ast::Expr) {
1024 1025
    // Warn if string and vector literals with sigils are immediately borrowed.
    // Those can have the sigil removed.
1026 1027 1028 1029
    match e.node {
        ast::ExprVstore(e2, ast::ExprVstoreUniq) |
        ast::ExprVstore(e2, ast::ExprVstoreBox) => {
            match e2.node {
A
Alex Crichton 已提交
1030 1031
                ast::ExprLit(@codemap::Spanned{node: ast::lit_str(..), ..}) |
                ast::ExprVec(..) => {}
1032
                _ => return
1033 1034 1035
            }
        }

1036
        _ => return
1037 1038
    }

1039 1040
    match cx.tcx.adjustments.find_copy(&e.id) {
        Some(@ty::AutoDerefRef(ty::AutoDerefRef {
A
Alex Crichton 已提交
1041
            autoref: Some(ty::AutoBorrowVec(..)), .. })) => {
1042 1043 1044
            cx.span_lint(unnecessary_allocation, e.span,
                         "unnecessary allocation, the sigil can be removed");
        }
1045

1046 1047
        _ => ()
    }
1048 1049
}

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
fn check_missing_doc_attrs(cx: &Context,
                           id: ast::NodeId,
                           attrs: &[ast::Attribute],
                           sp: Span,
                           desc: &'static str) {
    // If we're building a test harness, then warning about
    // documentation is probably not really relevant right now.
    if cx.tcx.sess.opts.test { return }

    // `#[doc(hidden)]` disables missing_doc check.
    if cx.is_doc_hidden { return }

    // Only check publicly-visible items, using the result from the
    // privacy pass.
    if !cx.exported_items.contains(&id) { return }

    if !attrs.iter().any(|a| a.node.is_sugared_doc) {
        cx.span_lint(missing_doc, sp,
                     format!("missing documentation for {}", desc));
1069
    }
1070
}
1071

1072 1073
fn check_missing_doc_item(cx: &mut Context, it: &ast::item) { // XXX doesn't need to be mut
    let desc = match it.node {
A
Alex Crichton 已提交
1074 1075 1076 1077 1078
        ast::item_fn(..) => "a function",
        ast::item_mod(..) => "a module",
        ast::item_enum(..) => "an enum",
        ast::item_struct(..) => "a struct",
        ast::item_trait(..) => "a trait",
1079 1080 1081 1082
        _ => return
    };
    check_missing_doc_attrs(cx, it.id, it.attrs, it.span, desc);
}
1083

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093
fn check_missing_doc_method(cx: &Context, m: &ast::method) {
    let did = ast::DefId {
        crate: ast::LOCAL_CRATE,
        node: m.id
    };
    match cx.tcx.methods.find(&did) {
        None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
        Some(md) => {
            match md.container {
                // Always check default methods defined on traits.
A
Alex Crichton 已提交
1094
                ty::TraitContainer(..) => {}
1095 1096 1097 1098 1099
                // For methods defined on impls, it depends on whether
                // it is an implementation for a trait or is a plain
                // impl.
                ty::ImplContainer(cid) => {
                    match ty::impl_trait_ref(cx.tcx, cid) {
A
Alex Crichton 已提交
1100
                        Some(..) => return, // impl for trait: don't doc
1101 1102
                        None => {} // plain impl: doc according to privacy
                    }
1103
                }
1104
            }
1105
        }
1106
    }
1107 1108
    check_missing_doc_attrs(cx, m.id, m.attrs, m.span, "a method");
}
1109

1110 1111 1112
fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
    check_missing_doc_attrs(cx, tm.id, tm.attrs, tm.span, "a type method");
}
1113

1114 1115 1116 1117 1118 1119
fn check_missing_doc_struct_field(cx: &Context, sf: &ast::struct_field) {
    match sf.node.kind {
        ast::named_field(_, vis) if vis != ast::private =>
            check_missing_doc_attrs(cx, cx.cur_struct_def_id, sf.node.attrs,
                                    sf.span, "a struct field"),
        _ => {}
1120
    }
1121 1122
}

1123 1124 1125 1126
fn check_missing_doc_variant(cx: &Context, v: &ast::variant) {
    check_missing_doc_attrs(cx, v.node.id, v.node.attrs, v.span, "a variant");
}

1127 1128
/// Checks for use of items with #[deprecated], #[experimental] and
/// #[unstable] (or none of them) attributes.
1129 1130
fn check_stability(cx: &Context, e: &ast::Expr) {
    let def = match e.node {
A
Alex Crichton 已提交
1131 1132 1133
        ast::ExprMethodCall(..) |
        ast::ExprPath(..) |
        ast::ExprStruct(..) => {
1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
            match cx.tcx.def_map.find(&e.id) {
                Some(&def) => def,
                None => return
            }
        }
        _ => return
    };

    let id = ast_util::def_id_of_def(def);

    let stability = if ast_util::is_local(id) {
        // this crate
        match cx.tcx.items.find(&id.node) {
            Some(ast_node) => {
1148 1149
                let s = ast_node.with_attrs(|attrs| {
                    attrs.map(|a| {
1150
                        attr::find_stability(a.iter().map(|a| a.meta()))
1151 1152
                    })
                });
1153 1154 1155 1156 1157 1158 1159
                match s {
                    Some(s) => s,

                    // no possibility of having attributes
                    // (e.g. it's a local variable), so just
                    // ignore it.
                    None => return
1160 1161
                }
            }
1162 1163 1164 1165 1166 1167 1168 1169
            _ => cx.tcx.sess.bug(format!("handle_def: {:?} not found", id))
        }
    } else {
        // cross-crate

        let mut s = None;
        // run through all the attributes and take the first
        // stability one.
1170
        csearch::get_item_attrs(cx.tcx.cstore, id, |meta_items| {
1171 1172
            if s.is_none() {
                s = attr::find_stability(meta_items.move_iter())
1173
            }
1174
        });
1175 1176
        s
    };
1177

1178 1179 1180
    let (lint, label) = match stability {
        // no stability attributes == Unstable
        None => (unstable, "unmarked"),
A
Alex Crichton 已提交
1181
        Some(attr::Stability { level: attr::Unstable, .. }) =>
1182
                (unstable, "unstable"),
A
Alex Crichton 已提交
1183
        Some(attr::Stability { level: attr::Experimental, .. }) =>
1184
                (experimental, "experimental"),
A
Alex Crichton 已提交
1185
        Some(attr::Stability { level: attr::Deprecated, .. }) =>
1186 1187 1188
                (deprecated, "deprecated"),
        _ => return
    };
1189

1190
    let msg = match stability {
A
Alex Crichton 已提交
1191
        Some(attr::Stability { text: Some(ref s), .. }) => {
1192 1193 1194 1195
            format!("use of {} item: {}", label, *s)
        }
        _ => format!("use of {} item", label)
    };
1196

1197
    cx.span_lint(lint, e.span, msg);
1198 1199
}

1200
impl<'self> Visitor<()> for Context<'self> {
1201
    fn visit_item(&mut self, it: @ast::item, _: ()) {
1202
        self.with_lint_attrs(it.attrs, |cx| {
1203 1204 1205 1206
            check_item_ctypes(cx, it);
            check_item_non_camel_case_types(cx, it);
            check_item_non_uppercase_statics(cx, it);
            check_heap_item(cx, it);
1207
            check_missing_doc_item(cx, it);
K
klutzy 已提交
1208
            check_attrs_usage(cx, it.attrs);
1209

1210
            cx.visit_ids(|v| v.visit_item(it, ()));
1211

1212
            visit::walk_item(cx, it, ());
1213
        })
1214 1215
    }

K
klutzy 已提交
1216
    fn visit_foreign_item(&mut self, it: @ast::foreign_item, _: ()) {
P
Patrick Walton 已提交
1217
        self.with_lint_attrs(it.attrs, |cx| {
K
klutzy 已提交
1218 1219
            check_attrs_usage(cx, it.attrs);
            visit::walk_foreign_item(cx, it, ());
P
Patrick Walton 已提交
1220
        })
K
klutzy 已提交
1221 1222 1223
    }

    fn visit_view_item(&mut self, i: &ast::view_item, _: ()) {
P
Patrick Walton 已提交
1224
        self.with_lint_attrs(i.attrs, |cx| {
K
klutzy 已提交
1225 1226
            check_attrs_usage(cx, i.attrs);
            visit::walk_view_item(cx, i, ());
P
Patrick Walton 已提交
1227
        })
K
klutzy 已提交
1228 1229
    }

S
Seo Sanghyeon 已提交
1230
    fn visit_pat(&mut self, p: &ast::Pat, _: ()) {
1231
        check_pat_non_uppercase_statics(self, p);
1232 1233
        check_unused_mut_pat(self, p);

1234
        visit::walk_pat(self, p, ());
1235 1236
    }

1237
    fn visit_expr(&mut self, e: @ast::Expr, _: ()) {
1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
        match e.node {
            ast::ExprUnary(_, ast::UnNeg, expr) => {
                // propagate negation, if the negation itself isn't negated
                if self.negated_expr_id != e.id {
                    self.negated_expr_id = expr.id;
                }
            },
            ast::ExprParen(expr) => if self.negated_expr_id == e.id {
                self.negated_expr_id = expr.id
            },
            _ => ()
        };

1251 1252 1253
        check_while_true_expr(self, e);
        check_stability(self, e);
        check_unused_unsafe(self, e);
D
Daniel Micay 已提交
1254
        check_unsafe_block(self, e);
1255 1256
        check_unnecessary_allocation(self, e);
        check_heap_expr(self, e);
1257

1258
        check_type_limits(self, e);
1259

1260
        visit::walk_expr(self, e, ());
1261 1262
    }

1263 1264
    fn visit_stmt(&mut self, s: @ast::Stmt, _: ()) {
        check_path_statement(self, s);
1265

1266 1267
        visit::walk_stmt(self, s, ());
    }
1268

1269 1270 1271 1272 1273
    fn visit_fn(&mut self, fk: &visit::fn_kind, decl: &ast::fn_decl,
                body: &ast::Block, span: Span, id: ast::NodeId, _: ()) {
        let recurse = |this: &mut Context| {
            visit::walk_fn(this, fk, decl, body, span, id, ());
        };
1274

1275 1276
        match *fk {
            visit::fk_method(_, _, m) => {
1277
                self.with_lint_attrs(m.attrs, |cx| {
1278
                    check_missing_doc_method(cx, m);
K
klutzy 已提交
1279
                    check_attrs_usage(cx, m.attrs);
1280

1281
                    cx.visit_ids(|v| {
1282
                        v.visit_fn(fk, decl, body, span, id, ());
1283
                    });
1284
                    recurse(cx);
1285
                })
1286 1287 1288
            }
            _ => recurse(self),
        }
1289
    }
1290

K
klutzy 已提交
1291

1292
    fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
1293
        self.with_lint_attrs(t.attrs, |cx| {
1294
            check_missing_doc_ty_method(cx, t);
K
klutzy 已提交
1295
            check_attrs_usage(cx, t.attrs);
1296 1297

            visit::walk_ty_method(cx, t, ());
1298
        })
1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
    }

    fn visit_struct_def(&mut self,
                        s: @ast::struct_def,
                        i: ast::Ident,
                        g: &ast::Generics,
                        id: ast::NodeId,
                        _: ()) {
        let old_id = self.cur_struct_def_id;
        self.cur_struct_def_id = id;
        visit::walk_struct_def(self, s, i, g, id, ());
        self.cur_struct_def_id = old_id;
    }

    fn visit_struct_field(&mut self, s: @ast::struct_field, _: ()) {
1314
        self.with_lint_attrs(s.node.attrs, |cx| {
1315
            check_missing_doc_struct_field(cx, s);
K
klutzy 已提交
1316
            check_attrs_usage(cx, s.node.attrs);
1317 1318

            visit::walk_struct_field(cx, s, ());
1319
        })
1320 1321 1322
    }

    fn visit_variant(&mut self, v: &ast::variant, g: &ast::Generics, _: ()) {
1323
        self.with_lint_attrs(v.node.attrs, |cx| {
1324
            check_missing_doc_variant(cx, v);
K
klutzy 已提交
1325
            check_attrs_usage(cx, v.node.attrs);
1326 1327

            visit::walk_variant(cx, v, g, ());
1328
        })
1329
    }
1330
}
1331

1332
impl<'self> IdVisitingOperation for Context<'self> {
1333 1334 1335 1336 1337 1338
    fn visit_id(&self, id: ast::NodeId) {
        match self.tcx.sess.lints.pop(&id) {
            None => {}
            Some(l) => {
                for (lint, span, msg) in l.move_iter() {
                    self.span_lint(lint, span, msg)
1339
                }
1340 1341
            }
        }
1342
    }
1343 1344
}

1345 1346 1347
pub fn check_crate(tcx: ty::ctxt,
                   exported_items: &privacy::ExportedItems,
                   crate: &ast::Crate) {
1348
    let mut cx = Context {
1349
        dict: @get_lint_dict(),
1350
        cur: SmallIntMap::new(),
1351
        tcx: tcx,
1352 1353 1354
        exported_items: exported_items,
        cur_struct_def_id: -1,
        is_doc_hidden: false,
1355
        lint_stack: ~[],
1356
        negated_expr_id: -1
1357 1358
    };

1359 1360
    // Install default lint levels, followed by the command line levels, and
    // then actually visit the whole crate.
D
Daniel Micay 已提交
1361
    for (_, spec) in cx.dict.iter() {
1362
        cx.set_level(spec.lint, spec.default, Default);
1363
    }
D
Daniel Micay 已提交
1364
    for &(lint, level) in tcx.sess.opts.lint_opts.iter() {
1365
        cx.set_level(lint, level, CommandLine);
1366
    }
1367
    cx.with_lint_attrs(crate.attrs, |cx| {
1368
        cx.visit_id(ast::CRATE_NODE_ID);
1369
        cx.visit_ids(|v| {
1370 1371
            v.visited_outermost = true;
            visit::walk_crate(v, crate, ());
1372
        });
K
klutzy 已提交
1373 1374 1375

        check_crate_attrs_usage(cx, crate.attrs);

1376
        visit::walk_crate(cx, crate, ());
1377
    });
1378

1379 1380
    // If we missed any lints added to the session, then there's a bug somewhere
    // in the iteration code.
D
Daniel Micay 已提交
1381
    for (id, v) in tcx.sess.lints.iter() {
1382 1383 1384 1385 1386 1387 1388
        for &(lint, span, ref msg) in v.iter() {
            tcx.sess.span_bug(span, format!("unprocessed lint {:?} at {}: {}",
                                            lint,
                                            ast_map::node_id_to_str(tcx.items,
                                                *id,
                                                token::get_ident_interner()),
                                            *msg))
1389 1390
        }
    }
1391

1392
    tcx.sess.abort_if_errors();
1393
}