Merge remote-tracking branch 'origin/master' into 1537-drop_copy

This commit is contained in:
Oliver Schneider 2017-03-24 10:11:46 +01:00
commit 8ae82eb4ab
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
52 changed files with 717 additions and 568 deletions

View File

@ -1,6 +1,24 @@
# Change Log
All notable changes to this project will be documented in this file.
## 0.0.121 — 2017-03-21
* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
## 0.0.120 — 2017-03-17
* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
## 0.0.119 — 2017-03-13
* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
## 0.0.118 — 2017-03-05
* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
## 0.0.117 — 2017-03-01
* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
## 0.0.116 — 2017-02-28
* Fix `cargo clippy` on 64 bit windows systems
## 0.0.115 — 2017-02-27
* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
@ -65,7 +83,7 @@ All notable changes to this project will be documented in this file.
* New lint: [`get_unwrap`]
## 0.0.98 — 2016-11-08
* Fixes a an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
## 0.0.97 — 2016-11-03
* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was

View File

@ -35,10 +35,9 @@ T-middle issues can be more involved and require verifying types. The
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
Should you add a lint, try it on clippy itself using `util/dogfood.sh`. You may find that clippy
contains some questionable code itself! Also before making a pull request, please run
`util/update_lints.py`, which will update `lib.rs` and `README.md` with the lint declarations. Our
travis build actually checks for this.
Compiling clippy can take almost a minute or more depending on your machine.
You can set the environment flag `CARGO_INCREMENTAL=1` to cut down that time to
almost a third on average, depending on the influence your change has.
Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected.
Of course there's little sense in writing the output yourself or copying it around.

View File

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.0.115"
version = "0.0.121"
authors = [
"Manish Goregaokar <manishsmail@gmail.com>",
"Andre Bogus <bogusandre@gmail.com>",
@ -30,7 +30,7 @@ test = false
[dependencies]
# begin automatic update
clippy_lints = { version = "0.0.115", path = "clippy_lints" }
clippy_lints = { version = "0.0.121", path = "clippy_lints" }
# end automatic update
cargo_metadata = "0.1.1"

View File

@ -50,7 +50,7 @@ Then build by enabling the feature: `cargo build --features "clippy"`
Instead of adding the `cfg_attr` attributes you can also run clippy on demand:
`cargo rustc --features clippy -- -Z no-trans -Z extra-plugins=clippy`
(the `-Z no trans`, while not neccessary, will stop the compilation process after
(the `-Z no trans`, while not necessary, will stop the compilation process after
typechecking (and lints) have completed, which can significantly reduce the runtime).
### As a cargo subcommand (`cargo clippy`)
@ -191,16 +191,16 @@ name
[assign_ops](https://github.com/Manishearth/rust-clippy/wiki#assign_ops) | allow | any compound assignment operation
[bad_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#bad_bit_mask) | warn | expressions of the form `_ & mask == select` that will only ever return `true` or `false`
[blacklisted_name](https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name) | warn | usage of a blacklisted/placeholder name
[block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces that can be eliminated in conditions, e.g `if { true } ...`
[block_in_if_condition_expr](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_expr) | warn | braces that can be eliminated in conditions, e.g. `if { true } ...`
[block_in_if_condition_stmt](https://github.com/Manishearth/rust-clippy/wiki#block_in_if_condition_stmt) | warn | complex blocks in conditions, e.g. `if { let x = true; x } ...`
[bool_comparison](https://github.com/Manishearth/rust-clippy/wiki#bool_comparison) | warn | comparing a variable to a boolean, e.g. `if x == true`
[box_vec](https://github.com/Manishearth/rust-clippy/wiki#box_vec) | warn | usage of `Box<Vec<T>>`, vector elements are already on the heap
[boxed_local](https://github.com/Manishearth/rust-clippy/wiki#boxed_local) | warn | using `Box<T>` where unnecessary
[builtin_type_shadow](https://github.com/Manishearth/rust-clippy/wiki#builtin_type_shadow) | warn | shadowing a builtin type
[cast_possible_truncation](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_truncation) | allow | casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, or `x as i32` where `x: f32`
[cast_possible_wrap](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_wrap) | allow | casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` and `x > i32::MAX`
[cast_precision_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_precision_loss) | allow | casts that cause loss of precision, e.g `x as f32` where `x: u64`
[cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g `x as u32` where `x: i32`
[cast_possible_truncation](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_truncation) | allow | casts that may cause truncation of the value, e.g. `x as u8` where `x: u32`, or `x as i32` where `x: f32`
[cast_possible_wrap](https://github.com/Manishearth/rust-clippy/wiki#cast_possible_wrap) | allow | casts that may cause wrapping around the value, e.g. `x as i32` where `x: u32` and `x > i32::MAX`
[cast_precision_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_precision_loss) | allow | casts that cause loss of precision, e.g. `x as f32` where `x: u64`
[cast_sign_loss](https://github.com/Manishearth/rust-clippy/wiki#cast_sign_loss) | allow | casts from signed types to unsigned types, e.g. `x as u32` where `x: i32`
[char_lit_as_u8](https://github.com/Manishearth/rust-clippy/wiki#char_lit_as_u8) | warn | casting a character literal to u8
[chars_next_cmp](https://github.com/Manishearth/rust-clippy/wiki#chars_next_cmp) | warn | using `.chars().next()` to check if a string starts with a char
[clone_double_ref](https://github.com/Manishearth/rust-clippy/wiki#clone_double_ref) | warn | using `clone` on `&&T`
@ -336,7 +336,7 @@ name
[should_implement_trait](https://github.com/Manishearth/rust-clippy/wiki#should_implement_trait) | warn | defining a method that should be implementing a std trait
[similar_names](https://github.com/Manishearth/rust-clippy/wiki#similar_names) | allow | similarly named items and bindings
[single_char_pattern](https://github.com/Manishearth/rust-clippy/wiki#single_char_pattern) | warn | using a single-character str where a char could be used, e.g. `_.split("x")`
[single_match](https://github.com/Manishearth/rust-clippy/wiki#single_match) | warn | a match statement with a single nontrivial arm (i.e, where the other arm is `_ => {}`) instead of `if let`
[single_match](https://github.com/Manishearth/rust-clippy/wiki#single_match) | warn | a match statement with a single nontrivial arm (i.e. where the other arm is `_ => {}`) instead of `if let`
[single_match_else](https://github.com/Manishearth/rust-clippy/wiki#single_match_else) | allow | a match statement with a two arms where the second arm's pattern is a wildcard instead of `if let`
[string_add](https://github.com/Manishearth/rust-clippy/wiki#string_add) | allow | using `x + ..` where x is a `String` instead of `push_str()`
[string_add_assign](https://github.com/Manishearth/rust-clippy/wiki#string_add_assign) | allow | using `x = x + ..` where x is a `String` instead of `push_str()`
@ -354,7 +354,7 @@ name
[type_complexity](https://github.com/Manishearth/rust-clippy/wiki#type_complexity) | warn | usage of very complex types that might be better factored into `type` definitions
[unicode_not_nfc](https://github.com/Manishearth/rust-clippy/wiki#unicode_not_nfc) | allow | using a unicode literal not in NFC normal form (see [unicode tr15](http://www.unicode.org/reports/tr15/) for further information)
[unit_cmp](https://github.com/Manishearth/rust-clippy/wiki#unit_cmp) | warn | comparing unit values
[unnecessary_cast](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_cast) | warn | cast to the same type, e.g `x as i32` where `x: i32`
[unnecessary_cast](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_cast) | warn | cast to the same type, e.g. `x as i32` where `x: i32`
[unnecessary_mut_passed](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_mut_passed) | warn | an argument passed as a mutable reference although the callee only demands an immutable reference
[unnecessary_operation](https://github.com/Manishearth/rust-clippy/wiki#unnecessary_operation) | warn | outer expressions with no effect
[unneeded_field_pattern](https://github.com/Manishearth/rust-clippy/wiki#unneeded_field_pattern) | warn | struct fields bound to a wildcard instead of using `..`

View File

@ -4,16 +4,15 @@ environment:
matrix:
- TARGET: i686-pc-windows-gnu
MSYS2_BITS: 32
RUN_CARGO_CLIPPY: true
- TARGET: i686-pc-windows-msvc
MSYS2_BITS: 32
RUN_CARGO_CLIPPY: true
- TARGET: x86_64-pc-windows-gnu
MSYS2_BITS: 64
- TARGET: x86_64-pc-windows-msvc
MSYS2_BITS: 64
install:
- set PATH=C:\Program Files\Git\mingw64\bin;%PATH%
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET% --default-toolchain nightly
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin;C:\Users\appveyor\.rustup\toolchains\nightly-%TARGET%\bin
@ -29,7 +28,7 @@ test_script:
- cargo test --features debugging
- copy target\debug\cargo-clippy.exe C:\Users\appveyor\.cargo\bin\
- cargo clippy -- -D clippy
- if defined RUN_CARGO_CLIPPY cd clippy_lints && cargo clippy -- -D clippy && cd ..
- cd clippy_lints && cargo clippy -- -D clippy && cd ..
notifications:
- provider: Email

View File

@ -1,7 +1,7 @@
[package]
name = "clippy_lints"
# begin automatic update
version = "0.0.115"
version = "0.0.121"
# end automatic update
authors = [
"Manish Goregaokar <manishsmail@gmail.com>",

View File

@ -1,9 +1,8 @@
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
use rustc_const_math::{ConstUsize, ConstIsize, ConstInt};
use rustc::hir;
use syntax::ast::RangeLimits;
use utils::{self, higher};
@ -61,11 +60,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Array with known size can be checked statically
let ty = cx.tables.expr_ty(array);
if let ty::TyArray(_, size) = ty.sty {
let size = ConstInt::Infer(size as u128);
let size = ConstInt::Usize(ConstUsize::new(size as u64, cx.sess().target.uint_type)
.expect("array size is invalid"));
let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
// Index is a constant uint
let const_index = constcx.eval(index, ExprTypeChecked);
let const_index = constcx.eval(index);
if let Ok(ConstVal::Integral(const_index)) = const_index {
if size <= const_index {
utils::span_lint(cx, OUT_OF_BOUNDS_INDEXING, e.span, "const index is out of bounds");
@ -77,10 +77,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Index is a constant range
if let Some(range) = higher::range(index) {
let start = range.start
.map(|start| constcx.eval(start, ExprTypeChecked))
.map(|start| constcx.eval(start))
.map(|v| v.ok());
let end = range.end
.map(|end| constcx.eval(end, ExprTypeChecked))
.map(|end| constcx.eval(end))
.map(|v| v.ok());
if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
@ -117,13 +117,31 @@ fn to_const_range(
let start = match *start {
Some(Some(ConstVal::Integral(x))) => x,
Some(_) => return None,
None => ConstInt::Infer(0),
None => ConstInt::U8(0),
};
let end = match *end {
Some(Some(ConstVal::Integral(x))) => {
if limits == RangeLimits::Closed {
(x + ConstInt::Infer(1)).expect("such a big array is not realistic")
match x {
ConstInt::U8(_) => (x + ConstInt::U8(1)),
ConstInt::U16(_) => (x + ConstInt::U16(1)),
ConstInt::U32(_) => (x + ConstInt::U32(1)),
ConstInt::U64(_) => (x + ConstInt::U64(1)),
ConstInt::U128(_) => (x + ConstInt::U128(1)),
ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
ConstInt::I8(_) => (x + ConstInt::I8(1)),
ConstInt::I16(_) => (x + ConstInt::I16(1)),
ConstInt::I32(_) => (x + ConstInt::I32(1)),
ConstInt::I64(_) => (x + ConstInt::I64(1)),
ConstInt::I128(_) => (x + ConstInt::I128(1)),
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}
.expect("such a big array is not realistic")
} else {
x
}

View File

@ -86,8 +86,8 @@ impl LintPass for AttrPass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
if let MetaItemKind::List(ref items) = attr.value.node {
if items.is_empty() || attr.name() != "deprecated" {
if let Some(ref items) = attr.meta_item_list() {
if items.is_empty() || attr.name().map_or(true, |n| n != "deprecated") {
return;
}
for item in items {
@ -110,31 +110,33 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
ItemExternCrate(_) |
ItemUse(_, _) => {
for attr in &item.attrs {
if let MetaItemKind::List(ref lint_list) = attr.value.node {
match &*attr.name().as_str() {
"allow" | "warn" | "deny" | "forbid" => {
// whitelist `unused_imports` and `deprecated`
for lint in lint_list {
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
if let ItemUse(_, _) = item.node {
return;
if let Some(ref lint_list) = attr.meta_item_list() {
if let Some(name) = attr.name() {
match &*name.as_str() {
"allow" | "warn" | "deny" | "forbid" => {
// whitelist `unused_imports` and `deprecated`
for lint in lint_list {
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
if let ItemUse(_, _) = item.node {
return;
}
}
}
}
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
if sugg.len() > 1 {
span_lint_and_then(cx,
USELESS_ATTRIBUTE,
attr.span,
"useless lint attribute",
|db| {
sugg.insert(1, '!');
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
});
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
if sugg.len() > 1 {
span_lint_and_then(cx,
USELESS_ATTRIBUTE,
attr.span,
"useless lint attribute",
|db| {
sugg.insert(1, '!');
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
});
}
}
}
},
_ => {},
},
_ => {},
}
}
}
}
@ -218,8 +220,8 @@ fn check_attrs(cx: &LateContext, span: Span, name: &Name, attrs: &[Attribute]) {
}
for attr in attrs {
if let MetaItemKind::List(ref values) = attr.value.node {
if values.len() != 1 || attr.name() != "inline" {
if let Some(ref values) = attr.meta_item_list() {
if values.len() != 1 || attr.name().map_or(true, |n| n != "inline") {
continue;
}
if is_word(&values[0], "always") {

View File

@ -237,6 +237,7 @@ fn check_ineffective_gt(cx: &LateContext, span: Span, m: u128, c: u128, op: &str
}
fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
use rustc::ty::subst::Substs;
match lit.node {
ExprLit(ref lit_ptr) => {
if let LitKind::Int(value, _) = lit_ptr.node {
@ -248,7 +249,7 @@ fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u128> {
ExprPath(ref qpath) => {
let def = cx.tables.qpath_def(qpath, lit.id);
if let Def::Const(def_id) = def {
lookup_const_by_id(cx.tcx, def_id, None).and_then(|(l, _tab, _ty)| fetch_int_literal(cx, l))
lookup_const_by_id(cx.tcx, def_id, Substs::empty()).and_then(|(l, _ty)| fetch_int_literal(cx, l))
} else {
None
}

View File

@ -18,7 +18,7 @@ use utils::*;
declare_lint! {
pub BLOCK_IN_IF_CONDITION_EXPR,
Warn,
"braces that can be eliminated in conditions, e.g `if { true } ...`"
"braces that can be eliminated in conditions, e.g. `if { true } ...`"
}
/// **What it does:** Checks for `if` conditions that use blocks containing

View File

@ -3,14 +3,15 @@
use rustc::lint::LateContext;
use rustc::hir::def::Def;
use rustc_const_eval::lookup_const_by_id;
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
use rustc_const_math::ConstInt;
use rustc::hir::*;
use rustc::ty::{self, TyCtxt};
use std::cmp::Ordering::{self, Equal};
use std::cmp::PartialOrd;
use std::hash::{Hash, Hasher};
use std::mem;
use std::rc::Rc;
use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy, IntTy, NodeId};
use syntax::ast::{FloatTy, LitKind, StrStyle, NodeId};
use syntax::ptr::P;
#[derive(Debug, Copy, Clone)]
@ -52,21 +53,6 @@ pub enum Constant {
Tuple(Vec<Constant>),
}
impl Constant {
/// Convert to `u64` if possible.
///
/// # panics
///
/// If the constant could not be converted to `u64` losslessly.
fn as_u64(&self) -> u64 {
if let Constant::Int(val) = *self {
val.to_u64().expect("negative constant can't be casted to `u64`")
} else {
panic!("Could not convert a `{:?}` to `u64`", self);
}
}
}
impl PartialEq for Constant {
fn eq(&self, other: &Constant) -> bool {
match (self, other) {
@ -174,28 +160,33 @@ impl PartialOrd for Constant {
/// parse a `LitKind` to a `Constant`
#[allow(cast_possible_wrap)]
pub fn lit_to_constant(lit: &LitKind) -> Constant {
pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut ty: ty::Ty<'tcx>) -> Constant {
use syntax::ast::*;
use syntax::ast::LitIntType::*;
use rustc::ty::util::IntTypeExt;
if let ty::TyAdt(adt, _) = ty.sty {
if adt.is_enum() {
ty = adt.repr.discr_type().to_ty(tcx)
}
}
match *lit {
LitKind::Str(ref is, style) => Constant::Str(is.to_string(), style),
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
LitKind::Char(c) => Constant::Char(c),
LitKind::Int(value, LitIntType::Unsuffixed) => Constant::Int(ConstInt::Infer(value)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::U8)) => Constant::Int(ConstInt::U8(value as u8)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::U16)) => Constant::Int(ConstInt::U16(value as u16)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::U32)) => Constant::Int(ConstInt::U32(value as u32)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::U128)) => Constant::Int(ConstInt::U128(value as u128)),
LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => {
Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32)))
},
LitKind::Int(value, LitIntType::Signed(IntTy::I8)) => Constant::Int(ConstInt::I8(value as i8)),
LitKind::Int(value, LitIntType::Signed(IntTy::I16)) => Constant::Int(ConstInt::I16(value as i16)),
LitKind::Int(value, LitIntType::Signed(IntTy::I32)) => Constant::Int(ConstInt::I32(value as i32)),
LitKind::Int(value, LitIntType::Signed(IntTy::I64)) => Constant::Int(ConstInt::I64(value as i64)),
LitKind::Int(value, LitIntType::Signed(IntTy::I128)) => Constant::Int(ConstInt::I128(value as i128)),
LitKind::Int(value, LitIntType::Signed(IntTy::Is)) => {
Constant::Int(ConstInt::Isize(ConstIsize::Is32(value as i32)))
LitKind::Int(n, hint) => {
match (&ty.sty, hint) {
(&ty::TyInt(ity), _) |
(_, Signed(ity)) => {
Constant::Int(ConstInt::new_signed_truncating(n as i128, ity, tcx.sess.target.int_type))
},
(&ty::TyUint(uty), _) |
(_, Unsigned(uty)) => {
Constant::Int(ConstInt::new_unsigned_truncating(n as u128, uty, tcx.sess.target.uint_type))
},
_ => bug!(),
}
},
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
@ -231,22 +222,20 @@ fn neg_float_str(s: &str) -> String {
pub fn constant(lcx: &LateContext, e: &Expr) -> Option<(Constant, bool)> {
let mut cx = ConstEvalLateContext {
lcx: Some(lcx),
tcx: lcx.tcx,
tables: lcx.tables,
needed_resolution: false,
};
cx.expr(e).map(|cst| (cst, cx.needed_resolution))
}
pub fn constant_simple(e: &Expr) -> Option<Constant> {
let mut cx = ConstEvalLateContext {
lcx: None,
needed_resolution: false,
};
cx.expr(e)
pub fn constant_simple(lcx: &LateContext, e: &Expr) -> Option<Constant> {
constant(lcx, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
}
struct ConstEvalLateContext<'c, 'cc: 'c> {
lcx: Option<&'c LateContext<'c, 'cc>>,
struct ConstEvalLateContext<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
tables: &'a ty::TypeckTables<'tcx>,
needed_resolution: bool,
}
@ -257,17 +246,15 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
ExprPath(ref qpath) => self.fetch_path(qpath, e.id),
ExprBlock(ref block) => self.block(block),
ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise),
ExprLit(ref lit) => Some(lit_to_constant(&lit.node)),
ExprLit(ref lit) => Some(lit_to_constant(&lit.node, self.tcx, self.tables.expr_ty(e))),
ExprArray(ref vec) => self.multi(vec).map(Constant::Vec),
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprRepeat(ref value, number_id) => {
if let Some(lcx) = self.lcx {
self.binop_apply(value,
&lcx.tcx.hir.body(number_id).value,
|v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize)))
} else {
None
}
ExprRepeat(ref value, _) => {
let n = match self.tables.expr_ty(e).sty {
ty::TyArray(_, n) => n,
_ => span_bug!(e.span, "typeck error"),
};
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
},
ExprUnary(op, ref operand) => {
self.expr(operand).and_then(|o| match op {
@ -292,24 +279,27 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
/// lookup a possibly constant expression from a ExprPath
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
if let Some(lcx) = self.lcx {
let def = lcx.tables.qpath_def(qpath, id);
match def {
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
let substs = Some(lcx.tables
.node_id_item_substs(id)
.unwrap_or_else(|| lcx.tcx.intern_substs(&[])));
if let Some((const_expr, _tab, _ty)) = lookup_const_by_id(lcx.tcx, def_id, substs) {
let ret = self.expr(const_expr);
if ret.is_some() {
self.needed_resolution = true;
}
return ret;
let def = self.tables.qpath_def(qpath, id);
match def {
Def::Const(def_id) |
Def::AssociatedConst(def_id) => {
let substs = self.tables
.node_id_item_substs(id)
.unwrap_or_else(|| self.tcx.intern_substs(&[]));
if let Some((const_expr, tables)) = lookup_const_by_id(self.tcx, def_id, substs) {
let mut cx = ConstEvalLateContext {
tcx: self.tcx,
tables: tables,
needed_resolution: false,
};
let ret = cx.expr(const_expr);
if ret.is_some() {
self.needed_resolution = true;
}
},
_ => {},
}
return ret;
}
},
_ => {},
}
None
}
@ -369,15 +359,4 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
_ => None,
}
}
fn binop_apply<F>(&mut self, left: &Expr, right: &Expr, op: F) -> Option<Constant>
where F: Fn(Constant, Constant) -> Option<Constant>
{
if let (Some(lc), Some(rc)) = (self.expr(left), self.expr(right)) {
op(lc, rc)
} else {
None
}
}
}

View File

@ -41,12 +41,13 @@ impl LintPass for CyclomaticComplexity {
}
impl CyclomaticComplexity {
fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr, span: Span) {
fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, body: &'tcx Body, span: Span) {
if in_macro(cx, span) {
return;
}
let cfg = CFG::new(cx.tcx, expr);
let cfg = CFG::new(cx.tcx, body);
let expr = &body.value;
let n = cfg.graph.len_nodes() as u64;
let e = cfg.graph.len_edges() as u64;
if e + 2 < n {
@ -101,7 +102,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
) {
let def_id = cx.tcx.hir.local_def_id(node_id);
if !cx.tcx.has_attr(def_id, "test") {
self.check(cx, &body.value, span);
self.check(cx, body, span);
}
}
@ -136,7 +137,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CCHelper<'a, 'tcx> {
let ty = self.cx.tables.node_id_to_type(callee.id);
match ty.sty {
ty::TyFnDef(_, _, ty) |
ty::TyFnPtr(ty) if ty.sig.skip_binder().output().sty == ty::TyNever => {
ty::TyFnPtr(ty) if ty.skip_binder().output().sty == ty::TyNever => {
self.divergence += 1;
},
_ => (),

View File

@ -89,11 +89,9 @@ pub fn check_attrs<'a>(cx: &EarlyContext, valid_idents: &[String], attrs: &'a [a
for attr in attrs {
if attr.is_sugared_doc {
if let ast::MetaItemKind::NameValue(ref doc) = attr.value.node {
if let ast::LitKind::Str(ref doc, _) = doc.node {
let doc = (*doc.as_str()).to_owned();
docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
}
if let Some(ref doc) = attr.value_str() {
let doc = (*doc.as_str()).to_owned();
docs.extend_from_slice(&strip_doc_comment_decoration((doc, attr.span)));
}
}
}

View File

@ -45,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
if let Some(body_id) = variant.disr_expr {
use rustc_const_eval::*;
let constcx = ConstContext::new(cx.tcx, body_id);
let bad = match constcx.eval(&cx.tcx.hir.body(body_id).value, EvalHint::ExprTypeChecked) {
let bad = match constcx.eval(&cx.tcx.hir.body(body_id).value) {
Ok(ConstVal::Integral(Usize(Us64(i)))) => i as u32 as u64 != i,
Ok(ConstVal::Integral(Isize(Is64(i)))) => i as i32 as i64 != i,
_ => false,

View File

@ -66,7 +66,8 @@ fn check_closure(cx: &LateContext, expr: &Expr) {
// Is it an unsafe function? They don't implement the closure traits
ty::TyFnDef(_, _, fn_ty) |
ty::TyFnPtr(fn_ty) => {
if fn_ty.unsafety == Unsafety::Unsafe || fn_ty.sig.skip_binder().output().sty == ty::TyNever {
if fn_ty.skip_binder().unsafety == Unsafety::Unsafe ||
fn_ty.skip_binder().output().sty == ty::TyNever {
return;
}
},

View File

@ -129,7 +129,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
match self.cx.tables.expr_ty(func).sty {
ty::TyFnDef(_, _, fn_ty) |
ty::TyFnPtr(fn_ty) => {
if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&fn_ty.sig).output().sty {
if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&fn_ty).output().sty {
self.report_diverging_sub_expr(e);
}
},

View File

@ -3,7 +3,7 @@ use rustc::lint::*;
use rustc::hir::*;
use syntax::codemap::Span;
use utils::{span_lint, snippet, in_macro};
use rustc_const_math::ConstInt;
use syntax::attr::IntType::{SignedInt, UnsignedInt};
/// **What it does:** Checks for identity operations, e.g. `x + 0`.
///
@ -60,11 +60,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
if let Some(v @ Constant::Int(_)) = constant_simple(e) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
if match m {
0 => v == Constant::Int(ConstInt::Infer(0)),
-1 => v == Constant::Int(ConstInt::InferSigned(-1)),
1 => v == Constant::Int(ConstInt::Infer(1)),
0 => v.to_u128_unchecked() == 0,
-1 => match v.int_type() {
SignedInt(_) => #[allow(cast_possible_wrap)] (v.to_u128_unchecked() as i128 == -1),
UnsignedInt(_) => false
},
1 => v.to_u128_unchecked() == 1,
_ => unreachable!(),
} {
span_lint(cx,

View File

@ -58,6 +58,10 @@ impl EarlyLintPass for ItemsAfterStatements {
if in_macro(cx, it.span) {
return;
}
if let ItemKind::MacroDef(..) = it.node {
// do not lint `macro_rules`, but continue processing further statements
continue;
}
span_lint(cx,
ITEMS_AFTER_STATEMENTS,
it.span,

View File

@ -2,10 +2,8 @@
use rustc::lint::*;
use rustc::hir::*;
use utils::{span_lint_and_then, snippet_opt};
use rustc::ty::layout::TargetDataLayout;
use utils::{span_lint_and_then, snippet_opt, type_size};
use rustc::ty::TypeFoldable;
use rustc::traits::Reveal;
/// **What it does:** Checks for large size differences between variants on `enum`s.
///
@ -55,28 +53,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
let mut largest_variant: Option<(_, _)> = None;
for (i, variant) in adt.variants.iter().enumerate() {
let data_layout = TargetDataLayout::parse(cx.sess());
cx.tcx.infer_ctxt((), Reveal::All).enter(|infcx| {
let size: u64 = variant.fields
.iter()
.map(|f| {
let ty = cx.tcx.item_type(f.did);
if ty.needs_subst() {
0 // we can't reason about generics, so we treat them as zero sized
} else {
ty.layout(&infcx)
.expect("layout should be computable for concrete type")
.size(&data_layout)
.bytes()
}
})
.sum();
let size: u64 = variant.fields
.iter()
.map(|f| {
let ty = cx.tcx.item_type(f.did);
if ty.needs_subst() {
0 // we can't reason about generics, so we treat them as zero sized
} else {
type_size(cx, ty).expect("size should be computable for concrete type")
}
})
.sum();
let grouped = (size, (i, variant));
let grouped = (size, (i, variant));
update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0);
update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0);
});
update_if(&mut smallest_variant, grouped, |a, b| b.0 <= a.0);
update_if(&mut largest_variant, grouped, |a, b| b.0 >= a.0);
}
if let (Some(smallest), Some(largest)) = (smallest_variant, largest_variant) {

View File

@ -186,7 +186,8 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
fn is_is_empty(cx: &LateContext, item: &ty::AssociatedItem) -> bool {
if let ty::AssociatedKind::Method = item.kind {
if &*item.name.as_str() == "is_empty" {
let ty = cx.tcx.item_type(item.def_id).fn_sig().skip_binder();
let sig = cx.tcx.item_type(item.def_id).fn_sig();
let ty = sig.skip_binder();
ty.inputs().len() == 1
} else {
false
@ -198,7 +199,7 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
/// Check the inherent impl's items for an `is_empty(self)` method.
fn has_is_empty_impl(cx: &LateContext, id: DefId) -> bool {
cx.tcx.inherent_impls.borrow().get(&id).map_or(false, |impls| {
cx.tcx.maps.inherent_impls.borrow().get(&id).map_or(false, |impls| {
impls.iter().any(|imp| cx.tcx.associated_items(*imp).any(|item| is_is_empty(cx, &item)))
})
}

View File

@ -9,7 +9,6 @@
#![feature(slice_patterns)]
#![feature(stmt_expr_attributes)]
#![feature(conservative_impl_trait)]
#![feature(collections_bound)]
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
#![allow(needless_lifetimes)]
@ -447,6 +446,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
misc::REDUNDANT_PATTERN,
misc::SHORT_CIRCUIT_STATEMENT,
misc::TOPLEVEL_REF_ARG,
misc::ZERO_PTR,
misc_early::BUILTIN_TYPE_SHADOW,
misc_early::DOUBLE_NEG,
misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
@ -454,7 +454,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
misc_early::REDUNDANT_CLOSURE_CALL,
misc_early::UNNEEDED_FIELD_PATTERN,
misc_early::ZERO_PREFIXED_LITERAL,
misc_early::ZERO_PTR,
mut_reference::UNNECESSARY_MUT_PASSED,
mutex_atomic::MUTEX_ATOMIC,
needless_bool::BOOL_COMPARISON,

View File

@ -257,7 +257,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
}
},
Def::Trait(def_id) => {
let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
let trait_def = self.cx.tcx.maps.trait_def.borrow()[&def_id];
for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions {
self.record(&None);
}

View File

@ -8,7 +8,6 @@ use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::middle::region::CodeExtent;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use std::collections::HashMap;
use syntax::ast;
@ -596,8 +595,8 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(arg) {
// ...and both sides are compile-time constant integers...
let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
if let Ok(start_idx) = constcx.eval(start, ExprTypeChecked) {
if let Ok(end_idx) = constcx.eval(end, ExprTypeChecked) {
if let Ok(start_idx) = constcx.eval(start) {
if let Ok(end_idx) = constcx.eval(end) {
// ...and the start index is greater than the end index,
// this loop will never run. This is often confusing for developers
// who think that this will iterate from the larger value to the

View File

@ -2,7 +2,6 @@ use rustc::hir::*;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
use std::cmp::Ordering;
@ -11,7 +10,7 @@ use syntax::ast::LitKind;
use syntax::codemap::Span;
use utils::paths;
use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, in_external_macro, expr_block, walk_ptrs_ty,
is_expn_of};
is_expn_of, remove_blocks};
use utils::sugg::Sugg;
/// **What it does:** Checks for matches with a single arm where an `if let`
@ -31,7 +30,7 @@ use utils::sugg::Sugg;
declare_lint! {
pub SINGLE_MATCH,
Warn,
"a match statement with a single nontrivial arm (i.e, where the other arm \
"a match statement with a single nontrivial arm (i.e. where the other arm \
is `_ => {}`) instead of `if let`"
}
@ -180,11 +179,12 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
if arms.len() == 2 &&
arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
arms[1].pats.len() == 1 && arms[1].guard.is_none() {
let els = if is_unit_expr(&arms[1].body) {
let els = remove_blocks(&arms[1].body);
let els = if is_unit_expr(els) {
None
} else if let ExprBlock(_) = arms[1].body.node {
} else if let ExprBlock(_) = els.node {
// matches with blocks that contain statements are prettier as `if let + else`
Some(&*arms[1].body)
Some(els)
} else {
// allow match arms with just expressions
return;
@ -199,29 +199,33 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
fn check_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
if arms[1].pats[0].node == PatKind::Wild {
let lint = if els.is_some() {
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. \
Consider using `if let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
report_single_match_single_pattern(cx, ex, arms, expr, els);
}
}
fn report_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, els: Option<&Expr>) {
let lint = if els.is_some() {
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
}
fn check_single_match_opt_like(
cx: &LateContext,
ex: &Expr,
@ -254,26 +258,7 @@ fn check_single_match_opt_like(
for &(ty_path, pat_path) in candidates {
if &path == pat_path && match_type(cx, ty, ty_path) {
let lint = if els.is_some() {
SINGLE_MATCH_ELSE
} else {
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_then(cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider \
using `if let`",
|db| {
db.span_suggestion(expr.span,
"try this",
format!("if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
});
report_single_match_single_pattern(cx, ex, arms, expr, els);
}
}
}
@ -415,7 +400,7 @@ fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: Match
}
/// Get all arms that are unbounded `PatRange`s.
fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &[Arm]) -> Vec<SpannedRange<ConstVal<'tcx>>> {
let constcx = ConstContext::with_tables(cx.tcx, cx.tables);
arms.iter()
.flat_map(|arm| {
@ -427,8 +412,8 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
.filter_map(|pat| {
if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs, ExprTypeChecked),
let Ok(rhs) = constcx.eval(rhs, ExprTypeChecked)
let Ok(lhs) = constcx.eval(lhs),
let Ok(rhs) = constcx.eval(rhs)
], {
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
@ -439,7 +424,7 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
if_let_chain! {[
let PatKind::Lit(ref value) = pat.node,
let Ok(value) = constcx.eval(value, ExprTypeChecked)
let Ok(value) = constcx.eval(value)
], {
return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
}}

View File

@ -36,7 +36,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
let forgot_ty = cx.tables.expr_ty(&args[0]);
if match forgot_ty.ty_adt_def() {
Some(def) => def.has_dtor(),
Some(def) => def.has_dtor(cx.tcx),
_ => false,
} {
span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type");

View File

@ -3,7 +3,6 @@ use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc::hir::def::Def;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use std::borrow::Cow;
use std::fmt;
@ -1240,7 +1239,7 @@ fn lint_chars_next(cx: &LateContext, expr: &hir::Expr, chain: &hir::Expr, other:
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr) {
if let Ok(ConstVal::Str(r)) = ConstContext::with_tables(cx.tcx, cx.tables).eval(arg, ExprTypeChecked) {
if let Ok(ConstVal::Str(r)) = ConstContext::with_tables(cx.tcx, cx.tables).eval(arg) {
if r.len() == 1 {
let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r));
span_lint_and_then(cx,

View File

@ -65,9 +65,9 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) {
fetch_const(args, MinMax::Min)
fetch_const(cx, args, MinMax::Min)
} else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) {
fetch_const(args, MinMax::Max)
fetch_const(cx, args, MinMax::Max)
} else {
None
}
@ -79,18 +79,18 @@ fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'
}
}
fn fetch_const(args: &[Expr], m: MinMax) -> Option<(MinMax, Constant, &Expr)> {
fn fetch_const<'a>(cx: &LateContext, args: &'a [Expr], m: MinMax) -> Option<(MinMax, Constant, &'a Expr)> {
if args.len() != 2 {
return None;
}
if let Some(c) = constant_simple(&args[0]) {
if constant_simple(&args[1]).is_none() {
if let Some(c) = constant_simple(cx, &args[0]) {
if constant_simple(cx, &args[1]).is_none() {
// otherwise ignore
Some((m, c, &args[1]))
} else {
None
}
} else if let Some(c) = constant_simple(&args[1]) {
} else if let Some(c) = constant_simple(cx, &args[1]) {
Some((m, c, &args[0]))
} else {
None

View File

@ -4,13 +4,13 @@ use rustc::hir::intravisit::FnKind;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstFloat;
use syntax::codemap::{Span, Spanned, ExpnFormat};
use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path, snippet,
span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats};
span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats, in_constant};
use utils::sugg::Sugg;
use syntax::ast::LitKind;
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
///
@ -173,6 +173,24 @@ declare_lint! {
"using a short circuit boolean condition as a statement"
}
/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 0 as *const u32
/// ```
declare_lint! {
pub ZERO_PTR,
Warn,
"using 0 as *{const, mut} T"
}
#[derive(Copy, Clone)]
pub struct Pass;
@ -185,7 +203,8 @@ impl LintPass for Pass {
MODULO_ONE,
REDUNDANT_PATTERN,
USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT)
SHORT_CIRCUIT_STATEMENT,
ZERO_PTR)
}
}
@ -264,41 +283,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
let op = cmp.node;
if op.is_comparison() {
if let ExprPath(QPath::Resolved(_, ref path)) = left.node {
check_nan(cx, path, expr.span);
match expr.node {
ExprCast(ref e, ref ty) => {
check_cast(cx, expr.span, e, ty);
return;
},
ExprBinary(ref cmp, ref left, ref right) => {
let op = cmp.node;
if op.is_comparison() {
if let ExprPath(QPath::Resolved(_, ref path)) = left.node {
check_nan(cx, path, expr);
}
if let ExprPath(QPath::Resolved(_, ref path)) = right.node {
check_nan(cx, path, expr);
}
check_to_owned(cx, left, right, true, cmp.span);
check_to_owned(cx, right, left, false, cmp.span)
}
if let ExprPath(QPath::Resolved(_, ref path)) = right.node {
check_nan(cx, path, expr.span);
}
check_to_owned(cx, left, right, true, cmp.span);
check_to_owned(cx, right, left, false, cmp.span)
}
if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
if is_allowed(cx, left) || is_allowed(cx, right) {
return;
}
if let Some(name) = get_item_name(cx, expr) {
let name = &*name.as_str();
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
name.ends_with("_eq") {
if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
if is_allowed(cx, left) || is_allowed(cx, right) {
return;
}
}
span_lint_and_then(cx, FLOAT_CMP, expr.span, "strict comparison of f32 or f64", |db| {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
if let Some(name) = get_item_name(cx, expr) {
let name = &*name.as_str();
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
name.ends_with("_eq") {
return;
}
}
span_lint_and_then(cx, FLOAT_CMP, expr.span, "strict comparison of f32 or f64", |db| {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
db.span_suggestion(expr.span,
"consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs));
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
});
} else if op == BiRem && is_integer_literal(right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
}
db.span_suggestion(expr.span,
"consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs));
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
});
} else if op == BiRem && is_integer_literal(right, 1) {
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
}
},
_ => {},
}
if in_attributes_expansion(cx, expr) {
// Don't lint things expanded by #[derive(...)], etc
@ -350,37 +376,43 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
}
fn check_nan(cx: &LateContext, path: &Path, span: Span) {
path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" {
span_lint(cx,
CMP_NAN,
span,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
});
fn check_nan(cx: &LateContext, path: &Path, expr: &Expr) {
if !in_constant(cx, expr.id) {
path.segments.last().map(|seg| if &*seg.name.as_str() == "NAN" {
span_lint(cx,
CMP_NAN,
expr.span,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
});
}
}
fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
let res = ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked);
let res = ConstContext::with_tables(cx.tcx, cx.tables).eval(expr);
if let Ok(ConstVal::Float(val)) = res {
use std::cmp::Ordering;
match val {
val @ ConstFloat::F32(_) => {
let zero = ConstFloat::F32(0.0);
let zero = ConstFloat::FInfer {
f32: 0.0,
f64: 0.0,
};
let infinity = ConstFloat::F32(::std::f32::INFINITY);
let infinity = ConstFloat::FInfer {
f32: ::std::f32::INFINITY,
f64: ::std::f64::INFINITY,
};
let neg_infinity = ConstFloat::F32(::std::f32::NEG_INFINITY);
let neg_infinity = ConstFloat::FInfer {
f32: ::std::f32::NEG_INFINITY,
f64: ::std::f64::NEG_INFINITY,
};
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
val @ ConstFloat::F64(_) => {
let zero = ConstFloat::F64(0.0);
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
let infinity = ConstFloat::F64(::std::f64::INFINITY);
let neg_infinity = ConstFloat::F64(::std::f64::NEG_INFINITY);
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
}
} else {
false
}
@ -486,3 +518,19 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
_ => false,
}
}
fn check_cast(cx: &LateContext, span: Span, e: &Expr, ty: &Ty) {
if_let_chain! {[
let TyPtr(MutTy { mutbl, .. }) = ty.node,
let ExprLit(ref lit) = e.node,
let LitKind::Int(value, ..) = lit.node,
value == 0,
!in_constant(cx, e.id)
], {
let msg = match mutbl {
Mutability::MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::MutImmutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}}
}

View File

@ -162,24 +162,6 @@ declare_lint! {
"shadowing a builtin type"
}
/// **What it does:** Catch casts from `0` to some pointer type
///
/// **Why is this bad?** This generally means `null` and is better expressed as
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
///
/// **Known problems:** None.
///
/// **Example:**
///
/// ```rust
/// 0 as *const u32
/// ```
declare_lint! {
pub ZERO_PTR,
Warn,
"using 0 as *{const, mut} T"
}
#[derive(Copy, Clone)]
pub struct MiscEarly;
@ -192,8 +174,7 @@ impl LintPass for MiscEarly {
MIXED_CASE_HEX_LITERALS,
UNSEPARATED_LITERAL_SUFFIX,
ZERO_PREFIXED_LITERAL,
BUILTIN_TYPE_SHADOW,
ZERO_PTR)
BUILTIN_TYPE_SHADOW)
}
}
@ -381,9 +362,6 @@ impl EarlyLintPass for MiscEarly {
}
}}
},
ExprKind::Cast(ref e, ref ty) => {
check_cast(cx, expr.span, e, ty);
},
_ => (),
}
}
@ -412,18 +390,3 @@ impl EarlyLintPass for MiscEarly {
}
}
}
fn check_cast(cx: &EarlyContext, span: Span, e: &Expr, ty: &Ty) {
if_let_chain! {[
let TyKind::Ptr(MutTy { mutbl, .. }) = ty.node,
let ExprKind::Lit(ref lit) = e.node,
let LitKind::Int(value, ..) = lit.node,
value == 0
], {
let msg = match mutbl {
Mutability::Mutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::Immutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}}
}

View File

@ -77,7 +77,7 @@ impl MissingDoc {
return;
}
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc");
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name().map_or(false, |n| n == "doc"));
if !has_doc {
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS,
sp,

View File

@ -62,7 +62,7 @@ fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS,
match type_definition.sty {
TypeVariants::TyFnDef(_, _, fn_type) |
TypeVariants::TyFnPtr(fn_type) => {
let parameters = fn_type.sig.skip_binder().inputs();
let parameters = fn_type.skip_binder().inputs();
for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
match parameter.sty {
TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |

View File

@ -41,6 +41,10 @@ impl LintPass for NeedlessPassByValue {
}
}
macro_rules! need {
($e: expr) => { if let Some(x) = $e { x } else { return; } };
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
fn check_fn(
&mut self,
@ -55,14 +59,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
return;
}
if !matches!(kind, FnKind::ItemFn(..)) {
return;
match kind {
FnKind::ItemFn(.., attrs) => {
for a in attrs {
if_let_chain!{[
a.meta_item_list().is_some(),
let Some(name) = a.name(),
&*name.as_str() == "proc_macro_derive",
], {
return;
}}
}
},
_ => return,
}
// Allows these to be passed by value.
let fn_trait = cx.tcx.lang_items.fn_trait().expect("failed to find `Fn` trait");
let asref_trait = get_trait_def_id(cx, &paths::ASREF_TRAIT).expect("failed to find `AsRef` trait");
let borrow_trait = get_trait_def_id(cx, &paths::BORROW_TRAIT).expect("failed to find `Borrow` trait");
let fn_trait = need!(cx.tcx.lang_items.fn_trait());
let asref_trait = need!(get_trait_def_id(cx, &paths::ASREF_TRAIT));
let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
let preds: Vec<ty::Predicate> = {
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
@ -85,7 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
let fn_def_id = cx.tcx.hir.local_def_id(node_id);
let param_env = ty::ParameterEnvironment::for_item(cx.tcx, node_id);
let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, fn_sig);
let fn_sig = cx.tcx.liberate_late_bound_regions(param_env.free_id_outlive, &fn_sig);
for ((input, ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {

View File

@ -47,7 +47,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
if_let_chain!([
let ExprLit(ref l) = lit.node,
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node),
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit)),
let Some(val) = ci.to_u64(),
val == 1,
cx.tables.expr_ty(exp).is_integral()

View File

@ -113,7 +113,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
let fn_def_id = cx.tcx.hir.local_def_id(fn_id);
let fn_ty = cx.tcx.item_type(fn_def_id).fn_sig().skip_binder();
let sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_ty = sig.skip_binder();
for (arg, ty) in decl.inputs.iter().zip(fn_ty.inputs()) {
if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {

View File

@ -2,7 +2,6 @@ use regex_syntax;
use rustc::hir::*;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use std::collections::HashSet;
use std::error::Error;
@ -151,7 +150,7 @@ fn str_span(base: Span, s: &str, c: usize) -> Span {
}
fn const_str(cx: &LateContext, e: &Expr) -> Option<InternedString> {
match ConstContext::with_tables(cx.tcx, cx.tables).eval(e, ExprTypeChecked) {
match ConstContext::with_tables(cx.tcx, cx.tables).eval(e) {
Ok(ConstVal::Str(r)) => Some(r),
_ => None,
}

View File

@ -150,9 +150,5 @@ impl EarlyLintPass for ReturnPass {
}
fn attr_is_cfg(attr: &ast::Attribute) -> bool {
if let ast::MetaItemKind::List(_) = attr.value.node {
attr.name() == "cfg"
} else {
false
}
attr.meta_item_list().is_some() && attr.name().map_or(false, |n| n == "cfg")
}

View File

@ -5,9 +5,10 @@ use rustc::lint::*;
use rustc::ty;
use std::cmp::Ordering;
use syntax::ast::{IntTy, UintTy, FloatTy};
use syntax::attr::IntType;
use syntax::codemap::Span;
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint,
opt_def_id, last_path_segment};
opt_def_id, last_path_segment, type_size};
use utils::paths;
/// Handles all the linting of funky types
@ -310,7 +311,7 @@ pub struct CastPass;
declare_lint! {
pub CAST_PRECISION_LOSS,
Allow,
"casts that cause loss of precision, e.g `x as f32` where `x: u64`"
"casts that cause loss of precision, e.g. `x as f32` where `x: u64`"
}
/// **What it does:** Checks for casts from a signed to an unsigned numerical
@ -331,7 +332,7 @@ declare_lint! {
declare_lint! {
pub CAST_SIGN_LOSS,
Allow,
"casts from signed types to unsigned types, e.g `x as u32` where `x: i32`"
"casts from signed types to unsigned types, e.g. `x as u32` where `x: i32`"
}
/// **What it does:** Checks for on casts between numerical types that may
@ -351,7 +352,7 @@ declare_lint! {
declare_lint! {
pub CAST_POSSIBLE_TRUNCATION,
Allow,
"casts that may cause truncation of the value, e.g `x as u8` where `x: u32`, \
"casts that may cause truncation of the value, e.g. `x as u8` where `x: u32`, \
or `x as i32` where `x: f32`"
}
@ -375,7 +376,7 @@ declare_lint! {
declare_lint! {
pub CAST_POSSIBLE_WRAP,
Allow,
"casts that may cause wrapping around the value, e.g `x as i32` where `x: u32` \
"casts that may cause wrapping around the value, e.g. `x as i32` where `x: u32` \
and `x > i32::MAX`"
}
@ -392,7 +393,7 @@ declare_lint! {
declare_lint! {
pub UNNECESSARY_CAST,
Warn,
"cast to the same type, e.g `x as i32` where `x: i32`"
"cast to the same type, e.g. `x as i32` where `x: i32`"
}
/// Returns the size in bits of an integral type.
@ -907,7 +908,6 @@ fn detect_absurd_comparison<'a>(
fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeExpr<'a>> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::*;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::*;
use types::ExtremeType::*;
@ -918,7 +918,7 @@ fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeEx
_ => return None,
};
let cv = match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked) {
let cv = match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr) {
Ok(val) => val,
Err(_) => return None,
};
@ -1077,7 +1077,13 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
use std::*;
if let ExprCast(ref cast_exp, _) = expr.node {
match cx.tables.expr_ty(cast_exp).sty {
let pre_cast_ty = cx.tables.expr_ty(cast_exp);
let cast_ty = cx.tables.expr_ty(expr);
// if it's a cast from i32 to u32 wrapping will invalidate all these checks
if type_size(cx, pre_cast_ty) == type_size(cx, cast_ty) {
return None;
}
match pre_cast_ty.sty {
TyInt(int_ty) => {
Some(match int_ty {
IntTy::I8 => (FullInt::S(i8::min_value() as i128), FullInt::S(i8::max_value() as i128)),
@ -1107,18 +1113,15 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use rustc_const_math::ConstInt;
match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr, ExprTypeChecked) {
match ConstContext::with_tables(cx.tcx, cx.tables).eval(expr) {
Ok(val) => {
if let Integral(const_int) = val {
Some(match const_int.erase_type() {
ConstInt::InferSigned(x) => FullInt::S(x as i128),
ConstInt::Infer(x) => FullInt::U(x as u128),
_ => unreachable!(),
})
match const_int.int_type() {
IntType::SignedInt(_) => #[allow(cast_possible_wrap)] Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
}
} else {
None
}

View File

@ -166,6 +166,7 @@ define_Conf! {
("doc-valid-idents", doc_valid_idents, [
"MiB", "GiB", "TiB", "PiB", "EiB",
"DirectX",
"ECMAScript",
"GPLv2", "GPLv3",
"GitHub",
"IPv4", "IPv6",

View File

@ -9,6 +9,8 @@ use rustc::traits::Reveal;
use rustc::traits;
use rustc::ty::subst::Subst;
use rustc::ty;
use rustc::ty::layout::TargetDataLayout;
use rustc::mir::transform::MirSource;
use rustc_errors;
use std::borrow::Cow;
use std::env;
@ -97,6 +99,17 @@ pub mod higher;
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
rhs.expn_id != lhs.expn_id
}
pub fn in_constant(cx: &LateContext, id: NodeId) -> bool {
let parent_id = cx.tcx.hir.get_parent(id);
match MirSource::from_node(cx.tcx, parent_id) {
MirSource::Fn(_) => false,
MirSource::Const(_) |
MirSource::Static(..) |
MirSource::Promoted(..) => true,
}
}
/// Returns true if this `expn_info` was expanded by any macro.
pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
cx.sess().codemap().with_expn_info(span.expn_id, |info| {
@ -384,7 +397,7 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
/// snippet(cx, expr.span, "..")
/// ```
pub fn snippet<'a, 'b, T: LintContext<'b>>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or_else(|_| Cow::Borrowed(default))
snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
}
/// Convert a span to a code snippet. Returns `None` if not available.
@ -665,17 +678,13 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
if attr.is_sugared_doc {
continue;
}
if let ast::MetaItemKind::NameValue(ref value) = attr.value.node {
if attr.name() == name {
if let LitKind::Str(ref s, _) = value.node {
if let Ok(value) = FromStr::from_str(&*s.as_str()) {
attr::mark_used(attr);
f(value)
} else {
sess.span_err(value.span, "not a number");
}
if let Some(ref value) = attr.value_str() {
if attr.name().map_or(false, |n| n == name) {
if let Ok(value) = FromStr::from_str(&*value.as_str()) {
attr::mark_used(attr);
f(value)
} else {
unreachable!()
sess.span_err(attr.span, "not a number");
}
}
}
@ -781,7 +790,7 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> ty::T
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_item);
let fn_def_id = cx.tcx.hir.local_def_id(fn_item);
let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, fn_sig);
let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, &fn_sig);
fn_sig.output()
}
@ -806,7 +815,7 @@ pub fn same_tys<'a, 'tcx>(
pub fn type_is_unsafe_function(ty: ty::Ty) -> bool {
match ty.sty {
ty::TyFnDef(_, _, f) |
ty::TyFnPtr(f) => f.unsafety == Unsafety::Unsafe,
ty::TyFnPtr(f) => f.unsafety() == Unsafety::Unsafe,
_ => false,
}
}
@ -972,3 +981,9 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
None
}
pub fn type_size<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>) -> Option<u64> {
cx.tcx
.infer_ctxt((), Reveal::All)
.enter(|infcx| ty.layout(&infcx).ok().map(|lay| lay.size(&TargetDataLayout::parse(cx.sess())).bytes()))
}

View File

@ -99,6 +99,7 @@ impl<'a> Sugg<'a> {
ast::ExprKind::Block(..) |
ast::ExprKind::Break(..) |
ast::ExprKind::Call(..) |
ast::ExprKind::Catch(..) |
ast::ExprKind::Continue(..) |
ast::ExprKind::Field(..) |
ast::ExprKind::ForLoop(..) |

View File

@ -1,7 +1,6 @@
use rustc::hir::*;
use rustc::lint::*;
use rustc::ty;
use rustc_const_eval::EvalHint::ExprTypeChecked;
use rustc_const_eval::ConstContext;
use syntax::codemap::Span;
use utils::{higher, is_copy, snippet, span_lint_and_then};
@ -60,7 +59,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
let snippet = match *vec_args {
higher::VecArgs::Repeat(elem, len) => {
if ConstContext::with_tables(cx.tcx, cx.tables).eval(len, ExprTypeChecked).is_ok() {
if ConstContext::with_tables(cx.tcx, cx.tables).eval(len).is_ok() {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
} else {
return;

View File

@ -36,8 +36,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// TODO - constant_simple does not fold many operations involving floats.
// That's probably fine for this lint - it's pretty unlikely that someone would
// do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(left),
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(right),
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left),
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right),
let Ok(0.0) = lhs_value.parse(),
let Ok(0.0) = rhs_value.parse()
], {

View File

@ -89,7 +89,6 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
lint_groups,
llvm_passes,
attributes,
mir_passes,
.. } = registry;
let sess = &state.session;
let mut ls = sess.lint_store.borrow_mut();
@ -105,7 +104,6 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
}
sess.plugin_llvm_passes.borrow_mut().extend(llvm_passes);
sess.mir_passes.borrow_mut().extend(mir_passes);
sess.plugin_attributes.borrow_mut().extend(attributes);
}
old(state);
@ -244,30 +242,32 @@ pub fn main() {
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
};
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
// without having to pass --sysroot or anything
let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
env::args().collect()
} else {
env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
};
rustc_driver::in_rustc_thread(|| {
// this conditional check for the --sysroot flag is there so users can call `cargo-clippy` directly
// without having to pass --sysroot or anything
let mut args: Vec<String> = if env::args().any(|s| s == "--sysroot") {
env::args().collect()
} else {
env::args().chain(Some("--sysroot".to_owned())).chain(Some(sys_root)).collect()
};
// this check ensures that dependencies are built but not linted and the final crate is
// linted but not built
let clippy_enabled = env::args().any(|s| s == "-Zno-trans");
// this check ensures that dependencies are built but not linted and the final crate is
// linted but not built
let clippy_enabled = env::args().any(|s| s == "-Zno-trans");
if clippy_enabled {
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
}
if clippy_enabled {
args.extend_from_slice(&["--cfg".to_owned(), r#"feature="cargo-clippy""#.to_owned()]);
}
let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);
if let Err(err_count) = result {
if err_count > 0 {
std::process::exit(1);
}
}
let mut ccc = ClippyCompilerCalls::new(clippy_enabled);
let (result, _) = rustc_driver::run_compiler(&args, &mut ccc, None, None);
if let Err(err_count) = result {
if err_count > 0 {
std::process::exit(1);
}
}
})
.expect("rustc_thread failed");
}
}

View File

@ -1,99 +0,0 @@
#![feature(rustc_private)]
extern crate clippy_lints;
extern crate rustc;
extern crate rustc_const_eval;
extern crate rustc_const_math;
extern crate syntax;
use clippy_lints::consts::{constant_simple, Constant, FloatWidth};
use rustc_const_math::ConstInt;
use rustc::hir::*;
use syntax::ast::{LitIntType, LitKind, NodeId, StrStyle};
use syntax::codemap::{Spanned, COMMAND_LINE_SP};
use syntax::symbol::Symbol;
use syntax::ptr::P;
use syntax::util::ThinVec;
fn spanned<T>(t: T) -> Spanned<T> {
Spanned {
node: t,
span: COMMAND_LINE_SP,
}
}
fn expr(n: Expr_) -> Expr {
Expr {
id: NodeId::new(1),
node: n,
span: COMMAND_LINE_SP,
attrs: ThinVec::new(),
}
}
fn lit(l: LitKind) -> Expr {
expr(ExprLit(P(spanned(l))))
}
fn binop(op: BinOp_, l: Expr, r: Expr) -> Expr {
expr(ExprBinary(spanned(op), P(l), P(r)))
}
fn check(expect: Constant, expr: &Expr) {
assert_eq!(Some(expect), constant_simple(expr))
}
const TRUE: Constant = Constant::Bool(true);
const FALSE: Constant = Constant::Bool(false);
const ZERO: Constant = Constant::Int(ConstInt::Infer(0));
const ONE: Constant = Constant::Int(ConstInt::Infer(1));
const TWO: Constant = Constant::Int(ConstInt::Infer(2));
#[test]
fn test_lit() {
check(TRUE, &lit(LitKind::Bool(true)));
check(FALSE, &lit(LitKind::Bool(false)));
check(ZERO, &lit(LitKind::Int(0, LitIntType::Unsuffixed)));
check(Constant::Str("cool!".into(), StrStyle::Cooked),
&lit(LitKind::Str(Symbol::intern("cool!"), StrStyle::Cooked)));
}
#[test]
fn test_ops() {
check(TRUE, &binop(BiOr, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
check(FALSE, &binop(BiAnd, lit(LitKind::Bool(false)), lit(LitKind::Bool(true))));
let litzero = lit(LitKind::Int(0, LitIntType::Unsuffixed));
let litone = lit(LitKind::Int(1, LitIntType::Unsuffixed));
check(TRUE, &binop(BiEq, litzero.clone(), litzero.clone()));
check(TRUE, &binop(BiGe, litzero.clone(), litzero.clone()));
check(TRUE, &binop(BiLe, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiNe, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiGt, litzero.clone(), litzero.clone()));
check(FALSE, &binop(BiLt, litzero.clone(), litzero.clone()));
check(ZERO, &binop(BiAdd, litzero.clone(), litzero.clone()));
check(TWO, &binop(BiAdd, litone.clone(), litone.clone()));
check(ONE, &binop(BiSub, litone.clone(), litzero.clone()));
check(ONE, &binop(BiMul, litone.clone(), litone.clone()));
check(ONE, &binop(BiDiv, litone.clone(), litone.clone()));
let half_any = Constant::Float("0.5".into(), FloatWidth::Any);
let half32 = Constant::Float("0.5".into(), FloatWidth::F32);
let half64 = Constant::Float("0.5".into(), FloatWidth::F64);
let pos_zero = Constant::Float("0.0".into(), FloatWidth::F64);
let neg_zero = Constant::Float("-0.0".into(), FloatWidth::F64);
assert_eq!(pos_zero, pos_zero);
assert_eq!(neg_zero, neg_zero);
assert_eq!(None, pos_zero.partial_cmp(&neg_zero));
assert_eq!(half_any, half32);
assert_eq!(half_any, half64);
// for transitivity
assert_eq!(half32, half64);
assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::U8(0)));
assert_eq!(Constant::Int(ConstInt::Infer(0)), Constant::Int(ConstInt::I8(0)));
assert_eq!(Constant::Int(ConstInt::InferSigned(-1)), Constant::Int(ConstInt::I8(-1)));
}

View File

@ -1,5 +1,4 @@
#![feature(rustc_private)]
#![feature(collections_bound)]
extern crate clippy_lints;
extern crate syntax;

View File

@ -0,0 +1,13 @@
#![feature(plugin)]
#![plugin(clippy)]
#![allow(clippy)]
fn main() {
match 1 {
1 => {}
2 => {
[0; 1];
}
_ => {}
}
}

View File

@ -1,12 +1,20 @@
#![feature(plugin)]
#![plugin(clippy)]
#![deny(mut_mut, zero_ptr, cmp_nan)]
#![allow(dead_code)]
#[macro_use]
extern crate lazy_static;
use std::collections::HashMap;
#[deny(mut_mut)]
// ensure that we don't suggest `is_nan` and `is_null` inside constants
// FIXME: once const fn is stable, suggest these functions again in constants
const BAA: *const i32 = 0 as *const i32;
static mut BAR: *const i32 = BAA;
static mut FOO: *const i32 = 0 as *const i32;
static mut BUH: bool = 42.0 < std::f32::NAN;
#[allow(unused_variables, unused_mut)]
fn main() {
lazy_static! {
@ -19,4 +27,6 @@ fn main() {
static ref MUT_COUNT : usize = MUT_MAP.len();
}
assert!(*MUT_COUNT == 1);
// FIXME: don't lint in array length, requires `check_body`
//let _ = [""; (42.0 < std::f32::NAN) as usize];
}

View File

@ -0,0 +1,11 @@
#![feature(plugin)]
#![plugin(clippy)]
#![warn(single_match_else)]
fn main() {
let n = match (42, 43) {
(42, n) => n,
_ => panic!("typeck error"),
};
assert_eq!(n, 43);
}

View File

@ -3,33 +3,79 @@
#![deny(invalid_upcast_comparisons)]
#![allow(unused, eq_op, no_effect, unnecessary_operation)]
fn mk_value<T>() -> T { unimplemented!() }
fn main() {
let zero: u32 = 0;
let u8_max: u8 = 255;
let u32: u32 = mk_value();
let u8: u8 = mk_value();
let i32: i32 = mk_value();
let i8: i8 = mk_value();
(u8_max as u32) > 300;
(u8_max as u32) > 20;
// always false, since no u8 can be > 300
(u8 as u32) > 300;
(u8 as i32) > 300;
(u8 as u32) == 300;
(u8 as i32) == 300;
300 < (u8 as u32);
300 < (u8 as i32);
300 == (u8 as u32);
300 == (u8 as i32);
// inverted of the above
(u8 as u32) <= 300;
(u8 as i32) <= 300;
(u8 as u32) != 300;
(u8 as i32) != 300;
300 >= (u8 as u32);
300 >= (u8 as i32);
300 != (u8 as u32);
300 != (u8 as i32);
(zero as i32) < -5;
(zero as i32) < 10;
// always false, since u8 -> i32 doesn't wrap
(u8 as i32) < 0;
-5 != (u8 as i32);
// inverted of the above
(u8 as i32) >= 0;
-5 == (u8 as i32);
-5 < (zero as i32);
0 <= (zero as i32);
0 < (zero as i32);
// always false, since no u8 can be 1337
1337 == (u8 as i32);
1337 == (u8 as u32);
// inverted of the above
1337 != (u8 as i32);
1337 != (u8 as u32);
-5 > (zero as i32);
-5 >= (u8_max as i32);
1337 == (u8_max as i32);
-5 == (zero as i32);
-5 != (u8_max as i32);
// Those are Ok:
42 == (u8_max as i32);
42 != (u8_max as i32);
42 > (u8_max as i32);
(u8_max as i32) == 42;
(u8_max as i32) != 42;
(u8_max as i32) > 42;
(u8_max as i32) < 42;
(u8 as u32) > 20;
42 == (u8 as i32);
42 != (u8 as i32);
42 > (u8 as i32);
(u8 as i32) == 42;
(u8 as i32) != 42;
(u8 as i32) > 42;
(u8 as i32) < 42;
(u8 as i8) == -1;
(u8 as i8) != -1;
(u8 as i32) > -1;
(u8 as i32) < -1;
(u32 as i32) < -5;
(u32 as i32) < 10;
(i8 as u8) == 1;
(i8 as u8) != 1;
(i8 as u8) < 1;
(i8 as u8) > 1;
(i32 as u32) < 5;
(i32 as u32) < 10;
-5 < (u32 as i32);
0 <= (u32 as i32);
0 < (u32 as i32);
-5 > (u32 as i32);
-5 >= (u8 as i32);
-5 == (u32 as i32);
}

View File

@ -1,8 +1,8 @@
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:10:5
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:16:5
|
10 | (u8_max as u32) > 300;
| ^^^^^^^^^^^^^^^^^^^^^
16 | (u8 as u32) > 300;
| ^^^^^^^^^^^^^^^^^
|
note: lint level defined here
--> $DIR/invalid_upcast_comparisons.rs:4:9
@ -10,53 +10,161 @@ note: lint level defined here
4 | #![deny(invalid_upcast_comparisons)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:13:5
|
13 | (zero as i32) < -5;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:16:5
|
16 | -5 < (zero as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always true
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:17:5
|
17 | 0 <= (zero as i32);
17 | (u8 as i32) > 300;
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:18:5
|
18 | (u8 as u32) == 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:19:5
|
19 | (u8 as i32) == 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:20:5
|
20 | -5 > (zero as i32);
| ^^^^^^^^^^^^^^^^^^
20 | 300 < (u8 as u32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:21:5
|
21 | -5 >= (u8_max as i32);
| ^^^^^^^^^^^^^^^^^^^^^
21 | 300 < (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always false
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:22:5
|
22 | 1337 == (u8_max as i32);
| ^^^^^^^^^^^^^^^^^^^^^^^
22 | 300 == (u8 as u32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `zero` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:24:5
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:23:5
|
24 | -5 == (zero as i32);
| ^^^^^^^^^^^^^^^^^^^
23 | 300 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8_max` prior to casting, this expression is always true
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:25:5
|
25 | -5 != (u8_max as i32);
| ^^^^^^^^^^^^^^^^^^^^^
25 | (u8 as u32) <= 300;
| ^^^^^^^^^^^^^^^^^^
error: aborting due to 9 previous errors
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:26:5
|
26 | (u8 as i32) <= 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:27:5
|
27 | (u8 as u32) != 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:28:5
|
28 | (u8 as i32) != 300;
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:29:5
|
29 | 300 >= (u8 as u32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:30:5
|
30 | 300 >= (u8 as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:31:5
|
31 | 300 != (u8 as u32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:32:5
|
32 | 300 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:35:5
|
35 | (u8 as i32) < 0;
| ^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:36:5
|
36 | -5 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:38:5
|
38 | (u8 as i32) >= 0;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:39:5
|
39 | -5 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:42:5
|
42 | 1337 == (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:43:5
|
43 | 1337 == (u8 as u32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:45:5
|
45 | 1337 != (u8 as i32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:46:5
|
46 | 1337 != (u8 as u32);
| ^^^^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always true
--> $DIR/invalid_upcast_comparisons.rs:61:5
|
61 | (u8 as i32) > -1;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:62:5
|
62 | (u8 as i32) < -1;
| ^^^^^^^^^^^^^^^^
error: because of the numeric bounds on `u8` prior to casting, this expression is always false
--> $DIR/invalid_upcast_comparisons.rs:78:5
|
78 | -5 >= (u8 as i32);
| ^^^^^^^^^^^^^^^^^
error: aborting due to 27 previous errors

View File

@ -17,3 +17,14 @@ fn main() {
fn foo() { println!("foo"); }
foo();
}
fn mac() {
let mut a = 5;
println!("{}", a);
// do not lint this, because it needs to be after `a`
macro_rules! b {
() => {{ a = 6 }}
}
b!();
println!("{}", a);
}

View File

@ -0,0 +1,11 @@
#![feature(plugin)]
#![plugin(clippy)]
#![crate_type = "proc-macro"]
#![deny(needless_pass_by_value)]
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(Foo)]
pub fn foo(_input: TokenStream) -> TokenStream { unimplemented!() }