lint.rs 32.2 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 35 36 37 38

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;
use std::map::{Map, HashMap};
use std::map;
use std::smallintmap::{Map, SmallIntMap};
use std::smallintmap;
P
Patrick Walton 已提交
39
use syntax::ast_util::{path_to_ident};
40 41
use syntax::attr;
use syntax::codemap::span;
P
Patrick Walton 已提交
42
use syntax::print::pprust::{expr_to_str, mode_to_str, pat_to_str};
43 44
use syntax::{ast, ast_util, visit};

45
export lint, ctypes, unused_imports, while_true, path_statement, old_vecs;
46
export unrecognized_lint, non_implicitly_copyable_typarams;
47
export vecs_implicitly_copyable, implicit_copies, legacy_modes;
48 49 50 51 52
export level, allow, warn, deny, forbid;
export lint_dict, get_lint_dict, level_to_str;
export get_lint_level, get_lint_settings_level;
export check_crate, build_settings_crate, mk_lint_settings;
export lint_settings;
53

54 55 56 57 58 59 60 61
/**
 * 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
62
 * order to allow other passes to take advantage of the lint attribute
63 64
 * 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
65
 * be omitted. If we start allowing lint attributes on expressions, we will
66 67 68
 * start having entries for expressions that do not share their enclosing
 * items settings.
 *
69
 * This module then, exports two passes: one that populates the lint
70 71 72 73
 * 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.
 */
74 75

enum lint {
P
Patrick Walton 已提交
76
    ctypes,
77
    unused_imports,
78 79
    while_true,
    path_statement,
80
    implicit_copies,
81
    unrecognized_lint,
82
    non_implicitly_copyable_typarams,
83
    vecs_implicitly_copyable,
84
    deprecated_mode,
85
    deprecated_pattern,
86
    non_camel_case_types,
87
    structural_records,
88
    type_limits,
89
    default_methods,
90
    deprecated_self,
91

92 93 94 95
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

96 97
    legacy_modes,

98 99 100
    // FIXME(#3266)--make liveness warnings lintable
    // unused_variable,
    // dead_assignment
101 102
}

103
impl lint : cmp::Eq {
104 105 106 107
    pure fn eq(&self, other: &lint) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &lint) -> bool { !(*self).eq(other) }
108
}
109

110
fn level_to_str(lv: level) -> &static/str {
111
    match lv {
112 113 114 115
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
116 117 118
    }
}

119
enum level {
120
    allow, warn, deny, forbid
121 122
}

123
impl level : cmp::Eq {
124 125 126 127
    pure fn eq(&self, other: &level) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &level) -> bool { !(*self).eq(other) }
128
}
129

130
type lint_spec = @{lint: lint,
131
                   desc: &static/str,
132 133
                   default: level};

B
Brian Anderson 已提交
134
type lint_dict = HashMap<~str,lint_spec>;
135

136 137 138 139
/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
140
fn get_lint_dict() -> lint_dict {
141
    let v = ~[
142
        (~"ctypes",
143
         @{lint: ctypes,
144
           desc: "proper use of core::libc types in foreign modules",
145 146
           default: warn}),

147
        (~"unused_imports",
148
         @{lint: unused_imports,
149
           desc: "imports that are never used",
150
           default: allow}),
151

152
        (~"while_true",
153
         @{lint: while_true,
154
           desc: "suggest using loop { } instead of while(true) { }",
155 156
           default: warn}),

157
        (~"path_statement",
158
         @{lint: path_statement,
159
           desc: "path statements with no effect",
160 161
           default: warn}),

162 163
        (~"unrecognized_lint",
         @{lint: unrecognized_lint,
164
           desc: "unrecognized lint attribute",
165 166
           default: warn}),

167
        (~"non_implicitly_copyable_typarams",
168
         @{lint: non_implicitly_copyable_typarams,
169
           desc: "passing non implicitly copyable types as copy type params",
170 171
           default: warn}),

172 173
        (~"vecs_implicitly_copyable",
         @{lint: vecs_implicitly_copyable,
174
           desc: "make vecs and strs not implicitly copyable \
175
                  (only checked at top level)",
176 177
           default: warn}),

178
        (~"implicit_copies",
179
         @{lint: implicit_copies,
180
           desc: "implicit copies of non implicitly copyable data",
181
           default: warn}),
182

183 184
        (~"deprecated_mode",
         @{lint: deprecated_mode,
185
           desc: "warn about deprecated uses of modes",
186
           default: warn}),
187

188 189
        (~"deprecated_pattern",
         @{lint: deprecated_pattern,
190
           desc: "warn about deprecated uses of pattern bindings",
191 192
           default: allow}),

193 194
        (~"non_camel_case_types",
         @{lint: non_camel_case_types,
195
           desc: "types, variants and traits should have camel case names",
196
           default: allow}),
197

198 199
        (~"managed_heap_memory",
         @{lint: managed_heap_memory,
200
           desc: "use of managed (@ type) heap memory",
201 202 203 204
           default: allow}),

        (~"owned_heap_memory",
         @{lint: owned_heap_memory,
205
           desc: "use of owned (~ type) heap memory",
206 207 208 209
           default: allow}),

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

213 214
        (~"structural_records",
         @{lint: structural_records,
215
           desc: "use of any structural records",
216 217
           default: allow}),

218 219
        (~"legacy modes",
         @{lint: legacy_modes,
220
           desc: "allow legacy modes",
221 222
           default: forbid}),

223 224
        (~"type_limits",
         @{lint: type_limits,
225
           desc: "comparisons made useless by limits of the types involved",
226 227 228 229
           default: warn}),

        (~"default_methods",
         @{lint: default_methods,
230
           desc: "allow default methods",
231
           default: deny}),
V
Viktor Dahl 已提交
232

233 234
        (~"deprecated_self",
         @{lint: deprecated_self,
235
           desc: "warn about deprecated uses of `self`",
236 237
           default: allow}),

238 239 240
        /* FIXME(#3266)--make liveness warnings lintable
        (~"unused_variable",
         @{lint: unused_variable,
241
           desc: "detect variables which are not used in any way",
242 243 244 245
           default: warn}),

        (~"dead_assignment",
         @{lint: dead_assignment,
246
           desc: "detect assignments that will never be read",
247 248
           default: warn}),
        */
249
    ];
250
    map::hash_from_vec(v)
251 252
}

253
// This is a highly not-optimal set of data structure decisions.
B
Brian Anderson 已提交
254
type lint_modes = SmallIntMap<level>;
B
Brian Anderson 已提交
255
type lint_mode_map = HashMap<ast::node_id, lint_modes>;
256

257
// settings_map maps node ids of items with non-default lint settings
258 259
// to their settings; default_settings contains the settings for everything
// not in the map.
260
type lint_settings = {
261 262 263 264
    default_settings: lint_modes,
    settings_map: lint_mode_map
};

265
fn mk_lint_settings() -> lint_settings {
266
    {default_settings: smallintmap::mk(),
267
     settings_map: HashMap()}
268 269
}

270
fn get_lint_level(modes: lint_modes, lint: lint) -> level {
271
    match modes.find(lint as uint) {
B
Brian Anderson 已提交
272 273
      Some(c) => c,
      None => allow
274 275 276
    }
}

277
fn get_lint_settings_level(settings: lint_settings,
278 279 280
                              lint_mode: lint,
                              _expr_id: ast::node_id,
                              item_id: ast::node_id) -> level {
281
    match settings.settings_map.find(item_id) {
B
Brian Anderson 已提交
282 283
      Some(modes) => get_lint_level(modes, lint_mode),
      None => get_lint_level(settings.default_settings, lint_mode)
284 285 286
    }
}

287 288 289
// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
fn clone_lint_modes(modes: lint_modes) -> lint_modes {
290
    smallintmap::SmallIntMap_(@{v: copy modes.v})
291 292
}

293 294 295
type ctxt_ = {dict: lint_dict,
              curr: lint_modes,
              is_default: bool,
296
              sess: Session};
297

298 299 300
enum ctxt {
    ctxt_(ctxt_)
}
301

B
Brian Anderson 已提交
302
impl ctxt {
303
    fn get_level(lint: lint) -> level {
304
        get_lint_level(self.curr, lint)
305 306
    }

307
    fn set_level(lint: lint, level: level) {
308
        if level == allow {
309
            self.curr.remove(lint as uint);
310
        } else {
311
            self.curr.insert(lint as uint, level);
312
        }
313 314
    }

315
    fn span_lint(level: level, span: span, +msg: ~str) {
316
        self.sess.span_lint_level(level, span, msg);
317 318
    }

319
    /**
320
     * Merge the lints specified by any lint attributes into the
321
     * current lint context, call the provided function, then reset the
322
     * lints in effect to their previous state.
323
     */
324
    fn with_lint_attrs(attrs: ~[ast::attribute], f: fn(ctxt)) {
325

326
        let mut new_ctxt = self;
327 328 329
        let mut triples = ~[];

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

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

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

                // 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);
                new_ctxt =
                    ctxt_({is_default: false,
                           curr: c,
B
Brian Anderson 已提交
390
                           .. *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: ctxt, v: visit::vt<ctxt>) {
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
fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
410

411
    let cx = ctxt_({dict: get_lint_dict(),
412
                    curr: smallintmap::mk(),
413 414
                    is_default: true,
                    sess: sess});
415 416

    // Install defaults.
B
Brian Anderson 已提交
417
    for cx.dict.each |_k, spec| { cx.set_level(spec.lint, spec.default); }
418 419

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

425
    do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
426
        // Copy out the default settings
B
Brian Anderson 已提交
427
        for cx.curr.each |k, v| {
428
            sess.lint_settings.default_settings.insert(k, v);
429 430
        }

B
Brian Anderson 已提交
431
        let cx = ctxt_({is_default: true,.. *cx});
432 433

        let visit = visit::mk_vt(@{
B
Brian Anderson 已提交
434 435
            visit_item: build_settings_item,
            .. *visit::default_visitor()
436 437 438 439 440 441 442 443 444 445 446
        });
        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);
447
    check_item_non_camel_case_types(cx, i);
448
    check_item_heap(cx, i);
449
    check_item_structural_records(cx, i);
450
    check_item_deprecated_modes(cx, i);
451
    check_item_type_limits(cx, i);
452
    check_item_default_methods(cx, i);
453
    check_item_deprecated_self(cx, i);
454 455
}

456 457 458 459 460
// 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> {
B
Brian Anderson 已提交
461
    visit::mk_vt(@{visit_item: |_i, _e, _v| { },.. **v})
462 463
}

464
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
465
    let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
466
        visit_expr: fn@(e: @ast::expr) {
467
           match e.node {
B
Brian Anderson 已提交
468
             ast::expr_while(cond, _) => {
469
                match cond.node {
470 471
                    ast::expr_lit(@ast::spanned { node: ast::lit_bool(true),
                                                  _}) => {
472
                            cx.sess.span_lint(
473
                                while_true, e.id, it.id,
474
                                e.span,
475
                                ~"denote infinite loops with loop { ... }");
476
                    }
B
Brian Anderson 已提交
477
                    _ => ()
478 479
                }
             }
B
Brian Anderson 已提交
480
             _ => ()
481
          }
B
Brian Anderson 已提交
482 483
        },
        .. *visit::default_simple_visitor()
484
    }));
485
    visit::visit_item(it, (), visit);
V
Viktor Dahl 已提交
486 487 488
}

fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
    pure fn is_valid<T: cmp::Ord>(binop: ast::binop, v: T,
            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,
            _ => fail
        }
    }

    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 {
        let (lit, expr, swap) = match (l.node, r.node) {
            (ast::expr_lit(_), _) => (l, r, true),
            (_, ast::expr_lit(_)) => (r, l, false),
            _ => 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
        };
546
        match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
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 574 575 576 577 578 579 580 581 582 583 584
            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
                    },
                    _ => fail
                };
                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
                    },
                    _ => fail
                };
                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
        }
    }

585 586 587 588 589 590 591 592
    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");
593 594
                }
            }
595 596 597 598 599 600
            _ => ()
        }
    };

    let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
        visit_expr: visit_expr,
601 602 603
        .. *visit::default_simple_visitor()
    }));
    visit::visit_item(it, (), visit);
604 605
}

606
fn check_item_default_methods(cx: ty::ctxt, item: @ast::item) {
607
    match /*bad*/copy item.node {
608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
        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");
                    }
                }
            }
        }
        _ => {}
    }
}

627 628 629 630 631 632 633 634 635 636 637 638 639
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) {
        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");
    }

640
    match /*bad*/copy item.node {
641 642
        ast::item_trait(_, _, methods) => {
            for methods.each |method| {
643
                match /*bad*/copy *method {
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
                    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);
            }
        }
        _ => {}
    }
}

662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678
fn check_item_structural_records(cx: ty::ctxt, it: @ast::item) {
    let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
        visit_expr: fn@(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);
}

679
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
680

681
    fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
682
                       decl: ast::fn_decl) {
B
Brian Anderson 已提交
683
        let tys = vec::map(decl.inputs, |a| a.ty );
684
        for vec::each(vec::append_one(tys, decl.output)) |ty| {
685
            match ty.node {
B
Brian Anderson 已提交
686
              ast::ty_path(_, id) => {
687
                match cx.def_map.get(id) {
B
Brian Anderson 已提交
688
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) => {
689
                    cx.sess.span_lint(
690
                        ctypes, id, fn_id,
691
                        ty.span,
692
                        ~"found rust type `int` in foreign module, while \
693
                         libc::c_int or libc::c_long should be used");
694
                  }
B
Brian Anderson 已提交
695
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) => {
696
                    cx.sess.span_lint(
697
                        ctypes, id, fn_id,
698
                        ty.span,
699
                        ~"found rust type `uint` in foreign module, while \
700
                         libc::c_uint or libc::c_ulong should be used");
701
                  }
B
Brian Anderson 已提交
702
                  _ => ()
703
                }
704
              }
B
Brian Anderson 已提交
705
              _ => ()
706 707 708 709
            }
        }
    }

710
    match it.node {
711 712 713
      ast::item_foreign_mod(ref nmod)
      if attr::foreign_abi(it.attrs) !=
            either::Right(ast::foreign_abi_rust_intrinsic) => {
B
Brian Anderson 已提交
714
        for nmod.items.each |ni| {
715
            match /*bad*/copy ni.node {
E
Erick Tryzelaar 已提交
716
              ast::foreign_item_fn(decl, _, _) => {
717
                check_foreign_fn(cx, it.id, decl);
718
              }
719
              ast::foreign_item_const(*) => {}  // XXX: Not implemented.
720 721
            }
        }
722
      }
B
Brian Anderson 已提交
723
      _ => {/* nothing to do */ }
724 725
    }
}
726

727 728 729 730 731 732 733 734 735 736 737 738
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| {
739
                match ty::get(t).sty {
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
                  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| {
768
                check_type_for_lint(cx, *lint, node, item, span, ty);
769 770 771 772 773 774 775
            }
    }

    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
776
      ast::item_struct(*) |
777 778 779 780 781 782 783 784 785
      ast::item_trait(*) => check_type(cx, it.id, it.id, it.span,
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

    let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
        visit_expr: fn@(e: @ast::expr) {
            let ty = ty::expr_ty(cx, e);
            check_type(cx, e.id, it.id, e.span, ty);
B
Brian Anderson 已提交
786 787
        },
        .. *visit::default_simple_visitor()
788 789 790 791
    }));
    visit::visit_item(it, (), visit);
}

792
fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
793
    let visit = item_stopping_visitor(visit::mk_simple_visitor(@{
794
        visit_stmt: fn@(s: @ast::stmt) {
795
            match s.node {
796
              ast::stmt_semi(@{id: id,
T
Tim Chevalier 已提交
797
                               callee_id: _,
E
Erick Tryzelaar 已提交
798
                               node: ast::expr_path(_),
B
Brian Anderson 已提交
799
                               span: _}, _) => {
800
                cx.sess.span_lint(
801
                    path_statement, id, it.id,
802
                    s.span,
803
                    ~"path statement with no effect");
804
              }
B
Brian Anderson 已提交
805
              _ => ()
806
            }
B
Brian Anderson 已提交
807 808
        },
        .. *visit::default_simple_visitor()
809
    }));
810 811 812
    visit::visit_item(it, (), visit);
}

813
fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
P
Paul Stansifer 已提交
814 815
    fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
        let ident = cx.sess.str_of(ident);
816
        assert ident.is_not_empty();
P
Paul Stansifer 已提交
817
        let ident = ident_without_trailing_underscores(ident);
818
        let ident = ident_without_leading_underscores(ident);
819
        char::is_uppercase(str::char_at(ident, 0)) &&
820 821 822
            !ident.contains_char('_')
    }

823
    fn ident_without_trailing_underscores(+ident: ~str) -> ~str {
824
        match str::rfind(ident, |c| c != '_') {
B
Brian Anderson 已提交
825 826
            Some(idx) => (ident).slice(0, idx + 1),
            None => { ident } // all underscores
827 828 829
        }
    }

830
    fn ident_without_leading_underscores(+ident: ~str) -> ~str {
831
        match str::find(ident, |c| c != '_') {
B
Brian Anderson 已提交
832 833
          Some(idx) => ident.slice(idx, ident.len()),
          None => {
834 835
            // all underscores
            ident
836 837 838 839
          }
        }
    }

840 841 842
    fn check_case(cx: ty::ctxt, ident: ast::ident,
                  expr_id: ast::node_id, item_id: ast::node_id,
                  span: span) {
P
Paul Stansifer 已提交
843
        if !is_camel_case(cx, ident) {
844 845
            cx.sess.span_lint(
                non_camel_case_types, expr_id, item_id, span,
846 847
                ~"type, variant, or trait should have \
                  a camel case identifier");
848 849 850
        }
    }

851
    match it.node {
852
      ast::item_ty(*) | ast::item_struct(*) |
853
      ast::item_trait(*) => {
854 855
        check_case(cx, it.ident, it.id, it.id, it.span)
      }
856
      ast::item_enum(ref enum_definition, _) => {
857
        check_case(cx, it.ident, it.id, it.id, it.span);
858
        for enum_definition.variants.each |variant| {
859 860 861 862
            check_case(cx, variant.node.name,
                       variant.node.id, it.id, variant.span);
        }
      }
B
Brian Anderson 已提交
863
      _ => ()
864 865 866
    }
}

867 868
fn check_fn(tcx: ty::ctxt, fk: visit::fn_kind, decl: ast::fn_decl,
            _body: ast::blk, span: span, id: ast::node_id) {
P
Paul Stansifer 已提交
869
    debug!("lint check_fn fk=%? id=%?", fk, id);
870 871 872

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

878
    let fn_ty = ty::node_id_to_type(tcx, id);
879 880 881 882 883
    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) {
884
    match ty::get(fn_ty).sty {
885
        ty::ty_fn(ref fn_ty) => {
886
            let mut counter = 0;
887
            for vec::each2(fn_ty.sig.inputs, decl.inputs) |arg_ty, arg_ast| {
888 889 890 891 892 893 894
                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 已提交
895 896 897 898 899 900
                        if !tcx.legacy_modes {
                            tcx.sess.span_lint(
                                deprecated_mode, id, id, span,
                                fmt!("argument %d uses by-copy mode",
                                     counter));
                        }
901
                    }
902

903 904 905 906
                    ast::expl(_) => {
                        tcx.sess.span_lint(
                            deprecated_mode, id, id,
                            span,
T
Tim Chevalier 已提交
907
                         fmt!("argument %d uses an explicit mode", counter));
908
                    }
909

910
                    ast::infer(_) => {
911 912 913 914 915 916 917 918 919 920
                        if tcx.legacy_modes {
                            let kind = ty::type_kind(tcx, arg_ty.ty);
                            if !ty::kind_is_safe_for_default_mode(kind) {
                                tcx.sess.span_lint(
                                    deprecated_mode, id, id,
                                    span,
                                    fmt!("argument %d uses the default mode \
                                          but shouldn't",
                                         counter));
                            }
921 922 923 924 925 926 927 928 929
                        }
                    }
                }

                match ty::get(arg_ty.ty).sty {
                    ty::ty_fn(*) => {
                        let span = arg_ast.ty.span;
                        // Recurse to check fn-type argument
                        match arg_ast.ty.node {
930
                            ast::ty_fn(f) => {
931
                                check_fn_deprecated_modes(tcx, arg_ty.ty,
932
                                                          f.decl, span, id);
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
                            }
                            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);
                                fail
                            }
                        };
                    }
                    _ => ()
951 952 953
                }
            }
        }
954 955 956 957 958 959 960 961 962 963

        _ => 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 {
964
                ast::ty_fn(f) => {
965 966
                    let fn_ty = ty::node_id_to_type(tcx, it.id);
                    check_fn_deprecated_modes(
967
                        tcx, fn_ty, f.decl, ty.span, it.id)
968 969 970 971 972
                }
                _ => ()
            }
        }
        _ => ()
973 974 975
    }
}

976
fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
977

978
    let v = visit::mk_simple_visitor(@{
979 980 981 982
        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 已提交
983
        .. *visit::default_simple_visitor()
984 985
    });
    visit::visit_crate(*crate, (), v);
986 987

    tcx.sess.abort_if_errors();
988
}
989

990 991 992 993 994 995 996 997 998
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//