2014-06-01 18:16:00 -05:00
|
|
|
// Copyright 2012-2014 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.
|
|
|
|
|
|
|
|
//! Lints built in to rustc.
|
2014-06-06 17:49:48 -05:00
|
|
|
//!
|
|
|
|
//! This is a sibling of `lint::context` in order to ensure that
|
|
|
|
//! lints implemented here use the same public API as lint plugins.
|
|
|
|
//!
|
|
|
|
//! To add a new lint to rustc, declare it here using `declare_lint!()`.
|
|
|
|
//! Then add code to emit the new lint in the appropriate circumstances.
|
|
|
|
//! You can do that in an existing `LintPass` if it makes sense, or in
|
|
|
|
//! a new `LintPass`, or using `Session::add_lint` elsewhere in the
|
|
|
|
//! compiler. Only do the latter if the check can't be written cleanly
|
|
|
|
//! as a `LintPass`.
|
|
|
|
//!
|
|
|
|
//! If you define a new `LintPass`, you will also need to add it to the
|
2014-06-13 00:20:47 -05:00
|
|
|
//! `add_builtin!` or `add_builtin_with_new!` invocation in `context.rs`.
|
|
|
|
//! Use the former for unit-like structs and the latter for structs with
|
|
|
|
//! a `pub fn new()`.
|
2014-06-01 18:16:00 -05:00
|
|
|
|
|
|
|
use metadata::csearch;
|
|
|
|
use middle::def::*;
|
2014-06-06 17:49:48 -05:00
|
|
|
use middle::typeck::astconv::ast_ty_to_ty;
|
2014-06-01 18:16:00 -05:00
|
|
|
use middle::typeck::infer;
|
2014-06-26 13:37:39 -05:00
|
|
|
use middle::{typeck, ty, def, pat_util, stability};
|
2014-11-01 03:10:02 -05:00
|
|
|
use middle::const_eval::{eval_const_expr_partial, const_int, const_uint};
|
2014-06-21 05:39:03 -05:00
|
|
|
use util::ppaux::{ty_to_string};
|
2014-06-02 17:27:15 -05:00
|
|
|
use util::nodemap::NodeSet;
|
2014-06-04 16:35:58 -05:00
|
|
|
use lint::{Context, LintPass, LintArray};
|
2014-06-01 18:16:00 -05:00
|
|
|
|
|
|
|
use std::cmp;
|
|
|
|
use std::collections::HashMap;
|
2014-10-30 20:25:08 -05:00
|
|
|
use std::collections::hash_map::{Occupied, Vacant};
|
2014-09-07 12:09:06 -05:00
|
|
|
use std::slice;
|
2014-11-01 03:10:02 -05:00
|
|
|
use std::{int, i8, i16, i32, i64, uint, u8, u16, u32, u64, f32, f64};
|
2014-06-01 18:16:00 -05:00
|
|
|
use syntax::abi;
|
|
|
|
use syntax::ast_map;
|
2014-11-01 03:10:02 -05:00
|
|
|
use syntax::ast_util::is_shift_binop;
|
2014-06-01 18:16:00 -05:00
|
|
|
use syntax::attr::AttrMetaMethods;
|
|
|
|
use syntax::attr;
|
2014-09-24 04:14:19 -05:00
|
|
|
use syntax::codemap::Span;
|
2014-06-01 18:16:00 -05:00
|
|
|
use syntax::parse::token;
|
|
|
|
use syntax::{ast, ast_util, visit};
|
2014-11-01 03:10:02 -05:00
|
|
|
use syntax::ast::{TyI, TyU, TyI8, TyU8, TyI16, TyU16, TyI32, TyU32, TyI64, TyU64};
|
2014-09-07 12:09:06 -05:00
|
|
|
use syntax::ptr::P;
|
2014-09-03 21:03:25 -05:00
|
|
|
use syntax::visit::Visitor;
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(WHILE_TRUE, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"suggest using `loop { }` instead of `while true { }`")
|
2014-06-02 17:27:15 -05:00
|
|
|
|
|
|
|
pub struct WhileTrue;
|
|
|
|
|
|
|
|
impl LintPass for WhileTrue {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-06-13 15:13:05 -05:00
|
|
|
lint_array!(WHILE_TRUE)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprWhile(ref cond, _, _) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match cond.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprLit(ref lit) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match lit.node {
|
|
|
|
ast::LitBool(true) => {
|
2014-06-13 15:13:05 -05:00
|
|
|
cx.span_lint(WHILE_TRUE, e.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
"denote infinite loops with loop \
|
|
|
|
{ ... }");
|
|
|
|
}
|
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_TYPECASTS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects unnecessary type casts, that can be removed")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct UnusedCasts;
|
|
|
|
|
|
|
|
impl LintPass for UnusedCasts {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_TYPECASTS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprCast(ref expr, ref ty) => {
|
|
|
|
let t_t = ast_ty_to_ty(cx, &infer::new_infer_ctxt(cx.tcx), &**ty);
|
|
|
|
if ty::get(ty::expr_ty(cx.tcx, &**expr)).sty == ty::get(t_t).sty {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_TYPECASTS, ty.span, "unnecessary type cast");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNSIGNED_NEGATION, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"using an unary minus operator on unsigned type")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_COMPARISONS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"comparisons made useless by limits of the types involved")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(OVERFLOWING_LITERALS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"literal out of range for its type")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-11-03 16:34:14 -06:00
|
|
|
declare_lint!(EXCEEDING_BITSHIFTS, Allow,
|
2014-11-01 03:10:02 -05:00
|
|
|
"shift exceeds the type's number of bits")
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct TypeLimits {
|
|
|
|
/// Id of the last visited negated expression
|
|
|
|
negated_expr_id: ast::NodeId,
|
|
|
|
}
|
|
|
|
|
2014-06-13 00:20:47 -05:00
|
|
|
impl TypeLimits {
|
|
|
|
pub fn new() -> TypeLimits {
|
2014-06-02 17:27:15 -05:00
|
|
|
TypeLimits {
|
|
|
|
negated_expr_id: -1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for TypeLimits {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-11-01 03:10:02 -05:00
|
|
|
lint_array!(UNSIGNED_NEGATION, UNUSED_COMPARISONS, OVERFLOWING_LITERALS,
|
|
|
|
EXCEEDING_BITSHIFTS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprUnary(ast::UnNeg, ref expr) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match expr.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprLit(ref lit) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match lit.node {
|
2014-08-05 02:59:03 -05:00
|
|
|
ast::LitInt(_, ast::UnsignedIntLit(_)) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNSIGNED_NEGATION, e.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
"negation of unsigned int literal may \
|
|
|
|
be unintentional");
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
2014-09-07 12:09:06 -05:00
|
|
|
let t = ty::expr_ty(cx.tcx, &**expr);
|
2014-06-02 17:27:15 -05:00
|
|
|
match ty::get(t).sty {
|
|
|
|
ty::ty_uint(_) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNSIGNED_NEGATION, e.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
"negation of unsigned int variable may \
|
|
|
|
be unintentional");
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
|
|
|
// propagate negation, if the negation itself isn't negated
|
|
|
|
if self.negated_expr_id != e.id {
|
|
|
|
self.negated_expr_id = expr.id;
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
},
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprParen(ref expr) if self.negated_expr_id == e.id => {
|
2014-06-02 17:27:15 -05:00
|
|
|
self.negated_expr_id = expr.id;
|
|
|
|
},
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprBinary(binop, ref l, ref r) => {
|
|
|
|
if is_comparison(binop) && !check_limits(cx.tcx, binop, &**l, &**r) {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_COMPARISONS, e.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
"comparison is useless due to type limits");
|
|
|
|
}
|
2014-11-01 03:10:02 -05:00
|
|
|
|
|
|
|
if is_shift_binop(binop) {
|
|
|
|
let opt_ty_bits = match ty::get(ty::expr_ty(cx.tcx, &**l)).sty {
|
|
|
|
ty::ty_int(t) => Some(int_ty_bits(t)),
|
|
|
|
ty::ty_uint(t) => Some(uint_ty_bits(t)),
|
|
|
|
_ => None
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(bits) = opt_ty_bits {
|
|
|
|
let exceeding = if let ast::ExprLit(ref lit) = r.node {
|
|
|
|
if let ast::LitInt(shift, _) = lit.node { shift > bits }
|
|
|
|
else { false }
|
|
|
|
} else {
|
|
|
|
match eval_const_expr_partial(cx.tcx, &**r) {
|
|
|
|
Ok(const_int(shift)) => { shift as u64 > bits },
|
|
|
|
Ok(const_uint(shift)) => { shift > bits },
|
|
|
|
_ => { false }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
if exceeding {
|
|
|
|
cx.span_lint(EXCEEDING_BITSHIFTS, e.span,
|
|
|
|
"bitshift exceeds the type's number of bits");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
},
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprLit(ref lit) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match ty::get(ty::expr_ty(cx.tcx, e)).sty {
|
|
|
|
ty::ty_int(t) => {
|
2014-08-26 02:42:58 -05:00
|
|
|
match lit.node {
|
2014-08-05 02:59:03 -05:00
|
|
|
ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
|
|
|
|
ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => {
|
2014-08-26 02:42:58 -05:00
|
|
|
let int_type = if t == ast::TyI {
|
|
|
|
cx.sess().targ_cfg.int_type
|
|
|
|
} else { t };
|
|
|
|
let (min, max) = int_ty_range(int_type);
|
|
|
|
let negative = self.negated_expr_id == e.id;
|
|
|
|
|
|
|
|
if (negative && v > (min.abs() as u64)) ||
|
|
|
|
(!negative && v > (max.abs() as u64)) {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
2014-08-05 02:59:03 -05:00
|
|
|
"literal out of range for its type");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
|
|
|
},
|
|
|
|
ty::ty_uint(t) => {
|
|
|
|
let uint_type = if t == ast::TyU {
|
2014-06-06 17:49:48 -05:00
|
|
|
cx.sess().targ_cfg.uint_type
|
2014-06-02 17:27:15 -05:00
|
|
|
} else { t };
|
|
|
|
let (min, max) = uint_ty_range(uint_type);
|
|
|
|
let lit_val: u64 = match lit.node {
|
2014-06-18 14:34:43 -05:00
|
|
|
ast::LitByte(_v) => return, // _v is u8, within range by definition
|
2014-08-05 02:59:03 -05:00
|
|
|
ast::LitInt(v, _) => v,
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
|
|
|
if lit_val < min || lit_val > max {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
"literal out of range for its type");
|
|
|
|
}
|
|
|
|
},
|
2014-07-09 02:45:32 -05:00
|
|
|
ty::ty_float(t) => {
|
|
|
|
let (min, max) = float_ty_range(t);
|
|
|
|
let lit_val: f64 = match lit.node {
|
|
|
|
ast::LitFloat(ref v, _) |
|
|
|
|
ast::LitFloatUnsuffixed(ref v) => match from_str(v.get()) {
|
|
|
|
Some(f) => f,
|
|
|
|
None => return
|
|
|
|
},
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-07-09 02:45:32 -05:00
|
|
|
};
|
|
|
|
if lit_val < min || lit_val > max {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(OVERFLOWING_LITERALS, e.span,
|
2014-07-09 02:45:32 -05:00
|
|
|
"literal out of range for its type");
|
|
|
|
}
|
|
|
|
},
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
|
|
|
};
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
};
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn is_valid<T:cmp::PartialOrd>(binop: ast::BinOp, v: T,
|
|
|
|
min: T, max: T) -> bool {
|
|
|
|
match binop {
|
|
|
|
ast::BiLt => v > min && v <= max,
|
|
|
|
ast::BiLe => v >= min && v < max,
|
|
|
|
ast::BiGt => v >= min && v < max,
|
|
|
|
ast::BiGe => v > min && v <= max,
|
|
|
|
ast::BiEq | ast::BiNe => v >= min && v <= max,
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn rev_binop(binop: ast::BinOp) -> ast::BinOp {
|
|
|
|
match binop {
|
|
|
|
ast::BiLt => ast::BiGt,
|
|
|
|
ast::BiLe => ast::BiGe,
|
|
|
|
ast::BiGt => ast::BiLt,
|
|
|
|
ast::BiGe => ast::BiLe,
|
|
|
|
_ => binop
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
// for int & uint, be conservative with the warnings, so that the
|
|
|
|
// warnings are consistent between 32- and 64-bit platforms
|
|
|
|
fn int_ty_range(int_ty: ast::IntTy) -> (i64, i64) {
|
|
|
|
match int_ty {
|
|
|
|
ast::TyI => (i64::MIN, i64::MAX),
|
|
|
|
ast::TyI8 => (i8::MIN as i64, i8::MAX as i64),
|
|
|
|
ast::TyI16 => (i16::MIN as i64, i16::MAX as i64),
|
|
|
|
ast::TyI32 => (i32::MIN as i64, i32::MAX as i64),
|
|
|
|
ast::TyI64 => (i64::MIN, i64::MAX)
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn uint_ty_range(uint_ty: ast::UintTy) -> (u64, u64) {
|
|
|
|
match uint_ty {
|
|
|
|
ast::TyU => (u64::MIN, u64::MAX),
|
|
|
|
ast::TyU8 => (u8::MIN as u64, u8::MAX as u64),
|
|
|
|
ast::TyU16 => (u16::MIN as u64, u16::MAX as u64),
|
|
|
|
ast::TyU32 => (u32::MIN as u64, u32::MAX as u64),
|
|
|
|
ast::TyU64 => (u64::MIN, u64::MAX)
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-07-09 02:45:32 -05:00
|
|
|
fn float_ty_range(float_ty: ast::FloatTy) -> (f64, f64) {
|
|
|
|
match float_ty {
|
|
|
|
ast::TyF32 => (f32::MIN_VALUE as f64, f32::MAX_VALUE as f64),
|
|
|
|
ast::TyF64 => (f64::MIN_VALUE, f64::MAX_VALUE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-01 03:10:02 -05:00
|
|
|
fn int_ty_bits(int_ty: ast::IntTy) -> u64 {
|
|
|
|
match int_ty {
|
|
|
|
ast::TyI => int::BITS as u64,
|
|
|
|
ast::TyI8 => i8::BITS as u64,
|
|
|
|
ast::TyI16 => i16::BITS as u64,
|
|
|
|
ast::TyI32 => i32::BITS as u64,
|
|
|
|
ast::TyI64 => i64::BITS as u64
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn uint_ty_bits(uint_ty: ast::UintTy) -> u64 {
|
|
|
|
match uint_ty {
|
|
|
|
ast::TyU => uint::BITS as u64,
|
|
|
|
ast::TyU8 => u8::BITS as u64,
|
|
|
|
ast::TyU16 => u16::BITS as u64,
|
|
|
|
ast::TyU32 => u32::BITS as u64,
|
|
|
|
ast::TyU64 => u64::BITS as u64
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_limits(tcx: &ty::ctxt, binop: ast::BinOp,
|
|
|
|
l: &ast::Expr, r: &ast::Expr) -> bool {
|
|
|
|
let (lit, expr, swap) = match (&l.node, &r.node) {
|
|
|
|
(&ast::ExprLit(_), _) => (l, r, true),
|
|
|
|
(_, &ast::ExprLit(_)) => (r, l, false),
|
|
|
|
_ => return true
|
|
|
|
};
|
|
|
|
// Normalize the binop so that the literal is always on the RHS in
|
|
|
|
// the comparison
|
|
|
|
let norm_binop = if swap { rev_binop(binop) } else { binop };
|
|
|
|
match ty::get(ty::expr_ty(tcx, expr)).sty {
|
|
|
|
ty::ty_int(int_ty) => {
|
|
|
|
let (min, max) = int_ty_range(int_ty);
|
|
|
|
let lit_val: i64 = match lit.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprLit(ref li) => match li.node {
|
2014-08-05 02:59:03 -05:00
|
|
|
ast::LitInt(v, ast::SignedIntLit(_, ast::Plus)) |
|
|
|
|
ast::LitInt(v, ast::UnsuffixedIntLit(ast::Plus)) => v as i64,
|
|
|
|
ast::LitInt(v, ast::SignedIntLit(_, ast::Minus)) |
|
|
|
|
ast::LitInt(v, ast::UnsuffixedIntLit(ast::Minus)) => -(v as i64),
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return true
|
|
|
|
},
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
|
|
|
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 {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprLit(ref li) => match li.node {
|
2014-08-05 02:59:03 -05:00
|
|
|
ast::LitInt(v, _) => v,
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return true
|
|
|
|
},
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!()
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
|
|
|
is_valid(norm_binop, lit_val, min, max)
|
|
|
|
}
|
|
|
|
_ => true
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn is_comparison(binop: ast::BinOp) -> bool {
|
|
|
|
match binop {
|
|
|
|
ast::BiEq | ast::BiLt | ast::BiLe |
|
|
|
|
ast::BiNe | ast::BiGe | ast::BiGt => true,
|
|
|
|
_ => false
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(IMPROPER_CTYPES, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"proper use of libc types in foreign modules")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
struct ImproperCTypesVisitor<'a, 'tcx: 'a> {
|
2014-04-22 07:56:37 -05:00
|
|
|
cx: &'a Context<'a, 'tcx>
|
2014-09-03 21:03:25 -05:00
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
2014-09-03 21:03:25 -05:00
|
|
|
fn check_def(&mut self, sp: Span, ty_id: ast::NodeId, path_id: ast::NodeId) {
|
|
|
|
match self.cx.tcx.def_map.borrow().get_copy(&path_id) {
|
|
|
|
def::DefPrimTy(ast::TyInt(ast::TyI)) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
2014-09-03 21:03:25 -05:00
|
|
|
"found rust type `int` in foreign module, while \
|
|
|
|
libc::c_int or libc::c_long should be used");
|
|
|
|
}
|
|
|
|
def::DefPrimTy(ast::TyUint(ast::TyU)) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
2014-09-03 21:03:25 -05:00
|
|
|
"found rust type `uint` in foreign module, while \
|
|
|
|
libc::c_uint or libc::c_ulong should be used");
|
|
|
|
}
|
|
|
|
def::DefTy(..) => {
|
|
|
|
let tty = match self.cx.tcx.ast_ty_to_ty_cache.borrow().find(&ty_id) {
|
|
|
|
Some(&ty::atttce_resolved(t)) => t,
|
2014-10-09 14:17:22 -05:00
|
|
|
_ => panic!("ast_ty_to_ty_cache was incomplete after typeck!")
|
2014-09-03 21:03:25 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
if !ty::is_ffi_safe(self.cx.tcx, tty) {
|
2014-10-14 13:37:16 -05:00
|
|
|
self.cx.span_lint(IMPROPER_CTYPES, sp,
|
2014-09-03 21:03:25 -05:00
|
|
|
"found type without foreign-function-safe
|
|
|
|
representation annotation in foreign module, consider \
|
|
|
|
adding a #[repr(...)] attribute to the type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for ImproperCTypesVisitor<'a, 'tcx> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_ty(&mut self, ty: &ast::Ty) {
|
2014-09-03 21:03:25 -05:00
|
|
|
match ty.node {
|
|
|
|
ast::TyPath(_, _, id) => self.check_def(ty.span, ty.id, id),
|
|
|
|
_ => (),
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_ty(self, ty);
|
2014-09-03 21:03:25 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct ImproperCTypes;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for ImproperCTypes {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(IMPROPER_CTYPES)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
|
|
|
fn check_ty(cx: &Context, ty: &ast::Ty) {
|
2014-10-14 13:37:16 -05:00
|
|
|
let mut vis = ImproperCTypesVisitor { cx: cx };
|
2014-09-12 05:10:30 -05:00
|
|
|
vis.visit_ty(ty);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_foreign_fn(cx: &Context, decl: &ast::FnDecl) {
|
|
|
|
for input in decl.inputs.iter() {
|
2014-06-18 14:34:43 -05:00
|
|
|
check_ty(cx, &*input.ty);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-18 14:34:43 -05:00
|
|
|
check_ty(cx, &*decl.output)
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
match it.node {
|
2014-09-03 21:03:25 -05:00
|
|
|
ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
|
|
|
|
for ni in nmod.items.iter() {
|
|
|
|
match ni.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),
|
|
|
|
ast::ForeignItemStatic(ref t, _) => check_ty(cx, &**t)
|
2014-09-03 21:03:25 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-09-03 21:03:25 -05:00
|
|
|
_ => (),
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(BOX_POINTERS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"use of owned (Box type) heap memory")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct BoxPointers;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl BoxPointers {
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_heap_type(&self, cx: &Context, span: Span, ty: ty::t) {
|
2014-06-27 14:30:25 -05:00
|
|
|
let mut n_uniq = 0i;
|
2014-06-04 16:35:58 -05:00
|
|
|
ty::fold_ty(cx.tcx, ty, |t| {
|
|
|
|
match ty::get(t).sty {
|
|
|
|
ty::ty_uniq(_) |
|
|
|
|
ty::ty_closure(box ty::ClosureTy {
|
|
|
|
store: ty::UniqTraitStore,
|
|
|
|
..
|
|
|
|
}) => {
|
|
|
|
n_uniq += 1;
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-04 16:35:58 -05:00
|
|
|
_ => ()
|
|
|
|
};
|
|
|
|
t
|
|
|
|
});
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-04 16:35:58 -05:00
|
|
|
if n_uniq > 0 {
|
2014-06-21 05:39:03 -05:00
|
|
|
let s = ty_to_string(cx.tcx, ty);
|
2014-06-04 16:35:58 -05:00
|
|
|
let m = format!("type uses owned (Box type) pointers: {}", s);
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(BOX_POINTERS, span, m.as_slice());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for BoxPointers {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(BOX_POINTERS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
|
|
|
match it.node {
|
|
|
|
ast::ItemFn(..) |
|
|
|
|
ast::ItemTy(..) |
|
|
|
|
ast::ItemEnum(..) |
|
2014-06-17 19:41:50 -05:00
|
|
|
ast::ItemStruct(..) =>
|
|
|
|
self.check_heap_type(cx, it.span,
|
|
|
|
ty::node_id_to_type(cx.tcx, it.id)),
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
// If it's a struct, we also have to check the fields' types
|
|
|
|
match it.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ItemStruct(ref struct_def, _) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
for struct_field in struct_def.fields.iter() {
|
|
|
|
self.check_heap_type(cx, struct_field.span,
|
2014-06-17 19:41:50 -05:00
|
|
|
ty::node_id_to_type(cx.tcx, struct_field.node.id));
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
let ty = ty::expr_ty(cx.tcx, e);
|
|
|
|
self.check_heap_type(cx, e.span, ty);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(RAW_POINTER_DERIVING, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"uses of #[deriving] with raw pointers are rarely correct")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct RawPtrDerivingVisitor<'a, 'tcx: 'a> {
|
|
|
|
cx: &'a Context<'a, 'tcx>
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-09-09 17:54:36 -05:00
|
|
|
impl<'a, 'tcx, 'v> Visitor<'v> for RawPtrDerivingVisitor<'a, 'tcx> {
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_ty(&mut self, ty: &ast::Ty) {
|
2014-06-01 18:16:00 -05:00
|
|
|
static MSG: &'static str = "use of `#[deriving]` with a raw pointer";
|
|
|
|
match ty.node {
|
2014-06-13 15:13:05 -05:00
|
|
|
ast::TyPtr(..) => self.cx.span_lint(RAW_POINTER_DERIVING, ty.span, MSG),
|
2014-06-01 18:16:00 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_ty(self, ty);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
// explicit override to a no-op to reduce code bloat
|
2014-09-12 05:10:30 -05:00
|
|
|
fn visit_expr(&mut self, _: &ast::Expr) {}
|
|
|
|
fn visit_block(&mut self, _: &ast::Block) {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct RawPointerDeriving {
|
|
|
|
checked_raw_pointers: NodeSet,
|
|
|
|
}
|
|
|
|
|
2014-06-13 00:20:47 -05:00
|
|
|
impl RawPointerDeriving {
|
|
|
|
pub fn new() -> RawPointerDeriving {
|
2014-06-02 17:27:15 -05:00
|
|
|
RawPointerDeriving {
|
|
|
|
checked_raw_pointers: NodeSet::new(),
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for RawPointerDeriving {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-06-13 15:13:05 -05:00
|
|
|
lint_array!(RAW_POINTER_DERIVING)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, item: &ast::Item) {
|
|
|
|
if !attr::contains_name(item.attrs.as_slice(), "automatically_derived") {
|
|
|
|
return
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
let did = match item.node {
|
|
|
|
ast::ItemImpl(..) => {
|
|
|
|
match ty::get(ty::node_id_to_type(cx.tcx, item.id)).sty {
|
|
|
|
ty::ty_enum(did, _) => did,
|
|
|
|
ty::ty_struct(did, _) => did,
|
|
|
|
_ => return,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
if !ast_util::is_local(did) { return }
|
|
|
|
let item = match cx.tcx.map.find(did.node) {
|
|
|
|
Some(ast_map::NodeItem(item)) => item,
|
|
|
|
_ => return,
|
|
|
|
};
|
|
|
|
if !self.checked_raw_pointers.insert(item.id) { return }
|
|
|
|
match item.node {
|
|
|
|
ast::ItemStruct(..) | ast::ItemEnum(..) => {
|
|
|
|
let mut visitor = RawPtrDerivingVisitor { cx: cx };
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_item(&mut visitor, &*item);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_ATTRIBUTES, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects attributes that were not used by the compiler")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnusedAttributes;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnusedAttributes {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_ATTRIBUTES)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_attribute(&mut self, cx: &Context, attr: &ast::Attribute) {
|
2014-07-04 02:56:57 -05:00
|
|
|
static ATTRIBUTE_WHITELIST: &'static [&'static str] = &[
|
2014-06-02 17:27:15 -05:00
|
|
|
// FIXME: #14408 whitelist docs since rustdoc looks at them
|
|
|
|
"doc",
|
|
|
|
|
|
|
|
// FIXME: #14406 these are processed in trans, which happens after the
|
|
|
|
// lint pass
|
|
|
|
"cold",
|
2014-08-14 14:23:39 -05:00
|
|
|
"export_name",
|
2014-06-02 17:27:15 -05:00
|
|
|
"inline",
|
|
|
|
"link",
|
|
|
|
"link_name",
|
|
|
|
"link_section",
|
|
|
|
"no_builtins",
|
|
|
|
"no_mangle",
|
|
|
|
"no_split_stack",
|
2014-09-05 19:28:24 -05:00
|
|
|
"no_stack_check",
|
2014-06-02 17:27:15 -05:00
|
|
|
"packed",
|
|
|
|
"static_assert",
|
|
|
|
"thread_local",
|
2014-07-10 10:01:11 -05:00
|
|
|
"no_debug",
|
2014-09-29 23:52:06 -05:00
|
|
|
"unsafe_no_drop_flag",
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-08-12 22:31:30 -05:00
|
|
|
// used in resolve
|
|
|
|
"prelude_import",
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
// not used anywhere (!?) but apparently we want to keep them around
|
|
|
|
"comment",
|
|
|
|
"desc",
|
|
|
|
"license",
|
|
|
|
|
|
|
|
// FIXME: #14407 these are only looked at on-demand so we can't
|
|
|
|
// guarantee they'll have already been checked
|
|
|
|
"deprecated",
|
|
|
|
"experimental",
|
|
|
|
"frozen",
|
|
|
|
"locked",
|
|
|
|
"must_use",
|
|
|
|
"stable",
|
|
|
|
"unstable",
|
|
|
|
];
|
|
|
|
|
2014-07-04 02:56:57 -05:00
|
|
|
static CRATE_ATTRS: &'static [&'static str] = &[
|
2014-07-07 21:04:34 -05:00
|
|
|
"crate_name",
|
2014-06-02 17:27:15 -05:00
|
|
|
"crate_type",
|
|
|
|
"feature",
|
|
|
|
"no_start",
|
|
|
|
"no_main",
|
|
|
|
"no_std",
|
|
|
|
"desc",
|
|
|
|
"comment",
|
|
|
|
"license",
|
|
|
|
"copyright",
|
|
|
|
"no_builtins",
|
|
|
|
];
|
|
|
|
|
|
|
|
for &name in ATTRIBUTE_WHITELIST.iter() {
|
|
|
|
if attr.check_name(name) {
|
|
|
|
break;
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
if !attr::is_used(attr) {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
|
2014-06-02 17:27:15 -05:00
|
|
|
if CRATE_ATTRS.contains(&attr.name().get()) {
|
|
|
|
let msg = match attr.node.style {
|
2014-06-17 19:41:50 -05:00
|
|
|
ast::AttrOuter => "crate-level attribute should be an inner \
|
|
|
|
attribute: add an exclamation mark: #![foo]",
|
2014-06-02 17:27:15 -05:00
|
|
|
ast::AttrInner => "crate-level attribute should be in the \
|
|
|
|
root module",
|
|
|
|
};
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub PATH_STATEMENTS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"path statements with no effect")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct PathStatements;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for PathStatements {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(PATH_STATEMENTS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
|
|
|
|
match s.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::StmtSemi(ref expr, _) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match expr.node {
|
2014-10-14 13:37:16 -05:00
|
|
|
ast::ExprPath(_) => cx.span_lint(PATH_STATEMENTS, s.span,
|
2014-06-04 16:35:58 -05:00
|
|
|
"path statement with no effect"),
|
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-12 20:58:35 -05:00
|
|
|
declare_lint!(pub UNUSED_MUST_USE, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unused result of a type flagged as #[must_use]")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNUSED_RESULTS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unused result of an expression in a statement")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnusedResults;
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnusedResults {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_MUST_USE, UNUSED_RESULTS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
|
|
|
|
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
|
|
|
|
let expr = match s.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::StmtSemi(ref expr, _) => &**expr,
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return
|
|
|
|
};
|
2014-09-16 12:23:19 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
match expr.node {
|
|
|
|
ast::ExprRet(..) => return,
|
|
|
|
_ => {}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-09-07 12:09:06 -05:00
|
|
|
let t = ty::expr_ty(cx.tcx, expr);
|
2014-06-02 17:27:15 -05:00
|
|
|
let mut warned = false;
|
|
|
|
match ty::get(t).sty {
|
2014-10-24 14:14:37 -05:00
|
|
|
ty::ty_nil | ty::ty_bool => return,
|
2014-06-02 17:27:15 -05:00
|
|
|
ty::ty_struct(did, _) |
|
|
|
|
ty::ty_enum(did, _) => {
|
|
|
|
if ast_util::is_local(did) {
|
|
|
|
match cx.tcx.map.get(did.node) {
|
|
|
|
ast_map::NodeItem(it) => {
|
2014-07-09 07:02:19 -05:00
|
|
|
warned |= check_must_use(cx, it.attrs.as_slice(), s.span);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
} else {
|
2014-06-06 17:49:48 -05:00
|
|
|
csearch::get_item_attrs(&cx.sess().cstore, did, |attrs| {
|
2014-07-09 07:02:19 -05:00
|
|
|
warned |= check_must_use(cx, attrs.as_slice(), s.span);
|
2014-06-02 17:27:15 -05:00
|
|
|
});
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
if !warned {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-07-09 07:02:19 -05:00
|
|
|
|
|
|
|
fn check_must_use(cx: &Context, attrs: &[ast::Attribute], sp: Span) -> bool {
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
if attr.check_name("must_use") {
|
|
|
|
let mut msg = "unused result which must be used".to_string();
|
|
|
|
// check for #[must_use="..."]
|
|
|
|
match attr.value_str() {
|
|
|
|
None => {}
|
|
|
|
Some(s) => {
|
|
|
|
msg.push_str(": ");
|
|
|
|
msg.push_str(s.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cx.span_lint(UNUSED_MUST_USE, sp, msg.as_slice());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-20 22:27:59 -05:00
|
|
|
declare_lint!(pub NON_CAMEL_CASE_TYPES, Warn,
|
2014-07-18 07:45:17 -05:00
|
|
|
"types, variants, traits and type parameters should have camel case names")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct NonCamelCaseTypes;
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
impl NonCamelCaseTypes {
|
|
|
|
fn check_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
|
2014-06-02 17:27:15 -05:00
|
|
|
fn is_camel_case(ident: ast::Ident) -> bool {
|
|
|
|
let ident = token::get_ident(ident);
|
2014-07-18 07:45:17 -05:00
|
|
|
if ident.get().is_empty() { return true; }
|
2014-06-02 17:27:15 -05:00
|
|
|
let ident = ident.get().trim_chars('_');
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
// start with a non-lowercase letter rather than non-uppercase
|
|
|
|
// ones (some scripts don't have a concept of upper/lowercase)
|
2014-08-23 09:03:28 -05:00
|
|
|
ident.len() > 0 && !ident.char_at(0).is_lowercase() && !ident.contains_char('_')
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn to_camel_case(s: &str) -> String {
|
|
|
|
s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
|
|
|
|
if i == 0 { c.to_uppercase() }
|
|
|
|
else { c }
|
|
|
|
)).collect()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
let s = token::get_ident(ident);
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
if !is_camel_case(ident) {
|
|
|
|
let c = to_camel_case(s.get());
|
|
|
|
let m = if c.is_empty() {
|
|
|
|
format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s)
|
|
|
|
} else {
|
|
|
|
format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
|
|
|
|
};
|
|
|
|
cx.span_lint(NON_CAMEL_CASE_TYPES, span, m.as_slice());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-07-18 07:45:17 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for NonCamelCaseTypes {
|
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(NON_CAMEL_CASE_TYPES)
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
2014-05-27 01:56:52 -05:00
|
|
|
let has_extern_repr = it.attrs.iter().map(|attr| {
|
|
|
|
attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
|
|
|
|
.any(|r| r == &attr::ReprExtern)
|
|
|
|
}).any(|x| x);
|
2014-07-10 12:19:38 -05:00
|
|
|
if has_extern_repr { return }
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
match it.node {
|
|
|
|
ast::ItemTy(..) | ast::ItemStruct(..) => {
|
2014-07-18 07:45:17 -05:00
|
|
|
self.check_case(cx, "type", it.ident, it.span)
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
ast::ItemTrait(..) => {
|
2014-07-18 07:45:17 -05:00
|
|
|
self.check_case(cx, "trait", it.ident, it.span)
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
ast::ItemEnum(ref enum_definition, _) => {
|
2014-05-27 01:56:52 -05:00
|
|
|
if has_extern_repr { return }
|
2014-07-18 07:45:17 -05:00
|
|
|
self.check_case(cx, "type", it.ident, it.span);
|
2014-06-02 17:27:15 -05:00
|
|
|
for variant in enum_definition.variants.iter() {
|
2014-07-18 07:45:17 -05:00
|
|
|
self.check_case(cx, "variant", variant.node.name, variant.span);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-07-18 07:45:17 -05:00
|
|
|
|
|
|
|
fn check_generics(&mut self, cx: &Context, it: &ast::Generics) {
|
|
|
|
for gen in it.ty_params.iter() {
|
|
|
|
self.check_case(cx, "type parameter", gen.ident, gen.span);
|
|
|
|
}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
#[deriving(PartialEq)]
|
|
|
|
enum MethodContext {
|
|
|
|
TraitDefaultImpl,
|
|
|
|
TraitImpl,
|
|
|
|
PlainImpl
|
|
|
|
}
|
|
|
|
|
|
|
|
fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
|
|
|
|
let did = ast::DefId {
|
|
|
|
krate: ast::LOCAL_CRATE,
|
|
|
|
node: m.id
|
|
|
|
};
|
|
|
|
|
2014-08-04 15:56:56 -05:00
|
|
|
match cx.tcx.impl_or_trait_items.borrow().find_copy(&did) {
|
2014-06-06 17:49:48 -05:00
|
|
|
None => cx.sess().span_bug(m.span, "missing method descriptor?!"),
|
2014-06-02 17:27:15 -05:00
|
|
|
Some(md) => {
|
2014-08-04 15:56:56 -05:00
|
|
|
match md {
|
|
|
|
ty::MethodTraitItem(md) => {
|
|
|
|
match md.container {
|
|
|
|
ty::TraitContainer(..) => TraitDefaultImpl,
|
|
|
|
ty::ImplContainer(cid) => {
|
|
|
|
match ty::impl_trait_ref(cx.tcx, cid) {
|
|
|
|
Some(..) => TraitImpl,
|
|
|
|
None => PlainImpl
|
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
}
|
2014-08-05 21:44:21 -05:00
|
|
|
ty::TypeTraitItem(typedef) => {
|
|
|
|
match typedef.container {
|
|
|
|
ty::TraitContainer(..) => TraitDefaultImpl,
|
|
|
|
ty::ImplContainer(cid) => {
|
|
|
|
match ty::impl_trait_ref(cx.tcx, cid) {
|
|
|
|
Some(..) => TraitImpl,
|
|
|
|
None => PlainImpl
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
2014-07-20 22:27:59 -05:00
|
|
|
declare_lint!(pub NON_SNAKE_CASE, Warn,
|
2014-07-18 07:45:17 -05:00
|
|
|
"methods, functions, lifetime parameters and modules should have snake case names")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
pub struct NonSnakeCase;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
impl NonSnakeCase {
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_snake_case(&self, cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
|
|
|
|
fn is_snake_case(ident: ast::Ident) -> bool {
|
|
|
|
let ident = token::get_ident(ident);
|
2014-07-18 07:45:17 -05:00
|
|
|
if ident.get().is_empty() { return true; }
|
|
|
|
let ident = ident.get().trim_left_chars('\'');
|
|
|
|
let ident = ident.trim_chars('_');
|
2014-06-02 17:27:15 -05:00
|
|
|
|
|
|
|
let mut allow_underscore = true;
|
|
|
|
ident.chars().all(|c| {
|
|
|
|
allow_underscore = match c {
|
|
|
|
c if c.is_lowercase() || c.is_digit() => true,
|
|
|
|
'_' if allow_underscore => false,
|
|
|
|
_ => return false,
|
|
|
|
};
|
|
|
|
true
|
|
|
|
})
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn to_snake_case(str: &str) -> String {
|
|
|
|
let mut words = vec![];
|
|
|
|
for s in str.split('_') {
|
2014-09-12 20:59:15 -05:00
|
|
|
let mut last_upper = false;
|
2014-06-02 17:27:15 -05:00
|
|
|
let mut buf = String::new();
|
|
|
|
if s.is_empty() { continue; }
|
|
|
|
for ch in s.chars() {
|
2014-09-12 20:59:15 -05:00
|
|
|
if !buf.is_empty() && buf.as_slice() != "'"
|
|
|
|
&& ch.is_uppercase()
|
|
|
|
&& !last_upper {
|
2014-06-02 17:27:15 -05:00
|
|
|
words.push(buf);
|
|
|
|
buf = String::new();
|
|
|
|
}
|
2014-09-12 20:59:15 -05:00
|
|
|
last_upper = ch.is_uppercase();
|
2014-10-15 01:05:01 -05:00
|
|
|
buf.push(ch.to_lowercase());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
words.push(buf);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
words.connect("_")
|
|
|
|
}
|
|
|
|
|
|
|
|
let s = token::get_ident(ident);
|
|
|
|
|
|
|
|
if !is_snake_case(ident) {
|
2014-07-18 07:45:17 -05:00
|
|
|
cx.span_lint(NON_SNAKE_CASE, span,
|
2014-06-02 17:27:15 -05:00
|
|
|
format!("{} `{}` should have a snake case name such as `{}`",
|
2014-06-17 19:41:50 -05:00
|
|
|
sort, s, to_snake_case(s.get())).as_slice());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
impl LintPass for NonSnakeCase {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-07-18 07:45:17 -05:00
|
|
|
lint_array!(NON_SNAKE_CASE)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_fn(&mut self, cx: &Context,
|
2014-09-09 17:54:36 -05:00
|
|
|
fk: visit::FnKind, _: &ast::FnDecl,
|
2014-06-02 17:27:15 -05:00
|
|
|
_: &ast::Block, span: Span, _: ast::NodeId) {
|
2014-09-09 17:54:36 -05:00
|
|
|
match fk {
|
2014-06-02 17:27:15 -05:00
|
|
|
visit::FkMethod(ident, _, m) => match method_context(cx, m) {
|
|
|
|
PlainImpl
|
|
|
|
=> self.check_snake_case(cx, "method", ident, span),
|
|
|
|
TraitDefaultImpl
|
|
|
|
=> self.check_snake_case(cx, "trait method", ident, span),
|
|
|
|
_ => (),
|
|
|
|
},
|
|
|
|
visit::FkItemFn(ident, _, _, _)
|
|
|
|
=> self.check_snake_case(cx, "function", ident, span),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-07-18 07:45:17 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
|
|
|
match it.node {
|
|
|
|
ast::ItemMod(_) => {
|
|
|
|
self.check_snake_case(cx, "module", it.ident, it.span);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_ty_method(&mut self, cx: &Context, t: &ast::TypeMethod) {
|
|
|
|
self.check_snake_case(cx, "trait method", t.ident, t.span);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-07-18 07:45:17 -05:00
|
|
|
|
2014-07-20 22:27:59 -05:00
|
|
|
fn check_lifetime_decl(&mut self, cx: &Context, t: &ast::LifetimeDef) {
|
|
|
|
self.check_snake_case(cx, "lifetime", t.lifetime.name.ident(), t.lifetime.span);
|
2014-07-18 07:45:17 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
|
|
|
|
match &p.node {
|
|
|
|
&ast::PatIdent(_, ref path1, _) => {
|
|
|
|
match cx.tcx.def_map.borrow().find(&p.id) {
|
2014-09-17 10:17:09 -05:00
|
|
|
Some(&def::DefLocal(_)) => {
|
2014-07-18 07:45:17 -05:00
|
|
|
self.check_snake_case(cx, "variable", path1.node, p.span);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_struct_def(&mut self, cx: &Context, s: &ast::StructDef,
|
|
|
|
_: ast::Ident, _: &ast::Generics, _: ast::NodeId) {
|
|
|
|
for sf in s.fields.iter() {
|
|
|
|
match sf.node {
|
|
|
|
ast::StructField_ { kind: ast::NamedField(ident, _), .. } => {
|
|
|
|
self.check_snake_case(cx, "structure field", ident, sf.span);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub NON_UPPER_CASE_GLOBALS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"static constants should have uppercase identifiers")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct NonUpperCaseGlobals;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for NonUpperCaseGlobals {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(NON_UPPER_CASE_GLOBALS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
|
|
|
match it.node {
|
|
|
|
// only check static constants
|
rustc: Add `const` globals to the language
This change is an implementation of [RFC 69][rfc] which adds a third kind of
global to the language, `const`. This global is most similar to what the old
`static` was, and if you're unsure about what to use then you should use a
`const`.
The semantics of these three kinds of globals are:
* A `const` does not represent a memory location, but only a value. Constants
are translated as rvalues, which means that their values are directly inlined
at usage location (similar to a #define in C/C++). Constant values are, well,
constant, and can not be modified. Any "modification" is actually a
modification to a local value on the stack rather than the actual constant
itself.
Almost all values are allowed inside constants, whether they have interior
mutability or not. There are a few minor restrictions listed in the RFC, but
they should in general not come up too often.
* A `static` now always represents a memory location (unconditionally). Any
references to the same `static` are actually a reference to the same memory
location. Only values whose types ascribe to `Sync` are allowed in a `static`.
This restriction is in place because many threads may access a `static`
concurrently. Lifting this restriction (and allowing unsafe access) is a
future extension not implemented at this time.
* A `static mut` continues to always represent a memory location. All references
to a `static mut` continue to be `unsafe`.
This is a large breaking change, and many programs will need to be updated
accordingly. A summary of the breaking changes is:
* Statics may no longer be used in patterns. Statics now always represent a
memory location, which can sometimes be modified. To fix code, repurpose the
matched-on-`static` to a `const`.
static FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
change this code to:
const FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
* Statics may no longer refer to other statics by value. Due to statics being
able to change at runtime, allowing them to reference one another could
possibly lead to confusing semantics. If you are in this situation, use a
constant initializer instead. Note, however, that statics may reference other
statics by address, however.
* Statics may no longer be used in constant expressions, such as array lengths.
This is due to the same restrictions as listed above. Use a `const` instead.
[breaking-change]
[rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
|
|
|
ast::ItemStatic(_, ast::MutImmutable, _) |
|
|
|
|
ast::ItemConst(..) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
let s = token::get_ident(it.ident);
|
|
|
|
// check for lowercase letters rather than non-uppercase
|
|
|
|
// ones (some scripts don't have a concept of
|
|
|
|
// upper/lowercase)
|
|
|
|
if s.get().chars().any(|c| c.is_lowercase()) {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(NON_UPPER_CASE_GLOBALS, it.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
format!("static constant `{}` should have an uppercase name \
|
2014-06-17 19:41:50 -05:00
|
|
|
such as `{}`",
|
|
|
|
s.get(), s.get().chars().map(|c| c.to_uppercase())
|
2014-06-02 17:27:15 -05:00
|
|
|
.collect::<String>().as_slice()).as_slice());
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_pat(&mut self, cx: &Context, p: &ast::Pat) {
|
|
|
|
// Lint for constants that look like binding identifiers (#7526)
|
|
|
|
match (&p.node, cx.tcx.def_map.borrow().find(&p.id)) {
|
rustc: Add `const` globals to the language
This change is an implementation of [RFC 69][rfc] which adds a third kind of
global to the language, `const`. This global is most similar to what the old
`static` was, and if you're unsure about what to use then you should use a
`const`.
The semantics of these three kinds of globals are:
* A `const` does not represent a memory location, but only a value. Constants
are translated as rvalues, which means that their values are directly inlined
at usage location (similar to a #define in C/C++). Constant values are, well,
constant, and can not be modified. Any "modification" is actually a
modification to a local value on the stack rather than the actual constant
itself.
Almost all values are allowed inside constants, whether they have interior
mutability or not. There are a few minor restrictions listed in the RFC, but
they should in general not come up too often.
* A `static` now always represents a memory location (unconditionally). Any
references to the same `static` are actually a reference to the same memory
location. Only values whose types ascribe to `Sync` are allowed in a `static`.
This restriction is in place because many threads may access a `static`
concurrently. Lifting this restriction (and allowing unsafe access) is a
future extension not implemented at this time.
* A `static mut` continues to always represent a memory location. All references
to a `static mut` continue to be `unsafe`.
This is a large breaking change, and many programs will need to be updated
accordingly. A summary of the breaking changes is:
* Statics may no longer be used in patterns. Statics now always represent a
memory location, which can sometimes be modified. To fix code, repurpose the
matched-on-`static` to a `const`.
static FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
change this code to:
const FOO: uint = 4;
match n {
FOO => { /* ... */ }
_ => { /* ... */ }
}
* Statics may no longer refer to other statics by value. Due to statics being
able to change at runtime, allowing them to reference one another could
possibly lead to confusing semantics. If you are in this situation, use a
constant initializer instead. Note, however, that statics may reference other
statics by address, however.
* Statics may no longer be used in constant expressions, such as array lengths.
This is due to the same restrictions as listed above. Use a `const` instead.
[breaking-change]
[rfc]: https://github.com/rust-lang/rfcs/pull/246
2014-10-06 10:17:01 -05:00
|
|
|
(&ast::PatIdent(_, ref path1, _), Some(&def::DefConst(..))) => {
|
2014-06-30 20:02:14 -05:00
|
|
|
let s = token::get_ident(path1.node);
|
2014-06-02 17:27:15 -05:00
|
|
|
if s.get().chars().any(|c| c.is_lowercase()) {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(NON_UPPER_CASE_GLOBALS, path1.span,
|
2014-06-02 17:27:15 -05:00
|
|
|
format!("static constant in pattern `{}` should have an uppercase \
|
2014-06-17 19:41:50 -05:00
|
|
|
name such as `{}`",
|
|
|
|
s.get(), s.get().chars().map(|c| c.to_uppercase())
|
|
|
|
.collect::<String>().as_slice()).as_slice());
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_PARENS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"`if`, `match`, `while` and `return` do not need parentheses")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnusedParens;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl UnusedParens {
|
2014-10-27 17:37:07 -05:00
|
|
|
fn check_unused_parens_core(&self, cx: &Context, value: &ast::Expr, msg: &str,
|
2014-06-24 18:00:46 -05:00
|
|
|
struct_lit_needs_parens: bool) {
|
2014-06-02 17:27:15 -05:00
|
|
|
match value.node {
|
2014-06-24 18:00:46 -05:00
|
|
|
ast::ExprParen(ref inner) => {
|
|
|
|
let necessary = struct_lit_needs_parens && contains_exterior_struct_lit(&**inner);
|
|
|
|
if !necessary {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_PARENS, value.span,
|
2014-06-24 18:00:46 -05:00
|
|
|
format!("unnecessary parentheses around {}",
|
|
|
|
msg).as_slice())
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-24 18:00:46 -05:00
|
|
|
|
2014-07-02 20:27:07 -05:00
|
|
|
/// Expressions that syntactically contain an "exterior" struct
|
2014-06-24 18:00:46 -05:00
|
|
|
/// literal i.e. not surrounded by any parens or other
|
|
|
|
/// delimiters, e.g. `X { y: 1 }`, `X { y: 1 }.method()`, `foo
|
|
|
|
/// == X { y: 1 }` and `X { y: 1 } == foo` all do, but `(X {
|
|
|
|
/// y: 1 }) == foo` does not.
|
|
|
|
fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
|
|
|
|
match value.node {
|
|
|
|
ast::ExprStruct(..) => true,
|
|
|
|
|
|
|
|
ast::ExprAssign(ref lhs, ref rhs) |
|
|
|
|
ast::ExprAssignOp(_, ref lhs, ref rhs) |
|
|
|
|
ast::ExprBinary(_, ref lhs, ref rhs) => {
|
|
|
|
// X { y: 1 } + X { y: 2 }
|
|
|
|
contains_exterior_struct_lit(&**lhs) ||
|
|
|
|
contains_exterior_struct_lit(&**rhs)
|
|
|
|
}
|
|
|
|
ast::ExprUnary(_, ref x) |
|
|
|
|
ast::ExprCast(ref x, _) |
|
|
|
|
ast::ExprField(ref x, _, _) |
|
2014-08-09 22:54:33 -05:00
|
|
|
ast::ExprTupField(ref x, _, _) |
|
2014-06-24 18:00:46 -05:00
|
|
|
ast::ExprIndex(ref x, _) => {
|
|
|
|
// &X { y: 1 }, X { y: 1 }.y
|
|
|
|
contains_exterior_struct_lit(&**x)
|
|
|
|
}
|
|
|
|
|
|
|
|
ast::ExprMethodCall(_, _, ref exprs) => {
|
|
|
|
// X { y: 1 }.bar(...)
|
2014-10-15 01:05:01 -05:00
|
|
|
contains_exterior_struct_lit(&*exprs[0])
|
2014-06-24 18:00:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnusedParens {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_PARENS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
2014-06-24 18:00:46 -05:00
|
|
|
let (value, msg, struct_lit_needs_parens) = match e.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprIf(ref cond, _, _) => (cond, "`if` condition", true),
|
|
|
|
ast::ExprWhile(ref cond, _, _) => (cond, "`while` condition", true),
|
2014-08-25 16:55:00 -05:00
|
|
|
ast::ExprMatch(ref head, _, source) => match source {
|
|
|
|
ast::MatchNormal => (head, "`match` head expression", true),
|
2014-10-02 23:41:24 -05:00
|
|
|
ast::MatchIfLetDesugar => (head, "`if let` head expression", true),
|
|
|
|
ast::MatchWhileLetDesugar => (head, "`while let` head expression", true),
|
2014-08-25 16:55:00 -05:00
|
|
|
},
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::ExprRet(Some(ref value)) => (value, "`return` value", false),
|
|
|
|
ast::ExprAssign(_, ref value) => (value, "assigned value", false),
|
|
|
|
ast::ExprAssignOp(_, _, ref value) => (value, "assigned value", false),
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return
|
|
|
|
};
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_unused_parens_core(cx, &**value, msg, struct_lit_needs_parens);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
|
|
|
|
let (value, msg) = match s.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::StmtDecl(ref decl, _) => match decl.node {
|
|
|
|
ast::DeclLocal(ref local) => match local.init {
|
|
|
|
Some(ref value) => (value, "assigned value"),
|
2014-06-02 17:27:15 -05:00
|
|
|
None => return
|
|
|
|
},
|
|
|
|
_ => return
|
2014-06-01 18:16:00 -05:00
|
|
|
},
|
|
|
|
_ => return
|
2014-06-02 17:27:15 -05:00
|
|
|
};
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_unused_parens_core(cx, &**value, msg, false);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_IMPORT_BRACES, Allow,
|
2014-08-31 20:27:54 -05:00
|
|
|
"unnecessary braces around an imported item")
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnusedImportBraces;
|
2014-08-31 20:27:54 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnusedImportBraces {
|
2014-08-31 20:27:54 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_IMPORT_BRACES)
|
2014-08-31 20:27:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_view_item(&mut self, cx: &Context, view_item: &ast::ViewItem) {
|
|
|
|
match view_item.node {
|
|
|
|
ast::ViewItemUse(ref view_path) => {
|
|
|
|
match view_path.node {
|
|
|
|
ast::ViewPathList(_, ref items, _) => {
|
|
|
|
if items.len() == 1 {
|
|
|
|
match items[0].node {
|
|
|
|
ast::PathListIdent {ref name, ..} => {
|
|
|
|
let m = format!("braces around {} is unnecessary",
|
|
|
|
token::get_ident(*name).get());
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_IMPORT_BRACES, view_item.span,
|
2014-08-31 20:27:54 -05:00
|
|
|
m.as_slice());
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-05 19:36:53 -05:00
|
|
|
declare_lint!(NON_SHORTHAND_FIELD_PATTERNS, Warn,
|
|
|
|
"using `Struct { x: x }` instead of `Struct { x }`")
|
|
|
|
|
|
|
|
pub struct NonShorthandFieldPatterns;
|
|
|
|
|
|
|
|
impl LintPass for NonShorthandFieldPatterns {
|
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(NON_SHORTHAND_FIELD_PATTERNS)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_pat(&mut self, cx: &Context, pat: &ast::Pat) {
|
|
|
|
let def_map = cx.tcx.def_map.borrow();
|
|
|
|
match pat.node {
|
|
|
|
ast::PatStruct(_, ref v, _) => {
|
|
|
|
for fieldpat in v.iter()
|
|
|
|
.filter(|fieldpat| !fieldpat.node.is_shorthand)
|
|
|
|
.filter(|fieldpat| def_map.find(&fieldpat.node.pat.id)
|
|
|
|
== Some(&def::DefLocal(fieldpat.node.pat.id))) {
|
|
|
|
match fieldpat.node.pat.node {
|
|
|
|
ast::PatIdent(_, ident, None) if ident.node.as_str()
|
|
|
|
== fieldpat.node.ident.as_str() => {
|
|
|
|
cx.span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span,
|
|
|
|
format!("the `{}:` in this pattern is redundant and can \
|
|
|
|
be removed", ident.node.as_str()).as_slice())
|
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-12 20:58:35 -05:00
|
|
|
declare_lint!(pub UNUSED_UNSAFE, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unnecessary use of an `unsafe` block")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct UnusedUnsafe;
|
|
|
|
|
|
|
|
impl LintPass for UnusedUnsafe {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-06-13 15:13:05 -05:00
|
|
|
lint_array!(UNUSED_UNSAFE)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
|
|
|
// Don't warn about generated blocks, that'll just pollute the output.
|
|
|
|
ast::ExprBlock(ref blk) => {
|
|
|
|
if blk.rules == ast::UnsafeBlock(ast::UserProvided) &&
|
|
|
|
!cx.tcx.used_unsafe.borrow().contains(&blk.id) {
|
2014-06-13 15:13:05 -05:00
|
|
|
cx.span_lint(UNUSED_UNSAFE, blk.span, "unnecessary `unsafe` block");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNSAFE_BLOCKS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"usage of an `unsafe` block")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnsafeBlocks;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnsafeBlocks {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNSAFE_BLOCKS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
|
|
|
// Don't warn about generated blocks, that'll just pollute the output.
|
|
|
|
ast::ExprBlock(ref blk) if blk.rules == ast::UnsafeBlock(ast::UserProvided) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNSAFE_BLOCKS, blk.span, "usage of an `unsafe` block");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-20 22:27:59 -05:00
|
|
|
declare_lint!(pub UNUSED_MUT, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detect mut variables which don't need to be mutable")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct UnusedMut;
|
|
|
|
|
|
|
|
impl UnusedMut {
|
2014-09-07 12:09:06 -05:00
|
|
|
fn check_unused_mut_pat(&self, cx: &Context, pats: &[P<ast::Pat>]) {
|
2014-06-02 17:27:15 -05:00
|
|
|
// collect all mutable pattern and group their NodeIDs by their Identifier to
|
|
|
|
// avoid false warnings in match arms with multiple patterns
|
2014-09-18 16:05:52 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
let mut mutables = HashMap::new();
|
2014-09-07 12:09:06 -05:00
|
|
|
for p in pats.iter() {
|
|
|
|
pat_util::pat_bindings(&cx.tcx.def_map, &**p, |mode, id, _, path1| {
|
2014-06-30 20:02:14 -05:00
|
|
|
let ident = path1.node;
|
2014-06-02 17:27:15 -05:00
|
|
|
match mode {
|
|
|
|
ast::BindByValue(ast::MutMutable) => {
|
|
|
|
if !token::get_ident(ident).get().starts_with("_") {
|
2014-09-18 16:05:52 -05:00
|
|
|
match mutables.entry(ident.name.uint()) {
|
|
|
|
Vacant(entry) => { entry.set(vec![id]); },
|
|
|
|
Occupied(mut entry) => { entry.get_mut().push(id); },
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let used_mutables = cx.tcx.used_mut_nodes.borrow();
|
|
|
|
for (_, v) in mutables.iter() {
|
|
|
|
if !v.iter().any(|e| used_mutables.contains(e)) {
|
2014-10-15 01:05:01 -05:00
|
|
|
cx.span_lint(UNUSED_MUT, cx.tcx.map.span(v[0]),
|
2014-06-17 19:41:50 -05:00
|
|
|
"variable does not need to be mutable");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LintPass for UnusedMut {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-06-13 15:13:05 -05:00
|
|
|
lint_array!(UNUSED_MUT)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
|
|
|
match e.node {
|
2014-08-25 16:55:00 -05:00
|
|
|
ast::ExprMatch(_, ref arms, _) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
for a in arms.iter() {
|
|
|
|
self.check_unused_mut_pat(cx, a.pats.as_slice())
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_stmt(&mut self, cx: &Context, s: &ast::Stmt) {
|
|
|
|
match s.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::StmtDecl(ref d, _) => {
|
2014-06-02 17:27:15 -05:00
|
|
|
match d.node {
|
2014-09-07 12:09:06 -05:00
|
|
|
ast::DeclLocal(ref l) => {
|
|
|
|
self.check_unused_mut_pat(cx, slice::ref_slice(&l.pat));
|
2014-06-02 17:27:15 -05:00
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {}
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_fn(&mut self, cx: &Context,
|
2014-09-09 17:54:36 -05:00
|
|
|
_: visit::FnKind, decl: &ast::FnDecl,
|
2014-06-02 17:27:15 -05:00
|
|
|
_: &ast::Block, _: Span, _: ast::NodeId) {
|
|
|
|
for a in decl.inputs.iter() {
|
2014-09-07 12:09:06 -05:00
|
|
|
self.check_unused_mut_pat(cx, slice::ref_slice(&a.pat));
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(UNUSED_ALLOCATION, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects unnecessary allocations that can be eliminated")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
pub struct UnusedAllocation;
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
impl LintPass for UnusedAllocation {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(UNUSED_ALLOCATION)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
2014-09-20 08:37:14 -05:00
|
|
|
match e.node {
|
2014-09-30 16:59:56 -05:00
|
|
|
ast::ExprUnary(ast::UnUniq, _) => (),
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return
|
2014-09-20 08:37:14 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
match cx.tcx.adjustments.borrow().find(&e.id) {
|
|
|
|
Some(adjustment) => {
|
|
|
|
match *adjustment {
|
2014-09-11 00:07:49 -05:00
|
|
|
ty::AdjustDerefRef(ty::AutoDerefRef { ref autoref, .. }) => {
|
2014-09-20 08:37:14 -05:00
|
|
|
match autoref {
|
|
|
|
&Some(ty::AutoPtr(_, ast::MutImmutable, None)) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_ALLOCATION, e.span,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unnecessary allocation, use & instead");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-09-20 08:37:14 -05:00
|
|
|
&Some(ty::AutoPtr(_, ast::MutMutable, None)) => {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(UNUSED_ALLOCATION, e.span,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unnecessary allocation, use &mut instead");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => {}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(MISSING_DOCS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects missing documentation for public members")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct MissingDoc {
|
|
|
|
/// Stack of IDs of struct definitions.
|
|
|
|
struct_def_stack: Vec<ast::NodeId>,
|
|
|
|
|
|
|
|
/// Stack of whether #[doc(hidden)] is set
|
|
|
|
/// at each level which has lint attributes.
|
|
|
|
doc_hidden_stack: Vec<bool>,
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-13 00:20:47 -05:00
|
|
|
impl MissingDoc {
|
|
|
|
pub fn new() -> MissingDoc {
|
2014-06-02 17:27:15 -05:00
|
|
|
MissingDoc {
|
|
|
|
struct_def_stack: vec!(),
|
|
|
|
doc_hidden_stack: vec!(false),
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn doc_hidden(&self) -> bool {
|
|
|
|
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-10-27 17:37:07 -05:00
|
|
|
fn check_missing_docs_attrs(&self,
|
2014-06-02 17:27:15 -05:00
|
|
|
cx: &Context,
|
|
|
|
id: Option<ast::NodeId>,
|
|
|
|
attrs: &[ast::Attribute],
|
|
|
|
sp: Span,
|
|
|
|
desc: &'static str) {
|
|
|
|
// If we're building a test harness, then warning about
|
|
|
|
// documentation is probably not really relevant right now.
|
2014-06-06 17:49:48 -05:00
|
|
|
if cx.sess().opts.test { return }
|
2014-06-02 17:27:15 -05:00
|
|
|
|
2014-10-27 17:37:07 -05:00
|
|
|
// `#[doc(hidden)]` disables missing_docs check.
|
2014-06-02 17:27:15 -05:00
|
|
|
if self.doc_hidden() { return }
|
|
|
|
|
|
|
|
// Only check publicly-visible items, using the result from the privacy pass.
|
|
|
|
// It's an option so the crate root can also use this function (it doesn't
|
|
|
|
// have a NodeId).
|
|
|
|
match id {
|
2014-06-17 18:55:34 -05:00
|
|
|
Some(ref id) if !cx.exported_items.contains(id) => return,
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
|
|
|
|
let has_doc = attrs.iter().any(|a| {
|
|
|
|
match a.node.value.node {
|
|
|
|
ast::MetaNameValue(ref name, _) if name.equiv(&("doc")) => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if !has_doc {
|
2014-10-14 13:37:16 -05:00
|
|
|
cx.span_lint(MISSING_DOCS, sp,
|
2014-06-04 16:35:58 -05:00
|
|
|
format!("missing documentation for {}", desc).as_slice());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
impl LintPass for MissingDoc {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-10-14 13:37:16 -05:00
|
|
|
lint_array!(MISSING_DOCS)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn enter_lint_attrs(&mut self, _: &Context, attrs: &[ast::Attribute]) {
|
|
|
|
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
|
|
|
|
attr.check_name("doc") && match attr.meta_item_list() {
|
|
|
|
None => false,
|
|
|
|
Some(l) => attr::contains_name(l.as_slice(), "hidden"),
|
|
|
|
}
|
|
|
|
});
|
|
|
|
self.doc_hidden_stack.push(doc_hidden);
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn exit_lint_attrs(&mut self, _: &Context, _: &[ast::Attribute]) {
|
|
|
|
self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_struct_def(&mut self, _: &Context,
|
|
|
|
_: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
|
|
|
|
self.struct_def_stack.push(id);
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_struct_def_post(&mut self, _: &Context,
|
|
|
|
_: &ast::StructDef, _: ast::Ident, _: &ast::Generics, id: ast::NodeId) {
|
|
|
|
let popped = self.struct_def_stack.pop().expect("empty struct_def_stack");
|
|
|
|
assert!(popped == id);
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-17 19:06:04 -05:00
|
|
|
fn check_crate(&mut self, cx: &Context, krate: &ast::Crate) {
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, None, krate.attrs.as_slice(),
|
2014-06-17 19:41:50 -05:00
|
|
|
krate.span, "crate");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_item(&mut self, cx: &Context, it: &ast::Item) {
|
|
|
|
let desc = match it.node {
|
|
|
|
ast::ItemFn(..) => "a function",
|
|
|
|
ast::ItemMod(..) => "a module",
|
|
|
|
ast::ItemEnum(..) => "an enum",
|
|
|
|
ast::ItemStruct(..) => "a struct",
|
|
|
|
ast::ItemTrait(..) => "a trait",
|
|
|
|
_ => return
|
|
|
|
};
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(it.id), it.attrs.as_slice(),
|
2014-06-17 19:41:50 -05:00
|
|
|
it.span, desc);
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_fn(&mut self, cx: &Context,
|
2014-09-09 17:54:36 -05:00
|
|
|
fk: visit::FnKind, _: &ast::FnDecl,
|
2014-06-06 17:49:48 -05:00
|
|
|
_: &ast::Block, _: Span, _: ast::NodeId) {
|
2014-09-09 17:54:36 -05:00
|
|
|
match fk {
|
2014-06-02 17:27:15 -05:00
|
|
|
visit::FkMethod(_, _, m) => {
|
|
|
|
// If the method is an impl for a trait, don't doc.
|
|
|
|
if method_context(cx, m) == TraitImpl { return; }
|
|
|
|
|
|
|
|
// Otherwise, doc according to privacy. This will also check
|
|
|
|
// doc for default methods defined on traits.
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(m.id), m.attrs.as_slice(),
|
2014-06-17 19:41:50 -05:00
|
|
|
m.span, "a method");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_ty_method(&mut self, cx: &Context, tm: &ast::TypeMethod) {
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(tm.id), tm.attrs.as_slice(),
|
2014-06-17 19:41:50 -05:00
|
|
|
tm.span, "a type method");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn check_struct_field(&mut self, cx: &Context, sf: &ast::StructField) {
|
|
|
|
match sf.node.kind {
|
|
|
|
ast::NamedField(_, vis) if vis == ast::Public => {
|
2014-06-06 17:49:48 -05:00
|
|
|
let cur_struct_def = *self.struct_def_stack.last()
|
|
|
|
.expect("empty struct_def_stack");
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(cur_struct_def),
|
2014-06-17 19:41:50 -05:00
|
|
|
sf.node.attrs.as_slice(), sf.span,
|
|
|
|
"a struct field")
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_variant(&mut self, cx: &Context, v: &ast::Variant, _: &ast::Generics) {
|
2014-10-27 17:37:07 -05:00
|
|
|
self.check_missing_docs_attrs(cx, Some(v.node.id), v.node.attrs.as_slice(),
|
2014-06-17 19:41:50 -05:00
|
|
|
v.span, "a variant");
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(DEPRECATED, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects use of #[deprecated] items")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-24 12:29:08 -05:00
|
|
|
// FIXME #6875: Change to Warn after std library stabilization is complete
|
|
|
|
declare_lint!(EXPERIMENTAL, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects use of #[experimental] items")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(UNSTABLE, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects use of #[unstable] items (incl. items with no stability attribute)")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-06 17:49:48 -05:00
|
|
|
/// Checks for use of items with `#[deprecated]`, `#[experimental]` and
|
|
|
|
/// `#[unstable]` attributes, or no stability attribute.
|
2014-06-02 17:27:15 -05:00
|
|
|
pub struct Stability;
|
|
|
|
|
|
|
|
impl LintPass for Stability {
|
2014-06-04 16:35:58 -05:00
|
|
|
fn get_lints(&self) -> LintArray {
|
2014-06-13 15:13:05 -05:00
|
|
|
lint_array!(DEPRECATED, EXPERIMENTAL, UNSTABLE)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
fn check_expr(&mut self, cx: &Context, e: &ast::Expr) {
|
2014-09-24 04:14:19 -05:00
|
|
|
// first, check if the given expression was generated by a macro or not
|
|
|
|
// we need to go back the expn_info tree to check only the arguments
|
|
|
|
// of the initial macro call, not the nested ones.
|
|
|
|
let mut expnid = e.span.expn_id;
|
|
|
|
let mut is_internal = false;
|
|
|
|
while cx.tcx.sess.codemap().with_expn_info(expnid, |expninfo| {
|
2014-09-17 17:15:36 -05:00
|
|
|
match expninfo {
|
|
|
|
Some(ref info) => {
|
2014-09-24 04:14:19 -05:00
|
|
|
// save the parent expn_id for next loop iteration
|
|
|
|
expnid = info.call_site.expn_id;
|
|
|
|
if info.callee.span.is_none() {
|
|
|
|
// it's a compiler built-in, we *really* don't want to mess with it
|
|
|
|
// so we skip it, unless it was called by a regular macro, in which case
|
|
|
|
// we will handle the caller macro next turn
|
|
|
|
is_internal = true;
|
|
|
|
true // continue looping
|
2014-09-17 17:15:36 -05:00
|
|
|
} else {
|
2014-09-24 04:14:19 -05:00
|
|
|
// was this expression from the current macro arguments ?
|
|
|
|
is_internal = !( e.span.lo > info.call_site.lo &&
|
|
|
|
e.span.hi < info.call_site.hi );
|
|
|
|
true // continue looping
|
2014-09-17 17:15:36 -05:00
|
|
|
}
|
|
|
|
},
|
2014-09-24 04:14:19 -05:00
|
|
|
_ => false // stop looping
|
2014-09-17 17:15:36 -05:00
|
|
|
}
|
2014-09-24 04:14:19 -05:00
|
|
|
}) { /* empty while loop body */ }
|
|
|
|
if is_internal { return; }
|
2014-07-16 15:50:33 -05:00
|
|
|
|
2014-09-17 05:34:18 -05:00
|
|
|
let mut span = e.span;
|
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
let id = match e.node {
|
|
|
|
ast::ExprPath(..) | ast::ExprStruct(..) => {
|
|
|
|
match cx.tcx.def_map.borrow().find(&e.id) {
|
|
|
|
Some(&def) => def.def_id(),
|
|
|
|
None => return
|
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-09-17 05:34:18 -05:00
|
|
|
ast::ExprMethodCall(i, _, _) => {
|
|
|
|
span = i.span;
|
2014-06-02 17:27:15 -05:00
|
|
|
let method_call = typeck::MethodCall::expr(e.id);
|
|
|
|
match cx.tcx.method_map.borrow().find(&method_call) {
|
|
|
|
Some(method) => {
|
|
|
|
match method.origin {
|
|
|
|
typeck::MethodStatic(def_id) => {
|
2014-06-26 13:37:39 -05:00
|
|
|
def_id
|
2014-06-02 17:27:15 -05:00
|
|
|
}
|
2014-05-29 00:26:56 -05:00
|
|
|
typeck::MethodStaticUnboxedClosure(def_id) => {
|
|
|
|
def_id
|
|
|
|
}
|
2014-09-11 00:07:49 -05:00
|
|
|
typeck::MethodTypeParam(typeck::MethodParam {
|
2014-10-05 19:36:53 -05:00
|
|
|
ref trait_ref,
|
2014-06-02 17:27:15 -05:00
|
|
|
method_num: index,
|
|
|
|
..
|
2014-09-12 10:42:58 -05:00
|
|
|
}) |
|
2014-09-11 00:07:49 -05:00
|
|
|
typeck::MethodTraitObject(typeck::MethodObject {
|
2014-10-05 19:36:53 -05:00
|
|
|
ref trait_ref,
|
2014-06-02 17:27:15 -05:00
|
|
|
method_num: index,
|
|
|
|
..
|
2014-08-04 15:56:56 -05:00
|
|
|
}) => {
|
2014-08-05 21:44:21 -05:00
|
|
|
ty::trait_item(cx.tcx,
|
|
|
|
trait_ref.def_id,
|
|
|
|
index).def_id()
|
2014-08-04 15:56:56 -05:00
|
|
|
}
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
None => return
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return
|
|
|
|
};
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-26 13:37:39 -05:00
|
|
|
let stability = stability::lookup(cx.tcx, id);
|
2014-08-11 18:39:34 -05:00
|
|
|
let cross_crate = !ast_util::is_local(id);
|
|
|
|
|
|
|
|
// stability attributes are promises made across crates; only
|
|
|
|
// check DEPRECATED for crate-local usage.
|
2014-06-02 17:27:15 -05:00
|
|
|
let (lint, label) = match stability {
|
|
|
|
// no stability attributes == Unstable
|
2014-08-11 18:39:34 -05:00
|
|
|
None if cross_crate => (UNSTABLE, "unmarked"),
|
|
|
|
Some(attr::Stability { level: attr::Unstable, .. }) if cross_crate =>
|
|
|
|
(UNSTABLE, "unstable"),
|
|
|
|
Some(attr::Stability { level: attr::Experimental, .. }) if cross_crate =>
|
|
|
|
(EXPERIMENTAL, "experimental"),
|
2014-06-02 17:27:15 -05:00
|
|
|
Some(attr::Stability { level: attr::Deprecated, .. }) =>
|
2014-08-11 18:39:34 -05:00
|
|
|
(DEPRECATED, "deprecated"),
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => return
|
|
|
|
};
|
2014-06-01 18:16:00 -05:00
|
|
|
|
2014-06-02 17:27:15 -05:00
|
|
|
let msg = match stability {
|
|
|
|
Some(attr::Stability { text: Some(ref s), .. }) => {
|
|
|
|
format!("use of {} item: {}", label, *s)
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
2014-06-02 17:27:15 -05:00
|
|
|
_ => format!("use of {} item", label)
|
|
|
|
};
|
|
|
|
|
2014-09-17 05:34:18 -05:00
|
|
|
cx.span_lint(lint, span, msg.as_slice());
|
2014-06-01 18:16:00 -05:00
|
|
|
}
|
|
|
|
}
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(pub UNUSED_IMPORTS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"imports that are never used")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNUSED_EXTERN_CRATES, Allow,
|
2014-09-11 12:14:43 -05:00
|
|
|
"extern crates that are never used")
|
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNUSED_QUALIFICATIONS, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects unnecessarily qualified names")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNKNOWN_LINTS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unrecognized lint attribute")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNUSED_VARIABLES, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detect variables which are not used in any way")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNUSED_ASSIGNMENTS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detect assignments that will never be read")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(pub DEAD_CODE, Warn,
|
2014-10-14 13:32:15 -05:00
|
|
|
"detect unused, unexported items")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(pub UNREACHABLE_CODE, Warn,
|
2014-10-14 13:32:15 -05:00
|
|
|
"detects unreachable code paths")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(pub WARNINGS, Warn,
|
2014-06-17 19:41:50 -05:00
|
|
|
"mass-change the level for lints which produce warnings")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-06-13 15:13:05 -05:00
|
|
|
declare_lint!(pub UNKNOWN_FEATURES, Deny,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unknown features found in crate-level #[feature] directives")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub UNKNOWN_CRATE_TYPES, Deny,
|
2014-06-17 19:41:50 -05:00
|
|
|
"unknown crate type found in #[crate_type] directive")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub VARIANT_SIZE_DIFFERENCES, Allow,
|
2014-06-17 19:41:50 -05:00
|
|
|
"detects enums with widely varying variant sizes")
|
2014-06-04 16:35:58 -05:00
|
|
|
|
2014-10-14 13:37:16 -05:00
|
|
|
declare_lint!(pub FAT_PTR_TRANSMUTES, Allow,
|
2014-08-06 04:59:40 -05:00
|
|
|
"detects transmutes of fat pointers")
|
|
|
|
|
2014-06-04 16:35:58 -05:00
|
|
|
/// Does nothing as a lint pass, but registers some `Lint`s
|
|
|
|
/// which are used by other parts of the compiler.
|
|
|
|
pub struct HardwiredLints;
|
|
|
|
|
|
|
|
impl LintPass for HardwiredLints {
|
|
|
|
fn get_lints(&self) -> LintArray {
|
|
|
|
lint_array!(
|
2014-06-18 14:35:48 -05:00
|
|
|
UNUSED_IMPORTS,
|
2014-10-14 13:37:16 -05:00
|
|
|
UNUSED_EXTERN_CRATES,
|
|
|
|
UNUSED_QUALIFICATIONS,
|
|
|
|
UNKNOWN_LINTS,
|
|
|
|
UNUSED_VARIABLES,
|
|
|
|
UNUSED_ASSIGNMENTS,
|
2014-06-18 14:35:48 -05:00
|
|
|
DEAD_CODE,
|
|
|
|
UNREACHABLE_CODE,
|
|
|
|
WARNINGS,
|
|
|
|
UNKNOWN_FEATURES,
|
2014-10-14 13:37:16 -05:00
|
|
|
UNKNOWN_CRATE_TYPES,
|
2014-10-14 15:36:19 -05:00
|
|
|
VARIANT_SIZE_DIFFERENCES,
|
|
|
|
FAT_PTR_TRANSMUTES
|
2014-06-18 14:35:48 -05:00
|
|
|
)
|
2014-06-04 16:35:58 -05:00
|
|
|
}
|
|
|
|
}
|