lint.rs 30.0 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::Session;
12
use driver::session;
P
Patrick Walton 已提交
13
use middle::ty;
14
use middle::pat_util;
P
Patrick Walton 已提交
15
use util::ppaux::{ty_to_str};
16

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

24 25 26 27 28 29 30 31
/**
 * 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.
 *
 * We also build up a table containing information about lint settings, in
32
 * order to allow other passes to take advantage of the lint attribute
33 34
 * infrastructure. To save space, the table is keyed by the id of /items/, not
 * of every expression. When an item has the default settings, the entry will
35
 * be omitted. If we start allowing lint attributes on expressions, we will
36 37 38
 * start having entries for expressions that do not share their enclosing
 * items settings.
 *
39
 * This module then, exports two passes: one that populates the lint
40 41 42 43
 * settings table in the session and is run early in the compile process, and
 * one that does a variety of lint checks, and is run late in the compile
 * process.
 */
44

45
#[deriving(Eq)]
46
pub enum lint {
P
Patrick Walton 已提交
47
    ctypes,
48
    unused_imports,
49 50
    while_true,
    path_statement,
51
    implicit_copies,
52
    unrecognized_lint,
53
    non_implicitly_copyable_typarams,
54
    vecs_implicitly_copyable,
55
    deprecated_pattern,
56
    non_camel_case_types,
57
    type_limits,
58
    default_methods,
59
    deprecated_mutable_fields,
60
    unused_unsafe,
61

62 63 64 65
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

66 67
    unused_variable,
    dead_assignment,
68
    unused_mut,
69 70
}

71
pub fn level_to_str(lv: level) -> &'static str {
72
    match lv {
73 74 75 76
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
77 78 79
    }
}

80
#[deriving(Eq)]
81
pub enum level {
82
    allow, warn, deny, forbid
83 84
}

85 86
struct LintSpec {
    lint: lint,
87
    desc: &'static str,
88
    default: level
89
}
90

91
pub type LintDict = @HashMap<~str, LintSpec>;
92

93 94 95 96
/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
97
pub fn get_lint_dict() -> LintDict {
98
    let v = ~[
99 100
        (~"ctypes",
         LintSpec {
101 102 103 104
            lint: ctypes,
            desc: "proper use of core::libc types in foreign modules",
            default: warn
         }),
105

106 107
        (~"unused_imports",
         LintSpec {
L
Luqman Aden 已提交
108
            lint: unused_imports,
109
            desc: "imports that are never used",
110
            default: warn
111
         }),
112

113 114
        (~"while_true",
         LintSpec {
L
Luqman Aden 已提交
115
            lint: while_true,
116 117 118
            desc: "suggest using loop { } instead of while(true) { }",
            default: warn
         }),
119

120 121
        (~"path_statement",
         LintSpec {
L
Luqman Aden 已提交
122
            lint: path_statement,
123 124 125
            desc: "path statements with no effect",
            default: warn
         }),
126

127 128
        (~"unrecognized_lint",
         LintSpec {
L
Luqman Aden 已提交
129
            lint: unrecognized_lint,
130 131 132
            desc: "unrecognized lint attribute",
            default: warn
         }),
133

134 135
        (~"non_implicitly_copyable_typarams",
         LintSpec {
L
Luqman Aden 已提交
136
            lint: non_implicitly_copyable_typarams,
137 138 139
            desc: "passing non implicitly copyable types as copy type params",
            default: warn
         }),
140

141 142
        (~"vecs_implicitly_copyable",
         LintSpec {
L
Luqman Aden 已提交
143
            lint: vecs_implicitly_copyable,
144
            desc: "make vecs and strs not implicitly copyable \
145
                  (only checked at top level)",
146 147
            default: warn
         }),
148

149 150
        (~"implicit_copies",
         LintSpec {
L
Luqman Aden 已提交
151
            lint: implicit_copies,
152 153 154
            desc: "implicit copies of non implicitly copyable data",
            default: warn
         }),
155

156 157
        (~"deprecated_pattern",
         LintSpec {
L
Luqman Aden 已提交
158
            lint: deprecated_pattern,
159 160 161
            desc: "warn about deprecated uses of pattern bindings",
            default: allow
         }),
162

163 164
        (~"non_camel_case_types",
         LintSpec {
L
Luqman Aden 已提交
165
            lint: non_camel_case_types,
166 167 168
            desc: "types, variants and traits should have camel case names",
            default: allow
         }),
169

170 171
        (~"managed_heap_memory",
         LintSpec {
L
Luqman Aden 已提交
172
            lint: managed_heap_memory,
173 174 175
            desc: "use of managed (@ type) heap memory",
            default: allow
         }),
176

177 178
        (~"owned_heap_memory",
         LintSpec {
L
Luqman Aden 已提交
179
            lint: owned_heap_memory,
180 181 182
            desc: "use of owned (~ type) heap memory",
            default: allow
         }),
183

184 185
        (~"heap_memory",
         LintSpec {
L
Luqman Aden 已提交
186
            lint: heap_memory,
187 188 189
            desc: "use of any (~ type or @ type) heap memory",
            default: allow
         }),
190

191 192
        (~"type_limits",
         LintSpec {
L
Luqman Aden 已提交
193
            lint: type_limits,
194 195 196
            desc: "comparisons made useless by limits of the types involved",
            default: warn
         }),
197

198 199
        (~"default_methods",
         LintSpec {
L
Luqman Aden 已提交
200
            lint: default_methods,
201 202 203
            desc: "allow default methods",
            default: deny
         }),
V
Viktor Dahl 已提交
204

205 206
        (~"deprecated_mutable_fields",
         LintSpec {
207 208 209 210 211
            lint: deprecated_mutable_fields,
            desc: "deprecated mutable fields in structures",
            default: deny
        }),

212 213 214
        (~"unused_unsafe",
         LintSpec {
            lint: unused_unsafe,
215
            desc: "unnecessary use of an `unsafe` block",
216 217 218
            default: warn
        }),

219 220
        (~"unused_variable",
         LintSpec {
L
Luqman Aden 已提交
221
            lint: unused_variable,
222 223
            desc: "detect variables which are not used in any way",
            default: warn
224
        }),
225

226 227
        (~"dead_assignment",
         LintSpec {
L
Luqman Aden 已提交
228
            lint: dead_assignment,
229 230
            desc: "detect assignments that will never be read",
            default: warn
231
        }),
232 233 234 235 236 237 238

        (~"unused_mut",
         LintSpec {
            lint: unused_mut,
            desc: "detect mut variables which don't need to be mutable",
            default: warn
        }),
239
    ];
240
    let mut map = HashMap::new();
241 242 243 244
    do vec::consume(v) |_, (k, v)| {
        map.insert(k, v);
    }
    return @map;
245 246
}

247
// This is a highly not-optimal set of data structure decisions.
D
Daniel Micay 已提交
248
type LintModes = @mut SmallIntMap<level>;
249
type LintModeMap = @mut HashMap<ast::node_id, LintModes>;
250

251
// settings_map maps node ids of items with non-default lint settings
252 253
// to their settings; default_settings contains the settings for everything
// not in the map.
254 255 256
pub struct LintSettings {
    default_settings: LintModes,
    settings_map: LintModeMap
257 258
}

259 260
pub fn mk_lint_settings() -> LintSettings {
    LintSettings {
D
Daniel Micay 已提交
261
        default_settings: @mut SmallIntMap::new(),
262
        settings_map: @mut HashMap::new()
263 264 265 266
    }
}

pub fn get_lint_level(modes: LintModes, lint: lint) -> level {
D
Daniel Micay 已提交
267 268
    match modes.find(&(lint as uint)) {
      Some(&c) => c,
B
Brian Anderson 已提交
269
      None => allow
270 271 272
    }
}

273
pub fn get_lint_settings_level(settings: LintSettings,
274 275 276 277
                               lint_mode: lint,
                               _expr_id: ast::node_id,
                               item_id: ast::node_id)
                            -> level {
278
    match settings.settings_map.find(&item_id) {
279
      Some(&modes) => get_lint_level(modes, lint_mode),
B
Brian Anderson 已提交
280
      None => get_lint_level(settings.default_settings, lint_mode)
281 282 283
    }
}

284 285
// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
286
fn clone_lint_modes(modes: LintModes) -> LintModes {
D
Daniel Micay 已提交
287
    @mut (copy *modes)
288 289
}

290 291 292 293 294
struct Context {
    dict: LintDict,
    curr: LintModes,
    is_default: bool,
    sess: Session
295
}
296

297
pub impl Context {
298
    fn get_level(&self, lint: lint) -> level {
299
        get_lint_level(self.curr, lint)
300 301
    }

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

310
    fn span_lint(&self, level: level, span: span, msg: ~str) {
311
        self.sess.span_lint_level(level, span, msg);
312 313
    }

314
    /**
315
     * Merge the lints specified by any lint attributes into the
316
     * current lint context, call the provided function, then reset the
317
     * lints in effect to their previous state.
318
     */
319
    fn with_lint_attrs(&self, attrs: ~[ast::attribute], f: &fn(Context)) {
320

321
        let mut new_ctxt = *self;
322 323 324
        let mut triples = ~[];

        for [allow, warn, deny, forbid].each |level| {
325
            let level_name = level_to_str(*level);
326
            let metas =
327
                attr::attr_metas(attr::find_attrs_by_name(attrs, level_name));
328
            for metas.each |meta| {
329 330
                match meta.node {
                  ast::meta_list(_, ref metas) => {
331
                    for metas.each |meta| {
332
                        match meta.node {
333
                          ast::meta_word(ref lintname) => {
334 335 336
                            triples.push((*meta,
                                          *level,
                                          /*bad*/copy *lintname));
337
                          }
B
Brian Anderson 已提交
338
                          _ => {
339 340 341
                            self.sess.span_err(
                                meta.span,
                                ~"malformed lint attribute");
342 343 344
                          }
                        }
                    }
345
                  }
B
Brian Anderson 已提交
346
                  _  => {
347 348 349
                    self.sess.span_err(meta.span,
                                       ~"malformed lint attribute");
                  }
350
                }
351 352 353
            }
        }

354 355 356 357 358 359 360
        for triples.each |triple| {
            // FIXME(#3874): it would be nicer to write this...
            // let (meta, level, lintname) = /*bad*/copy *pair;
            let (meta, level, lintname) = match *triple {
                (ref meta, level, lintname) => (meta, level, lintname)
            };

361
            match self.dict.find(lintname) {
B
Brian Anderson 已提交
362
              None => {
363 364 365
                self.span_lint(
                    new_ctxt.get_level(unrecognized_lint),
                    meta.span,
P
Paul Stansifer 已提交
366
                    fmt!("unknown `%s` attribute: `%s`",
367
                         level_to_str(level), *lintname));
368
              }
B
Brian Anderson 已提交
369
              Some(lint) => {
370 371 372 373 374 375

                if new_ctxt.get_level(lint.lint) == forbid &&
                    level != forbid {
                    self.span_lint(
                        forbid,
                        meta.span,
P
Paul Stansifer 已提交
376
                        fmt!("%s(%s) overruled by outer forbid(%s)",
377
                             level_to_str(level),
378
                             *lintname, *lintname));
379 380 381 382 383 384 385
                }

                // we do multiple unneeded copies of the
                // map if many attributes are set, but
                // this shouldn't actually be a problem...

                let c = clone_lint_modes(new_ctxt.curr);
386 387 388 389 390
                new_ctxt = Context {
                    is_default: false,
                    curr: c,
                    .. new_ctxt
                };
391
                new_ctxt.set_level(lint.lint, level);
392
              }
393 394
            }
        }
395
        f(new_ctxt);
396 397 398 399
    }
}


400
fn build_settings_item(i: @ast::item, cx: Context, v: visit::vt<Context>) {
401
    do cx.with_lint_attrs(/*bad*/copy i.attrs) |cx| {
402
        if !cx.is_default {
403
            cx.sess.lint_settings.settings_map.insert(i.id, cx.curr);
404
        }
405
        visit::visit_item(i, cx, v);
406
    }
407
}
408

409
pub fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
410 411
    let cx = Context {
        dict: get_lint_dict(),
D
Daniel Micay 已提交
412
        curr: @mut SmallIntMap::new(),
413 414 415
        is_default: true,
        sess: sess
    };
416 417

    // Install defaults.
418
    for cx.dict.each_value |&spec| {
D
Daniel Micay 已提交
419 420
        cx.set_level(spec.lint, spec.default);
    }
421 422

    // Install command-line options, overriding defaults.
B
Brian Anderson 已提交
423
    for sess.opts.lint_opts.each |pair| {
424
        let (lint,level) = *pair;
425 426 427
        cx.set_level(lint, level);
    }

428
    do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
429
        // Copy out the default settings
430
        for cx.curr.each |&k, &v| {
431
            sess.lint_settings.default_settings.insert(k, v);
432 433
        }

434 435 436 437
        let cx = Context {
            is_default: true,
            .. cx
        };
438

439
        let visit = visit::mk_vt(@visit::Visitor {
B
Brian Anderson 已提交
440 441
            visit_item: build_settings_item,
            .. *visit::default_visitor()
442
        });
443
        visit::visit_crate(crate, cx, visit);
444 445 446 447 448 449 450 451 452
    }

    sess.abort_if_errors();
}

fn check_item(i: @ast::item, cx: ty::ctxt) {
    check_item_ctypes(cx, i);
    check_item_while_true(cx, i);
    check_item_path_statement(cx, i);
453
    check_item_non_camel_case_types(cx, i);
454
    check_item_heap(cx, i);
455
    check_item_type_limits(cx, i);
456
    check_item_default_methods(cx, i);
457
    check_item_deprecated_mutable_fields(cx, i);
458
    check_item_unused_unsafe(cx, i);
459
    check_item_unused_mut(cx, i);
460 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.
fn item_stopping_visitor<E>(v: visit::vt<E>) -> visit::vt<E> {
467 468 469 470 471 472
    visit::mk_vt(@visit::Visitor {visit_item: |_i, _e, _v| { },
        .. **(ty_stopping_visitor(v))})
}

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

475
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
476 477 478 479 480 481
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: |e: @ast::expr| {
                match e.node {
                    ast::expr_while(cond, _) => {
                        match cond.node {
J
John Clements 已提交
482
                            ast::expr_lit(@codemap::spanned {
483 484 485 486 487 488 489 490 491 492
                                node: ast::lit_bool(true), _}) =>
                            {
                                cx.sess.span_lint(
                                    while_true, e.id, it.id,
                                    e.span,
                                    ~"denote infinite loops \
                                      with loop { ... }");
                            }
                            _ => ()
                        }
493
                    }
B
Brian Anderson 已提交
494
                    _ => ()
495
                }
496 497 498
            },
            .. *visit::default_simple_visitor()
        }));
499
    visit::visit_item(it, (), visit);
V
Viktor Dahl 已提交
500 501 502
}

fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
503
    fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
504 505 506 507 508 509 510
            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,
511
            _ => fail!()
512 513 514
        }
    }

515
    fn rev_binop(binop: ast::binop) -> ast::binop {
516 517 518 519 520 521 522 523 524
        match binop {
            ast::lt => ast::gt,
            ast::le => ast::ge,
            ast::gt => ast::lt,
            ast::ge => ast::le,
            _ => binop
        }
    }

525
    fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
526 527 528 529 530 531 532 533 534 535
        match int_ty {
            ast::ty_i =>    (int::min_value as i64, int::max_value as i64),
            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)
        }
    }

536
    fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
537 538 539 540 541 542 543 544 545 546 547
        match uint_ty {
            ast::ty_u =>   (uint::min_value as u64, uint::max_value as u64),
            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)
        }
    }

    fn check_limits(cx: ty::ctxt, binop: ast::binop, l: &ast::expr,
                    r: &ast::expr) -> bool {
548 549 550
        let (lit, expr, swap) = match (&l.node, &r.node) {
            (&ast::expr_lit(_), _) => (l, r, true),
            (_, &ast::expr_lit(_)) => (r, l, false),
551 552 553 554 555 556 557 558 559
            _ => return true
        };
        // Normalize the binop so that the literal is always on the RHS in
        // the comparison
        let norm_binop = if (swap) {
            rev_binop(binop)
        } else {
            binop
        };
560
        match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
561 562 563 564 565 566 567 568 569
            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
                    },
570
                    _ => fail!()
571 572 573 574 575 576 577 578 579 580 581 582
                };
                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
                    },
583
                    _ => fail!()
584 585 586 587 588 589 590
                };
                is_valid(norm_binop, lit_val, min, max)
            }
            _ => true
        }
    }

591
    fn is_comparison(binop: ast::binop) -> bool {
592 593 594 595 596 597 598
        match binop {
            ast::eq | ast::lt | ast::le |
            ast::ne | ast::ge | ast::gt => true,
            _ => false
        }
    }

599 600 601 602 603 604 605 606
    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) {
                    cx.sess.span_lint(
                        type_limits, e.id, it.id, e.span,
                        ~"comparison is useless due to type limits");
607 608
                }
            }
609 610 611 612
            _ => ()
        }
    };

613 614 615 616 617
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: visit_expr,
            .. *visit::default_simple_visitor()
        }));
618
    visit::visit_item(it, (), visit);
619 620
}

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

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
fn check_item_deprecated_mutable_fields(cx: ty::ctxt, item: @ast::item) {
    match item.node {
        ast::item_struct(struct_def, _) => {
            for struct_def.fields.each |field| {
                match field.node.kind {
                    ast::named_field(_, ast::struct_mutable, _) => {
                        cx.sess.span_lint(deprecated_mutable_fields,
                                          item.id,
                                          item.id,
                                          field.span,
                                          ~"mutable fields are deprecated");
                    }
                    ast::named_field(*) | ast::unnamed_field => {}
                }
            }
        }
        _ => {}
    }
}

662
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
663

664
    fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
665
                        decl: &ast::fn_decl) {
B
Brian Anderson 已提交
666
        let tys = vec::map(decl.inputs, |a| a.ty );
667
        for vec::each(vec::append_one(tys, decl.output)) |ty| {
668
            match ty.node {
B
Brian Anderson 已提交
669
              ast::ty_path(_, id) => {
670
                match *cx.def_map.get(&id) {
B
Brian Anderson 已提交
671
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
672
                    cx.sess.span_lint(
673
                        ctypes, id, fn_id,
674
                        ty.span,
675
                        ~"found rust type `int` in foreign module, while \
676
                         libc::c_int or libc::c_long should be used");
677
                  }
B
Brian Anderson 已提交
678
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) => {
679
                    cx.sess.span_lint(
680
                        ctypes, id, fn_id,
681
                        ty.span,
682
                        ~"found rust type `uint` in foreign module, while \
683
                         libc::c_uint or libc::c_ulong should be used");
684
                  }
B
Brian Anderson 已提交
685
                  _ => ()
686
                }
687
              }
B
Brian Anderson 已提交
688
              _ => ()
689 690 691 692
            }
        }
    }

693
    match it.node {
694
      ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
B
Brian Anderson 已提交
695
        for nmod.items.each |ni| {
696
            match ni.node {
697
              ast::foreign_item_fn(ref decl, _, _) => {
698
                check_foreign_fn(cx, it.id, decl);
699
              }
700 701
              // FIXME #4622: Not implemented.
              ast::foreign_item_const(*) => {}
702 703
            }
        }
704
      }
B
Brian Anderson 已提交
705
      _ => {/* nothing to do */ }
706 707
    }
}
708

709 710 711 712 713 714 715 716 717 718 719 720
fn check_item_heap(cx: ty::ctxt, it: @ast::item) {

    fn check_type_for_lint(cx: ty::ctxt, lint: lint,
                           node: ast::node_id,
                           item: ast::node_id,
                           span: span, ty: ty::t) {

        if get_lint_settings_level(cx.sess.lint_settings,
                                   lint, node, item) != allow {
            let mut n_box = 0;
            let mut n_uniq = 0;
            ty::fold_ty(cx, ty, |t| {
721
                match ty::get(t).sty {
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
                  ty::ty_box(_) => n_box += 1,
                  ty::ty_uniq(_) => n_uniq += 1,
                  _ => ()
                };
                t
            });

            if (n_uniq > 0 && lint != managed_heap_memory) {
                let s = ty_to_str(cx, ty);
                let m = ~"type uses owned (~ type) pointers: " + s;
                cx.sess.span_lint(lint, node, item, span, m);
            }

            if (n_box > 0 && lint != owned_heap_memory) {
                let s = ty_to_str(cx, ty);
                let m = ~"type uses managed (@ type) pointers: " + s;
                cx.sess.span_lint(lint, node, item, span, m);
            }
        }
    }

    fn check_type(cx: ty::ctxt,
                  node: ast::node_id,
                  item: ast::node_id,
                  span: span, ty: ty::t) {
            for [managed_heap_memory,
                 owned_heap_memory,
                 heap_memory].each |lint| {
750
                check_type_for_lint(cx, *lint, node, item, span, ty);
751 752 753 754 755 756 757
            }
    }

    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
758
      ast::item_struct(*) => check_type(cx, it.id, it.id, it.span,
759 760 761 762
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

763 764 765 766 767 768 769 770 771 772 773 774
    // 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.node.id, it.id,
                           struct_field.span,
                           ty::node_id_to_type(cx, struct_field.node.id));
            }
        }
        _ => ()
    }

775 776 777 778 779 780 781 782
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: |e: @ast::expr| {
                let ty = ty::expr_ty(cx, e);
                check_type(cx, e.id, it.id, e.span, ty);
            },
            .. *visit::default_simple_visitor()
        }));
783 784 785
    visit::visit_item(it, (), visit);
}

786
fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
787 788 789 790
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_stmt: |s: @ast::stmt| {
                match s.node {
791 792 793 794
                    ast::stmt_semi(
                        @ast::expr { id: id, node: ast::expr_path(_), _ },
                        _
                    ) => {
795 796 797 798 799 800 801 802 803 804
                        cx.sess.span_lint(
                            path_statement, id, it.id,
                            s.span,
                            ~"path statement with no effect");
                    }
                    _ => ()
                }
            },
            .. *visit::default_simple_visitor()
        }));
805 806 807
    visit::visit_item(it, (), visit);
}

808
fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
P
Paul Stansifer 已提交
809 810
    fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
        let ident = cx.sess.str_of(ident);
P
Patrick Walton 已提交
811
        assert!(!ident.is_empty());
812
        let ident = ident_without_trailing_underscores(*ident);
813
        let ident = ident_without_leading_underscores(ident);
814
        char::is_uppercase(str::char_at(ident, 0)) &&
815 816 817
            !ident.contains_char('_')
    }

818
    fn ident_without_trailing_underscores<'r>(ident: &'r str) -> &'r str {
819
        match str::rfind(ident, |c| c != '_') {
820
            Some(idx) => str::slice(ident, 0, idx + 1),
821
            None => ident, // all underscores
822 823 824
        }
    }

825
    fn ident_without_leading_underscores<'r>(ident: &'r str) -> &'r str {
826
        match str::find(ident, |c| c != '_') {
827
            Some(idx) => str::slice(ident, idx, ident.len()),
828
            None => ident // all underscores
829 830 831
        }
    }

832 833 834
    fn check_case(cx: ty::ctxt, ident: ast::ident,
                  expr_id: ast::node_id, item_id: ast::node_id,
                  span: span) {
P
Paul Stansifer 已提交
835
        if !is_camel_case(cx, ident) {
836 837
            cx.sess.span_lint(
                non_camel_case_types, expr_id, item_id, span,
838 839
                ~"type, variant, or trait should have \
                  a camel case identifier");
840 841 842
        }
    }

843
    match it.node {
E
Erick Tryzelaar 已提交
844 845 846
        ast::item_ty(*) | ast::item_struct(*) |
        ast::item_trait(*) => {
            check_case(cx, it.ident, it.id, it.id, it.span)
847
        }
E
Erick Tryzelaar 已提交
848 849 850 851 852 853 854 855
        ast::item_enum(ref enum_definition, _) => {
            check_case(cx, it.ident, it.id, it.id, it.span);
            for enum_definition.variants.each |variant| {
                check_case(cx, variant.node.name,
                           variant.node.id, it.id, variant.span);
            }
        }
        _ => ()
856 857 858
    }
}

859 860 861 862 863 864 865
fn check_item_unused_unsafe(cx: ty::ctxt, it: @ast::item) {
    let visit_expr: @fn(@ast::expr) = |e| {
        match e.node {
            ast::expr_block(ref blk) if blk.node.rules == ast::unsafe_blk => {
                if !cx.used_unsafe.contains(&blk.node.id) {
                    cx.sess.span_lint(unused_unsafe, blk.node.id, it.id,
                                      blk.span,
866
                                      ~"unnecessary `unsafe` block");
867 868 869 870 871 872 873 874 875 876 877 878 879 880
                }
            }
            _ => ()
        }
    };

    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: visit_expr,
            .. *visit::default_simple_visitor()
        }));
    visit::visit_item(it, (), visit);
}

881 882 883 884 885 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
fn check_item_unused_mut(tcx: ty::ctxt, it: @ast::item) {
    let check_pat: @fn(@ast::pat) = |p| {
        let mut used = false;
        let mut bindings = 0;
        do pat_util::pat_bindings(tcx.def_map, p) |_, id, _, _| {
            used = used || tcx.used_mut_nodes.contains(&id);
            bindings += 1;
        }
        if !used {
            let msg = if bindings == 1 {
                ~"variable does not need to be mutable"
            } else {
                ~"variables do not need to be mutable"
            };
            tcx.sess.span_lint(unused_mut, p.id, it.id, p.span, msg);
        }
    };

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

    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            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()
        }));
    visit::visit_item(it, (), visit);
}

928 929 930 931 932 933
fn check_fn(_: ty::ctxt,
            fk: &visit::fn_kind,
            _: &ast::fn_decl,
            _: &ast::blk,
            _: span,
            id: ast::node_id) {
P
Paul Stansifer 已提交
934
    debug!("lint check_fn fk=%? id=%?", fk, id);
935 936
}

937
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
938
    let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
939 940 941 942
        visit_item: |it|
            check_item(it, tcx),
        visit_fn: |fk, decl, body, span, id|
            check_fn(tcx, fk, decl, body, span, id),
B
Brian Anderson 已提交
943
        .. *visit::default_simple_visitor()
944
    });
945
    visit::visit_crate(crate, (), v);
946 947

    tcx.sess.abort_if_errors();
948
}
949

950 951 952 953 954 955 956 957 958
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//