lint.rs 33.3 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
pub enum level {
106
    allow, warn, deny, forbid
107 108
}

109
impl cmp::Eq for level {
110 111 112 113
    pure fn eq(&self, other: &level) -> bool {
        ((*self) as uint) == ((*other) as uint)
    }
    pure fn ne(&self, other: &level) -> bool { !(*self).eq(other) }
114
}
115

116
type lint_spec = @{lint: lint,
117
                   desc: &static/str,
118 119
                   default: level};

120
pub type lint_dict = HashMap<@~str, lint_spec>;
121

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

133
        (@~"unused_imports",
134
         @{lint: unused_imports,
135
           desc: "imports that are never used",
136
           default: allow}),
137

138
        (@~"while_true",
139
         @{lint: while_true,
140
           desc: "suggest using loop { } instead of while(true) { }",
141 142
           default: warn}),

143
        (@~"path_statement",
144
         @{lint: path_statement,
145
           desc: "path statements with no effect",
146 147
           default: warn}),

148
        (@~"unrecognized_lint",
149
         @{lint: unrecognized_lint,
150
           desc: "unrecognized lint attribute",
151 152
           default: warn}),

153
        (@~"non_implicitly_copyable_typarams",
154
         @{lint: non_implicitly_copyable_typarams,
155
           desc: "passing non implicitly copyable types as copy type params",
156 157
           default: warn}),

158
        (@~"vecs_implicitly_copyable",
159
         @{lint: vecs_implicitly_copyable,
160
           desc: "make vecs and strs not implicitly copyable \
161
                  (only checked at top level)",
162 163
           default: warn}),

164
        (@~"implicit_copies",
165
         @{lint: implicit_copies,
166
           desc: "implicit copies of non implicitly copyable data",
167
           default: warn}),
168

169
        (@~"deprecated_mode",
170
         @{lint: deprecated_mode,
171
           desc: "warn about deprecated uses of modes",
172
           default: warn}),
173

174
        (@~"deprecated_pattern",
175
         @{lint: deprecated_pattern,
176
           desc: "warn about deprecated uses of pattern bindings",
177 178
           default: allow}),

179
        (@~"non_camel_case_types",
180
         @{lint: non_camel_case_types,
181
           desc: "types, variants and traits should have camel case names",
182
           default: allow}),
183

184
        (@~"managed_heap_memory",
185
         @{lint: managed_heap_memory,
186
           desc: "use of managed (@ type) heap memory",
187 188
           default: allow}),

189
        (@~"owned_heap_memory",
190
         @{lint: owned_heap_memory,
191
           desc: "use of owned (~ type) heap memory",
192 193
           default: allow}),

194
        (@~"heap_memory",
195
         @{lint: heap_memory,
196
           desc: "use of any (~ type or @ type) heap memory",
197 198
           default: allow}),

199
        (@~"structural_records",
200
         @{lint: structural_records,
201
           desc: "use of any structural records",
202
           default: deny}),
203

204
        (@~"legacy modes",
205
         @{lint: legacy_modes,
206
           desc: "allow legacy modes",
207 208
           default: forbid}),

209
        (@~"type_limits",
210
         @{lint: type_limits,
211
           desc: "comparisons made useless by limits of the types involved",
212 213
           default: warn}),

214
        (@~"default_methods",
215
         @{lint: default_methods,
216
           desc: "allow default methods",
217
           default: deny}),
V
Viktor Dahl 已提交
218

219
        (@~"deprecated_self",
220
         @{lint: deprecated_self,
221
           desc: "warn about deprecated uses of `self`",
222
           default: warn}),
223

224
        /* FIXME(#3266)--make liveness warnings lintable
225
        (@~"unused_variable",
226
         @{lint: unused_variable,
227
           desc: "detect variables which are not used in any way",
228 229
           default: warn}),

230
        (@~"dead_assignment",
231
         @{lint: dead_assignment,
232
           desc: "detect assignments that will never be read",
233 234
           default: warn}),
        */
235
    ];
236
    oldmap::hash_from_vec(v)
237 238
}

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

243
// settings_map maps node ids of items with non-default lint settings
244 245
// to their settings; default_settings contains the settings for everything
// not in the map.
246
pub type lint_settings = {
247 248 249 250
    default_settings: lint_modes,
    settings_map: lint_mode_map
};

251
pub fn mk_lint_settings() -> lint_settings {
252
    {default_settings: oldsmallintmap::mk(),
253
     settings_map: HashMap()}
254 255
}

256
pub fn get_lint_level(modes: lint_modes, lint: lint) -> level {
257
    match modes.find(lint as uint) {
B
Brian Anderson 已提交
258 259
      Some(c) => c,
      None => allow
260 261 262
    }
}

263 264 265 266 267
pub fn get_lint_settings_level(settings: lint_settings,
                               lint_mode: lint,
                               _expr_id: ast::node_id,
                               item_id: ast::node_id)
                            -> level {
268
    match settings.settings_map.find(&item_id) {
B
Brian Anderson 已提交
269 270
      Some(modes) => get_lint_level(modes, lint_mode),
      None => get_lint_level(settings.default_settings, lint_mode)
271 272 273
    }
}

274 275 276
// 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 {
277 278
    oldsmallintmap::SmallIntMap_(@oldsmallintmap::SmallIntMap_
    {v: copy modes.v})
279 280
}

281 282 283
type ctxt_ = {dict: lint_dict,
              curr: lint_modes,
              is_default: bool,
284
              sess: Session};
285

286 287 288
enum ctxt {
    ctxt_(ctxt_)
}
289

B
Brian Anderson 已提交
290
impl ctxt {
291
    fn get_level(lint: lint) -> level {
292
        get_lint_level(self.curr, lint)
293 294
    }

295
    fn set_level(lint: lint, level: level) {
296
        if level == allow {
297
            self.curr.remove(lint as uint);
298
        } else {
299
            self.curr.insert(lint as uint, level);
300
        }
301 302
    }

303
    fn span_lint(level: level, span: span, +msg: ~str) {
304
        self.sess.span_lint_level(level, span, msg);
305 306
    }

307
    /**
308
     * Merge the lints specified by any lint attributes into the
309
     * current lint context, call the provided function, then reset the
310
     * lints in effect to their previous state.
311
     */
312
    fn with_lint_attrs(attrs: ~[ast::attribute], f: fn(ctxt)) {
313

314
        let mut new_ctxt = self;
315 316 317
        let mut triples = ~[];

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

347 348 349 350 351 352 353 354
        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 已提交
355
              None => {
356 357 358
                self.span_lint(
                    new_ctxt.get_level(unrecognized_lint),
                    meta.span,
P
Paul Stansifer 已提交
359
                    fmt!("unknown `%s` attribute: `%s`",
360
                         level_to_str(level), *lintname));
361
              }
B
Brian Anderson 已提交
362
              Some(lint) => {
363 364 365 366 367 368

                if new_ctxt.get_level(lint.lint) == forbid &&
                    level != forbid {
                    self.span_lint(
                        forbid,
                        meta.span,
P
Paul Stansifer 已提交
369
                        fmt!("%s(%s) overruled by outer forbid(%s)",
370
                             level_to_str(level),
371
                             *lintname, *lintname));
372 373 374 375 376 377 378 379 380 381
                }

                // 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 已提交
382
                           .. *new_ctxt});
383
                new_ctxt.set_level(lint.lint, level);
384
              }
385 386
            }
        }
387
        f(new_ctxt);
388 389 390 391
    }
}


392
fn build_settings_item(i: @ast::item, &&cx: ctxt, v: visit::vt<ctxt>) {
393
    do cx.with_lint_attrs(/*bad*/copy i.attrs) |cx| {
394
        if !cx.is_default {
395
            cx.sess.lint_settings.settings_map.insert(i.id, cx.curr);
396
        }
397
        visit::visit_item(i, cx, v);
398
    }
399
}
400

401
pub fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
402
    let cx = ctxt_({dict: get_lint_dict(),
403
                    curr: oldsmallintmap::mk(),
404 405
                    is_default: true,
                    sess: sess});
406 407

    // Install defaults.
408
    for cx.dict.each_value |&spec| {
D
Daniel Micay 已提交
409 410
        cx.set_level(spec.lint, spec.default);
    }
411 412

    // Install command-line options, overriding defaults.
B
Brian Anderson 已提交
413
    for sess.opts.lint_opts.each |pair| {
414
        let (lint,level) = *pair;
415 416 417
        cx.set_level(lint, level);
    }

418
    do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
419
        // Copy out the default settings
B
Brian Anderson 已提交
420
        for cx.curr.each |k, v| {
421
            sess.lint_settings.default_settings.insert(k, v);
422 423
        }

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

426
        let visit = visit::mk_vt(@visit::Visitor {
B
Brian Anderson 已提交
427 428
            visit_item: build_settings_item,
            .. *visit::default_visitor()
429 430 431 432 433 434 435 436 437 438 439
        });
        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);
440
    check_item_non_camel_case_types(cx, i);
441
    check_item_heap(cx, i);
442
    check_item_structural_records(cx, i);
443
    check_item_deprecated_modes(cx, i);
444
    check_item_type_limits(cx, i);
445
    check_item_default_methods(cx, i);
446
    check_item_deprecated_self(cx, i);
447 448
}

449 450 451 452 453
// 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> {
454
    visit::mk_vt(@visit::Visitor {visit_item: |_i, _e, _v| { },.. **v})
455 456
}

457
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
458 459 460 461 462 463
    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 已提交
464
                            ast::expr_lit(@codemap::spanned {
465 466 467 468 469 470 471 472 473 474
                                node: ast::lit_bool(true), _}) =>
                            {
                                cx.sess.span_lint(
                                    while_true, e.id, it.id,
                                    e.span,
                                    ~"denote infinite loops \
                                      with loop { ... }");
                            }
                            _ => ()
                        }
475
                    }
B
Brian Anderson 已提交
476
                    _ => ()
477
                }
478 479 480
            },
            .. *visit::default_simple_visitor()
        }));
481
    visit::visit_item(it, (), visit);
V
Viktor Dahl 已提交
482 483 484
}

fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
485 486 487 488 489 490 491 492
    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,
493
            _ => fail!()
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
        }
    }

    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 {
530 531 532
        let (lit, expr, swap) = match (&l.node, &r.node) {
            (&ast::expr_lit(_), _) => (l, r, true),
            (_, &ast::expr_lit(_)) => (r, l, false),
533 534 535 536 537 538 539 540 541
            _ => 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
        };
542
        match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
543 544 545 546 547 548 549 550 551
            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
                    },
552
                    _ => fail!()
553 554 555 556 557 558 559 560 561 562 563 564
                };
                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
                    },
565
                    _ => fail!()
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
                };
                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
        }
    }

581 582 583 584 585 586 587 588
    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");
589 590
                }
            }
591 592 593 594
            _ => ()
        }
    };

595 596 597 598 599
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: visit_expr,
            .. *visit::default_simple_visitor()
        }));
600
    visit::visit_item(it, (), visit);
601 602
}

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

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

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

664
fn check_item_structural_records(cx: ty::ctxt, it: @ast::item) {
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
    if !cx.legacy_records {
        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);
    }
682 683
}

684
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
685

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

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

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

    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
782
      ast::item_struct(*) |
783 784 785 786 787
      ast::item_trait(*) => check_type(cx, it.id, it.id, it.span,
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

788 789 790 791 792 793 794 795 796 797 798 799
    // If it's a struct, we also have to check the fields' types
    match it.node {
        ast::item_struct(struct_def, _) => {
            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));
            }
        }
        _ => ()
    }

800 801 802 803 804 805 806 807
    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()
        }));
808 809 810
    visit::visit_item(it, (), visit);
}

811
fn check_item_path_statement(cx: ty::ctxt, it: @ast::item) {
812 813 814 815
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_stmt: |s: @ast::stmt| {
                match s.node {
816 817 818 819
                    ast::stmt_semi(
                        @ast::expr { id: id, node: ast::expr_path(_), _ },
                        _
                    ) => {
820 821 822 823 824 825 826 827 828 829
                        cx.sess.span_lint(
                            path_statement, id, it.id,
                            s.span,
                            ~"path statement with no effect");
                    }
                    _ => ()
                }
            },
            .. *visit::default_simple_visitor()
        }));
830 831 832
    visit::visit_item(it, (), visit);
}

833
fn check_item_non_camel_case_types(cx: ty::ctxt, it: @ast::item) {
P
Paul Stansifer 已提交
834 835
    fn is_camel_case(cx: ty::ctxt, ident: ast::ident) -> bool {
        let ident = cx.sess.str_of(ident);
836
        assert !ident.is_empty();
P
Paul Stansifer 已提交
837
        let ident = ident_without_trailing_underscores(ident);
838
        let ident = ident_without_leading_underscores(ident);
839
        char::is_uppercase(str::char_at(ident, 0)) &&
840 841 842
            !ident.contains_char('_')
    }

843
    fn ident_without_trailing_underscores(+ident: ~str) -> ~str {
844
        match str::rfind(ident, |c| c != '_') {
B
Brian Anderson 已提交
845 846
            Some(idx) => (ident).slice(0, idx + 1),
            None => { ident } // all underscores
847 848 849
        }
    }

850
    fn ident_without_leading_underscores(+ident: ~str) -> ~str {
851
        match str::find(ident, |c| c != '_') {
B
Brian Anderson 已提交
852 853
          Some(idx) => ident.slice(idx, ident.len()),
          None => {
854 855
            // all underscores
            ident
856 857 858 859
          }
        }
    }

860 861 862
    fn check_case(cx: ty::ctxt, ident: ast::ident,
                  expr_id: ast::node_id, item_id: ast::node_id,
                  span: span) {
P
Paul Stansifer 已提交
863
        if !is_camel_case(cx, ident) {
864 865
            cx.sess.span_lint(
                non_camel_case_types, expr_id, item_id, span,
866 867
                ~"type, variant, or trait should have \
                  a camel case identifier");
868 869 870
        }
    }

871
    match it.node {
872
      ast::item_ty(*) | ast::item_struct(*) |
873
      ast::item_trait(*) => {
874 875
        check_case(cx, it.ident, it.id, it.id, it.span)
      }
876
      ast::item_enum(ref enum_definition, _) => {
877
        check_case(cx, it.ident, it.id, it.id, it.span);
878
        for enum_definition.variants.each |variant| {
879 880 881 882
            check_case(cx, variant.node.name,
                       variant.node.id, it.id, variant.span);
        }
      }
B
Brian Anderson 已提交
883
      _ => ()
884 885 886
    }
}

887 888
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 已提交
889
    debug!("lint check_fn fk=%? id=%?", fk, id);
890 891 892

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

898
    let fn_ty = ty::node_id_to_type(tcx, id);
899 900 901 902 903
    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) {
904
    match ty::get(fn_ty).sty {
905 906
        ty::ty_closure(ty::ClosureTy {sig: ref sig, _}) |
        ty::ty_bare_fn(ty::BareFnTy {sig: ref sig, _}) => {
907
            let mut counter = 0;
908
            for vec::each2(sig.inputs, decl.inputs) |arg_ty, arg_ast| {
909 910 911 912 913 914 915
                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 已提交
916 917 918 919 920 921
                        if !tcx.legacy_modes {
                            tcx.sess.span_lint(
                                deprecated_mode, id, id, span,
                                fmt!("argument %d uses by-copy mode",
                                     counter));
                        }
922
                    }
923

924 925 926 927
                    ast::expl(_) => {
                        tcx.sess.span_lint(
                            deprecated_mode, id, id,
                            span,
T
Tim Chevalier 已提交
928
                         fmt!("argument %d uses an explicit mode", counter));
929
                    }
930

931
                    ast::infer(_) => {
932
                        if tcx.legacy_modes {
933 934
                            let kind = ty::type_contents(tcx, arg_ty.ty);
                            if !kind.is_safe_for_default_mode(tcx) {
935 936 937 938 939 940 941
                                tcx.sess.span_lint(
                                    deprecated_mode, id, id,
                                    span,
                                    fmt!("argument %d uses the default mode \
                                          but shouldn't",
                                         counter));
                            }
942 943 944 945 946
                        }
                    }
                }

                match ty::get(arg_ty.ty).sty {
947
                    ty::ty_closure(*) | ty::ty_bare_fn(*) => {
948 949 950
                        let span = arg_ast.ty.span;
                        // Recurse to check fn-type argument
                        match arg_ast.ty.node {
951 952
                            ast::ty_closure(@ast::TyClosure{decl: ref d, _}) |
                            ast::ty_bare_fn(@ast::TyBareFn{decl: ref d, _})=>{
953
                                check_fn_deprecated_modes(tcx, arg_ty.ty,
954
                                                          *d, span, id);
955 956 957 958 959 960 961 962 963 964 965 966 967
                            }
                            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);
968
                                fail!()
969 970 971 972
                            }
                        };
                    }
                    _ => ()
973 974 975
                }
            }
        }
976 977 978 979 980 981 982 983 984 985

        _ => 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 {
986 987
                ast::ty_closure(@ast::TyClosure {decl: ref decl, _}) |
                ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => {
988 989
                    let fn_ty = ty::node_id_to_type(tcx, it.id);
                    check_fn_deprecated_modes(
990
                        tcx, fn_ty, *decl, ty.span, it.id)
991 992 993 994 995
                }
                _ => ()
            }
        }
        _ => ()
996 997 998
    }
}

999
pub fn check_crate(tcx: ty::ctxt, crate: @ast::crate) {
1000
    let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
1001 1002 1003 1004
        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 已提交
1005
        .. *visit::default_simple_visitor()
1006 1007
    });
    visit::visit_crate(*crate, (), v);
1008 1009

    tcx.sess.abort_if_errors();
1010
}
1011

1012 1013 1014 1015 1016 1017 1018 1019 1020
//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//