lint.rs 34.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11
use core::prelude::*;
12

13
use driver::session::Session;
14
use driver::session;
P
Patrick Walton 已提交
15 16
use middle::ty;
use util::ppaux::{ty_to_str};
17

18
use core::hashmap::HashMap;
19 20 21 22 23 24 25 26 27 28 29 30 31 32
use core::char;
use core::cmp;
use core::i8;
use core::i16;
use core::i32;
use core::i64;
use core::int;
use core::str;
use core::u8;
use core::u16;
use core::u32;
use core::u64;
use core::uint;
use core::vec;
D
Daniel Micay 已提交
33
use std::smallintmap::SmallIntMap;
34 35
use syntax::attr;
use syntax::codemap::span;
J
John Clements 已提交
36
use syntax::codemap;
A
Alex Crichton 已提交
37 38
use syntax::print::pprust::mode_to_str;
use syntax::{ast, visit};
39

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

61
#[deriving(Eq)]
62
pub enum lint {
P
Patrick Walton 已提交
63
    ctypes,
64
    unused_imports,
65 66
    while_true,
    path_statement,
67
    implicit_copies,
68
    unrecognized_lint,
69
    non_implicitly_copyable_typarams,
70
    vecs_implicitly_copyable,
71
    deprecated_mode,
72
    deprecated_pattern,
73
    non_camel_case_types,
74
    type_limits,
75
    default_methods,
76
    deprecated_mutable_fields,
77
    deprecated_drop,
78
    foreign_mode,
79

80 81 82 83
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

84 85
    legacy_modes,

86 87 88
    // FIXME(#3266)--make liveness warnings lintable
    // unused_variable,
    // dead_assignment
89 90
}

91
pub fn level_to_str(lv: level) -> &'static str {
92
    match lv {
93 94 95 96
      allow => "allow",
      warn => "warn",
      deny => "deny",
      forbid => "forbid"
97 98 99
    }
}

100
#[deriving(Eq)]
101
pub enum level {
102
    allow, warn, deny, forbid
103 104
}

105 106
struct LintSpec {
    lint: lint,
107
    desc: &'static str,
108
    default: level
109
}
110

111
pub type LintDict = @HashMap<~str, LintSpec>;
112

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

126 127
        (~"unused_imports",
         LintSpec {
L
Luqman Aden 已提交
128
            lint: unused_imports,
129
            desc: "imports that are never used",
130
            default: warn
131
         }),
132

133 134
        (~"while_true",
         LintSpec {
L
Luqman Aden 已提交
135
            lint: while_true,
136 137 138
            desc: "suggest using loop { } instead of while(true) { }",
            default: warn
         }),
139

140 141
        (~"path_statement",
         LintSpec {
L
Luqman Aden 已提交
142
            lint: path_statement,
143 144 145
            desc: "path statements with no effect",
            default: warn
         }),
146

147 148
        (~"unrecognized_lint",
         LintSpec {
L
Luqman Aden 已提交
149
            lint: unrecognized_lint,
150 151 152
            desc: "unrecognized lint attribute",
            default: warn
         }),
153

154 155
        (~"non_implicitly_copyable_typarams",
         LintSpec {
L
Luqman Aden 已提交
156
            lint: non_implicitly_copyable_typarams,
157 158 159
            desc: "passing non implicitly copyable types as copy type params",
            default: warn
         }),
160

161 162
        (~"vecs_implicitly_copyable",
         LintSpec {
L
Luqman Aden 已提交
163
            lint: vecs_implicitly_copyable,
164
            desc: "make vecs and strs not implicitly copyable \
165
                  (only checked at top level)",
166 167
            default: warn
         }),
168

169 170
        (~"implicit_copies",
         LintSpec {
L
Luqman Aden 已提交
171
            lint: implicit_copies,
172 173 174
            desc: "implicit copies of non implicitly copyable data",
            default: warn
         }),
175

176 177
        (~"deprecated_mode",
         LintSpec {
L
Luqman Aden 已提交
178
            lint: deprecated_mode,
179 180 181
            desc: "warn about deprecated uses of modes",
            default: warn
         }),
182

183 184
        (~"foreign_mode",
         LintSpec {
185 186 187 188 189
            lint: foreign_mode,
            desc: "warn about deprecated uses of modes in foreign fns",
            default: warn
         }),

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

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

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

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

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

225 226
        (~"legacy modes",
         LintSpec {
L
Luqman Aden 已提交
227
            lint: legacy_modes,
228 229 230
            desc: "allow legacy modes",
            default: forbid
         }),
231

232 233
        (~"type_limits",
         LintSpec {
L
Luqman Aden 已提交
234
            lint: type_limits,
235 236 237
            desc: "comparisons made useless by limits of the types involved",
            default: warn
         }),
238

239 240
        (~"default_methods",
         LintSpec {
L
Luqman Aden 已提交
241
            lint: default_methods,
242 243 244
            desc: "allow default methods",
            default: deny
         }),
V
Viktor Dahl 已提交
245

246 247
        (~"deprecated_mutable_fields",
         LintSpec {
248 249 250 251 252
            lint: deprecated_mutable_fields,
            desc: "deprecated mutable fields in structures",
            default: deny
        }),

253 254
        (~"deprecated_drop",
         LintSpec {
255 256 257 258 259
            lint: deprecated_drop,
            desc: "deprecated \"drop\" notation for the destructor",
            default: deny
        }),

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

268
        (@~"dead_assignment",
269
         @LintSpec {
L
Luqman Aden 已提交
270
            lint: dead_assignment,
271 272 273
            desc: "detect assignments that will never be read",
            default: warn
         }),
274
        */
275
    ];
276
    let mut map = HashMap::new();
277 278 279 280
    do vec::consume(v) |_, (k, v)| {
        map.insert(k, v);
    }
    return @map;
281 282
}

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

287
// settings_map maps node ids of items with non-default lint settings
288 289
// to their settings; default_settings contains the settings for everything
// not in the map.
290 291 292
pub struct LintSettings {
    default_settings: LintModes,
    settings_map: LintModeMap
293 294
}

295 296
pub fn mk_lint_settings() -> LintSettings {
    LintSettings {
D
Daniel Micay 已提交
297
        default_settings: @mut SmallIntMap::new(),
298
        settings_map: @mut HashMap::new()
299 300 301 302
    }
}

pub fn get_lint_level(modes: LintModes, lint: lint) -> level {
D
Daniel Micay 已提交
303 304
    match modes.find(&(lint as uint)) {
      Some(&c) => c,
B
Brian Anderson 已提交
305
      None => allow
306 307 308
    }
}

309
pub fn get_lint_settings_level(settings: LintSettings,
310 311 312 313
                               lint_mode: lint,
                               _expr_id: ast::node_id,
                               item_id: ast::node_id)
                            -> level {
314
    match settings.settings_map.find(&item_id) {
315
      Some(&modes) => get_lint_level(modes, lint_mode),
B
Brian Anderson 已提交
316
      None => get_lint_level(settings.default_settings, lint_mode)
317 318 319
    }
}

320 321
// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
322
fn clone_lint_modes(modes: LintModes) -> LintModes {
D
Daniel Micay 已提交
323
    @mut (copy *modes)
324 325
}

326 327 328 329 330
struct Context {
    dict: LintDict,
    curr: LintModes,
    is_default: bool,
    sess: Session
331
}
332

333
pub impl Context {
334
    fn get_level(&self, lint: lint) -> level {
335
        get_lint_level(self.curr, lint)
336 337
    }

338
    fn set_level(&self, lint: lint, level: level) {
339
        if level == allow {
D
Daniel Micay 已提交
340
            self.curr.remove(&(lint as uint));
341
        } else {
342
            self.curr.insert(lint as uint, level);
343
        }
344 345
    }

346
    fn span_lint(&self, level: level, span: span, +msg: ~str) {
347
        self.sess.span_lint_level(level, span, msg);
348 349
    }

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

357
        let mut new_ctxt = *self;
358 359 360
        let mut triples = ~[];

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

390 391 392 393 394 395 396
        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)
            };

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

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

                // 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);
422 423 424 425 426
                new_ctxt = Context {
                    is_default: false,
                    curr: c,
                    .. new_ctxt
                };
427
                new_ctxt.set_level(lint.lint, level);
428
              }
429 430
            }
        }
431
        f(new_ctxt);
432 433 434 435
    }
}


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

445
pub fn build_settings_crate(sess: session::Session, crate: @ast::crate) {
446 447
    let cx = Context {
        dict: get_lint_dict(),
D
Daniel Micay 已提交
448
        curr: @mut SmallIntMap::new(),
449 450 451
        is_default: true,
        sess: sess
    };
452 453

    // Install defaults.
454
    for cx.dict.each_value |&spec| {
D
Daniel Micay 已提交
455 456
        cx.set_level(spec.lint, spec.default);
    }
457 458

    // Install command-line options, overriding defaults.
B
Brian Anderson 已提交
459
    for sess.opts.lint_opts.each |pair| {
460
        let (lint,level) = *pair;
461 462 463
        cx.set_level(lint, level);
    }

464
    do cx.with_lint_attrs(/*bad*/copy crate.node.attrs) |cx| {
465
        // Copy out the default settings
D
Daniel Micay 已提交
466
        for cx.curr.each |&(k, &v)| {
467
            sess.lint_settings.default_settings.insert(k, v);
468 469
        }

470 471 472 473
        let cx = Context {
            is_default: true,
            .. cx
        };
474

475
        let visit = visit::mk_vt(@visit::Visitor {
B
Brian Anderson 已提交
476 477
            visit_item: build_settings_item,
            .. *visit::default_visitor()
478 479 480 481 482 483 484 485 486 487 488
        });
        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);
489
    check_item_non_camel_case_types(cx, i);
490
    check_item_heap(cx, i);
491
    check_item_deprecated_modes(cx, i);
492
    check_item_type_limits(cx, i);
493
    check_item_default_methods(cx, i);
494
    check_item_deprecated_mutable_fields(cx, i);
495
    check_item_deprecated_drop(cx, i);
496 497
}

498 499 500 501 502
// 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> {
503 504 505 506 507 508
    visit::mk_vt(@visit::Visitor {visit_item: |_i, _e, _v| { },
        .. **(ty_stopping_visitor(v))})
}

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

511
fn check_item_while_true(cx: ty::ctxt, it: @ast::item) {
512 513 514 515 516 517
    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 已提交
518
                            ast::expr_lit(@codemap::spanned {
519 520 521 522 523 524 525 526 527 528
                                node: ast::lit_bool(true), _}) =>
                            {
                                cx.sess.span_lint(
                                    while_true, e.id, it.id,
                                    e.span,
                                    ~"denote infinite loops \
                                      with loop { ... }");
                            }
                            _ => ()
                        }
529
                    }
B
Brian Anderson 已提交
530
                    _ => ()
531
                }
532 533 534
            },
            .. *visit::default_simple_visitor()
        }));
535
    visit::visit_item(it, (), visit);
V
Viktor Dahl 已提交
536 537 538
}

fn check_item_type_limits(cx: ty::ctxt, it: @ast::item) {
539
    fn is_valid<T:cmp::Ord>(binop: ast::binop, v: T,
540 541 542 543 544 545 546
            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,
547
            _ => fail!()
548 549 550
        }
    }

551
    fn rev_binop(binop: ast::binop) -> ast::binop {
552 553 554 555 556 557 558 559 560
        match binop {
            ast::lt => ast::gt,
            ast::le => ast::ge,
            ast::gt => ast::lt,
            ast::ge => ast::le,
            _ => binop
        }
    }

561
    fn int_ty_range(int_ty: ast::int_ty) -> (i64, i64) {
562 563 564 565 566 567 568 569 570 571
        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)
        }
    }

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

627
    fn is_comparison(binop: ast::binop) -> bool {
628 629 630 631 632 633 634
        match binop {
            ast::eq | ast::lt | ast::le |
            ast::ne | ast::ge | ast::gt => true,
            _ => false
        }
    }

635 636 637 638 639 640 641 642
    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");
643 644
                }
            }
645 646 647 648
            _ => ()
        }
    };

649 650 651 652 653
    let visit = item_stopping_visitor(
        visit::mk_simple_visitor(@visit::SimpleVisitor {
            visit_expr: visit_expr,
            .. *visit::default_simple_visitor()
        }));
654
    visit::visit_item(it, (), visit);
655 656
}

657
fn check_item_default_methods(cx: ty::ctxt, item: @ast::item) {
658 659
    match item.node {
        ast::item_trait(_, _, ref methods) => {
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
            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");
                    }
                }
            }
        }
        _ => {}
    }
}

678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
fn check_item_deprecated_mutable_fields(cx: ty::ctxt, item: @ast::item) {
    match item.node {
        ast::item_struct(struct_def, _) => {
            for struct_def.fields.each |field| {
                match field.node.kind {
                    ast::named_field(_, ast::struct_mutable, _) => {
                        cx.sess.span_lint(deprecated_mutable_fields,
                                          item.id,
                                          item.id,
                                          field.span,
                                          ~"mutable fields are deprecated");
                    }
                    ast::named_field(*) | ast::unnamed_field => {}
                }
            }
        }
        _ => {}
    }
}

698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
fn check_item_deprecated_drop(cx: ty::ctxt, item: @ast::item) {
    match item.node {
        ast::item_struct(struct_def, _) => {
            match struct_def.dtor {
                None => {}
                Some(ref dtor) => {
                    cx.sess.span_lint(deprecated_drop,
                                      item.id,
                                      item.id,
                                      dtor.span,
                                      ~"`drop` notation for destructors is \
                                        deprecated; implement the `Drop` \
                                        trait instead");
                }
            }
        }
        _ => {}
    }
}

718
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
719

720
    fn check_foreign_fn(cx: ty::ctxt, fn_id: ast::node_id,
721
                        decl: &ast::fn_decl) {
722 723 724 725
        // warn about `&&` mode on foreign functions, both because it is
        // deprecated and because its semantics have changed recently:
        for decl.inputs.eachi |i, arg| {
            match ty::resolved_mode(cx, arg.mode) {
726
                ast::by_copy => {}
727 728 729 730 731 732 733 734 735
                ast::by_ref => {
                    cx.sess.span_lint(
                        foreign_mode, fn_id, fn_id, arg.ty.span,
                        fmt!("foreign function uses `&&` mode \
                              on argument %u", i));
                }
            }
        }

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

763
    match it.node {
764
      ast::item_foreign_mod(ref nmod) if !nmod.abis.is_intrinsic() => {
B
Brian Anderson 已提交
765
        for nmod.items.each |ni| {
766
            match ni.node {
767
              ast::foreign_item_fn(ref decl, _, _) => {
768
                check_foreign_fn(cx, it.id, decl);
769
              }
770 771
              // FIXME #4622: Not implemented.
              ast::foreign_item_const(*) => {}
772 773
            }
        }
774
      }
B
Brian Anderson 已提交
775
      _ => {/* nothing to do */ }
776 777
    }
}
778

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

    match it.node {
      ast::item_fn(*) |
      ast::item_ty(*) |
      ast::item_enum(*) |
828
      ast::item_struct(*) => check_type(cx, it.id, it.id, it.span,
829 830 831 832
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

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

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

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

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

888
    fn ident_without_trailing_underscores<'r>(ident: &'r str) -> &'r str {
889
        match str::rfind(ident, |c| c != '_') {
890
            Some(idx) => str::slice(ident, 0, idx + 1),
891
            None => ident, // all underscores
892 893 894
        }
    }

895
    fn ident_without_leading_underscores<'r>(ident: &'r str) -> &'r str {
896
        match str::find(ident, |c| c != '_') {
897
            Some(idx) => str::slice(ident, idx, ident.len()),
898
            None => ident // all underscores
899 900 901
        }
    }

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

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

929 930
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 已提交
931
    debug!("lint check_fn fk=%? id=%?", fk, id);
932 933 934

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

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

944
fn check_fn_deprecated_modes(tcx: ty::ctxt, fn_ty: ty::t, decl: &ast::fn_decl,
945
                             span: span, id: ast::node_id) {
946
    match ty::get(fn_ty).sty {
947 948
        ty::ty_closure(ty::ClosureTy {sig: ref sig, _}) |
        ty::ty_bare_fn(ty::BareFnTy {sig: ref sig, _}) => {
949
            let mut counter = 0;
950
            for vec::each2(sig.inputs, decl.inputs) |arg_ty, arg_ast| {
951 952 953 954 955 956 957
                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 已提交
958 959 960 961 962 963
                        if !tcx.legacy_modes {
                            tcx.sess.span_lint(
                                deprecated_mode, id, id, span,
                                fmt!("argument %d uses by-copy mode",
                                     counter));
                        }
964
                    }
965

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

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

                match ty::get(arg_ty.ty).sty {
989
                    ty::ty_closure(*) | ty::ty_bare_fn(*) => {
990 991 992
                        let span = arg_ast.ty.span;
                        // Recurse to check fn-type argument
                        match arg_ast.ty.node {
993 994
                            ast::ty_closure(@ast::TyClosure{decl: ref d, _}) |
                            ast::ty_bare_fn(@ast::TyBareFn{decl: ref d, _})=>{
995
                                check_fn_deprecated_modes(tcx, arg_ty.ty,
996
                                                          d, span, id);
997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
                            }
                            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);
1010
                                fail!()
1011 1012 1013 1014
                            }
                        };
                    }
                    _ => ()
1015 1016 1017
                }
            }
        }
1018 1019 1020 1021 1022 1023 1024 1025 1026 1027

        _ => 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 {
1028 1029
                ast::ty_closure(@ast::TyClosure {decl: ref decl, _}) |
                ast::ty_bare_fn(@ast::TyBareFn {decl: ref decl, _}) => {
1030 1031
                    let fn_ty = ty::node_id_to_type(tcx, it.id);
                    check_fn_deprecated_modes(
1032
                        tcx, fn_ty, decl, ty.span, it.id)
1033 1034 1035 1036 1037
                }
                _ => ()
            }
        }
        _ => ()
1038 1039 1040
    }
}

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

    tcx.sess.abort_if_errors();
1052
}
1053

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