lint.rs 34.1 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 19 20 21 22 23 24 25 26 27 28 29 30 31 32

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::str;
use core::u8;
use core::u16;
use core::u32;
use core::u64;
use core::uint;
use core::vec;
33 34
use std::oldmap::{Map, HashMap};
use std::oldmap;
D
Daniel Micay 已提交
35
use std::smallintmap::SmallIntMap;
36 37
use syntax::attr;
use syntax::codemap::span;
J
John Clements 已提交
38
use syntax::codemap;
P
Patrick Walton 已提交
39
use syntax::print::pprust::{expr_to_str, mode_to_str, pat_to_str};
40 41
use syntax::{ast, ast_util, visit};

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

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

82 83 84 85
    managed_heap_memory,
    owned_heap_memory,
    heap_memory,

86 87
    legacy_modes,

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

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

102
#[deriving_eq]
103
pub enum level {
104
    allow, warn, deny, forbid
105 106
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

355
        let mut new_ctxt = *self;
356 357 358
        let mut triples = ~[];

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

388 389 390 391 392 393 394 395
        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 已提交
396
              None => {
397 398 399
                self.span_lint(
                    new_ctxt.get_level(unrecognized_lint),
                    meta.span,
P
Paul Stansifer 已提交
400
                    fmt!("unknown `%s` attribute: `%s`",
401
                         level_to_str(level), *lintname));
402
              }
B
Brian Anderson 已提交
403
              Some(lint) => {
404 405 406 407 408 409

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

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


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

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

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

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

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

468 469 470 471
        let cx = Context {
            is_default: true,
            .. cx
        };
472

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

496 497 498 499 500
// 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> {
501 502 503 504 505 506
    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})
507 508
}

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

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

    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 {
582 583 584
        let (lit, expr, swap) = match (&l.node, &r.node) {
            (&ast::expr_lit(_), _) => (l, r, true),
            (_, &ast::expr_lit(_)) => (r, l, false),
585 586 587 588 589 590 591 592 593
            _ => 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
        };
594
        match ty::get(ty::expr_ty(cx, @/*bad*/copy *expr)).sty {
595 596 597 598 599 600 601 602 603
            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
                    },
604
                    _ => fail!()
605 606 607 608 609 610 611 612 613 614 615 616
                };
                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
                    },
617
                    _ => fail!()
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
                };
                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
        }
    }

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

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

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

676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
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 => {}
                }
            }
        }
        _ => {}
    }
}

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
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");
                }
            }
        }
        _ => {}
    }
}

716
fn check_item_ctypes(cx: ty::ctxt, it: @ast::item) {
717

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

761
    match it.node {
762 763 764
      ast::item_foreign_mod(ref nmod)
      if attr::foreign_abi(it.attrs) !=
            either::Right(ast::foreign_abi_rust_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(*) |
829 830 831 832 833
      ast::item_trait(*) => check_type(cx, it.id, it.id, it.span,
                                       ty::node_id_to_type(cx, it.id)),
      _ => ()
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    tcx.sess.abort_if_errors();
1053
}
1054

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