lint.rs 32.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 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.

11
use driver::session;
P
Patrick Walton 已提交
12
use middle::ty;
13
use middle::pat_util;
P
Patrick Walton 已提交
14
use util::ppaux::{ty_to_str};
15

16
use core::hashmap::HashMap;
D
Daniel Micay 已提交
17
use std::smallintmap::SmallIntMap;
18 19
use syntax::attr;
use syntax::codemap::span;
J
John Clements 已提交
20
use syntax::codemap;
21
use syntax::{ast, visit, ast_util};
22

23 24 25 26 27 28 29
/**
 * 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.
 *
30 31 32 33 34
 * 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
35
 * level at that location is.
36
 *
37 38 39 40 41 42
 * 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.
43
 *
44 45 46 47 48 49 50 51 52 53 54 55 56 57
 * At each node of the ast which can modify lint attributes, all known lint
 * passes are also applied.  Each lint pass is a visit::vt<()> structure. These
 * visitors are constructed via the lint_*() functions below. There are also
 * some lint checks which operate directly on ast nodes (such as @ast::item),
 * and those are organized as check_item_*(). Each visitor added to the lint
 * context is modified to stop once it reaches a node which could alter the lint
 * levels. This means that everything is looked at once and only once by every
 * lint pass.
 *
 * With this all in place, 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
 * lint pass in this module which is just an ast visitor. The context used when
 * traversing the ast has a `span_lint` method which only needs the span of the
 * item that's being warned about.
58
 */
59

60
#[deriving(Eq)]
61
pub enum lint {
P
Patrick Walton 已提交
62
    ctypes,
63
    unused_imports,
64 65
    while_true,
    path_statement,
66
    implicit_copies,
67
    unrecognized_lint,
68
    non_implicitly_copyable_typarams,
69
    deprecated_pattern,
70
    non_camel_case_types,
71
    type_limits,
72
    default_methods,
73
    unused_unsafe,
74

75 76 77 78
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

79 80
    unused_variable,
    dead_assignment,
81
    unused_mut,
82
    unnecessary_allocation,
83 84
}

85
pub fn level_to_str(lv: level) -> &'static str {
86
    match lv {
87 88 89 90
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
91 92 93
    }
}

94
#[deriving(Eq)]
95
pub enum level {
96
    allow, warn, deny, forbid
97 98
}

99 100
struct LintSpec {
    lint: lint,
101
    desc: &'static str,
102
    default: level
103
}
104

105 106 107 108 109 110 111
pub type LintDict = HashMap<~str, LintSpec>;

enum AttributedNode<'self> {
    Item(@ast::item),
    Method(&'self ast::method),
    Crate(@ast::crate),
}
112

113 114 115 116 117 118 119
#[deriving(Eq)]
enum LintSource {
    Node(span),
    Default,
    CommandLine
}

S
Sangeun Kim 已提交
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 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 186 187 188 189 190 191 192 193 194 195 196 197 198 199 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 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
static lint_table: &'static [(&'static str, LintSpec)] = &[
    ("ctypes",
     LintSpec {
        lint: ctypes,
        desc: "proper use of core::libc types in foreign modules",
        default: warn
     }),

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

    ("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_implicitly_copyable_typarams",
     LintSpec {
        lint: non_implicitly_copyable_typarams,
        desc: "passing non implicitly copyable types as copy type params",
        default: warn
     }),

    ("implicit_copies",
     LintSpec {
        lint: implicit_copies,
        desc: "implicit copies of non implicitly copyable data",
        default: warn
     }),

    ("deprecated_pattern",
     LintSpec {
        lint: deprecated_pattern,
        desc: "warn about deprecated uses of pattern bindings",
        default: allow
     }),

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

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

    ("default_methods",
     LintSpec {
        lint: default_methods,
        desc: "allow default methods",
        default: deny
     }),

    ("unused_unsafe",
     LintSpec {
        lint: unused_unsafe,
        desc: "unnecessary use of an `unsafe` block",
        default: warn
    }),

    ("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
    }),
246 247 248 249 250 251 252

    ("unnecessary_allocation",
     LintSpec {
        lint: unnecessary_allocation,
        desc: "detects unnecessary allocations that can be eliminated",
        default: warn
    }),
S
Sangeun Kim 已提交
253 254
];

255 256 257 258
/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
259
pub fn get_lint_dict() -> LintDict {
260
    let mut map = HashMap::new();
S
Sangeun Kim 已提交
261
    for lint_table.each|&(k, v)| {
S
Sangeun Kim 已提交
262
        map.insert(k.to_str(), v);
263
    }
264
    return map;
265 266
}

267
struct Context {
268 269 270
    // All known lint modes (string versions)
    dict: @LintDict,
    // Current levels of each lint warning
271
    curr: SmallIntMap<(level, LintSource)>,
272 273 274 275 276
    // context we're checking in (used to access fields like sess)
    tcx: ty::ctxt,
    // 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.
277
    lint_stack: ~[(lint, level, LintSource)],
278 279 280 281 282 283
    // Each of these visitors represents a lint pass. A number of the lint
    // attributes are registered by adding a visitor to iterate over the ast.
    // Others operate directly on @ast::item structures (or similar). Finally,
    // others still are added to the Session object via `add_lint`, and these
    // are all passed with the lint_session visitor.
    visitors: ~[visit::vt<()>],
284
}
285

286
impl Context {
287
    fn get_level(&self, lint: lint) -> level {
288
        match self.curr.find(&(lint as uint)) {
289
          Some(&(lvl, _)) => lvl,
290 291
          None => allow
        }
292 293
    }

294 295 296 297 298 299 300 301
    fn get_source(&self, lint: lint) -> LintSource {
        match self.curr.find(&(lint as uint)) {
          Some(&(_, src)) => src,
          None => Default
        }
    }

    fn set_level(&mut self, lint: lint, level: level, src: LintSource) {
302
        if level == allow {
D
Daniel Micay 已提交
303
            self.curr.remove(&(lint as uint));
304
        } else {
305 306 307 308 309 310 311 312 313
            self.curr.insert(lint as uint, (level, src));
        }
    }

    fn lint_to_str(&self, lint: lint) -> ~str {
        for self.dict.each |k, v| {
            if v.lint == lint {
                return copy *k;
            }
314
        }
315
        fail!("unregistered lint %?", lint);
316 317
    }

318
    fn span_lint(&self, lint: lint, span: span, msg: &str) {
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
        let (level, src) = match self.curr.find(&(lint as uint)) {
            Some(&pair) => pair,
            None => { return; }
        };
        if level == allow { return; }

        let mut note = None;
        let msg = match src {
            Default | CommandLine => {
                fmt!("%s [-%c %s%s]", msg, match level {
                        warn => 'W', deny => 'D', forbid => 'F',
                        allow => fail!()
                    }, str::replace(self.lint_to_str(lint), "_", "-"),
                    if src == Default { " (default)" } else { "" })
            },
            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);  }
            allow => fail!(),
        }

        for note.each |&span| {
            self.tcx.sess.span_note(span, "lint level defined here");
        }
348 349
    }

350
    /**
351
     * Merge the lints specified by any lint attributes into the
352
     * current lint context, call the provided function, then reset the
353
     * lints in effect to their previous state.
354
     */
355 356 357 358 359 360
    fn with_lint_attrs(@mut self, attrs: &[ast::attribute], f: &fn()) {
        // 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;
361
        for each_lint(self.tcx.sess, attrs) |meta, level, lintname| {
362
            let lint = match self.dict.find(lintname) {
B
Brian Anderson 已提交
363
              None => {
364
                self.span_lint(
365
                    unrecognized_lint,
366
                    meta.span,
P
Paul Stansifer 已提交
367
                    fmt!("unknown `%s` attribute: `%s`",
368
                         level_to_str(level), *lintname));
369
                loop
370
              }
371 372
              Some(lint) => { lint.lint }
            };
373

374 375 376 377 378 379
            let now = self.get_level(lint);
            if now == forbid && level != forbid {
                self.tcx.sess.span_err(meta.span,
                    fmt!("%s(%s) overruled by outer forbid(%s)",
                         level_to_str(level),
                         *lintname, *lintname));
380
                loop;
381
            }
382

383
            if now != level {
384 385
                let src = self.get_source(lint);
                self.lint_stack.push((lint, now, src));
386
                pushed += 1;
387
                self.set_level(lint, level, Node(meta.span));
388
            }
389
        }
390

391
        f();
392

393 394
        // rollback
        for pushed.times {
395 396
            let (lint, lvl, src) = self.lint_stack.pop();
            self.set_level(lint, lvl, src);
397
        }
398 399
    }

400 401
    fn add_lint(&mut self, v: visit::vt<()>) {
        self.visitors.push(item_stopping_visitor(v));
402 403
    }

404
    fn process(&self, n: AttributedNode) {
405
        match n {
406 407 408 409 410 411 412 413 414 415
            Item(it) => {
                for self.visitors.each |v| {
                    visit::visit_item(it, (), *v);
                }
            }
            Crate(c) => {
                for self.visitors.each |v| {
                    visit::visit_crate(c, (), *v);
                }
            }
416 417 418
            // Can't use visit::visit_method_helper because the
            // item_stopping_visitor has overridden visit_fn(&fk_method(... ))
            // to be a no-op, so manually invoke visit_fn.
419 420 421 422 423 424 425
            Method(m) => {
                let fk = visit::fk_method(copy m.ident, &m.generics, m);
                for self.visitors.each |v| {
                    visit::visit_fn(&fk, &m.decl, &m.body, m.span, m.id,
                                    (), *v);
                }
            }
426 427 428 429
        }
    }
}

430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
pub fn each_lint(sess: session::Session,
                 attrs: &[ast::attribute],
                 f: &fn(@ast::meta_item, level, &~str) -> bool) -> bool
{
    for [allow, warn, deny, forbid].each |&level| {
        let level_name = level_to_str(level);
        let attrs = attr::find_attrs_by_name(attrs, level_name);
        for attrs.each |attr| {
            let meta = attr.node.value;
            let metas = match meta.node {
                ast::meta_list(_, ref metas) => metas,
                _ => {
                    sess.span_err(meta.span, ~"malformed lint attribute");
                    loop;
                }
            };
            for metas.each |meta| {
                match meta.node {
                    ast::meta_word(lintname) => {
                        if !f(*meta, level, lintname) {
                            return false;
                        }
                    }
                    _ => {
454 455 456 457 458 459
                        sess.span_err(meta.span, ~"malformed lint attribute");
                    }
                }
            }
        }
    }
460
    return true;
461 462
}

463 464 465 466
// Take a visitor, and modify it so that it will not proceed past subitems.
// This is used to make the simple visitors used for the lint passes
// not traverse into subitems, since that is handled by the outer
// lint visitor.
467 468 469 470 471 472 473 474 475 476
fn item_stopping_visitor<E: Copy>(v: visit::vt<E>) -> visit::vt<E> {
    visit::mk_vt(@visit::Visitor {
        visit_item: |_i, _e, _v| { },
        visit_fn: |fk, fd, b, s, id, e, v| {
            match *fk {
                visit::fk_method(*) => {}
                _ => visit::visit_fn(fk, fd, b, s, id, e, v)
            }
        },
    .. **(ty_stopping_visitor(v))})
477 478 479 480
}

fn ty_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
    visit::mk_vt(@visit::Visitor {visit_ty: |_t, _e, _v| { },.. **v})
481 482
}

483 484
fn lint_while_true(cx: @mut Context) -> visit::vt<()> {
    visit::mk_simple_visitor(@visit::SimpleVisitor {
485 486 487 488 489 490 491 492 493 494
        visit_expr: |e: @ast::expr| {
            match e.node {
                ast::expr_while(cond, _) => {
                    match cond.node {
                        ast::expr_lit(@codemap::spanned {
                            node: ast::lit_bool(true), _}) =>
                        {
                            cx.span_lint(while_true, e.span,
                                         "denote infinite loops with \
                                          loop { ... }");
495
                        }
496
                        _ => ()
497 498
                    }
                }
499 500 501 502
                _ => ()
            }
        },
        .. *visit::default_simple_visitor()
503
    })
V
Viktor Dahl 已提交
504 505
}

506
fn lint_type_limits(cx: @mut Context) -> visit::vt<()> {
507
    fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
508 509 510 511 512 513 514
            min: T, max: T) -> bool {
        match binop {
            ast::lt => v <= max,
            ast::le => v < max,
            ast::gt => v >= min,
            ast::ge => v > min,
            ast::eq | ast::ne => v >= min && v <= max,
515
            _ => fail!()
516 517 518
        }
    }

519
    fn rev_binop(binop: ast::binop) -> ast::binop {
520 521 522 523 524 525 526 527 528
        match binop {
            ast::lt => ast::gt,
            ast::le => ast::ge,
            ast::gt => ast::lt,
            ast::ge => ast::le,
            _ => binop
        }
    }

529 530
    // for int & uint, be conservative with the warnings, so that the
    // warnings are consistent between 32- and 64-bit platforms
531
    fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
532
        match int_ty {
533
            ast::ty_i =>    (i64::min_value,        i64::max_value),
534 535 536 537 538 539 540 541
            ast::ty_char => (u32::min_value as i64, u32::max_value as i64),
            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)
        }
    }

542
    fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
543
        match uint_ty {
544
            ast::ty_u =>   (u64::min_value,         u64::max_value),
545 546 547 548 549 550 551
            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)
        }
    }

552
    fn check_limits(cx: @mut Context, binop: ast::binop, l: &ast::expr,
553
                    r: &ast::expr) -> bool {
554 555 556
        let (lit, expr, swap) = match (&l.node, &r.node) {
            (&ast::expr_lit(_), _) => (l, r, true),
            (_, &ast::expr_lit(_)) => (r, l, false),
557 558 559 560
            _ => return true
        };
        // Normalize the binop so that the literal is always on the RHS in
        // the comparison
561
        let norm_binop = if swap {
562 563 564 565
            rev_binop(binop)
        } else {
            binop
        };
566
        match ty::get(ty::expr_ty(cx.tcx, @/*bad*/copy *expr)).sty {
567 568 569 570 571 572 573 574 575
            ty::ty_int(int_ty) => {
                let (min, max) = int_ty_range(int_ty);
                let lit_val: i64 = match lit.node {
                    ast::expr_lit(@li) => match li.node {
                        ast::lit_int(v, _) => v,
                        ast::lit_uint(v, _) => v as i64,
                        ast::lit_int_unsuffixed(v) => v,
                        _ => return true
                    },
576
                    _ => fail!()
577 578 579 580 581 582 583 584 585 586 587 588
                };
                is_valid(norm_binop, lit_val, min, max)
            }
            ty::ty_uint(uint_ty) => {
                let (min, max): (u64, u64) = uint_ty_range(uint_ty);
                let lit_val: u64 = match lit.node {
                    ast::expr_lit(@li) => match li.node {
                        ast::lit_int(v, _) => v as u64,
                        ast::lit_uint(v, _) => v,
                        ast::lit_int_unsuffixed(v) => v as u64,
                        _ => return true
                    },
589
                    _ => fail!()
590 591 592 593 594 595 596
                };
                is_valid(norm_binop, lit_val, min, max)
            }
            _ => true
        }
    }

597
    fn is_comparison(binop: ast::binop) -> bool {
598 599 600 601 602 603 604
        match binop {
            ast::eq | ast::lt | ast::le |
            ast::ne | ast::ge | ast::gt => true,
            _ => false
        }
    }

605 606 607 608 609
    let visit_expr: @fn(@ast::expr) = |e| {
        match e.node {
            ast::expr_binary(ref binop, @ref l, @ref r) => {
                if is_comparison(*binop)
                    && !check_limits(cx, *binop, l, r) {
610 611
                    cx.span_lint(type_limits, e.span,
                                 "comparison is useless due to type limits");
612 613
                }
            }
614 615 616 617
            _ => ()
        }
    };

618
    visit::mk_simple_visitor(@visit::SimpleVisitor {
619 620
        visit_expr: visit_expr,
        .. *visit::default_simple_visitor()
621
    })
622 623
}

624
fn check_item_default_methods(cx: @mut Context, item: @ast::item) {
625 626
    match item.node {
        ast::item_trait(_, _, ref methods) => {
627 628 629 630
            for methods.each |method| {
                match *method {
                    ast::required(*) => {}
                    ast::provided(*) => {
631 632
                        cx.span_lint(default_methods, item.span,
                                     "default methods are experimental");
633 634 635 636 637 638 639 640
                    }
                }
            }
        }
        _ => {}
    }
}

641 642 643
fn check_item_ctypes(cx: @mut Context, it: @ast::item) {

    fn check_foreign_fn(cx: @mut Context, decl: &ast::fn_decl) {
B
Brian Anderson 已提交
644
        let tys = vec::map(decl.inputs, |a| a.ty );
645
        for vec::each(vec::append_one(tys, decl.output)) |ty| {
646
            match ty.node {
B
Brian Anderson 已提交
647
              ast::ty_path(_, id) => {
648
                match cx.tcx.def_map.get_copy(&id) {
B
Brian Anderson 已提交
649
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
650
                    cx.span_lint(ctypes, ty.span,
S
Seo Sanghyeon 已提交
651
                        "found rust type `int` in foreign module, while \
652
                         libc::c_int or libc::c_long should be used");
653
                  }
B
Brian Anderson 已提交
654
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) => {
655
                    cx.span_lint(ctypes, ty.span,
S
Seo Sanghyeon 已提交
656
                        "found rust type `uint` in foreign module, while \
657
                         libc::c_uint or libc::c_ulong should be used");
658
                  }
B
Brian Anderson 已提交
659
                  _ => ()
660
                }
661
              }
B
Brian Anderson 已提交
662
              _ => ()
663 664 665 666
            }
        }
    }

667
    match it.node {
668
      ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
B
Brian Anderson 已提交
669
        for nmod.items.each |ni| {
670
            match ni.node {
671
              ast::foreign_item_fn(ref decl, _, _) => {
672
                check_foreign_fn(cx, decl);
673
              }
674 675
              // FIXME #4622: Not implemented.
              ast::foreign_item_const(*) => {}
676 677
            }
        }
678
      }
B
Brian Anderson 已提交
679
      _ => {/* nothing to do */ }
680 681
    }
}
682

683 684
fn check_type_for_lint(cx: @mut Context, lint: lint, span: span, ty: ty::t) {
    if cx.get_level(lint) == allow { return }
685

686 687 688 689 690 691 692 693 694 695
    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
    });
696

697 698 699 700 701
    if n_uniq > 0 && lint != managed_heap_memory {
        let s = ty_to_str(cx.tcx, ty);
        let m = ~"type uses owned (~ type) pointers: " + s;
        cx.span_lint(lint, span, m);
    }
702

703 704 705 706
    if n_box > 0 && lint != owned_heap_memory {
        let s = ty_to_str(cx.tcx, ty);
        let m = ~"type uses managed (@ type) pointers: " + s;
        cx.span_lint(lint, span, m);
707
    }
708
}
709

710 711 712
fn check_type(cx: @mut Context, span: span, ty: ty::t) {
    for [managed_heap_memory, owned_heap_memory, heap_memory].each |lint| {
        check_type_for_lint(cx, *lint, span, ty);
713
    }
714
}
715

716 717 718 719 720 721 722 723 724 725
fn check_item_heap(cx: @mut Context, it: @ast::item) {
    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
      ast::item_struct(*) => check_type(cx, it.span,
                                        ty::node_id_to_type(cx.tcx,
                                                            it.id)),
      _ => ()
    }
726

727 728 729 730 731 732 733
    // If it's a struct, we also have to check the fields' types
    match it.node {
        ast::item_struct(struct_def, _) => {
            for struct_def.fields.each |struct_field| {
                check_type(cx, struct_field.span,
                           ty::node_id_to_type(cx.tcx,
                                               struct_field.node.id));
734 735 736 737
            }
        }
        _ => ()
    }
738
}
739

740 741
fn lint_heap(cx: @mut Context) -> visit::vt<()> {
    visit::mk_simple_visitor(@visit::SimpleVisitor {
742 743 744 745 746
        visit_expr: |e| {
            let ty = ty::expr_ty(cx.tcx, e);
            check_type(cx, e.span, ty);
        },
        .. *visit::default_simple_visitor()
747
    })
748 749
}

750 751
fn lint_path_statement(cx: @mut Context) -> visit::vt<()> {
    visit::mk_simple_visitor(@visit::SimpleVisitor {
752 753 754 755 756 757 758 759
        visit_stmt: |s| {
            match s.node {
                ast::stmt_semi(
                    @ast::expr { node: ast::expr_path(_), _ },
                    _
                ) => {
                    cx.span_lint(path_statement, s.span,
                                 "path statement with no effect");
760
                }
761 762 763 764
                _ => ()
            }
        },
        .. *visit::default_simple_visitor()
765
    })
766 767
}

768
fn check_item_non_camel_case_types(cx: @mut Context, it: @ast::item) {
P
Paul Stansifer 已提交
769 770
    fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
        let ident = cx.sess.str_of(ident);
P
Patrick Walton 已提交
771
        assert!(!ident.is_empty());
772
        let ident = ident_without_trailing_underscores(*ident);
773
        let ident = ident_without_leading_underscores(ident);
774
        char::is_uppercase(str::char_at(ident, 0)) &&
775 776 777
            !ident.contains_char('_')
    }

778
    fn ident_without_trailing_underscores<'r>(ident: &'r str) -> &'r str {
779
        match str::rfind(ident, |c| c != '_') {
780
            Some(idx) => str::slice(ident, 0, idx + 1),
781
            None => ident, // all underscores
782 783 784
        }
    }

785
    fn ident_without_leading_underscores<'r>(ident: &'r str) -> &'r str {
786
        match str::find(ident, |c| c != '_') {
787
            Some(idx) => str::slice(ident, idx, ident.len()),
788
            None => ident // all underscores
789 790 791
        }
    }

792 793 794 795 796
    fn check_case(cx: @mut Context, ident: ast::ident, span: span) {
        if !is_camel_case(cx.tcx, ident) {
            cx.span_lint(non_camel_case_types, span,
                         "type, variant, or trait should have \
                          a camel case identifier");
797 798 799
        }
    }

800
    match it.node {
E
Erick Tryzelaar 已提交
801 802
        ast::item_ty(*) | ast::item_struct(*) |
        ast::item_trait(*) => {
803
            check_case(cx, it.ident, it.span)
804
        }
E
Erick Tryzelaar 已提交
805
        ast::item_enum(ref enum_definition, _) => {
806
            check_case(cx, it.ident, it.span);
E
Erick Tryzelaar 已提交
807
            for enum_definition.variants.each |variant| {
808
                check_case(cx, variant.node.name, variant.span);
E
Erick Tryzelaar 已提交
809 810 811
            }
        }
        _ => ()
812 813 814
    }
}

815
fn lint_unused_unsafe(cx: @mut Context) -> visit::vt<()> {
816 817 818
    let visit_expr: @fn(@ast::expr) = |e| {
        match e.node {
            ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
819 820 821
                if !cx.tcx.used_unsafe.contains(&blk.node.id) {
                    cx.span_lint(unused_unsafe, blk.span,
                                 "unnecessary `unsafe` block");
822 823 824 825 826 827
                }
            }
            _ => ()
        }
    };

828
    visit::mk_simple_visitor(@visit::SimpleVisitor {
829 830
        visit_expr: visit_expr,
        .. *visit::default_simple_visitor()
831
    })
832 833
}

834
fn lint_unused_mut(cx: @mut Context) -> visit::vt<()> {
835 836 837
    let check_pat: @fn(@ast::pat) = |p| {
        let mut used = false;
        let mut bindings = 0;
838 839
        do pat_util::pat_bindings(cx.tcx.def_map, p) |_, id, _, _| {
            used = used || cx.tcx.used_mut_nodes.contains(&id);
840 841 842 843
            bindings += 1;
        }
        if !used {
            let msg = if bindings == 1 {
S
Seo Sanghyeon 已提交
844
                "variable does not need to be mutable"
845
            } else {
S
Seo Sanghyeon 已提交
846
                "variables do not need to be mutable"
847
            };
848
            cx.span_lint(unused_mut, p.span, msg);
849 850 851 852 853 854 855 856 857 858 859
        }
    };

    let visit_fn_decl: @fn(&ast::fn_decl) = |fd| {
        for fd.inputs.each |arg| {
            if arg.is_mutbl {
                check_pat(arg.pat);
            }
        }
    };

860
    visit::mk_simple_visitor(@visit::SimpleVisitor {
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
        visit_local: |l| {
            if l.node.is_mutbl {
                check_pat(l.node.pat);
            }
        },
        visit_fn: |_, fd, _, _, _| visit_fn_decl(fd),
        visit_ty_method: |tm| visit_fn_decl(&tm.decl),
        visit_struct_method: |sm| visit_fn_decl(&sm.decl),
        visit_trait_method: |tm| {
            match *tm {
                ast::required(ref tm) => visit_fn_decl(&tm.decl),
                ast::provided(m) => visit_fn_decl(&m.decl),
            }
        },
        .. *visit::default_simple_visitor()
876
    })
877 878
}

879 880
fn lint_session(cx: @mut Context) -> visit::vt<()> {
    ast_util::id_visitor(|id| {
881 882 883 884 885
        match cx.tcx.sess.lints.pop(&id) {
            None => {},
            Some(l) => {
                do vec::consume(l) |_, (lint, span, msg)| {
                    cx.span_lint(lint, span, msg)
886
                }
887 888
            }
        }
889
    })
890 891
}

892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
fn lint_unnecessary_allocations(cx: @mut Context) -> visit::vt<()> {
    // If the expression `e` has an allocated type, but `t` dictates that it's
    // something like a slice (doesn't need allocation), emit a warning with the
    // specified span.
    //
    // Currently, this only applies to string and vector literals with sigils in
    // front. Those can have the sigil removed to get a borrowed pointer
    // automatically.
    fn check(cx: @mut Context, e: @ast::expr, t: ty::t) {
        match e.node {
            ast::expr_vstore(e2, ast::expr_vstore_uniq) |
            ast::expr_vstore(e2, ast::expr_vstore_box) => {
                match e2.node {
                    ast::expr_lit(@codemap::spanned{
                            node: ast::lit_str(*), _}) |
                    ast::expr_vec(*) => {}
                    _ => return
                }
            }

            _ => return
        }

        match ty::get(t).sty {
            ty::ty_estr(ty::vstore_slice(*)) |
            ty::ty_evec(_, ty::vstore_slice(*)) => {
                cx.span_lint(unnecessary_allocation,
                             e.span, "unnecessary allocation, the sigil can be \
                                      removed");
            }

            _ => ()
        }
    }

    let visit_expr: @fn(@ast::expr) = |e| {
        match e.node {
            ast::expr_call(c, ref args, _) => {
                let t = ty::node_id_to_type(cx.tcx, c.id);
                let s = ty::ty_fn_sig(t);
                for vec::each2(*args, s.inputs) |e, t| {
                    check(cx, *e, *t);
                }
            }
            ast::expr_method_call(_, _, _, ref args, _) => {
                let t = ty::node_id_to_type(cx.tcx, e.callee_id);
                let s = ty::ty_fn_sig(t);
                for vec::each2(*args, s.inputs) |e, t| {
                    check(cx, *e, *t);
                }
            }
            _ => {}
        }
    };

    visit::mk_simple_visitor(@visit::SimpleVisitor {
        visit_expr: visit_expr,
        .. *visit::default_simple_visitor()
    })
}

953
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
954 955 956 957 958
    let cx = @mut Context {
        dict: @get_lint_dict(),
        curr: SmallIntMap::new(),
        tcx: tcx,
        lint_stack: ~[],
959
        visitors: ~[],
960 961 962 963
    };

    // Install defaults.
    for cx.dict.each_value |spec| {
964
        cx.set_level(spec.lint, spec.default, Default);
965 966 967 968
    }

    // Install command-line options, overriding defaults.
    for tcx.sess.opts.lint_opts.each |&(lint, level)| {
969
        cx.set_level(lint, level, CommandLine);
970 971
    }

972 973 974 975 976 977 978 979
    // Register each of the lint passes with the context
    cx.add_lint(lint_while_true(cx));
    cx.add_lint(lint_path_statement(cx));
    cx.add_lint(lint_heap(cx));
    cx.add_lint(lint_type_limits(cx));
    cx.add_lint(lint_unused_unsafe(cx));
    cx.add_lint(lint_unused_mut(cx));
    cx.add_lint(lint_session(cx));
980
    cx.add_lint(lint_unnecessary_allocations(cx));
981

982 983 984 985 986 987 988 989 990 991 992
    // type inference doesn't like this being declared below, we need to tell it
    // what the type of this first function is...
    let visit_item:
        @fn(@ast::item, @mut Context, visit::vt<@mut Context>) =
    |it, cx, vt| {
        do cx.with_lint_attrs(it.attrs) {
            check_item_ctypes(cx, it);
            check_item_non_camel_case_types(cx, it);
            check_item_default_methods(cx, it);
            check_item_heap(cx, it);

993
            cx.process(Item(it));
994 995 996
            visit::visit_item(it, cx, vt);
        }
    };
997

998
    // Actually perform the lint checks (iterating the ast)
999
    do cx.with_lint_attrs(crate.node.attrs) {
1000
        cx.process(Crate(crate));
1001 1002 1003 1004 1005 1006 1007

        visit::visit_crate(crate, cx, visit::mk_vt(@visit::Visitor {
            visit_item: visit_item,
            visit_fn: |fk, decl, body, span, id, cx, vt| {
                match *fk {
                    visit::fk_method(_, _, m) => {
                        do cx.with_lint_attrs(m.attrs) {
1008
                            cx.process(Method(m));
1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
                            visit::visit_fn(fk, decl, body, span, id, cx, vt);
                        }
                    }
                    _ => {
                        visit::visit_fn(fk, decl, body, span, id, cx, vt);
                    }
                }
            },
            .. *visit::default_visitor()
        }));
    }

1021 1022
    // If we missed any lints added to the session, then there's a bug somewhere
    // in the iteration code.
1023 1024 1025 1026 1027 1028 1029 1030 1031
    for tcx.sess.lints.each |_, v| {
        for v.each |t| {
            match *t {
                (lint, span, ref msg) =>
                    tcx.sess.span_bug(span, fmt!("unprocessed lint %?: %s",
                                                 lint, *msg))
            }
        }
    }
1032

1033
    tcx.sess.abort_if_errors();
1034
}