lint.rs 33.8 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 core::prelude::*;
12

13
use driver::session::Session;
14 15
use driver::session;
use middle::pat_util::{pat_bindings};
P
Patrick Walton 已提交
16 17
use middle::ty;
use util::ppaux::{ty_to_str};
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

use core::char;
use core::cmp;
use core::either;
use core::i8;
use core::i16;
use core::i32;
use core::i64;
use core::int;
use core::io::WriterUtil;
use core::str;
use core::u8;
use core::u16;
use core::u32;
use core::u64;
use core::uint;
use core::vec;
35 36
use std::oldmap::{Map, HashMap};
use std::oldmap;
37 38
use std::oldsmallintmap::{Map, SmallIntMap};
use std::oldsmallintmap;
P
Patrick Walton 已提交
39
use syntax::ast_util::{path_to_ident};
40 41
use syntax::attr;
use syntax::codemap::span;
J
John Clements 已提交
42
use syntax::codemap;
P
Patrick Walton 已提交
43
use syntax::print::pprust::{expr_to_str, mode_to_str, pat_to_str};
44 45
use syntax::{ast, ast_util, visit};

46 47 48 49 50 51 52 53
/**
 * 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
54
 * order to allow other passes to take advantage of the lint attribute
55 56
 * 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
57
 * be omitted. If we start allowing lint attributes on expressions, we will
58 59 60
 * start having entries for expressions that do not share their enclosing
 * items settings.
 *
61
 * This module then, exports two passes: one that populates the lint
62 63 64 65
 * 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.
 */
66

67 68
#[deriving_eq]
pub enum lint {
P
Patrick Walton 已提交
69
    ctypes,
70
    unused_imports,
71 72
    while_true,
    path_statement,
73
    implicit_copies,
74
    unrecognized_lint,
75
    non_implicitly_copyable_typarams,
76
    vecs_implicitly_copyable,
77
    deprecated_mode,
78
    deprecated_pattern,
79
    non_camel_case_types,
80
    structural_records,
81
    type_limits,
82
    default_methods,
83
    deprecated_self,
84

85 86 87 88
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

89 90
    legacy_modes,

91 92 93
    // FIXME(#3266)--make liveness warnings lintable
    // unused_variable,
    // dead_assignment
94 95
}

96
pub fn level_to_str(lv: level) -> &static/str {
97
    match lv {
98 99 100 101
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
102 103 104
    }
}

105
#[deriving_eq]
106
pub enum level {
107
    allow, warn, deny, forbid
108 109
}

110 111 112 113
struct LintSpec {
    lint: lint,
    desc: &static/str,
    default: level
114
}
115

T
Merge  
Tim Chevalier 已提交
116
pub type LintDict = HashMap<@~str, @LintSpec>;
117

118 119 120 121
/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
122
pub fn get_lint_dict() -> LintDict {
123
    let v = ~[
124
        (@~"ctypes",
125 126 127 128 129
         @LintSpec {
            lint: ctypes,
            desc: "proper use of core::libc types in foreign modules",
            default: warn
         }),
130

131
        (@~"unused_imports",
132
         @LintSpec {
L
Luqman Aden 已提交
133
            lint: unused_imports,
134 135 136
            desc: "imports that are never used",
            default: allow
         }),
137

138
        (@~"while_true",
139
         @LintSpec {
L
Luqman Aden 已提交
140
            lint: while_true,
141 142 143
            desc: "suggest using loop { } instead of while(true) { }",
            default: warn
         }),
144

145
        (@~"path_statement",
146
         @LintSpec {
L
Luqman Aden 已提交
147
            lint: path_statement,
148 149 150
            desc: "path statements with no effect",
            default: warn
         }),
151

152
        (@~"unrecognized_lint",
153
         @LintSpec {
L
Luqman Aden 已提交
154
            lint: unrecognized_lint,
155 156 157
            desc: "unrecognized lint attribute",
            default: warn
         }),
158

159
        (@~"non_implicitly_copyable_typarams",
160
         @LintSpec {
L
Luqman Aden 已提交
161
            lint: non_implicitly_copyable_typarams,
162 163 164
            desc: "passing non implicitly copyable types as copy type params",
            default: warn
         }),
165

166
        (@~"vecs_implicitly_copyable",
167
         @LintSpec {
L
Luqman Aden 已提交
168
            lint: vecs_implicitly_copyable,
169
            desc: "make vecs and strs not implicitly copyable \
170
                  (only checked at top level)",
171 172
            default: warn
         }),
173

174
        (@~"implicit_copies",
175
         @LintSpec {
L
Luqman Aden 已提交
176
            lint: implicit_copies,
177 178 179
            desc: "implicit copies of non implicitly copyable data",
            default: warn
         }),
180

181
        (@~"deprecated_mode",
182
         @LintSpec {
L
Luqman Aden 已提交
183
            lint: deprecated_mode,
184 185 186
            desc: "warn about deprecated uses of modes",
            default: warn
         }),
187

188
        (@~"deprecated_pattern",
189
         @LintSpec {
L
Luqman Aden 已提交
190
            lint: deprecated_pattern,
191 192 193
            desc: "warn about deprecated uses of pattern bindings",
            default: allow
         }),
194

195
        (@~"non_camel_case_types",
196
         @LintSpec {
L
Luqman Aden 已提交
197
            lint: non_camel_case_types,
198 199 200
            desc: "types, variants and traits should have camel case names",
            default: allow
         }),
201

202
        (@~"managed_heap_memory",
203
         @LintSpec {
L
Luqman Aden 已提交
204
            lint: managed_heap_memory,
205 206 207
            desc: "use of managed (@ type) heap memory",
            default: allow
         }),
208

209
        (@~"owned_heap_memory",
210
         @LintSpec {
L
Luqman Aden 已提交
211
            lint: owned_heap_memory,
212 213 214
            desc: "use of owned (~ type) heap memory",
            default: allow
         }),
215

216
        (@~"heap_memory",
217
         @LintSpec {
L
Luqman Aden 已提交
218
            lint: heap_memory,
219 220 221
            desc: "use of any (~ type or @ type) heap memory",
            default: allow
         }),
222

223
        (@~"structural_records",
224
         @LintSpec {
L
Luqman Aden 已提交
225
            lint: structural_records,
226 227 228
            desc: "use of any structural records",
            default: deny
         }),
229

230
        (@~"legacy modes",
231
         @LintSpec {
L
Luqman Aden 已提交
232
            lint: legacy_modes,
233 234 235
            desc: "allow legacy modes",
            default: forbid
         }),
236

237
        (@~"type_limits",
238
         @LintSpec {
L
Luqman Aden 已提交
239
            lint: type_limits,
240 241 242
            desc: "comparisons made useless by limits of the types involved",
            default: warn
         }),
243

244
        (@~"default_methods",
245
         @LintSpec {
L
Luqman Aden 已提交
246
            lint: default_methods,
247 248 249
            desc: "allow default methods",
            default: deny
         }),
V
Viktor Dahl 已提交
250

251
        (@~"deprecated_self",
252
         @LintSpec {
L
Luqman Aden 已提交
253
            lint: deprecated_self,
254 255 256
            desc: "warn about deprecated uses of `self`",
            default: warn
         }),
257

258
        /* FIXME(#3266)--make liveness warnings lintable
259
        (@~"unused_variable",
260
         @LintSpec {
L
Luqman Aden 已提交
261
            lint: unused_variable,
262 263 264
            desc: "detect variables which are not used in any way",
            default: warn
         }),
265

266
        (@~"dead_assignment",
267
         @LintSpec {
L
Luqman Aden 已提交
268
            lint: dead_assignment,
269 270 271
            desc: "detect assignments that will never be read",
            default: warn
         }),
272
        */
273
    ];
274
    oldmap::hash_from_vec(v)
275 276
}

277
// This is a highly not-optimal set of data structure decisions.
278 279
type LintModes = SmallIntMap<level>;
type LintModeMap = HashMap<ast::node_id, LintModes>;
280

281
// settings_map maps node ids of items with non-default lint settings
282 283
// to their settings; default_settings contains the settings for everything
// not in the map.
284 285 286
pub struct LintSettings {
    default_settings: LintModes,
    settings_map: LintModeMap
287 288
}

289 290 291 292 293 294 295 296
pub fn mk_lint_settings() -> LintSettings {
    LintSettings {
        default_settings: oldsmallintmap::mk(),
        settings_map: HashMap()
    }
}

pub fn get_lint_level(modes: LintModes, lint: lint) -> level {
297
    match modes.find(lint as uint) {
B
Brian Anderson 已提交
298 299
      Some(c) => c,
      None => allow
300 301 302
    }
}

303
pub fn get_lint_settings_level(settings: LintSettings,
304 305 306 307
                               lint_mode: lint,
                               _expr_id: ast::node_id,
                               item_id: ast::node_id)
                            -> level {
308
    match settings.settings_map.find(&item_id) {
B
Brian Anderson 已提交
309 310
      Some(modes) => get_lint_level(modes, lint_mode),
      None => get_lint_level(settings.default_settings, lint_mode)
311 312 313
    }
}

314 315
// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
316
fn clone_lint_modes(modes: LintModes) -> LintModes {
317 318
    oldsmallintmap::SmallIntMap_(@oldsmallintmap::SmallIntMap_
    {v: copy modes.v})
319 320
}

321 322 323 324 325
struct Context {
    dict: LintDict,
    curr: LintModes,
    is_default: bool,
    sess: Session
326
}
327

328 329
impl Context {
    fn get_level(&self, lint: lint) -> level {
330
        get_lint_level(self.curr, lint)
331 332
    }

333
    fn set_level(&self, lint: lint, level: level) {
334
        if level == allow {
335
            self.curr.remove(lint as uint);
336
        } else {
337
            self.curr.insert(lint as uint, level);
338
        }
339 340
    }

341
    fn span_lint(&self, level: level, span: span, +msg: ~str) {
342
        self.sess.span_lint_level(level, span, msg);
343 344
    }

345
    /**
346
     * Merge the lints specified by any lint attributes into the
347
     * current lint context, call the provided function, then reset the
348
     * lints in effect to their previous state.
349
     */
350
    fn with_lint_attrs(&self, attrs: ~[ast::attribute], f: fn(Context)) {
351

352
        let mut new_ctxt = *self;
353 354 355
        let mut triples = ~[];

        for [allow, warn, deny, forbid].each |level| {
356
            let level_name = level_to_str(*level);
357
            let metas =
358
                attr::attr_metas(attr::find_attrs_by_name(attrs, level_name));
359
            for metas.each |meta| {
360
                match /*bad*/copy meta.node {
B
Brian Anderson 已提交
361
                  ast::meta_list(_, metas) => {
362
                    for metas.each |meta| {
363
                        match meta.node {
364
                          ast::meta_word(ref lintname) => {
365 366 367
                            triples.push((*meta,
                                          *level,
                                          /*bad*/copy *lintname));
368
                          }
B
Brian Anderson 已提交
369
                          _ => {
370 371 372
                            self.sess.span_err(
                                meta.span,
                                ~"malformed lint attribute");
373 374 375
                          }
                        }
                    }
376
                  }
B
Brian Anderson 已提交
377
                  _  => {
378 379 380
                    self.sess.span_err(meta.span,
                                       ~"malformed lint attribute");
                  }
381
                }
382 383 384
            }
        }

385 386 387 388 389 390 391 392
        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)
            };

            match self.dict.find(&lintname) {
B
Brian Anderson 已提交
393
              None => {
394 395 396
                self.span_lint(
                    new_ctxt.get_level(unrecognized_lint),
                    meta.span,
P
Paul Stansifer 已提交
397
                    fmt!("unknown `%s` attribute: `%s`",
398
                         level_to_str(level), *lintname));
399
              }
B
Brian Anderson 已提交
400
              Some(lint) => {
401 402 403 404 405 406

                if new_ctxt.get_level(lint.lint) == forbid &&
                    level != forbid {
                    self.span_lint(
                        forbid,
                        meta.span,
P
Paul Stansifer 已提交
407
                        fmt!("%s(%s) overruled by outer forbid(%s)",
408
                             level_to_str(level),
409
                             *lintname, *lintname));
410 411 412 413 414 415 416
                }

                // 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);
417 418 419 420 421
                new_ctxt = Context {
                    is_default: false,
                    curr: c,
                    .. new_ctxt
                };
422
                new_ctxt.set_level(lint.lint, level);
423
              }
424 425
            }
        }
426
        f(new_ctxt);
427 428 429 430
    }
}


431
fn build_settings_item(i: @ast::item, &&cx: Context, v: visit::vt<Context>) {
432
    do cx.with_lint_attrs(/*bad*/copy i.attrs) |cx| {
433
        if !cx.is_default {
434
            cx.sess.lint_settings.settings_map.insert(i.id, cx.curr);
435
        }
436
        visit::visit_item(i, cx, v);
437
    }
438
}
439

440
pub fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
441 442 443 444 445 446
    let cx = Context {
        dict: get_lint_dict(),
        curr: oldsmallintmap::mk(),
        is_default: true,
        sess: sess
    };
447 448

    // Install defaults.
449
    for cx.dict.each_value |&spec| {
D
Daniel Micay 已提交
450 451
        cx.set_level(spec.lint, spec.default);
    }
452 453

    // Install command-line options, overriding defaults.
B
Brian Anderson 已提交
454
    for sess.opts.lint_opts.each |pair| {
455
        let (lint,level) = *pair;
456 457 458
        cx.set_level(lint, level);
    }

459
    do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
460
        // Copy out the default settings
B
Brian Anderson 已提交
461
        for cx.curr.each |k, v| {
462
            sess.lint_settings.default_settings.insert(k, v);
463 464
        }

465 466 467 468
        let cx = Context {
            is_default: true,
            .. cx
        };
469

470
        let visit = visit::mk_vt(@visit::Visitor {
B
Brian Anderson 已提交
471 472
            visit_item: build_settings_item,
            .. *visit::default_visitor()
473 474 475 476 477 478 479 480 481 482 483
        });
        visit::visit_crate(*crate, cx, visit);
    }

    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);
484
    check_item_non_camel_case_types(cx, i);
485
    check_item_heap(cx, i);
486
    check_item_structural_records(cx, i);
487
    check_item_deprecated_modes(cx, i);
488
    check_item_type_limits(cx, i);
489
    check_item_default_methods(cx, i);
490
    check_item_deprecated_self(cx, i);
491 492
}

493 494 495 496 497
// 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> {
498
    visit::mk_vt(@visit::Visitor {visit_item: |_i, _e, _v| { },.. **v})
499 500
}

501
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
502 503 504 505 506 507
    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 已提交
508
                            ast::expr_lit(@codemap::spanned {
509 510 511 512 513 514 515 516 517 518
                                node: ast::lit_bool(true), _}) =>
                            {
                                cx.sess.span_lint(
                                    while_true, e.id, it.id,
                                    e.span,
                                    ~"denote infinite loops \
                                      with loop { ... }");
                            }
                            _ => ()
                        }
519
                    }
B
Brian Anderson 已提交
520
                    _ => ()
521
                }
522 523 524
            },
            .. *visit::default_simple_visitor()
        }));
525
    visit::visit_item(it, (), visit);
V
Viktor Dahl 已提交
526 527 528
}

fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
529
    pure fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
530 531 532 533 534 535 536
            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,
537
            _ => fail!()
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
        }
    }

    pure fn rev_binop(binop: ast::binop) -> ast::binop {
        match binop {
            ast::lt => ast::gt,
            ast::le => ast::ge,
            ast::gt => ast::lt,
            ast::ge => ast::le,
            _ => binop
        }
    }

    pure fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
        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)
        }
    }

    pure fn uint_ty_range(uint_ty: ast::uint_ty) -> (u64, u64) {
        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 {
574 575 576
        let (lit, expr, swap) = match (&l.node, &r.node) {
            (&ast::expr_lit(_), _) => (l, r, true),
            (_, &ast::expr_lit(_)) => (r, l, false),
577 578 579 580 581 582 583 584 585
            _ => 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
        };
586
        match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
587 588 589 590 591 592 593 594 595
            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
                    },
596
                    _ => fail!()
597 598 599 600 601 602 603 604 605 606 607 608
                };
                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
                    },
609
                    _ => fail!()
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
                };
                is_valid(norm_binop, lit_val, min, max)
            }
            _ => true
        }
    }

    pure fn is_comparison(binop: ast::binop) -> bool {
        match binop {
            ast::eq | ast::lt | ast::le |
            ast::ne | ast::ge | ast::gt => true,
            _ => false
        }
    }

625 626 627 628 629 630 631 632
    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");
633 634
                }
            }
635 636 637 638
            _ => ()
        }
    };

639 640 641 642 643
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: visit_expr,
            .. *visit::default_simple_visitor()
        }));
644
    visit::visit_item(it, (), visit);
645 646
}

647
fn check_item_default_methods(cx: ty::ctxt, item: @ast::item) {
648
    match /*bad*/copy item.node {
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
        ast::item_trait(_, _, methods) => {
            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");
                    }
                }
            }
        }
        _ => {}
    }
}

668 669 670 671
fn check_item_deprecated_self(cx: ty::ctxt, item: @ast::item) {
    fn maybe_warn(cx: ty::ctxt,
                  item: @ast::item,
                  self_ty: ast::self_ty) {
672 673 674 675 676 677 678 679 680 681 682 683
        match self_ty.node {
            ast::sty_by_ref => {
                cx.sess.span_lint(
                    deprecated_self,
                    item.id,
                    item.id,
                    self_ty.span,
                    ~"this method form is deprecated; use an explicit `self` \
                      parameter or mark the method as static");
            }
            _ => {}
        }
684 685
    }

686
    match /*bad*/copy item.node {
687 688
        ast::item_trait(_, _, methods) => {
            for methods.each |method| {
689
                match /*bad*/copy *method {
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
                    ast::required(ty_method) => {
                        maybe_warn(cx, item, ty_method.self_ty);
                    }
                    ast::provided(method) => {
                        maybe_warn(cx, item, method.self_ty);
                    }
                }
            }
        }
        ast::item_impl(_, _, _, methods) => {
            for methods.each |method| {
                maybe_warn(cx, item, method.self_ty);
            }
        }
        _ => {}
    }
}

708
fn check_item_structural_records(cx: ty::ctxt, it: @ast::item) {
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: |e: @ast::expr| {
                match e.node {
                    ast::expr_rec(*) =>
                        cx.sess.span_lint(
                            structural_records, e.id, it.id,
                            e.span,
                            ~"structural records are deprecated"),
                    _ => ()
                }
            },
            .. *visit::default_simple_visitor()
        }));
    visit::visit_item(it, (), visit);
724 725
}

726
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
727

728
    fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
729
                       decl: ast::fn_decl) {
B
Brian Anderson 已提交
730
        let tys = vec::map(decl.inputs, |a| a.ty );
731
        for vec::each(vec::append_one(tys, decl.output)) |ty| {
732
            match ty.node {
B
Brian Anderson 已提交
733
              ast::ty_path(_, id) => {
734
                match cx.def_map.get(&id) {
B
Brian Anderson 已提交
735
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
736
                    cx.sess.span_lint(
737
                        ctypes, id, fn_id,
738
                        ty.span,
739
                        ~"found rust type `int` in foreign module, while \
740
                         libc::c_int or libc::c_long should be used");
741
                  }
B
Brian Anderson 已提交
742
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) => {
743
                    cx.sess.span_lint(
744
                        ctypes, id, fn_id,
745
                        ty.span,
746
                        ~"found rust type `uint` in foreign module, while \
747
                         libc::c_uint or libc::c_ulong should be used");
748
                  }
B
Brian Anderson 已提交
749
                  _ => ()
750
                }
751
              }
B
Brian Anderson 已提交
752
              _ => ()
753 754 755 756
            }
        }
    }

757
    match it.node {
758 759 760
      ast::item_foreign_mod(ref nmod)
      if attr::foreign_abi(it.attrs) !=
            either::Right(ast::foreign_abi_rust_intrinsic) => {
B
Brian Anderson 已提交
761
        for nmod.items.each |ni| {
762
            match /*bad*/copy ni.node {
E
Erick Tryzelaar 已提交
763
              ast::foreign_item_fn(decl, _, _) => {
764
                check_foreign_fn(cx, it.id, decl);
765
              }
766 767
              // FIXME #4622: Not implemented.
              ast::foreign_item_const(*) => {}
768 769
            }
        }
770
      }
B
Brian Anderson 已提交
771
      _ => {/* nothing to do */ }
772 773
    }
}
774

775 776 777 778 779 780 781 782 783 784 785 786
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| {
787
                match ty::get(t).sty {
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
                  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| {
816
                check_type_for_lint(cx, *lint, node, item, span, ty);
817 818 819 820 821 822 823
            }
    }

    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
824
      ast::item_struct(*) |
825 826 827 828 829
      ast::item_trait(*) => check_type(cx, it.id, it.id, it.span,
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

830 831 832 833 834 835 836 837 838 839 840 841
    // 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));
            }
        }
        _ => ()
    }

842 843 844 845 846 847 848 849
    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()
        }));
850 851 852
    visit::visit_item(it, (), visit);
}

853
fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
854 855 856 857
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_stmt: |s: @ast::stmt| {
                match s.node {
858 859 860 861
                    ast::stmt_semi(
                        @ast::expr { id: id, node: ast::expr_path(_), _ },
                        _
                    ) => {
862 863 864 865 866 867 868 869 870 871
                        cx.sess.span_lint(
                            path_statement, id, it.id,
                            s.span,
                            ~"path statement with no effect");
                    }
                    _ => ()
                }
            },
            .. *visit::default_simple_visitor()
        }));
872 873 874
    visit::visit_item(it, (), visit);
}

875
fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
P
Paul Stansifer 已提交
876 877
    fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
        let ident = cx.sess.str_of(ident);
878
        assert !ident.is_empty();
879
        let ident = ident_without_trailing_underscores(*ident);
880
        let ident = ident_without_leading_underscores(ident);
881
        char::is_uppercase(str::char_at(ident, 0)) &&
882 883 884
            !ident.contains_char('_')
    }

885
    fn ident_without_trailing_underscores(ident: &r/str) -> &r/str {
886
        match str::rfind(ident, |c| c != '_') {
887 888
            Some(idx) => str::view(ident, 0, idx + 1),
            None => ident, // all underscores
889 890 891
        }
    }

892
    fn ident_without_leading_underscores(ident: &r/str) -> &r/str {
893
        match str::find(ident, |c| c != '_') {
894 895
            Some(idx) => str::view(ident, idx, ident.len()),
            None => ident // all underscores
896 897 898
        }
    }

899 900 901
    fn check_case(cx: ty::ctxt, ident: ast::ident,
                  expr_id: ast::node_id, item_id: ast::node_id,
                  span: span) {
P
Paul Stansifer 已提交
902
        if !is_camel_case(cx, ident) {
903 904
            cx.sess.span_lint(
                non_camel_case_types, expr_id, item_id, span,
905 906
                ~"type, variant, or trait should have \
                  a camel case identifier");
907 908 909
        }
    }

910
    match it.node {
E
Erick Tryzelaar 已提交
911 912 913
        ast::item_ty(*) | ast::item_struct(*) |
        ast::item_trait(*) => {
            check_case(cx, it.ident, it.id, it.id, it.span)
914
        }
E
Erick Tryzelaar 已提交
915 916 917 918 919 920 921 922
        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);
            }
        }
        _ => ()
923 924 925
    }
}

926
fn check_fn(tcx: ty::ctxt, fk: &visit::fn_kind, decl: ast::fn_decl,
927
            _body: ast::blk, span: span, id: ast::node_id) {
P
Paul Stansifer 已提交
928
    debug!("lint check_fn fk=%? id=%?", fk, id);
929 930 931

    // don't complain about blocks, since they tend to get their modes
    // specified from the outside
932
    match *fk {
B
Brian Anderson 已提交
933
      visit::fk_fn_block(*) => { return; }
934 935 936
      _ => {}
    }

937
    let fn_ty = ty::node_id_to_type(tcx, id);
938 939 940 941 942
    check_fn_deprecated_modes(tcx, fn_ty, decl, span, id);
}

fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: ast::fn_decl,
                             span: span, id: ast::node_id) {
943
    match ty::get(fn_ty).sty {
944 945
        ty::ty_closure(ty::ClosureTy {sig: ref sig, _}) |
        ty::ty_bare_fn(ty::BareFnTy {sig: ref sig, _}) => {
946
            let mut counter = 0;
947
            for vec::each2(sig.inputs, decl.inputs) |arg_ty, arg_ast| {
948 949 950 951 952 953 954
                counter += 1;
                debug!("arg %d, ty=%s, mode=%s",
                       counter,
                       ty_to_str(tcx, arg_ty.ty),
                       mode_to_str(arg_ast.mode));
                match arg_ast.mode {
                    ast::expl(ast::by_copy) => {
T
Tim Chevalier 已提交
955 956 957 958 959 960
                        if !tcx.legacy_modes {
                            tcx.sess.span_lint(
                                deprecated_mode, id, id, span,
                                fmt!("argument %d uses by-copy mode",
                                     counter));
                        }
961
                    }
962

963 964 965 966
                    ast::expl(_) => {
                        tcx.sess.span_lint(
                            deprecated_mode, id, id,
                            span,
T
Tim Chevalier 已提交
967
                         fmt!("argument %d uses an explicit mode", counter));
968
                    }
969

970
                    ast::infer(_) => {
971
                        if tcx.legacy_modes {
972 973
                            let kind = ty::type_contents(tcx, arg_ty.ty);
                            if !kind.is_safe_for_default_mode(tcx) {
974 975 976 977 978 979 980
                                tcx.sess.span_lint(
                                    deprecated_mode, id, id,
                                    span,
                                    fmt!("argument %d uses the default mode \
                                          but shouldn't",
                                         counter));
                            }
981 982 983 984 985
                        }
                    }
                }

                match ty::get(arg_ty.ty).sty {
986
                    ty::ty_closure(*) | ty::ty_bare_fn(*) => {
987 988 989
                        let span = arg_ast.ty.span;
                        // Recurse to check fn-type argument
                        match arg_ast.ty.node {
990 991
                            ast::ty_closure(@ast::TyClosure{decl: ref d, _}) |
                            ast::ty_bare_fn(@ast::TyBareFn{decl: ref d, _})=>{
992
                                check_fn_deprecated_modes(tcx, arg_ty.ty,
993
                                                          *d, span, id);
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
                            }
                            ast::ty_path(*) => {
                                // This is probably a typedef, so we can't
                                // see the actual fn decl
                                // e.g. fn foo(f: InitOp<T>)
                            }
                            _ => {
                                tcx.sess.span_warn(span, ~"what");
                                error!("arg %d, ty=%s, mode=%s",
                                       counter,
                                       ty_to_str(tcx, arg_ty.ty),
                                       mode_to_str(arg_ast.mode));
                                error!("%?",arg_ast.ty.node);
1007
                                fail!()
1008 1009 1010 1011
                            }
                        };
                    }
                    _ => ()
1012 1013 1014
                }
            }
        }
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024

        _ => tcx.sess.impossible_case(span, ~"check_fn: function has \
                                              non-fn type")
    }
}

fn check_item_deprecated_modes(tcx: ty::ctxt, it: @ast::item) {
    match it.node {
        ast::item_ty(ty, _) => {
            match ty.node {
1025 1026
                ast::ty_closure(@ast::TyClosure {decl: ref decl, _}) |
                ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => {
1027 1028
                    let fn_ty = ty::node_id_to_type(tcx, it.id);
                    check_fn_deprecated_modes(
1029
                        tcx, fn_ty, *decl, ty.span, it.id)
1030 1031 1032 1033 1034
                }
                _ => ()
            }
        }
        _ => ()
1035 1036 1037
    }
}

1038
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
1039
    let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
1040 1041 1042 1043
        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 已提交
1044
        .. *visit::default_simple_visitor()
1045 1046
    });
    visit::visit_crate(*crate, (), v);
1047 1048

    tcx.sess.abort_if_errors();
1049
}
1050

1051 1052 1053 1054 1055 1056 1057 1058 1059
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//