mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 15:14:29 +00:00
Merge remote-tracking branch 'origin/master' into regex_macro
This commit is contained in:
commit
3b0b9e0e06
25 changed files with 611 additions and 127 deletions
|
@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
|
|||
[Jump to usage instructions](#usage)
|
||||
|
||||
##Lints
|
||||
There are 113 lints included in this crate:
|
||||
There are 117 lints included in this crate:
|
||||
|
||||
name | default | meaning
|
||||
---------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
@ -34,6 +34,7 @@ name
|
|||
[drop_ref](https://github.com/Manishearth/rust-clippy/wiki#drop_ref) | warn | call to `std::mem::drop` with a reference instead of an owned value, which will not call the `Drop::drop` method on the underlying value
|
||||
[duplicate_underscore_argument](https://github.com/Manishearth/rust-clippy/wiki#duplicate_underscore_argument) | warn | Function arguments having names which only differ by an underscore
|
||||
[empty_loop](https://github.com/Manishearth/rust-clippy/wiki#empty_loop) | warn | empty `loop {}` detected
|
||||
[enum_glob_use](https://github.com/Manishearth/rust-clippy/wiki#enum_glob_use) | allow | finds use items that import all variants of an enum
|
||||
[enum_variant_names](https://github.com/Manishearth/rust-clippy/wiki#enum_variant_names) | warn | finds enums where all variants share a prefix/postfix
|
||||
[eq_op](https://github.com/Manishearth/rust-clippy/wiki#eq_op) | warn | equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)
|
||||
[expl_impl_clone_on_copy](https://github.com/Manishearth/rust-clippy/wiki#expl_impl_clone_on_copy) | warn | implementing `Clone` explicitly on `Copy` types
|
||||
|
@ -46,6 +47,8 @@ name
|
|||
[for_loop_over_option](https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_option) | warn | for-looping over an `Option`, which is more clearly expressed as an `if let`
|
||||
[for_loop_over_result](https://github.com/Manishearth/rust-clippy/wiki#for_loop_over_result) | warn | for-looping over a `Result`, which is more clearly expressed as an `if let`
|
||||
[identity_op](https://github.com/Manishearth/rust-clippy/wiki#identity_op) | warn | using identity operations, e.g. `x + 0` or `y / 1`
|
||||
[if_same_then_else](https://github.com/Manishearth/rust-clippy/wiki#if_same_then_else) | warn | if with the same *then* and *else* blocks
|
||||
[ifs_same_cond](https://github.com/Manishearth/rust-clippy/wiki#ifs_same_cond) | warn | consecutive `ifs` with the same condition
|
||||
[ineffective_bit_mask](https://github.com/Manishearth/rust-clippy/wiki#ineffective_bit_mask) | warn | expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`
|
||||
[inline_always](https://github.com/Manishearth/rust-clippy/wiki#inline_always) | warn | `#[inline(always)]` is a bad idea in most cases
|
||||
[invalid_regex](https://github.com/Manishearth/rust-clippy/wiki#invalid_regex) | deny | finds invalid regular expressions in `Regex::new(_)` invocations
|
||||
|
@ -88,6 +91,7 @@ name
|
|||
[range_zip_with_len](https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len) | warn | zipping iterator with a range when enumerate() would do
|
||||
[redundant_closure](https://github.com/Manishearth/rust-clippy/wiki#redundant_closure) | warn | using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)
|
||||
[redundant_pattern](https://github.com/Manishearth/rust-clippy/wiki#redundant_pattern) | warn | using `name @ _` in a pattern
|
||||
[regex_macro](https://github.com/Manishearth/rust-clippy/wiki#regex_macro) | warn | finds use of `regex!(_)`, suggests `Regex::new(_)` instead
|
||||
[result_unwrap_used](https://github.com/Manishearth/rust-clippy/wiki#result_unwrap_used) | allow | using `Result.unwrap()`, which might be better handled
|
||||
[reverse_range_loop](https://github.com/Manishearth/rust-clippy/wiki#reverse_range_loop) | warn | Iterating over an empty range, such as `10..0` or `5..5`
|
||||
[search_is_some](https://github.com/Manishearth/rust-clippy/wiki#search_is_some) | warn | using an iterator search followed by `is_some()`, which is more succinctly expressed as a call to `any()`
|
||||
|
|
|
@ -2,9 +2,7 @@ use rustc::lint::*;
|
|||
use rustc_front::hir::*;
|
||||
use std::f64::consts as f64;
|
||||
use utils::span_lint;
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit;
|
||||
use syntax::ast::FloatTy::*;
|
||||
use syntax::ast::{Lit, Lit_, FloatTy};
|
||||
|
||||
/// **What it does:** This lint checks for floating point literals that approximate constants which are defined in [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants) or [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants), respectively, suggesting to use the predefined constant.
|
||||
///
|
||||
|
@ -57,9 +55,9 @@ impl LateLintPass for ApproxConstant {
|
|||
|
||||
fn check_lit(cx: &LateContext, lit: &Lit, e: &Expr) {
|
||||
match lit.node {
|
||||
LitFloat(ref s, TyF32) => check_known_consts(cx, e, s, "f32"),
|
||||
LitFloat(ref s, TyF64) => check_known_consts(cx, e, s, "f64"),
|
||||
LitFloatUnsuffixed(ref s) => check_known_consts(cx, e, s, "f{32, 64}"),
|
||||
Lit_::LitFloat(ref s, FloatTy::TyF32) => check_known_consts(cx, e, s, "f32"),
|
||||
Lit_::LitFloat(ref s, FloatTy::TyF64) => check_known_consts(cx, e, s, "f64"),
|
||||
Lit_::LitFloatUnsuffixed(ref s) => check_known_consts(cx, e, s, "f{32, 64}"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use rustc::middle::def::{Def, PathResolution};
|
|||
use rustc_front::hir::*;
|
||||
use rustc_front::util::is_comparison_binop;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit_;
|
||||
|
||||
use utils::span_lint;
|
||||
|
||||
|
@ -254,7 +254,7 @@ fn check_ineffective_gt(cx: &LateContext, span: Span, m: u64, c: u64, op: &str)
|
|||
fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u64> {
|
||||
match lit.node {
|
||||
ExprLit(ref lit_ptr) => {
|
||||
if let LitInt(value, _) = lit_ptr.node {
|
||||
if let Lit_::LitInt(value, _) = lit_ptr.node {
|
||||
Some(value) //TODO: Handle sign
|
||||
} else {
|
||||
None
|
||||
|
|
110
src/consts.rs
110
src/consts.rs
|
@ -12,14 +12,10 @@ use std::cmp::Ordering::{self, Greater, Less, Equal};
|
|||
use std::rc::Rc;
|
||||
use std::ops::Deref;
|
||||
use std::fmt;
|
||||
use self::FloatWidth::*;
|
||||
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit_;
|
||||
use syntax::ast::LitIntType::*;
|
||||
use syntax::ast::LitIntType;
|
||||
use syntax::ast::{UintTy, FloatTy, StrStyle};
|
||||
use syntax::ast::FloatTy::*;
|
||||
use syntax::ast::Sign::{self, Plus, Minus};
|
||||
|
||||
|
||||
|
@ -33,8 +29,8 @@ pub enum FloatWidth {
|
|||
impl From<FloatTy> for FloatWidth {
|
||||
fn from(ty: FloatTy) -> FloatWidth {
|
||||
match ty {
|
||||
TyF32 => Fw32,
|
||||
TyF64 => Fw64,
|
||||
FloatTy::TyF32 => FloatWidth::Fw32,
|
||||
FloatTy::TyF64 => FloatWidth::Fw64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +103,7 @@ impl PartialEq for Constant {
|
|||
lv == rv && (is_negative(lty) & (lv != 0)) == (is_negative(rty) & (rv != 0))
|
||||
}
|
||||
(&Constant::Float(ref ls, lw), &Constant::Float(ref rs, rw)) => {
|
||||
use self::FloatWidth::*;
|
||||
if match (lw, rw) {
|
||||
(FwAny, _) | (_, FwAny) | (Fw32, Fw32) | (Fw64, Fw64) => true,
|
||||
_ => false,
|
||||
|
@ -149,6 +146,7 @@ impl PartialOrd for Constant {
|
|||
})
|
||||
}
|
||||
(&Constant::Float(ref ls, lw), &Constant::Float(ref rs, rw)) => {
|
||||
use self::FloatWidth::*;
|
||||
if match (lw, rw) {
|
||||
(FwAny, _) | (_, FwAny) | (Fw32, Fw32) | (Fw64, Fw64) => true,
|
||||
_ => false,
|
||||
|
@ -261,76 +259,51 @@ impl fmt::Display for Constant {
|
|||
|
||||
fn lit_to_constant(lit: &Lit_) -> Constant {
|
||||
match *lit {
|
||||
LitStr(ref is, style) => Constant::Str(is.to_string(), style),
|
||||
LitByte(b) => Constant::Byte(b),
|
||||
LitByteStr(ref s) => Constant::Binary(s.clone()),
|
||||
LitChar(c) => Constant::Char(c),
|
||||
LitInt(value, ty) => Constant::Int(value, ty),
|
||||
LitFloat(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||
LitFloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FwAny),
|
||||
LitBool(b) => Constant::Bool(b),
|
||||
Lit_::LitStr(ref is, style) => Constant::Str(is.to_string(), style),
|
||||
Lit_::LitByte(b) => Constant::Byte(b),
|
||||
Lit_::LitByteStr(ref s) => Constant::Binary(s.clone()),
|
||||
Lit_::LitChar(c) => Constant::Char(c),
|
||||
Lit_::LitInt(value, ty) => Constant::Int(value, ty),
|
||||
Lit_::LitFloat(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||
Lit_::LitFloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::FwAny),
|
||||
Lit_::LitBool(b) => Constant::Bool(b),
|
||||
}
|
||||
}
|
||||
|
||||
fn constant_not(o: Constant) -> Option<Constant> {
|
||||
Some(match o {
|
||||
Constant::Bool(b) => Constant::Bool(!b),
|
||||
Constant::Int(value, ty) => {
|
||||
let (nvalue, nty) = match ty {
|
||||
SignedIntLit(ity, Plus) => {
|
||||
if value == ::std::u64::MAX {
|
||||
return None;
|
||||
}
|
||||
(value + 1, SignedIntLit(ity, Minus))
|
||||
}
|
||||
SignedIntLit(ity, Minus) => {
|
||||
if value == 0 {
|
||||
(1, SignedIntLit(ity, Minus))
|
||||
} else {
|
||||
(value - 1, SignedIntLit(ity, Plus))
|
||||
}
|
||||
}
|
||||
UnsignedIntLit(ity) => {
|
||||
let mask = match ity {
|
||||
UintTy::TyU8 => ::std::u8::MAX as u64,
|
||||
UintTy::TyU16 => ::std::u16::MAX as u64,
|
||||
UintTy::TyU32 => ::std::u32::MAX as u64,
|
||||
UintTy::TyU64 => ::std::u64::MAX,
|
||||
UintTy::TyUs => {
|
||||
return None;
|
||||
} // refuse to guess
|
||||
};
|
||||
(!value & mask, UnsignedIntLit(ity))
|
||||
}
|
||||
UnsuffixedIntLit(_) => {
|
||||
use syntax::ast::LitIntType::*;
|
||||
use self::Constant::*;
|
||||
match o {
|
||||
Bool(b) => Some(Bool(!b)),
|
||||
Int(::std::u64::MAX, SignedIntLit(_, Plus)) => None,
|
||||
Int(value, SignedIntLit(ity, Plus)) => Some(Int(value + 1, SignedIntLit(ity, Minus))),
|
||||
Int(0, SignedIntLit(ity, Minus)) => Some(Int(1, SignedIntLit(ity, Minus))),
|
||||
Int(value, SignedIntLit(ity, Minus)) => Some(Int(value - 1, SignedIntLit(ity, Plus))),
|
||||
Int(value, UnsignedIntLit(ity)) => {
|
||||
let mask = match ity {
|
||||
UintTy::TyU8 => ::std::u8::MAX as u64,
|
||||
UintTy::TyU16 => ::std::u16::MAX as u64,
|
||||
UintTy::TyU32 => ::std::u32::MAX as u64,
|
||||
UintTy::TyU64 => ::std::u64::MAX,
|
||||
UintTy::TyUs => {
|
||||
return None;
|
||||
} // refuse to guess
|
||||
};
|
||||
Constant::Int(nvalue, nty)
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
Some(Int(!value & mask, UnsignedIntLit(ity)))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn constant_negate(o: Constant) -> Option<Constant> {
|
||||
Some(match o {
|
||||
Constant::Int(value, ty) => {
|
||||
Constant::Int(value,
|
||||
match ty {
|
||||
SignedIntLit(ity, sign) => SignedIntLit(ity, neg_sign(sign)),
|
||||
UnsuffixedIntLit(sign) => UnsuffixedIntLit(neg_sign(sign)),
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
}
|
||||
Constant::Float(is, ty) => Constant::Float(neg_float_str(is), ty),
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
})
|
||||
use syntax::ast::LitIntType::*;
|
||||
use self::Constant::*;
|
||||
match o {
|
||||
Int(value, SignedIntLit(ity, sign)) => Some(Int(value, SignedIntLit(ity, neg_sign(sign)))),
|
||||
Int(value, UnsuffixedIntLit(sign)) => Some(Int(value, UnsuffixedIntLit(neg_sign(sign)))),
|
||||
Float(is, ty) => Some(Float(neg_float_str(is), ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn neg_sign(s: Sign) -> Sign {
|
||||
|
@ -357,12 +330,13 @@ fn neg_float_str(s: String) -> String {
|
|||
/// ```
|
||||
pub fn is_negative(ty: LitIntType) -> bool {
|
||||
match ty {
|
||||
SignedIntLit(_, sign) | UnsuffixedIntLit(sign) => sign == Minus,
|
||||
UnsignedIntLit(_) => false,
|
||||
LitIntType::SignedIntLit(_, sign) | LitIntType::UnsuffixedIntLit(sign) => sign == Minus,
|
||||
LitIntType::UnsignedIntLit(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_int_type(l: LitIntType, r: LitIntType, s: Sign) -> Option<LitIntType> {
|
||||
use syntax::ast::LitIntType::*;
|
||||
match (l, r) {
|
||||
(SignedIntLit(lty, _), SignedIntLit(rty, _)) => {
|
||||
if lty == rty {
|
||||
|
|
102
src/copies.rs
Normal file
102
src/copies.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use rustc::lint::*;
|
||||
use rustc_front::hir::*;
|
||||
use utils::{get_parent_expr, in_macro, is_block_equal, is_exp_equal, span_lint, span_note_and_lint};
|
||||
|
||||
/// **What it does:** This lint checks for consecutive `ifs` with the same condition. This lint is
|
||||
/// `Warn` by default.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:** `if a == b { .. } else if a == b { .. }`
|
||||
declare_lint! {
|
||||
pub IFS_SAME_COND,
|
||||
Warn,
|
||||
"consecutive `ifs` with the same condition"
|
||||
}
|
||||
|
||||
/// **What it does:** This lint checks for `if/else` with the same body as the *then* part and the
|
||||
/// *else* part. This lint is `Warn` by default.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:** `if .. { 42 } else { 42 }`
|
||||
declare_lint! {
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
Warn,
|
||||
"if with the same *then* and *else* blocks"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct CopyAndPaste;
|
||||
|
||||
impl LintPass for CopyAndPaste {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array![
|
||||
IFS_SAME_COND,
|
||||
IF_SAME_THEN_ELSE
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
if !in_macro(cx, expr.span) {
|
||||
lint_same_then_else(cx, expr);
|
||||
lint_same_cond(cx, expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `IF_SAME_THEN_ELSE`.
|
||||
fn lint_same_then_else(cx: &LateContext, expr: &Expr) {
|
||||
if let ExprIf(_, ref then_block, Some(ref else_expr)) = expr.node {
|
||||
if let ExprBlock(ref else_block) = else_expr.node {
|
||||
if is_block_equal(cx, &then_block, &else_block, false) {
|
||||
span_lint(cx, IF_SAME_THEN_ELSE, expr.span, "this if has the same then and else blocks");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation of `IFS_SAME_COND`.
|
||||
fn lint_same_cond(cx: &LateContext, expr: &Expr) {
|
||||
// skip ifs directly in else, it will be checked in the parent if
|
||||
if let Some(&Expr{node: ExprIf(_, _, Some(ref else_expr)), ..}) = get_parent_expr(cx, expr) {
|
||||
if else_expr.id == expr.id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let conds = condition_sequence(expr);
|
||||
|
||||
for (n, i) in conds.iter().enumerate() {
|
||||
for j in conds.iter().skip(n+1) {
|
||||
if is_exp_equal(cx, i, j, true) {
|
||||
span_note_and_lint(cx, IFS_SAME_COND, j.span, "this if has the same condition as a previous if", i.span, "same as this");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the list of condition expressions in a sequence of `if/else`.
|
||||
/// Eg. would return `[a, b]` for the expression `if a {..} else if b {..}`.
|
||||
fn condition_sequence(mut expr: &Expr) -> Vec<&Expr> {
|
||||
let mut result = vec![];
|
||||
|
||||
while let ExprIf(ref cond, _, ref else_expr) = expr.node {
|
||||
result.push(&**cond);
|
||||
|
||||
if let Some(ref else_expr) = *else_expr {
|
||||
expr = else_expr;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
|
@ -89,7 +89,7 @@ fn check_for_insert(cx: &LateContext, span: Span, map: &Expr, key: &Expr, expr:
|
|||
params.len() == 3,
|
||||
name.node.as_str() == "insert",
|
||||
get_item_name(cx, map) == get_item_name(cx, &*params[0]),
|
||||
is_exp_equal(cx, key, ¶ms[1])
|
||||
is_exp_equal(cx, key, ¶ms[1], false)
|
||||
], {
|
||||
let help = if sole_expr {
|
||||
format!("{}.entry({}).or_insert({})",
|
||||
|
|
63
src/enum_glob_use.rs
Normal file
63
src/enum_glob_use.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
//! lint on `use`ing all variants of an enum
|
||||
|
||||
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray, LintContext};
|
||||
use rustc_front::hir::*;
|
||||
use rustc::front::map::Node::NodeItem;
|
||||
use rustc::front::map::definitions::DefPathData;
|
||||
use rustc::middle::ty::TyEnum;
|
||||
use utils::span_lint;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::NodeId;
|
||||
|
||||
/// **What it does:** Warns when `use`ing all variants of an enum
|
||||
///
|
||||
/// **Why is this bad?** It is usually better style to use the prefixed name of an enum variant, rather than importing variants
|
||||
///
|
||||
/// **Known problems:** Old-style enums that prefix the variants are still around
|
||||
///
|
||||
/// **Example:** `use std::cmp::Ordering::*;`
|
||||
declare_lint! { pub ENUM_GLOB_USE, Allow,
|
||||
"finds use items that import all variants of an enum" }
|
||||
|
||||
pub struct EnumGlobUse;
|
||||
|
||||
impl LintPass for EnumGlobUse {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(ENUM_GLOB_USE)
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for EnumGlobUse {
|
||||
fn check_mod(&mut self, cx: &LateContext, m: &Mod, _: Span, _: NodeId) {
|
||||
// only check top level `use` statements
|
||||
for item in &m.item_ids {
|
||||
self.lint_item(cx, cx.krate.item(item.id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EnumGlobUse {
|
||||
fn lint_item(&self, cx: &LateContext, item: &Item) {
|
||||
if item.vis == Visibility::Public {
|
||||
return; // re-exports are fine
|
||||
}
|
||||
if let ItemUse(ref item_use) = item.node {
|
||||
if let ViewPath_::ViewPathGlob(_) = item_use.node {
|
||||
let def = cx.tcx.def_map.borrow()[&item.id];
|
||||
if let Some(NodeItem(it)) = cx.tcx.map.get_if_local(def.def_id()) {
|
||||
if let ItemEnum(..) = it.node {
|
||||
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
|
||||
}
|
||||
} else {
|
||||
if let Some(dp) = cx.sess().cstore.def_path(def.def_id()).last() {
|
||||
if let DefPathData::Type(_) = dp.data {
|
||||
if let TyEnum(..) = cx.sess().cstore.item_type(&cx.tcx, def.def_id()).ty.sty {
|
||||
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
src/eq_op.rs
14
src/eq_op.rs
|
@ -4,9 +4,11 @@ use rustc_front::util as ast_util;
|
|||
|
||||
use utils::{is_exp_equal, span_lint};
|
||||
|
||||
/// **What it does:** This lint checks for equal operands to comparisons and bitwise binary operators (`&`, `|` and `^`).
|
||||
/// **What it does:** This lint checks for equal operands to comparison, logical and bitwise,
|
||||
/// difference and division binary operators (`==`, `>`, etc., `&&`, `||`, `&`, `|`, `^`, `-` and
|
||||
/// `/`).
|
||||
///
|
||||
/// **Why is this bad?** This is usually just a typo.
|
||||
/// **Why is this bad?** This is usually just a typo or a copy and paste error.
|
||||
///
|
||||
/// **Known problems:** False negatives: We had some false positives regarding calls (notably [racer](https://github.com/phildawes/racer) had one instance of `x.pop() && x.pop()`), so we removed matching any function or method calls. We may introduce a whitelist of known pure functions in the future.
|
||||
///
|
||||
|
@ -29,19 +31,21 @@ impl LintPass for EqOp {
|
|||
impl LateLintPass for EqOp {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
if let ExprBinary(ref op, ref left, ref right) = e.node {
|
||||
if is_cmp_or_bit(op) && is_exp_equal(cx, left, right) {
|
||||
if is_valid_operator(op) && is_exp_equal(cx, left, right, true) {
|
||||
span_lint(cx,
|
||||
EQ_OP,
|
||||
e.span,
|
||||
&format!("equal expressions as operands to {}", ast_util::binop_to_string(op.node)));
|
||||
&format!("equal expressions as operands to `{}`", ast_util::binop_to_string(op.node)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_cmp_or_bit(op: &BinOp) -> bool {
|
||||
fn is_valid_operator(op: &BinOp) -> bool {
|
||||
match op.node {
|
||||
BiSub |
|
||||
BiDiv |
|
||||
BiEq |
|
||||
BiLt |
|
||||
BiLe |
|
||||
|
|
|
@ -6,8 +6,7 @@ use syntax::codemap::{Span, Spanned};
|
|||
use rustc::middle::def_id::DefId;
|
||||
use rustc::middle::ty::{self, MethodTraitItemId, ImplOrTraitItemId};
|
||||
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit;
|
||||
use syntax::ast::{Lit, Lit_};
|
||||
|
||||
use utils::{get_item_name, snippet, span_lint, walk_ptrs_ty};
|
||||
|
||||
|
@ -152,7 +151,7 @@ fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str)
|
|||
}
|
||||
|
||||
fn check_len_zero(cx: &LateContext, span: Span, name: &Name, args: &[P<Expr>], lit: &Lit, op: &str) {
|
||||
if let Spanned{node: LitInt(0, _), ..} = *lit {
|
||||
if let Spanned{node: Lit_::LitInt(0, _), ..} = *lit {
|
||||
if name.as_str() == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
|
||||
span_lint(cx,
|
||||
LEN_ZERO,
|
||||
|
|
|
@ -37,9 +37,11 @@ use rustc_plugin::Registry;
|
|||
|
||||
#[macro_use]
|
||||
pub mod utils;
|
||||
pub mod copies;
|
||||
pub mod consts;
|
||||
pub mod types;
|
||||
pub mod misc;
|
||||
pub mod enum_glob_use;
|
||||
pub mod eq_op;
|
||||
pub mod bit_mask;
|
||||
pub mod ptr_arg;
|
||||
|
@ -99,6 +101,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
|
|||
reg.register_late_lint_pass(box misc::CmpNan);
|
||||
reg.register_late_lint_pass(box eq_op::EqOp);
|
||||
reg.register_early_lint_pass(box enum_variants::EnumVariantNames);
|
||||
reg.register_late_lint_pass(box enum_glob_use::EnumGlobUse);
|
||||
reg.register_late_lint_pass(box bit_mask::BitMask);
|
||||
reg.register_late_lint_pass(box ptr_arg::PtrArg);
|
||||
reg.register_late_lint_pass(box needless_bool::NeedlessBool);
|
||||
|
@ -155,8 +158,10 @@ pub fn plugin_registrar(reg: &mut Registry) {
|
|||
reg.register_late_lint_pass(box drop_ref::DropRefPass);
|
||||
reg.register_late_lint_pass(box types::AbsurdUnsignedComparisons);
|
||||
reg.register_late_lint_pass(box regex::RegexPass);
|
||||
reg.register_late_lint_pass(box copies::CopyAndPaste);
|
||||
|
||||
reg.register_lint_group("clippy_pedantic", vec![
|
||||
enum_glob_use::ENUM_GLOB_USE,
|
||||
matches::SINGLE_MATCH_ELSE,
|
||||
methods::OPTION_UNWRAP_USED,
|
||||
methods::RESULT_UNWRAP_USED,
|
||||
|
@ -187,6 +192,8 @@ pub fn plugin_registrar(reg: &mut Registry) {
|
|||
block_in_if_condition::BLOCK_IN_IF_CONDITION_EXPR,
|
||||
block_in_if_condition::BLOCK_IN_IF_CONDITION_STMT,
|
||||
collapsible_if::COLLAPSIBLE_IF,
|
||||
copies::IF_SAME_THEN_ELSE,
|
||||
copies::IFS_SAME_COND,
|
||||
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
|
||||
derive::DERIVE_HASH_NOT_EQ,
|
||||
derive::EXPL_IMPL_CLONE_ON_COPY,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use rustc::lint::*;
|
||||
use rustc_front::hir::*;
|
||||
use syntax::ptr::P;
|
||||
use std::cmp::PartialOrd;
|
||||
use std::cmp::Ordering::*;
|
||||
use std::cmp::{PartialOrd, Ordering};
|
||||
|
||||
use consts::{Constant, constant_simple};
|
||||
use utils::{match_def_path, span_lint};
|
||||
|
@ -37,7 +36,7 @@ impl LateLintPass for MinMaxPass {
|
|||
return;
|
||||
}
|
||||
match (outer_max, outer_c.partial_cmp(&inner_c)) {
|
||||
(_, None) | (Max, Some(Less)) | (Min, Some(Greater)) => (),
|
||||
(_, None) | (Max, Some(Ordering::Less)) | (Min, Some(Ordering::Greater)) => (),
|
||||
_ => {
|
||||
span_lint(cx, MIN_MAX, expr.span, "this min/max combination leads to constant result");
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
use rustc::lint::*;
|
||||
use rustc_front::hir::*;
|
||||
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit_;
|
||||
|
||||
use utils::{span_lint, snippet};
|
||||
|
||||
|
@ -90,7 +90,7 @@ fn fetch_bool_expr(expr: &Expr) -> Option<bool> {
|
|||
match expr.node {
|
||||
ExprBlock(ref block) => fetch_bool_block(block),
|
||||
ExprLit(ref lit_ptr) => {
|
||||
if let LitBool(value) = lit_ptr.node {
|
||||
if let Lit_::LitBool(value) = lit_ptr.node {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -84,7 +84,7 @@ impl LateLintPass for StringAdd {
|
|||
if let Some(ref p) = parent {
|
||||
if let ExprAssign(ref target, _) = p.node {
|
||||
// avoid duplicate matches
|
||||
if is_exp_equal(cx, target, left) {
|
||||
if is_exp_equal(cx, target, left, false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ fn is_string(cx: &LateContext, e: &Expr) -> bool {
|
|||
|
||||
fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
|
||||
match src.node {
|
||||
ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => is_exp_equal(cx, target, left),
|
||||
ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => is_exp_equal(cx, target, left, false),
|
||||
ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
|
||||
}
|
||||
|
|
10
src/types.rs
10
src/types.rs
|
@ -5,9 +5,7 @@ use rustc_front::util::{is_comparison_binop, binop_to_string};
|
|||
use syntax::codemap::Span;
|
||||
use rustc_front::intravisit::{FnKind, Visitor, walk_ty};
|
||||
use rustc::middle::ty;
|
||||
use syntax::ast::IntTy::*;
|
||||
use syntax::ast::UintTy::*;
|
||||
use syntax::ast::FloatTy::*;
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
|
||||
use utils::*;
|
||||
|
||||
|
@ -236,7 +234,7 @@ fn int_ty_to_nbits(typ: &ty::TyS) -> usize {
|
|||
|
||||
fn is_isize_or_usize(typ: &ty::TyS) -> bool {
|
||||
match typ.sty {
|
||||
ty::TyInt(TyIs) | ty::TyUint(TyUs) => true,
|
||||
ty::TyInt(IntTy::TyIs) | ty::TyUint(UintTy::TyUs) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -361,7 +359,7 @@ impl LateLintPass for CastPass {
|
|||
match (cast_from.is_integral(), cast_to.is_integral()) {
|
||||
(true, false) => {
|
||||
let from_nbits = int_ty_to_nbits(cast_from);
|
||||
let to_nbits = if let ty::TyFloat(TyF32) = cast_to.sty {
|
||||
let to_nbits = if let ty::TyFloat(FloatTy::TyF32) = cast_to.sty {
|
||||
32
|
||||
} else {
|
||||
64
|
||||
|
@ -392,7 +390,7 @@ impl LateLintPass for CastPass {
|
|||
check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
|
||||
}
|
||||
(false, false) => {
|
||||
if let (&ty::TyFloat(TyF64), &ty::TyFloat(TyF32)) = (&cast_from.sty, &cast_to.sty) {
|
||||
if let (&ty::TyFloat(FloatTy::TyF64), &ty::TyFloat(FloatTy::TyF32)) = (&cast_from.sty, &cast_to.sty) {
|
||||
span_lint(cx,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
expr.span,
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustc::lint::*;
|
|||
use rustc_front::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit_;
|
||||
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
|
||||
|
@ -59,7 +59,7 @@ impl LintPass for Unicode {
|
|||
impl LateLintPass for Unicode {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
if let ExprLit(ref lit) = expr.node {
|
||||
if let LitStr(_, _) = lit.node {
|
||||
if let Lit_::LitStr(_, _) = lit.node {
|
||||
check_str(cx, lit.span)
|
||||
}
|
||||
}
|
||||
|
|
198
src/utils.rs
198
src/utils.rs
|
@ -1,7 +1,7 @@
|
|||
use consts::constant;
|
||||
use reexport::*;
|
||||
use rustc::front::map::Node::*;
|
||||
use rustc::lint::*;
|
||||
use rustc::front::map::Node;
|
||||
use rustc::lint::{LintContext, LateContext, Level, Lint};
|
||||
use rustc::middle::def_id::DefId;
|
||||
use rustc::middle::{cstore, def, infer, ty, traits};
|
||||
use rustc::session::Session;
|
||||
|
@ -10,7 +10,7 @@ use std::borrow::Cow;
|
|||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
use syntax::ast::Lit_::*;
|
||||
use syntax::ast::Lit_;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{ExpnInfo, Span, ExpnFormat};
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
|
@ -296,9 +296,9 @@ pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option<Vec<&'a
|
|||
pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
|
||||
let parent_id = cx.tcx.map.get_parent(expr.id);
|
||||
match cx.tcx.map.find(parent_id) {
|
||||
Some(NodeItem(&Item{ ref name, .. })) |
|
||||
Some(NodeTraitItem(&TraitItem{ ref name, .. })) |
|
||||
Some(NodeImplItem(&ImplItem{ ref name, .. })) => Some(*name),
|
||||
Some(Node::NodeItem(&Item{ ref name, .. })) |
|
||||
Some(Node::NodeTraitItem(&TraitItem{ ref name, .. })) |
|
||||
Some(Node::NodeImplItem(&ImplItem{ ref name, .. })) => Some(*name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +408,7 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> {
|
|||
return None;
|
||||
}
|
||||
map.find(parent_id).and_then(|node| {
|
||||
if let NodeExpr(parent) = node {
|
||||
if let Node::NodeExpr(parent) = node {
|
||||
Some(parent)
|
||||
} else {
|
||||
None
|
||||
|
@ -422,8 +422,8 @@ pub fn get_enclosing_block<'c>(cx: &'c LateContext, node: NodeId) -> Option<&'c
|
|||
.and_then(|enclosing_id| map.find(enclosing_id));
|
||||
if let Some(node) = enclosing_node {
|
||||
match node {
|
||||
NodeBlock(ref block) => Some(block),
|
||||
NodeItem(&Item{ node: ItemFn(_, _, _, _, _, ref block), .. }) => Some(block),
|
||||
Node::NodeBlock(ref block) => Some(block),
|
||||
Node::NodeItem(&Item{ node: ItemFn(_, _, _, _, _, ref block), .. }) => Some(block),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
@ -529,7 +529,7 @@ pub fn walk_ptrs_ty_depth(ty: ty::Ty) -> (ty::Ty, usize) {
|
|||
pub fn is_integer_literal(expr: &Expr, value: u64) -> bool {
|
||||
// FIXME: use constant folding
|
||||
if let ExprLit(ref spanned) = expr.node {
|
||||
if let LitInt(v, _) = spanned.node {
|
||||
if let Lit_::LitInt(v, _) = spanned.node {
|
||||
return v == value;
|
||||
}
|
||||
}
|
||||
|
@ -575,7 +575,7 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
}
|
||||
if let ast::MetaNameValue(ref key, ref value) = attr.value.node {
|
||||
if *key == name {
|
||||
if let LitStr(ref s, _) = value.node {
|
||||
if let Lit_::LitStr(ref s, _) = value.node {
|
||||
if let Ok(value) = FromStr::from_str(s) {
|
||||
f(value)
|
||||
} else {
|
||||
|
@ -589,29 +589,183 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_exp_equal(cx: &LateContext, left: &Expr, right: &Expr) -> bool {
|
||||
/// Check whether two statements are the same.
|
||||
/// See also `is_exp_equal`.
|
||||
pub fn is_stmt_equal(cx: &LateContext, left: &Stmt, right: &Stmt, ignore_fn: bool) -> bool {
|
||||
match (&left.node, &right.node) {
|
||||
(&StmtDecl(ref l, _), &StmtDecl(ref r, _)) => {
|
||||
if let (&DeclLocal(ref l), &DeclLocal(ref r)) = (&l.node, &r.node) {
|
||||
// TODO: tys
|
||||
l.ty.is_none() && r.ty.is_none() &&
|
||||
both(&l.init, &r.init, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
(&StmtExpr(ref l, _), &StmtExpr(ref r, _)) => is_exp_equal(cx, l, r, ignore_fn),
|
||||
(&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => is_exp_equal(cx, l, r, ignore_fn),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether two blocks are the same.
|
||||
/// See also `is_exp_equal`.
|
||||
pub fn is_block_equal(cx: &LateContext, left: &Block, right: &Block, ignore_fn: bool) -> bool {
|
||||
over(&left.stmts, &right.stmts, |l, r| is_stmt_equal(cx, l, r, ignore_fn)) &&
|
||||
both(&left.expr, &right.expr, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
|
||||
/// Check whether two pattern are the same.
|
||||
/// See also `is_exp_equal`.
|
||||
pub fn is_pat_equal(cx: &LateContext, left: &Pat, right: &Pat, ignore_fn: bool) -> bool {
|
||||
match (&left.node, &right.node) {
|
||||
(&PatBox(ref l), &PatBox(ref r)) => {
|
||||
is_pat_equal(cx, l, r, ignore_fn)
|
||||
}
|
||||
(&PatEnum(ref lp, ref la), &PatEnum(ref rp, ref ra)) => {
|
||||
is_path_equal(lp, rp) &&
|
||||
both(la, ra, |l, r| {
|
||||
over(l, r, |l, r| is_pat_equal(cx, l, r, ignore_fn))
|
||||
})
|
||||
}
|
||||
(&PatIdent(ref lb, ref li, ref lp), &PatIdent(ref rb, ref ri, ref rp)) => {
|
||||
lb == rb && li.node.name.as_str() == ri.node.name.as_str() &&
|
||||
both(lp, rp, |l, r| is_pat_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&PatLit(ref l), &PatLit(ref r)) => {
|
||||
is_exp_equal(cx, l, r, ignore_fn)
|
||||
}
|
||||
(&PatQPath(ref ls, ref lp), &PatQPath(ref rs, ref rp)) => {
|
||||
is_qself_equal(ls, rs) && is_path_equal(lp, rp)
|
||||
}
|
||||
(&PatTup(ref l), &PatTup(ref r)) => {
|
||||
over(l, r, |l, r| is_pat_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&PatRange(ref ls, ref le), &PatRange(ref rs, ref re)) => {
|
||||
is_exp_equal(cx, ls, rs, ignore_fn) &&
|
||||
is_exp_equal(cx, le, re, ignore_fn)
|
||||
}
|
||||
(&PatRegion(ref le, ref lm), &PatRegion(ref re, ref rm)) => {
|
||||
lm == rm && is_pat_equal(cx, le, re, ignore_fn)
|
||||
}
|
||||
(&PatVec(ref ls, ref li, ref le), &PatVec(ref rs, ref ri, ref re)) => {
|
||||
over(ls, rs, |l, r| is_pat_equal(cx, l, r, ignore_fn)) &&
|
||||
over(le, re, |l, r| is_pat_equal(cx, l, r, ignore_fn)) &&
|
||||
both(li, ri, |l, r| is_pat_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&PatWild, &PatWild) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether two expressions are the same. This is different from the operator `==` on
|
||||
/// expression as this operator would compare true equality with ID and span.
|
||||
/// If `ignore_fn` is true, never consider as equal fonction calls.
|
||||
///
|
||||
/// Note that some expression kinds are not considered but could be added.
|
||||
#[allow(cyclomatic_complexity)] // ok, it’s a big function, but mostly one big match with simples cases
|
||||
pub fn is_exp_equal(cx: &LateContext, left: &Expr, right: &Expr, ignore_fn: bool) -> bool {
|
||||
if let (Some(l), Some(r)) = (constant(cx, left), constant(cx, right)) {
|
||||
if l == r {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
match (&left.node, &right.node) {
|
||||
(&ExprAddrOf(ref lmut, ref le), &ExprAddrOf(ref rmut, ref re)) => {
|
||||
lmut == rmut && is_exp_equal(cx, le, re, ignore_fn)
|
||||
}
|
||||
(&ExprAgain(li), &ExprAgain(ri)) => {
|
||||
both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str())
|
||||
}
|
||||
(&ExprAssign(ref ll, ref lr), &ExprAssign(ref rl, ref rr)) => {
|
||||
is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn)
|
||||
}
|
||||
(&ExprAssignOp(ref lo, ref ll, ref lr), &ExprAssignOp(ref ro, ref rl, ref rr)) => {
|
||||
lo.node == ro.node && is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn)
|
||||
}
|
||||
(&ExprBlock(ref l), &ExprBlock(ref r)) => {
|
||||
is_block_equal(cx, l, r, ignore_fn)
|
||||
}
|
||||
(&ExprBinary(lop, ref ll, ref lr), &ExprBinary(rop, ref rl, ref rr)) => {
|
||||
lop.node == rop.node && is_exp_equal(cx, ll, rl, ignore_fn) && is_exp_equal(cx, lr, rr, ignore_fn)
|
||||
}
|
||||
(&ExprBreak(li), &ExprBreak(ri)) => {
|
||||
both(&li, &ri, |l, r| l.node.name.as_str() == r.node.name.as_str())
|
||||
}
|
||||
(&ExprBox(ref l), &ExprBox(ref r)) => {
|
||||
is_exp_equal(cx, l, r, ignore_fn)
|
||||
}
|
||||
(&ExprCall(ref lfun, ref largs), &ExprCall(ref rfun, ref rargs)) => {
|
||||
!ignore_fn &&
|
||||
is_exp_equal(cx, lfun, rfun, ignore_fn) &&
|
||||
is_exps_equal(cx, largs, rargs, ignore_fn)
|
||||
}
|
||||
(&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) => {
|
||||
is_exp_equal(cx, lx, rx, ignore_fn) && is_cast_ty_equal(lt, rt)
|
||||
}
|
||||
(&ExprField(ref lfexp, ref lfident), &ExprField(ref rfexp, ref rfident)) => {
|
||||
lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp)
|
||||
lfident.node == rfident.node && is_exp_equal(cx, lfexp, rfexp, ignore_fn)
|
||||
}
|
||||
(&ExprIndex(ref la, ref li), &ExprIndex(ref ra, ref ri)) => {
|
||||
is_exp_equal(cx, la, ra, ignore_fn) && is_exp_equal(cx, li, ri, ignore_fn)
|
||||
}
|
||||
(&ExprIf(ref lc, ref lt, ref le), &ExprIf(ref rc, ref rt, ref re)) => {
|
||||
is_exp_equal(cx, lc, rc, ignore_fn) &&
|
||||
is_block_equal(cx, lt, rt, ignore_fn) &&
|
||||
both(le, re, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
|
||||
(&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => {
|
||||
ls == rs &&
|
||||
is_exp_equal(cx, le, re, ignore_fn) &&
|
||||
over(la, ra, |l, r| {
|
||||
is_exp_equal(cx, &l.body, &r.body, ignore_fn) &&
|
||||
both(&l.guard, &r.guard, |l, r| is_exp_equal(cx, l, r, ignore_fn)) &&
|
||||
over(&l.pats, &r.pats, |l, r| is_pat_equal(cx, l, r, ignore_fn))
|
||||
})
|
||||
}
|
||||
(&ExprMethodCall(ref lname, ref ltys, ref largs), &ExprMethodCall(ref rname, ref rtys, ref rargs)) => {
|
||||
// TODO: tys
|
||||
!ignore_fn &&
|
||||
lname.node == rname.node &&
|
||||
ltys.is_empty() &&
|
||||
rtys.is_empty() &&
|
||||
is_exps_equal(cx, largs, rargs, ignore_fn)
|
||||
}
|
||||
(&ExprRange(ref lb, ref le), &ExprRange(ref rb, ref re)) => {
|
||||
both(lb, rb, |l, r| is_exp_equal(cx, l, r, ignore_fn)) &&
|
||||
both(le, re, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&ExprRepeat(ref le, ref ll), &ExprRepeat(ref re, ref rl)) => {
|
||||
is_exp_equal(cx, le, re, ignore_fn) && is_exp_equal(cx, ll, rl, ignore_fn)
|
||||
}
|
||||
(&ExprRet(ref l), &ExprRet(ref r)) => {
|
||||
both(l, r, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
(&ExprPath(ref lqself, ref lsubpath), &ExprPath(ref rqself, ref rsubpath)) => {
|
||||
both(lqself, rqself, is_qself_equal) && is_path_equal(lsubpath, rsubpath)
|
||||
}
|
||||
(&ExprTup(ref ltup), &ExprTup(ref rtup)) => is_exps_equal(cx, ltup, rtup),
|
||||
(&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r),
|
||||
(&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) => is_exp_equal(cx, lx, rx) && is_cast_ty_equal(lt, rt),
|
||||
(&ExprTup(ref ltup), &ExprTup(ref rtup)) => is_exps_equal(cx, ltup, rtup, ignore_fn),
|
||||
(&ExprTupField(ref le, li), &ExprTupField(ref re, ri)) => {
|
||||
li.node == ri.node && is_exp_equal(cx, le, re, ignore_fn)
|
||||
}
|
||||
(&ExprUnary(lop, ref le), &ExprUnary(rop, ref re)) => {
|
||||
lop == rop && is_exp_equal(cx, le, re, ignore_fn)
|
||||
}
|
||||
(&ExprVec(ref l), &ExprVec(ref r)) => is_exps_equal(cx, l, r, ignore_fn),
|
||||
(&ExprWhile(ref lc, ref lb, ref ll), &ExprWhile(ref rc, ref rb, ref rl)) => {
|
||||
is_exp_equal(cx, lc, rc, ignore_fn) &&
|
||||
is_block_equal(cx, lb, rb, ignore_fn) &&
|
||||
both(ll, rl, |l, r| l.name.as_str() == r.name.as_str())
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_exps_equal(cx: &LateContext, left: &[P<Expr>], right: &[P<Expr>]) -> bool {
|
||||
over(left, right, |l, r| is_exp_equal(cx, l, r))
|
||||
fn is_exps_equal(cx: &LateContext, left: &[P<Expr>], right: &[P<Expr>], ignore_fn: bool) -> bool {
|
||||
over(left, right, |l, r| is_exp_equal(cx, l, r, ignore_fn))
|
||||
}
|
||||
|
||||
fn is_path_equal(left: &Path, right: &Path) -> bool {
|
||||
|
@ -620,20 +774,22 @@ fn is_path_equal(left: &Path, right: &Path) -> bool {
|
|||
left.global == right.global &&
|
||||
over(&left.segments,
|
||||
&right.segments,
|
||||
|l, r| l.identifier.name == r.identifier.name && l.parameters == r.parameters)
|
||||
|l, r| l.identifier.name.as_str() == r.identifier.name.as_str() && l.parameters == r.parameters)
|
||||
}
|
||||
|
||||
fn is_qself_equal(left: &QSelf, right: &QSelf) -> bool {
|
||||
left.ty.node == right.ty.node && left.position == right.position
|
||||
}
|
||||
|
||||
fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
|
||||
/// Check if two slices are equal as per `eq_fn`.
|
||||
pub fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
|
||||
where F: FnMut(&X, &X) -> bool
|
||||
{
|
||||
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
|
||||
}
|
||||
|
||||
fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
|
||||
/// Check if the two `Option`s are both `None` or some equal values as per `eq_fn`.
|
||||
pub fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
|
||||
where F: FnMut(&X, &X) -> bool
|
||||
{
|
||||
l.as_ref().map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
|
||||
|
|
135
tests/compile-fail/copies.rs
Executable file
135
tests/compile-fail/copies.rs
Executable file
|
@ -0,0 +1,135 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(let_and_return)]
|
||||
#![allow(needless_return)]
|
||||
#![allow(unused_variables)]
|
||||
#![deny(if_same_then_else)]
|
||||
#![deny(ifs_same_cond)]
|
||||
|
||||
fn foo() -> bool { unimplemented!() }
|
||||
|
||||
fn if_same_then_else() -> &'static str {
|
||||
if true { //~ERROR this if has the same then and else blocks
|
||||
foo();
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
}
|
||||
|
||||
if true {
|
||||
foo();
|
||||
foo();
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
}
|
||||
|
||||
let _ = if true { //~ERROR this if has the same then and else blocks
|
||||
foo();
|
||||
42
|
||||
}
|
||||
else {
|
||||
foo();
|
||||
42
|
||||
};
|
||||
|
||||
if true {
|
||||
foo();
|
||||
}
|
||||
|
||||
let _ = if true { //~ERROR this if has the same then and else blocks
|
||||
42
|
||||
}
|
||||
else {
|
||||
42
|
||||
};
|
||||
|
||||
if true { //~ERROR this if has the same then and else blocks
|
||||
let bar = if true {
|
||||
42
|
||||
}
|
||||
else {
|
||||
43
|
||||
};
|
||||
|
||||
while foo() { break; }
|
||||
bar + 1;
|
||||
}
|
||||
else {
|
||||
let bar = if true {
|
||||
42
|
||||
}
|
||||
else {
|
||||
43
|
||||
};
|
||||
|
||||
while foo() { break; }
|
||||
bar + 1;
|
||||
}
|
||||
|
||||
if true { //~ERROR this if has the same then and else blocks
|
||||
match 42 {
|
||||
42 => (),
|
||||
a if a > 0 => (),
|
||||
10...15 => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
else {
|
||||
match 42 {
|
||||
42 => (),
|
||||
a if a > 0 => (),
|
||||
10...15 => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if true { //~ERROR this if has the same then and else blocks
|
||||
if let Some(a) = Some(42) {}
|
||||
}
|
||||
else {
|
||||
if let Some(a) = Some(42) {}
|
||||
}
|
||||
|
||||
if true { //~ERROR this if has the same then and else blocks
|
||||
let foo = "";
|
||||
return &foo[0..];
|
||||
}
|
||||
else {
|
||||
let foo = "";
|
||||
return &foo[0..];
|
||||
}
|
||||
}
|
||||
|
||||
fn ifs_same_cond() {
|
||||
let a = 0;
|
||||
|
||||
if a == 1 {
|
||||
}
|
||||
else if a == 1 { //~ERROR this if has the same condition as a previous if
|
||||
}
|
||||
|
||||
if 2*a == 1 {
|
||||
}
|
||||
else if 2*a == 2 {
|
||||
}
|
||||
else if 2*a == 1 { //~ERROR this if has the same condition as a previous if
|
||||
}
|
||||
else if a == 1 {
|
||||
}
|
||||
|
||||
let mut v = vec![1];
|
||||
if v.pop() == None { // ok, functions
|
||||
}
|
||||
else if v.pop() == None {
|
||||
}
|
||||
|
||||
if v.len() == 42 { // ok, functions
|
||||
}
|
||||
else if v.len() == 42 {
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
20
tests/compile-fail/enum_glob_use.rs
Normal file
20
tests/compile-fail/enum_glob_use.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
#![deny(clippy, clippy_pedantic)]
|
||||
#![allow(unused_imports, dead_code)]
|
||||
|
||||
use std::cmp::Ordering::*; //~ ERROR: don't use glob imports for enum variants
|
||||
|
||||
enum Enum {}
|
||||
|
||||
use self::Enum::*; //~ ERROR: don't use glob imports for enum variants
|
||||
|
||||
fn blarg() {
|
||||
use self::Enum::*; // ok, just for a function
|
||||
}
|
||||
|
||||
mod blurg {
|
||||
pub use std::cmp::Ordering::*; // ok, re-export
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -19,9 +19,9 @@ fn main() {
|
|||
// unary and binary operators
|
||||
(-(2) < -(2)); //~ERROR equal expressions
|
||||
((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
|
||||
//~^ ERROR equal expressions
|
||||
//~^^ ERROR equal expressions
|
||||
//~^^^ ERROR equal expressions
|
||||
//~^ ERROR equal expressions as operands to `==`
|
||||
//~^^ ERROR equal expressions as operands to `&`
|
||||
//~^^^ ERROR equal expressions as operands to `&`
|
||||
(1 * 2) + (3 * 4) == 1 * 2 + 3 * 4; //~ERROR equal expressions
|
||||
|
||||
// various other things
|
||||
|
@ -31,5 +31,16 @@ fn main() {
|
|||
|
||||
// const folding
|
||||
1 + 1 == 2; //~ERROR equal expressions
|
||||
1 - 1 == 0; //~ERROR equal expressions
|
||||
1 - 1 == 0; //~ERROR equal expressions as operands to `==`
|
||||
//~^ ERROR equal expressions as operands to `-`
|
||||
|
||||
1 - 1; //~ERROR equal expressions
|
||||
1 / 1; //~ERROR equal expressions
|
||||
true && true; //~ERROR equal expressions
|
||||
true || true; //~ERROR equal expressions
|
||||
|
||||
let mut a = vec![1];
|
||||
a == a; //~ERROR equal expressions
|
||||
2*a.len() == 2*a.len(); // ok, functions
|
||||
a.pop() == a.pop(); // ok, functions
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ const ONE : i64 = 1;
|
|||
const NEG_ONE : i64 = -1;
|
||||
const ZERO : i64 = 0;
|
||||
|
||||
#[allow(eq_op)]
|
||||
#[deny(identity_op)]
|
||||
fn main() {
|
||||
let x = 0;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
|
||||
#[allow(if_same_then_else)]
|
||||
#[deny(needless_bool)]
|
||||
fn main() {
|
||||
let x = true;
|
||||
|
|
|
@ -5,9 +5,13 @@
|
|||
#[deny(zero_divided_by_zero)]
|
||||
fn main() {
|
||||
let nan = 0.0 / 0.0; //~ERROR constant division of 0.0 with 0.0 will always result in NaN
|
||||
//~^ equal expressions as operands to `/`
|
||||
let f64_nan = 0.0 / 0.0f64; //~ERROR constant division of 0.0 with 0.0 will always result in NaN
|
||||
//~^ equal expressions as operands to `/`
|
||||
let other_f64_nan = 0.0f64 / 0.0; //~ERROR constant division of 0.0 with 0.0 will always result in NaN
|
||||
//~^ equal expressions as operands to `/`
|
||||
let one_more_f64_nan = 0.0f64/0.0f64; //~ERROR constant division of 0.0 with 0.0 will always result in NaN
|
||||
//~^ equal expressions as operands to `/`
|
||||
let zero = 0.0;
|
||||
let other_zero = 0.0;
|
||||
let other_nan = zero / other_zero; // fine - this lint doesn't propegate constants.
|
||||
|
|
|
@ -21,5 +21,6 @@ fn run_mode(mode: &'static str) {
|
|||
|
||||
#[test]
|
||||
fn compile_test() {
|
||||
run_mode("run-pass");
|
||||
run_mode("compile-fail");
|
||||
}
|
||||
|
|
7
tests/run-pass/enum-glob-import-crate.rs
Normal file
7
tests/run-pass/enum-glob-import-crate.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
#![deny(clippy)]
|
||||
|
||||
use std::*;
|
||||
|
||||
fn main() { }
|
|
@ -79,7 +79,7 @@ errors of missing symbols.
|
|||
"""
|
||||
|
||||
|
||||
template = """# `%s`
|
||||
template = """\n# `%s`
|
||||
|
||||
**Default level:** %s
|
||||
|
||||
|
|
Loading…
Reference in a new issue