mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-14 09:36:06 +00:00
Merge pull request #2021 from marcusklaas/needless-loop-2
Add lint to detect manual slice copies
This commit is contained in:
commit
2e3328503e
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use std::f64::consts as f64;
|
||||
use syntax::ast::{Lit, LitKind, FloatTy};
|
||||
use syntax::ast::{FloatTy, Lit, LitKind};
|
||||
use syntax::symbol;
|
||||
use utils::span_lint;
|
||||
|
||||
@ -91,7 +91,7 @@ fn check_known_consts(cx: &LateContext, e: &Expr, s: &symbol::Symbol, module: &s
|
||||
e.span,
|
||||
&format!(
|
||||
"approximate value of `{}::consts::{}` found. \
|
||||
Consider using it directly",
|
||||
Consider using it directly",
|
||||
module,
|
||||
&name
|
||||
),
|
||||
|
@ -55,8 +55,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
|
||||
match expr.node {
|
||||
hir::ExprBinary(ref op, ref l, ref r) => {
|
||||
match op.node {
|
||||
hir::BiAnd | hir::BiOr | hir::BiBitAnd | hir::BiBitOr | hir::BiBitXor | hir::BiShl |
|
||||
hir::BiShr | hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => return,
|
||||
hir::BiAnd |
|
||||
hir::BiOr |
|
||||
hir::BiBitAnd |
|
||||
hir::BiBitOr |
|
||||
hir::BiBitXor |
|
||||
hir::BiShl |
|
||||
hir::BiShr |
|
||||
hir::BiEq |
|
||||
hir::BiLt |
|
||||
hir::BiLe |
|
||||
hir::BiNe |
|
||||
hir::BiGe |
|
||||
hir::BiGt => return,
|
||||
_ => (),
|
||||
}
|
||||
let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
|
||||
|
@ -3,7 +3,7 @@ use rustc::middle::const_val::ConstVal;
|
||||
use rustc::ty;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use rustc_const_math::{ConstUsize, ConstIsize, ConstInt};
|
||||
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
|
||||
use rustc::hir;
|
||||
use syntax::ast::RangeLimits;
|
||||
use utils::{self, higher};
|
||||
@ -124,29 +124,27 @@ fn to_const_range(
|
||||
};
|
||||
|
||||
let end = match *end {
|
||||
Some(Some(ConstVal::Integral(x))) => {
|
||||
if limits == RangeLimits::Closed {
|
||||
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
|
||||
}
|
||||
Some(Some(ConstVal::Integral(x))) => if limits == RangeLimits::Closed {
|
||||
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
|
||||
},
|
||||
Some(_) => return None,
|
||||
None => array_size,
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::hir;
|
||||
use rustc::lint::*;
|
||||
use syntax::ast;
|
||||
use utils::{span_lint_and_then, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
|
||||
use utils::{get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, SpanlessEq};
|
||||
use utils::{higher, sugg};
|
||||
|
||||
/// **What it does:** Checks for compound assignment operations (`+=` and
|
||||
@ -88,19 +88,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
||||
if let hir::ExprBinary(binop, ref l, ref r) = rhs.node {
|
||||
if op.node == binop.node {
|
||||
let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
|
||||
span_lint_and_then(cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
expr.span,
|
||||
"variable appears on both sides of an assignment operation",
|
||||
|db| if let (Some(snip_a), Some(snip_r)) =
|
||||
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) {
|
||||
db.span_suggestion(expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}",
|
||||
snip_a,
|
||||
op.node.as_str(),
|
||||
snip_r));
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
expr.span,
|
||||
"variable appears on both sides of an assignment operation",
|
||||
|db| if let (Some(snip_a), Some(snip_r)) =
|
||||
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
|
||||
{
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
// lhs op= l op r
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
|
||||
@ -167,8 +169,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
||||
BitXor: BiBitXor,
|
||||
Shr: BiShr,
|
||||
Shl: BiShl
|
||||
)
|
||||
{
|
||||
) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
ASSIGN_OP_PATTERN,
|
||||
@ -193,7 +194,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
||||
// a = b commutative_op a
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(assignee, r) {
|
||||
match op.node {
|
||||
hir::BiAdd | hir::BiMul | hir::BiAnd | hir::BiOr | hir::BiBitXor | hir::BiBitAnd |
|
||||
hir::BiAdd |
|
||||
hir::BiMul |
|
||||
hir::BiAnd |
|
||||
hir::BiOr |
|
||||
hir::BiBitXor |
|
||||
hir::BiBitAnd |
|
||||
hir::BiBitOr => {
|
||||
lint(assignee, l);
|
||||
},
|
||||
|
@ -7,7 +7,7 @@ use rustc::ty::{self, TyCtxt};
|
||||
use semver::Version;
|
||||
use syntax::ast::{Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
|
||||
use syntax::codemap::Span;
|
||||
use utils::{in_macro, match_def_path, paths, span_lint, span_lint_and_then, snippet_opt};
|
||||
use utils::{in_macro, match_def_path, paths, snippet_opt, span_lint, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
@ -110,8 +110,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
|
||||
check_attrs(cx, item.span, &item.name, &item.attrs)
|
||||
}
|
||||
match item.node {
|
||||
ItemExternCrate(_) |
|
||||
ItemUse(_, _) => {
|
||||
ItemExternCrate(_) | ItemUse(_, _) => {
|
||||
for attr in &item.attrs {
|
||||
if let Some(ref lint_list) = attr.meta_item_list() {
|
||||
if let Some(name) = attr.name() {
|
||||
@ -196,14 +195,13 @@ fn is_relevant_block(tcx: TyCtxt, tables: &ty::TypeckTables, block: &Block) -> b
|
||||
if let Some(stmt) = block.stmts.first() {
|
||||
match stmt.node {
|
||||
StmtDecl(_, _) => true,
|
||||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => is_relevant_expr(tcx, tables, expr),
|
||||
StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => is_relevant_expr(tcx, tables, expr),
|
||||
}
|
||||
} else {
|
||||
block.expr.as_ref().map_or(
|
||||
false,
|
||||
|e| is_relevant_expr(tcx, tables, e),
|
||||
)
|
||||
block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or(false, |e| is_relevant_expr(tcx, tables, e))
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,15 +209,12 @@ fn is_relevant_expr(tcx: TyCtxt, tables: &ty::TypeckTables, expr: &Expr) -> bool
|
||||
match expr.node {
|
||||
ExprBlock(ref block) => is_relevant_block(tcx, tables, block),
|
||||
ExprRet(Some(ref e)) => is_relevant_expr(tcx, tables, e),
|
||||
ExprRet(None) |
|
||||
ExprBreak(_, None) => false,
|
||||
ExprCall(ref path_expr, _) => {
|
||||
if let ExprPath(ref qpath) = path_expr.node {
|
||||
let fun_id = tables.qpath_def(qpath, path_expr.hir_id).def_id();
|
||||
!match_def_path(tcx, fun_id, &paths::BEGIN_PANIC)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
ExprRet(None) | ExprBreak(_, None) => false,
|
||||
ExprCall(ref path_expr, _) => if let ExprPath(ref qpath) = path_expr.node {
|
||||
let fun_id = tables.qpath_def(qpath, path_expr.hir_id).def_id();
|
||||
!match_def_path(tcx, fun_id, &paths::BEGIN_PANIC)
|
||||
} else {
|
||||
true
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
|
@ -158,118 +158,100 @@ fn check_compare(cx: &LateContext, bit_op: &Expr, cmp_op: BinOp_, cmp_value: u12
|
||||
|
||||
fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value: u128, cmp_value: u128, span: &Span) {
|
||||
match cmp_op {
|
||||
BiEq | BiNe => {
|
||||
match bit_op {
|
||||
BiBitAnd => {
|
||||
if mask_value & cmp_value != cmp_value {
|
||||
if cmp_value != 0 {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` can never be equal to `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
}
|
||||
},
|
||||
BiBitOr => {
|
||||
if mask_value | cmp_value != cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` can never be equal to `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
BiEq | BiNe => match bit_op {
|
||||
BiBitAnd => if mask_value & cmp_value != cmp_value {
|
||||
if cmp_value != 0 {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` can never be equal to `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
},
|
||||
BiBitOr => if mask_value | cmp_value != cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` can never be equal to `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
BiLt | BiGe => {
|
||||
match bit_op {
|
||||
BiBitAnd => {
|
||||
if mask_value < cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` will always be lower than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
}
|
||||
},
|
||||
BiBitOr => {
|
||||
if mask_value >= cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` will never be lower than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else {
|
||||
check_ineffective_lt(cx, *span, mask_value, cmp_value, "|");
|
||||
}
|
||||
},
|
||||
BiBitXor => check_ineffective_lt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
}
|
||||
BiLt | BiGe => match bit_op {
|
||||
BiBitAnd => if mask_value < cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` will always be lower than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
},
|
||||
BiBitOr => if mask_value >= cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` will never be lower than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else {
|
||||
check_ineffective_lt(cx, *span, mask_value, cmp_value, "|");
|
||||
},
|
||||
BiBitXor => check_ineffective_lt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
},
|
||||
BiLe | BiGt => {
|
||||
match bit_op {
|
||||
BiBitAnd => {
|
||||
if mask_value <= cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` will never be higher than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
}
|
||||
},
|
||||
BiBitOr => {
|
||||
if mask_value > cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` will always be higher than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else {
|
||||
check_ineffective_gt(cx, *span, mask_value, cmp_value, "|");
|
||||
}
|
||||
},
|
||||
BiBitXor => check_ineffective_gt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
}
|
||||
BiLe | BiGt => match bit_op {
|
||||
BiBitAnd => if mask_value <= cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ & {}` will never be higher than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
},
|
||||
BiBitOr => if mask_value > cmp_value {
|
||||
span_lint(
|
||||
cx,
|
||||
BAD_BIT_MASK,
|
||||
*span,
|
||||
&format!(
|
||||
"incompatible bit mask: `_ | {}` will always be higher than `{}`",
|
||||
mask_value,
|
||||
cmp_value
|
||||
),
|
||||
);
|
||||
} else {
|
||||
check_ineffective_gt(cx, *span, mask_value, cmp_value, "|");
|
||||
},
|
||||
BiBitXor => check_ineffective_gt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ pub struct BlackListedName {
|
||||
|
||||
impl BlackListedName {
|
||||
pub fn new(blacklist: Vec<String>) -> Self {
|
||||
Self { blacklist: blacklist }
|
||||
Self {
|
||||
blacklist: blacklist,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::{LateLintPass, LateContext, LintArray, LintPass};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use utils::*;
|
||||
|
||||
/// **What it does:** Checks for `if` conditions that use blocks to contain an
|
||||
@ -93,15 +93,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
|
||||
check.span,
|
||||
BRACED_EXPR_MESSAGE,
|
||||
&format!("try\nif {} {} ... ",
|
||||
snippet_block(cx, ex.span, ".."),
|
||||
snippet_block(cx, then.span, "..")),
|
||||
snippet_block(cx, ex.span, ".."),
|
||||
snippet_block(cx, then.span, "..")),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let span = block.expr.as_ref().map_or_else(
|
||||
|| block.stmts[0].span,
|
||||
|e| e.span,
|
||||
);
|
||||
let span = block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or_else(|| block.stmts[0].span, |e| e.span);
|
||||
if in_macro(span) || differing_macro_contexts(expr.span, span) {
|
||||
return;
|
||||
}
|
||||
@ -112,8 +112,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
|
||||
check.span,
|
||||
COMPLEX_BLOCK_MESSAGE,
|
||||
&format!("try\nlet res = {};\nif res {} ... ",
|
||||
snippet_block(cx, block.span, ".."),
|
||||
snippet_block(cx, then.span, "..")),
|
||||
snippet_block(cx, block.span, ".."),
|
||||
snippet_block(cx, then.span, "..")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use rustc::lint::{LintArray, LateLintPass, LateContext, LintPass};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::*;
|
||||
use syntax::ast::{LitKind, DUMMY_NODE_ID, NodeId};
|
||||
use syntax::codemap::{DUMMY_SP, dummy_spanned, Span};
|
||||
use syntax::ast::{LitKind, NodeId, DUMMY_NODE_ID};
|
||||
use syntax::codemap::{dummy_spanned, Span, DUMMY_SP};
|
||||
use syntax::util::ThinVec;
|
||||
use utils::{span_lint_and_then, in_macro, snippet_opt, SpanlessEq};
|
||||
use utils::{in_macro, snippet_opt, span_lint_and_then, SpanlessEq};
|
||||
|
||||
/// **What it does:** Checks for boolean expressions that can be written more
|
||||
/// concisely.
|
||||
@ -96,26 +96,23 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
||||
if !in_macro(e.span) {
|
||||
match e.node {
|
||||
ExprUnary(UnNot, ref inner) => return Ok(Bool::Not(box self.run(inner)?)),
|
||||
ExprBinary(binop, ref lhs, ref rhs) => {
|
||||
match binop.node {
|
||||
BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
|
||||
BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
|
||||
_ => (),
|
||||
}
|
||||
ExprBinary(binop, ref lhs, ref rhs) => match binop.node {
|
||||
BiOr => return Ok(Bool::Or(self.extract(BiOr, &[lhs, rhs], Vec::new())?)),
|
||||
BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
|
||||
_ => (),
|
||||
},
|
||||
ExprLit(ref lit) => {
|
||||
match lit.node {
|
||||
LitKind::Bool(true) => return Ok(Bool::True),
|
||||
LitKind::Bool(false) => return Ok(Bool::False),
|
||||
_ => (),
|
||||
}
|
||||
ExprLit(ref lit) => match lit.node {
|
||||
LitKind::Bool(true) => return Ok(Bool::True),
|
||||
LitKind::Bool(false) => return Ok(Bool::False),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
for (n, expr) in self.terminals.iter().enumerate() {
|
||||
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
|
||||
#[allow(cast_possible_truncation)] return Ok(Bool::Term(n as u8));
|
||||
#[allow(cast_possible_truncation)]
|
||||
return Ok(Bool::Term(n as u8));
|
||||
}
|
||||
let negated = match e.node {
|
||||
ExprBinary(binop, ref lhs, ref rhs) => {
|
||||
@ -141,13 +138,15 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
||||
_ => continue,
|
||||
};
|
||||
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(&negated, expr) {
|
||||
#[allow(cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
|
||||
#[allow(cast_possible_truncation)]
|
||||
return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
|
||||
}
|
||||
}
|
||||
let n = self.terminals.len();
|
||||
self.terminals.push(e);
|
||||
if n < 32 {
|
||||
#[allow(cast_possible_truncation)] Ok(Bool::Term(n as u8))
|
||||
#[allow(cast_possible_truncation)]
|
||||
Ok(Bool::Term(n as u8))
|
||||
} else {
|
||||
Err("too many literals".to_owned())
|
||||
}
|
||||
@ -167,40 +166,36 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
||||
s.push_str("false");
|
||||
s
|
||||
},
|
||||
Not(ref inner) => {
|
||||
match **inner {
|
||||
And(_) | Or(_) => {
|
||||
s.push('!');
|
||||
recurse(true, cx, inner, terminals, s)
|
||||
},
|
||||
Term(n) => {
|
||||
if let ExprBinary(binop, ref lhs, ref rhs) = terminals[n as usize].node {
|
||||
let op = match binop.node {
|
||||
BiEq => " != ",
|
||||
BiNe => " == ",
|
||||
BiLt => " >= ",
|
||||
BiGt => " <= ",
|
||||
BiLe => " > ",
|
||||
BiGe => " < ",
|
||||
_ => {
|
||||
s.push('!');
|
||||
return recurse(true, cx, inner, terminals, s);
|
||||
},
|
||||
};
|
||||
s.push_str(&snip(lhs));
|
||||
s.push_str(op);
|
||||
s.push_str(&snip(rhs));
|
||||
s
|
||||
} else {
|
||||
Not(ref inner) => match **inner {
|
||||
And(_) | Or(_) => {
|
||||
s.push('!');
|
||||
recurse(true, cx, inner, terminals, s)
|
||||
},
|
||||
Term(n) => if let ExprBinary(binop, ref lhs, ref rhs) = terminals[n as usize].node {
|
||||
let op = match binop.node {
|
||||
BiEq => " != ",
|
||||
BiNe => " == ",
|
||||
BiLt => " >= ",
|
||||
BiGt => " <= ",
|
||||
BiLe => " > ",
|
||||
BiGe => " < ",
|
||||
_ => {
|
||||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
},
|
||||
}
|
||||
return recurse(true, cx, inner, terminals, s);
|
||||
},
|
||||
};
|
||||
s.push_str(&snip(lhs));
|
||||
s.push_str(op);
|
||||
s.push_str(&snip(rhs));
|
||||
s
|
||||
} else {
|
||||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
},
|
||||
_ => {
|
||||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
},
|
||||
},
|
||||
And(ref v) => {
|
||||
if brackets {
|
||||
@ -319,7 +314,6 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
||||
cx: self.cx,
|
||||
};
|
||||
if let Ok(expr) = h2q.run(e) {
|
||||
|
||||
if h2q.terminals.len() > 8 {
|
||||
// QMC has exponentially slow behavior as the number of terminals increases
|
||||
// 8 is reasonable, it takes approximately 0.2 seconds.
|
||||
@ -360,7 +354,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
||||
db.span_help(
|
||||
h2q.terminals[i].span,
|
||||
"this expression can be optimized out by applying boolean operations to the \
|
||||
outer expression",
|
||||
outer expression",
|
||||
);
|
||||
db.span_suggestion(
|
||||
e.span,
|
||||
@ -411,12 +405,10 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
|
||||
}
|
||||
match e.node {
|
||||
ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
|
||||
ExprUnary(UnNot, ref inner) => {
|
||||
if self.cx.tables.node_types()[inner.hir_id].is_bool() {
|
||||
self.bool_expr(e);
|
||||
} else {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
ExprUnary(UnNot, ref inner) => if self.cx.tables.node_types()[inner.hir_id].is_bool() {
|
||||
self.bool_expr(e);
|
||||
} else {
|
||||
walk_expr(self, e);
|
||||
},
|
||||
_ => walk_expr(self, e),
|
||||
}
|
||||
|
@ -97,23 +97,18 @@ fn get_pat_name(pat: &Pat) -> Option<Name> {
|
||||
match pat.node {
|
||||
PatKind::Binding(_, _, ref spname, _) => Some(spname.node),
|
||||
PatKind::Path(ref qpath) => single_segment_path(qpath).map(|ps| ps.name),
|
||||
PatKind::Box(ref p) |
|
||||
PatKind::Ref(ref p, _) => get_pat_name(&*p),
|
||||
PatKind::Box(ref p) | PatKind::Ref(ref p, _) => get_pat_name(&*p),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_path_name(expr: &Expr) -> Option<Name> {
|
||||
match expr.node {
|
||||
ExprBox(ref e) |
|
||||
ExprAddrOf(_, ref e) |
|
||||
ExprUnary(UnOp::UnDeref, ref e) => get_path_name(e),
|
||||
ExprBlock(ref b) => {
|
||||
if b.stmts.is_empty() {
|
||||
b.expr.as_ref().and_then(|p| get_path_name(p))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ExprBox(ref e) | ExprAddrOf(_, ref e) | ExprUnary(UnOp::UnDeref, ref e) => get_path_name(e),
|
||||
ExprBlock(ref b) => if b.stmts.is_empty() {
|
||||
b.expr.as_ref().and_then(|p| get_path_name(p))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
ExprPath(ref qpath) => single_segment_path(qpath).map(|ps| ps.name),
|
||||
_ => None,
|
||||
|
@ -15,7 +15,7 @@
|
||||
use rustc::lint::*;
|
||||
use syntax::ast;
|
||||
|
||||
use utils::{in_macro, snippet_block, span_lint_and_then, span_lint_and_sugg};
|
||||
use utils::{in_macro, snippet_block, span_lint_and_sugg, span_lint_and_then};
|
||||
use utils::sugg::Sugg;
|
||||
|
||||
/// **What it does:** Checks for nested `if` statements which can be collapsed
|
||||
@ -87,12 +87,10 @@ impl EarlyLintPass for CollapsibleIf {
|
||||
|
||||
fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
|
||||
match expr.node {
|
||||
ast::ExprKind::If(ref check, ref then, ref else_) => {
|
||||
if let Some(ref else_) = *else_ {
|
||||
check_collapsible_maybe_if_let(cx, else_);
|
||||
} else {
|
||||
check_collapsible_no_if_let(cx, expr, check, then);
|
||||
}
|
||||
ast::ExprKind::If(ref check, ref then, ref else_) => if let Some(ref else_) = *else_ {
|
||||
check_collapsible_maybe_if_let(cx, else_);
|
||||
} else {
|
||||
check_collapsible_no_if_let(cx, expr, check, then);
|
||||
},
|
||||
ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
|
||||
check_collapsible_maybe_if_let(cx, else_);
|
||||
@ -147,8 +145,7 @@ fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
|
||||
|
||||
if let (Some(stmt), None) = (it.next(), it.next()) {
|
||||
match stmt.node {
|
||||
ast::StmtKind::Expr(ref expr) |
|
||||
ast::StmtKind::Semi(ref expr) => Some(expr),
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
@ -5,8 +5,8 @@ use rustc::hir::def::Def;
|
||||
use rustc_const_eval::lookup_const_by_id;
|
||||
use rustc_const_math::ConstInt;
|
||||
use rustc::hir::*;
|
||||
use rustc::ty::{self, TyCtxt, Ty};
|
||||
use rustc::ty::subst::{Substs, Subst};
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::subst::{Subst, Substs};
|
||||
use std::cmp::Ordering::{self, Equal};
|
||||
use std::cmp::PartialOrd;
|
||||
use std::hash::{Hash, Hasher};
|
||||
@ -76,7 +76,7 @@ impl PartialEq for Constant {
|
||||
(&Constant::Vec(ref l), &Constant::Vec(ref r)) => l == r,
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
|
||||
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) => l == r,
|
||||
_ => false, //TODO: Are there inter-type equalities?
|
||||
_ => false, // TODO: Are there inter-type equalities?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,8 +110,7 @@ impl Hash for Constant {
|
||||
Constant::Bool(b) => {
|
||||
b.hash(state);
|
||||
},
|
||||
Constant::Vec(ref v) |
|
||||
Constant::Tuple(ref v) => {
|
||||
Constant::Vec(ref v) | Constant::Tuple(ref v) => {
|
||||
v.hash(state);
|
||||
},
|
||||
Constant::Repeat(ref c, l) => {
|
||||
@ -125,12 +124,10 @@ impl Hash for Constant {
|
||||
impl PartialOrd for Constant {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
match (self, other) {
|
||||
(&Constant::Str(ref ls, ref l_sty), &Constant::Str(ref rs, ref r_sty)) => {
|
||||
if l_sty == r_sty {
|
||||
Some(ls.cmp(rs))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
(&Constant::Str(ref ls, ref l_sty), &Constant::Str(ref rs, ref r_sty)) => if l_sty == r_sty {
|
||||
Some(ls.cmp(rs))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)),
|
||||
@ -147,15 +144,14 @@ impl PartialOrd for Constant {
|
||||
}
|
||||
},
|
||||
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) |
|
||||
(&Constant::Vec(ref l), &Constant::Vec(ref r)) => l.partial_cmp(r),
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => {
|
||||
match lv.partial_cmp(rv) {
|
||||
Some(Equal) => Some(ls.cmp(rs)),
|
||||
x => x,
|
||||
}
|
||||
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => {
|
||||
l.partial_cmp(r)
|
||||
},
|
||||
_ => None, //TODO: Are there any useful inter-type orderings?
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => match lv.partial_cmp(rv) {
|
||||
Some(Equal) => Some(ls.cmp(rs)),
|
||||
x => x,
|
||||
},
|
||||
_ => None, // TODO: Are there any useful inter-type orderings?
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,18 +173,14 @@ pub fn lit_to_constant<'a, 'tcx>(lit: &LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, mut
|
||||
LitKind::Byte(b) => Constant::Int(ConstInt::U8(b)),
|
||||
LitKind::ByteStr(ref s) => Constant::Binary(s.clone()),
|
||||
LitKind::Char(c) => Constant::Char(c),
|
||||
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::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),
|
||||
@ -262,13 +254,11 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
||||
};
|
||||
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
|
||||
},
|
||||
ExprUnary(op, ref operand) => {
|
||||
self.expr(operand).and_then(|o| match op {
|
||||
UnNot => constant_not(&o),
|
||||
UnNeg => constant_negate(o),
|
||||
UnDeref => Some(o),
|
||||
})
|
||||
},
|
||||
ExprUnary(op, ref operand) => self.expr(operand).and_then(|o| match op {
|
||||
UnNot => constant_not(&o),
|
||||
UnNeg => constant_negate(o),
|
||||
UnDeref => Some(o),
|
||||
}),
|
||||
ExprBinary(op, ref left, ref right) => self.binop(op, left, right),
|
||||
// TODO: add other expressions
|
||||
_ => None,
|
||||
@ -287,8 +277,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
||||
fn fetch_path(&mut self, qpath: &QPath, id: HirId) -> Option<Constant> {
|
||||
let def = self.tables.qpath_def(qpath, id);
|
||||
match def {
|
||||
Def::Const(def_id) |
|
||||
Def::AssociatedConst(def_id) => {
|
||||
Def::Const(def_id) | Def::AssociatedConst(def_id) => {
|
||||
let substs = self.tables.node_substs(id);
|
||||
let substs = if self.substs.is_empty() {
|
||||
substs
|
||||
@ -358,8 +347,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
||||
(BiRem, Constant::Int(l), Some(Constant::Int(r))) => (l % r).ok().map(Constant::Int),
|
||||
(BiAnd, Constant::Bool(false), _) => Some(Constant::Bool(false)),
|
||||
(BiOr, Constant::Bool(true), _) => Some(Constant::Bool(true)),
|
||||
(BiAnd, Constant::Bool(true), Some(r)) |
|
||||
(BiOr, Constant::Bool(false), Some(r)) => Some(r),
|
||||
(BiAnd, Constant::Bool(true), Some(r)) | (BiOr, Constant::Bool(false), Some(r)) => Some(r),
|
||||
(BiBitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
|
||||
(BiBitXor, Constant::Int(l), Some(Constant::Int(r))) => (l ^ r).ok().map(Constant::Int),
|
||||
(BiBitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
|
||||
|
@ -6,7 +6,7 @@ use std::collections::hash_map::Entry;
|
||||
use syntax::symbol::InternedString;
|
||||
use syntax::util::small_vector::SmallVector;
|
||||
use utils::{SpanlessEq, SpanlessHash};
|
||||
use utils::{get_parent_expr, in_macro, span_lint_and_then, span_note_and_lint, snippet};
|
||||
use utils::{get_parent_expr, in_macro, snippet, span_lint_and_then, span_note_and_lint};
|
||||
|
||||
/// **What it does:** Checks for consecutive `if`s with the same condition.
|
||||
///
|
||||
@ -114,7 +114,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if !in_macro(expr.span) {
|
||||
// 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 let Some(&Expr {
|
||||
node: ExprIf(_, _, Some(ref else_expr)),
|
||||
..
|
||||
}) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if else_expr.id == expr.id {
|
||||
return;
|
||||
}
|
||||
@ -267,12 +271,9 @@ fn if_sequence(mut expr: &Expr) -> (SmallVector<&Expr>, SmallVector<&Block>) {
|
||||
fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<InternedString, Ty<'tcx>> {
|
||||
fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat, map: &mut HashMap<InternedString, Ty<'tcx>>) {
|
||||
match pat.node {
|
||||
PatKind::Box(ref pat) |
|
||||
PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
|
||||
PatKind::TupleStruct(_, ref pats, _) => {
|
||||
for pat in pats {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
|
||||
PatKind::TupleStruct(_, ref pats, _) => for pat in pats {
|
||||
bindings_impl(cx, pat, map);
|
||||
},
|
||||
PatKind::Binding(_, _, ref ident, ref as_pat) => {
|
||||
if let Entry::Vacant(v) = map.entry(ident.node.as_str()) {
|
||||
@ -282,15 +283,11 @@ fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<Interned
|
||||
bindings_impl(cx, as_pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, ref fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, &pat.node.pat, map);
|
||||
}
|
||||
PatKind::Struct(_, ref fields, _) => for pat in fields {
|
||||
bindings_impl(cx, &pat.node.pat, map);
|
||||
},
|
||||
PatKind::Tuple(ref fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
PatKind::Tuple(ref fields, _) => for pat in fields {
|
||||
bindings_impl(cx, pat, map);
|
||||
},
|
||||
PatKind::Slice(ref lhs, ref mid, ref rhs) => {
|
||||
for pat in lhs {
|
||||
@ -303,10 +300,7 @@ fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<Interned
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
},
|
||||
PatKind::Lit(..) |
|
||||
PatKind::Range(..) |
|
||||
PatKind::Wild |
|
||||
PatKind::Path(..) => (),
|
||||
PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
|
||||
}
|
||||
}
|
||||
|
||||
@ -335,11 +329,9 @@ where
|
||||
|
||||
for expr in exprs {
|
||||
match map.entry(hash(expr)) {
|
||||
Entry::Occupied(o) => {
|
||||
for o in o.get() {
|
||||
if eq(o, expr) {
|
||||
return Some((o, expr));
|
||||
}
|
||||
Entry::Occupied(o) => for o in o.get() {
|
||||
if eq(o, expr) {
|
||||
return Some((o, expr));
|
||||
}
|
||||
},
|
||||
Entry::Vacant(v) => {
|
||||
|
@ -4,11 +4,11 @@ use rustc::cfg::CFG;
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::ty;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use syntax::ast::{Attribute, NodeId};
|
||||
use syntax::codemap::Span;
|
||||
|
||||
use utils::{in_macro, LimitStack, span_help_and_lint, paths, match_type, is_allowed};
|
||||
use utils::{in_macro, is_allowed, match_type, paths, span_help_and_lint, LimitStack};
|
||||
|
||||
/// **What it does:** Checks for methods with high cyclomatic complexity.
|
||||
///
|
||||
@ -31,7 +31,9 @@ pub struct CyclomaticComplexity {
|
||||
|
||||
impl CyclomaticComplexity {
|
||||
pub fn new(limit: u64) -> Self {
|
||||
Self { limit: LimitStack::new(limit) }
|
||||
Self {
|
||||
limit: LimitStack::new(limit),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,18 +127,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
|
||||
}
|
||||
|
||||
fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.push_attrs(
|
||||
cx.sess(),
|
||||
attrs,
|
||||
"cyclomatic_complexity",
|
||||
);
|
||||
self.limit
|
||||
.push_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
}
|
||||
fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.pop_attrs(
|
||||
cx.sess(),
|
||||
attrs,
|
||||
"cyclomatic_complexity",
|
||||
);
|
||||
self.limit
|
||||
.pop_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,7 +190,7 @@ fn report_cc_bug(_: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, re
|
||||
span_bug!(
|
||||
span,
|
||||
"Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \
|
||||
div = {}, shorts = {}, returns = {}. Please file a bug report.",
|
||||
div = {}, shorts = {}, returns = {}. Please file a bug report.",
|
||||
cc,
|
||||
narms,
|
||||
div,
|
||||
@ -210,9 +206,9 @@ fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, r
|
||||
span,
|
||||
&format!(
|
||||
"Clippy encountered a bug calculating cyclomatic complexity \
|
||||
(hide this message with `#[allow(cyclomatic_complexity)]`): \
|
||||
cc = {}, arms = {}, div = {}, shorts = {}, returns = {}. \
|
||||
Please file a bug report.",
|
||||
(hide this message with `#[allow(cyclomatic_complexity)]`): \
|
||||
cc = {}, arms = {}, div = {}, shorts = {}, returns = {}. \
|
||||
Please file a bug report.",
|
||||
cc,
|
||||
narms,
|
||||
div,
|
||||
|
@ -3,7 +3,7 @@ use rustc::ty::{self, Ty};
|
||||
use rustc::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::paths;
|
||||
use utils::{is_automatically_derived, span_lint_and_then, match_path, is_copy};
|
||||
use utils::{is_automatically_derived, is_copy, match_path, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
|
||||
/// explicitly.
|
||||
@ -141,31 +141,31 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref
|
||||
ty::TyAdt(def, _) if def.is_union() => return,
|
||||
|
||||
// Some types are not Clone by default but could be cloned “by hand” if necessary
|
||||
ty::TyAdt(def, substs) => {
|
||||
for variant in &def.variants {
|
||||
for field in &variant.fields {
|
||||
match field.ty(cx.tcx, substs).sty {
|
||||
ty::TyArray(_, size) if size > 32 => {
|
||||
return;
|
||||
},
|
||||
ty::TyFnPtr(..) => {
|
||||
return;
|
||||
},
|
||||
ty::TyTuple(tys, _) if tys.len() > 12 => {
|
||||
return;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
ty::TyAdt(def, substs) => for variant in &def.variants {
|
||||
for field in &variant.fields {
|
||||
match field.ty(cx.tcx, substs).sty {
|
||||
ty::TyArray(_, size) if size > 32 => {
|
||||
return;
|
||||
},
|
||||
ty::TyFnPtr(..) => {
|
||||
return;
|
||||
},
|
||||
ty::TyTuple(tys, _) if tys.len() > 12 => {
|
||||
return;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
span_lint_and_then(cx,
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
item.span,
|
||||
"you are implementing `Clone` explicitly on a `Copy` type",
|
||||
|db| { db.span_note(item.span, "consider deriving `Clone` or removing `Copy`"); });
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
item.span,
|
||||
"you are implementing `Clone` explicitly on a `Copy` type",
|
||||
|db| { db.span_note(item.span, "consider deriving `Clone` or removing `Copy`"); },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use itertools::Itertools;
|
||||
use pulldown_cmark;
|
||||
use rustc::lint::*;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::{Span, BytePos};
|
||||
use syntax::codemap::{BytePos, Span};
|
||||
use syntax_pos::Pos;
|
||||
use utils::span_lint;
|
||||
|
||||
@ -37,7 +37,9 @@ pub struct Doc {
|
||||
|
||||
impl Doc {
|
||||
pub fn new(valid_idents: Vec<String>) -> Self {
|
||||
Self { valid_idents: valid_idents }
|
||||
Self {
|
||||
valid_idents: valid_idents,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,17 +198,13 @@ fn check_doc<'a, Events: Iterator<Item = (usize, pulldown_cmark::Event<'a>)>>(
|
||||
|
||||
for (offset, event) in docs {
|
||||
match event {
|
||||
Start(CodeBlock(_)) |
|
||||
Start(Code) => in_code = true,
|
||||
End(CodeBlock(_)) |
|
||||
End(Code) => in_code = false,
|
||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||
Html(_html) |
|
||||
InlineHtml(_html) => (), // HTML is weird, just ignore it
|
||||
Start(CodeBlock(_)) | Start(Code) => in_code = true,
|
||||
End(CodeBlock(_)) | End(Code) => in_code = false,
|
||||
Start(_tag) | End(_tag) => (), // We don't care about other tags
|
||||
Html(_html) | InlineHtml(_html) => (), // HTML is weird, just ignore it
|
||||
SoftBreak => (),
|
||||
HardBreak => (),
|
||||
FootnoteReference(text) |
|
||||
Text(text) => {
|
||||
FootnoteReference(text) | Text(text) => {
|
||||
if !in_code {
|
||||
let index = match spans.binary_search_by(|c| c.0.cmp(&offset)) {
|
||||
Ok(o) => o,
|
||||
|
@ -1,5 +1,5 @@
|
||||
use syntax::ast::*;
|
||||
use rustc::lint::{EarlyContext, LintContext, LintArray, LintPass, EarlyLintPass};
|
||||
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
|
||||
|
||||
/// **What it does:** Checks for unnecessary double parentheses.
|
||||
///
|
||||
@ -31,29 +31,22 @@ impl LintPass for DoubleParens {
|
||||
impl EarlyLintPass for DoubleParens {
|
||||
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
|
||||
match expr.node {
|
||||
ExprKind::Paren(ref in_paren) => {
|
||||
match in_paren.node {
|
||||
ExprKind::Paren(_) |
|
||||
ExprKind::Tup(_) => {
|
||||
cx.span_lint(DOUBLE_PARENS, expr.span, "Consider removing unnecessary double parentheses");
|
||||
},
|
||||
_ => {},
|
||||
ExprKind::Paren(ref in_paren) => match in_paren.node {
|
||||
ExprKind::Paren(_) | ExprKind::Tup(_) => {
|
||||
cx.span_lint(DOUBLE_PARENS, expr.span, "Consider removing unnecessary double parentheses");
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
ExprKind::Call(_, ref params) => if params.len() == 1 {
|
||||
let param = ¶ms[0];
|
||||
if let ExprKind::Paren(_) = param.node {
|
||||
cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses");
|
||||
}
|
||||
},
|
||||
ExprKind::Call(_, ref params) => {
|
||||
if params.len() == 1 {
|
||||
let param = ¶ms[0];
|
||||
if let ExprKind::Paren(_) = param.node {
|
||||
cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses");
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(_, ref params) => {
|
||||
if params.len() == 2 {
|
||||
let param = ¶ms[1];
|
||||
if let ExprKind::Paren(_) = param.node {
|
||||
cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses");
|
||||
}
|
||||
ExprKind::MethodCall(_, ref params) => if params.len() == 2 {
|
||||
let param = ¶ms[1];
|
||||
if let ExprKind::Paren(_) = param.node {
|
||||
cx.span_lint(DOUBLE_PARENS, param.span, "Consider removing unnecessary double parentheses");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use utils::{match_def_path, paths, span_note_and_lint, is_copy};
|
||||
use utils::{is_copy, match_def_path, paths, span_note_and_lint};
|
||||
|
||||
/// **What it does:** Checks for calls to `std::mem::drop` with a reference
|
||||
/// instead of an owned value.
|
||||
@ -96,13 +96,13 @@ declare_lint! {
|
||||
}
|
||||
|
||||
const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
|
||||
Dropping a reference does nothing.";
|
||||
Dropping a reference does nothing.";
|
||||
const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
|
||||
Forgetting a reference does nothing.";
|
||||
Forgetting a reference does nothing.";
|
||||
const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements Copy. \
|
||||
Dropping a copy leaves the original intact.";
|
||||
Dropping a copy leaves the original intact.";
|
||||
const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements Copy. \
|
||||
Forgetting a copy leaves the original intact.";
|
||||
Forgetting a copy leaves the original intact.";
|
||||
|
||||
#[allow(missing_copy_implementations)]
|
||||
pub struct Pass;
|
||||
|
@ -36,9 +36,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
|
||||
let did = cx.tcx.hir.local_def_id(item.id);
|
||||
if let ItemEnum(..) = item.node {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
let adt = ty.ty_adt_def().expect(
|
||||
"already checked whether this is an enum",
|
||||
);
|
||||
let adt = ty.ty_adt_def()
|
||||
.expect("already checked whether this is an enum");
|
||||
if adt.variants.is_empty() {
|
||||
span_lint_and_then(cx, EMPTY_ENUM, item.span, "enum with no variants", |db| {
|
||||
db.span_help(item.span, "consider using the uninhabited type `!` or a wrapper around it");
|
||||
|
@ -1,5 +1,5 @@
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc::lint::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::SpanlessEq;
|
||||
@ -47,12 +47,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapLint {
|
||||
// in case of `if !m.contains_key(&k) { m.insert(k, v); }`
|
||||
// we can give a better error message
|
||||
let sole_expr = {
|
||||
else_block.is_none() &&
|
||||
if let ExprBlock(ref then_block) = then_block.node {
|
||||
(then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
|
||||
} else {
|
||||
true
|
||||
}
|
||||
else_block.is_none() && if let ExprBlock(ref then_block) = then_block.node {
|
||||
(then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
|
||||
} else {
|
||||
true
|
||||
}
|
||||
};
|
||||
|
||||
let mut visitor = InsertVisitor {
|
||||
|
@ -51,9 +51,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
|
||||
let did = cx.tcx.hir.body_owner_def_id(body_id);
|
||||
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
|
||||
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
|
||||
let bad = match cx.tcx.at(expr.span).const_eval(
|
||||
param_env.and((did, substs)),
|
||||
) {
|
||||
let bad = match cx.tcx
|
||||
.at(expr.span)
|
||||
.const_eval(param_env.and((did, substs)))
|
||||
{
|
||||
Ok(ConstVal::Integral(Usize(Us64(i)))) => u64::from(i as u32) != i,
|
||||
Ok(ConstVal::Integral(Isize(Is64(i)))) => i64::from(i as i32) != i,
|
||||
_ => false,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! lint on `use`ing all variants of an enum
|
||||
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::codemap::Span;
|
||||
use utils::span_lint;
|
||||
|
@ -204,7 +204,7 @@ fn check_variant(
|
||||
&format!("All variants have the same {}fix: `{}`", what, value),
|
||||
&format!(
|
||||
"remove the {}fixes and use full paths to \
|
||||
the variants instead of glob imports",
|
||||
the variants instead of glob imports",
|
||||
what
|
||||
),
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use utils::{SpanlessEq, span_lint, span_lint_and_then, multispan_sugg, snippet, implements_trait, is_copy};
|
||||
use utils::{implements_trait, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq};
|
||||
|
||||
/// **What it does:** Checks for equal operands to comparison, logical and
|
||||
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
|
||||
@ -82,8 +82,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
|
||||
#[allow(match_same_arms)]
|
||||
match (&left.node, &right.node) {
|
||||
// do not suggest to dereference literals
|
||||
(&ExprLit(..), _) |
|
||||
(_, &ExprLit(..)) => {},
|
||||
(&ExprLit(..), _) | (_, &ExprLit(..)) => {},
|
||||
// &foo == &bar
|
||||
(&ExprAddrOf(_, ref l), &ExprAddrOf(_, ref r)) => {
|
||||
let lty = cx.tables.expr_ty(l);
|
||||
|
@ -133,7 +133,6 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
self.set.remove(&lid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
fn borrow(&mut self, _: NodeId, _: Span, cmt: cmt<'tcx>, _: ty::Region, _: ty::BorrowKind, loan_cause: LoanCause) {
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use utils::{snippet_opt, span_lint_and_then, is_adjusted, iter_input_pats};
|
||||
use utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then};
|
||||
|
||||
#[allow(missing_copy_implementations)]
|
||||
pub struct EtaPass;
|
||||
@ -37,11 +37,8 @@ impl LintPass for EtaPass {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
match expr.node {
|
||||
ExprCall(_, ref args) |
|
||||
ExprMethodCall(_, _, ref args) => {
|
||||
for arg in args {
|
||||
check_closure(cx, arg)
|
||||
}
|
||||
ExprCall(_, ref args) | ExprMethodCall(_, _, ref args) => for arg in args {
|
||||
check_closure(cx, arg)
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::*;
|
||||
use rustc::ty;
|
||||
use rustc::lint::*;
|
||||
use utils::{get_parent_expr, span_note_and_lint, span_lint};
|
||||
use utils::{get_parent_expr, span_lint, span_note_and_lint};
|
||||
|
||||
/// **What it does:** Checks for a read and a write to the same variable where
|
||||
/// whether the read occurs before or after the write depends on the evaluation
|
||||
@ -62,20 +62,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
// Find a write to a local variable.
|
||||
match expr.node {
|
||||
ExprAssign(ref lhs, _) |
|
||||
ExprAssignOp(_, ref lhs, _) => {
|
||||
if let ExprPath(ref qpath) = lhs.node {
|
||||
if let QPath::Resolved(_, ref path) = *qpath {
|
||||
if path.segments.len() == 1 {
|
||||
let var = cx.tables.qpath_def(qpath, lhs.hir_id).def_id();
|
||||
let mut visitor = ReadVisitor {
|
||||
cx: cx,
|
||||
var: var,
|
||||
write_expr: expr,
|
||||
last_expr: expr,
|
||||
};
|
||||
check_for_unsequenced_reads(&mut visitor);
|
||||
}
|
||||
ExprAssign(ref lhs, _) | ExprAssignOp(_, ref lhs, _) => if let ExprPath(ref qpath) = lhs.node {
|
||||
if let QPath::Resolved(_, ref path) = *qpath {
|
||||
if path.segments.len() == 1 {
|
||||
let var = cx.tables.qpath_def(qpath, lhs.hir_id).def_id();
|
||||
let mut visitor = ReadVisitor {
|
||||
cx: cx,
|
||||
var: var,
|
||||
write_expr: expr,
|
||||
last_expr: expr,
|
||||
};
|
||||
check_for_unsequenced_reads(&mut visitor);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -84,13 +81,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
|
||||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
|
||||
match stmt.node {
|
||||
StmtExpr(ref e, _) |
|
||||
StmtSemi(ref e, _) => DivergenceVisitor { cx: cx }.maybe_walk_expr(e),
|
||||
StmtDecl(ref d, _) => {
|
||||
if let DeclLocal(ref local) = d.node {
|
||||
if let Local { init: Some(ref e), .. } = **local {
|
||||
DivergenceVisitor { cx: cx }.visit_expr(e);
|
||||
}
|
||||
StmtExpr(ref e, _) | StmtSemi(ref e, _) => DivergenceVisitor { cx: cx }.maybe_walk_expr(e),
|
||||
StmtDecl(ref d, _) => if let DeclLocal(ref local) = d.node {
|
||||
if let Local {
|
||||
init: Some(ref e), ..
|
||||
} = **local
|
||||
{
|
||||
DivergenceVisitor { cx: cx }.visit_expr(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -230,8 +227,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr) -> St
|
||||
ExprStruct(_, _, _) => {
|
||||
walk_expr(vis, expr);
|
||||
},
|
||||
ExprBinary(op, _, _) |
|
||||
ExprAssignOp(op, _, _) => {
|
||||
ExprBinary(op, _, _) | ExprAssignOp(op, _, _) => {
|
||||
if op.node == BiAnd || op.node == BiOr {
|
||||
// x && y and x || y always evaluate x first, so these are
|
||||
// strictly sequenced.
|
||||
@ -265,8 +261,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr) -> St
|
||||
|
||||
fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt) -> StopEarly {
|
||||
match stmt.node {
|
||||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => check_expr(vis, expr),
|
||||
StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => check_expr(vis, expr),
|
||||
StmtDecl(ref decl, _) => {
|
||||
// If the declaration is of a local variable, check its initializer
|
||||
// expression if it has one. Otherwise, keep going.
|
||||
@ -274,10 +269,9 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt) -> St
|
||||
DeclLocal(ref local) => Some(local),
|
||||
_ => None,
|
||||
};
|
||||
local.and_then(|local| local.init.as_ref()).map_or(
|
||||
StopEarly::KeepGoing,
|
||||
|expr| check_expr(vis, expr),
|
||||
)
|
||||
local
|
||||
.and_then(|local| local.init.as_ref())
|
||||
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -57,11 +57,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
}}
|
||||
},
|
||||
// `format!("foo")` expansion contains `match () { () => [], }`
|
||||
ExprMatch(ref matchee, _, _) => {
|
||||
if let ExprTup(ref tup) = matchee.node {
|
||||
if tup.is_empty() {
|
||||
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
|
||||
}
|
||||
ExprMatch(ref matchee, _, _) => if let ExprTup(ref tup) = matchee.node {
|
||||
if tup.is_empty() {
|
||||
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
|
@ -117,7 +117,7 @@ fn check_assign(cx: &EarlyContext, expr: &ast::Expr) {
|
||||
eqop_span,
|
||||
&format!(
|
||||
"this looks like you are trying to use `.. {op}= ..`, but you \
|
||||
really are doing `.. = ({op} ..)`",
|
||||
really are doing `.. = ({op} ..)`",
|
||||
op = op
|
||||
),
|
||||
eqop_span,
|
||||
@ -142,9 +142,9 @@ fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
|
||||
// the snippet should look like " else \n " with maybe comments anywhere
|
||||
// it’s bad when there is a ‘\n’ after the “else”
|
||||
if let Some(else_snippet) = snippet_opt(cx, else_span) {
|
||||
let else_pos = else_snippet.find("else").expect(
|
||||
"there must be a `else` here",
|
||||
);
|
||||
let else_pos = else_snippet
|
||||
.find("else")
|
||||
.expect("there must be a `else` here");
|
||||
|
||||
if else_snippet[else_pos..].contains('\n') {
|
||||
span_note_and_lint(
|
||||
@ -154,7 +154,7 @@ fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
|
||||
"this is an `else if` but the formatting might hide it",
|
||||
else_span,
|
||||
"to remove this lint, remove the `else` or remove the new line between `else` \
|
||||
and `if`",
|
||||
and `if`",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -205,7 +205,7 @@ fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Exp
|
||||
"this looks like an `else if` but the `else` is missing",
|
||||
else_span,
|
||||
"to remove this lint, add the missing `else` or add a new line before the second \
|
||||
`if`",
|
||||
`if`",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -215,8 +215,9 @@ fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Exp
|
||||
/// Match `if` or `if let` expressions and return the `then` and `else` block.
|
||||
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
|
||||
match expr.node {
|
||||
ast::ExprKind::If(_, ref then, ref else_) |
|
||||
ast::ExprKind::IfLet(_, _, ref then, ref else_) => Some((then, else_)),
|
||||
ast::ExprKind::If(_, ref then, ref else_) | ast::ExprKind::IfLet(_, _, ref then, ref else_) => {
|
||||
Some((then, else_))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use std::collections::HashSet;
|
||||
use syntax::ast;
|
||||
use syntax::abi::Abi;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{span_lint, type_is_unsafe_function, iter_input_pats};
|
||||
use utils::{iter_input_pats, span_lint, type_is_unsafe_function};
|
||||
|
||||
/// **What it does:** Checks for functions with too many parameters.
|
||||
///
|
||||
@ -60,7 +60,9 @@ pub struct Functions {
|
||||
|
||||
impl Functions {
|
||||
pub fn new(threshold: u64) -> Self {
|
||||
Self { threshold: threshold }
|
||||
Self {
|
||||
threshold: threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ use consts::{constant_simple, Constant};
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{span_lint, snippet, in_macro};
|
||||
use utils::{in_macro, snippet, span_lint};
|
||||
use syntax::attr::IntType::{SignedInt, UnsignedInt};
|
||||
|
||||
/// **What it does:** Checks for identity operations, e.g. `x + 0`.
|
||||
@ -63,16 +63,13 @@ fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
|
||||
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
|
||||
if match m {
|
||||
0 => v.to_u128_unchecked() == 0,
|
||||
-1 => {
|
||||
match v.int_type() {
|
||||
SignedInt(_) => (v.to_u128_unchecked() as i128 == -1),
|
||||
UnsignedInt(_) => false,
|
||||
}
|
||||
-1 => match v.int_type() {
|
||||
SignedInt(_) => (v.to_u128_unchecked() as i128 == -1),
|
||||
UnsignedInt(_) => false,
|
||||
},
|
||||
1 => v.to_u128_unchecked() == 1,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
{
|
||||
} {
|
||||
span_lint(
|
||||
cx,
|
||||
IDENTITY_OP,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{paths, span_lint_and_then, match_qpath, snippet};
|
||||
use utils::{match_qpath, paths, snippet, span_lint_and_then};
|
||||
|
||||
/// **What it does:*** Lint for redundant pattern matching over `Result` or
|
||||
/// `Option`
|
||||
@ -45,11 +45,8 @@ impl LintPass for Pass {
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
|
||||
if let ExprMatch(ref op, ref arms, MatchSource::IfLetDesugar { .. }) = expr.node {
|
||||
|
||||
if arms[0].pats.len() == 1 {
|
||||
|
||||
let good_method = match arms[0].pats[0].node {
|
||||
PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 && pats[0].node == PatKind::Wild => {
|
||||
if match_qpath(path, &paths::RESULT_OK) {
|
||||
@ -68,16 +65,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pats[0].span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|db| {
|
||||
let span = expr.span.with_hi(op.span.hi());
|
||||
db.span_suggestion(span, "try this", format!("if {}.{}", snippet(cx, op.span, "_"), good_method));
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IF_LET_REDUNDANT_PATTERN_MATCHING,
|
||||
arms[0].pats[0].span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|db| {
|
||||
let span = expr.span.with_hi(op.span.hi());
|
||||
db.span_suggestion(
|
||||
span,
|
||||
"try this",
|
||||
format!("if {}.{}", snippet(cx, op.span, "_"), good_method),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use utils::{get_trait_def_id, implements_trait, higher, match_qpath, paths, span_lint};
|
||||
use utils::{get_trait_def_id, higher, implements_trait, match_qpath, paths, span_lint};
|
||||
|
||||
/// **What it does:** Checks for iteration that is guaranteed to be infinite.
|
||||
///
|
||||
@ -66,14 +66,13 @@ enum Finiteness {
|
||||
Finite,
|
||||
}
|
||||
|
||||
use self::Finiteness::{Infinite, MaybeInfinite, Finite};
|
||||
use self::Finiteness::{Finite, Infinite, MaybeInfinite};
|
||||
|
||||
impl Finiteness {
|
||||
fn and(self, b: Self) -> Self {
|
||||
match (self, b) {
|
||||
(Finite, _) | (_, Finite) => Finite,
|
||||
(MaybeInfinite, _) |
|
||||
(_, MaybeInfinite) => MaybeInfinite,
|
||||
(MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
|
||||
_ => Infinite,
|
||||
}
|
||||
}
|
||||
@ -81,8 +80,7 @@ impl Finiteness {
|
||||
fn or(self, b: Self) -> Self {
|
||||
match (self, b) {
|
||||
(Infinite, _) | (_, Infinite) => Infinite,
|
||||
(MaybeInfinite, _) |
|
||||
(_, MaybeInfinite) => MaybeInfinite,
|
||||
(MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
|
||||
_ => Finite,
|
||||
}
|
||||
}
|
||||
@ -90,7 +88,11 @@ impl Finiteness {
|
||||
|
||||
impl From<bool> for Finiteness {
|
||||
fn from(b: bool) -> Self {
|
||||
if b { Infinite } else { Finite }
|
||||
if b {
|
||||
Infinite
|
||||
} else {
|
||||
Finite
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ enum Heuristic {
|
||||
All,
|
||||
}
|
||||
|
||||
use self::Heuristic::{Always, First, Any, All};
|
||||
use self::Heuristic::{All, Always, Any, First};
|
||||
|
||||
/// a slice of (method name, number of args, heuristic, bounds) tuples
|
||||
/// that will be used to determine whether the method in question
|
||||
@ -143,11 +145,11 @@ fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
|
||||
for &(name, len, heuristic, cap) in HEURISTICS.iter() {
|
||||
if method.name == name && args.len() == len {
|
||||
return (match heuristic {
|
||||
Always => Infinite,
|
||||
First => is_infinite(cx, &args[0]),
|
||||
Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
|
||||
All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
|
||||
}).and(cap);
|
||||
Always => Infinite,
|
||||
First => is_infinite(cx, &args[0]),
|
||||
Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
|
||||
All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
|
||||
}).and(cap);
|
||||
}
|
||||
}
|
||||
if method.name == "flat_map" && args.len() == 2 {
|
||||
@ -159,20 +161,15 @@ fn is_infinite(cx: &LateContext, expr: &Expr) -> Finiteness {
|
||||
Finite
|
||||
},
|
||||
ExprBlock(ref block) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
|
||||
ExprBox(ref e) |
|
||||
ExprAddrOf(_, ref e) => is_infinite(cx, e),
|
||||
ExprCall(ref path, _) => {
|
||||
if let ExprPath(ref qpath) = path.node {
|
||||
match_qpath(qpath, &paths::REPEAT).into()
|
||||
} else {
|
||||
Finite
|
||||
}
|
||||
},
|
||||
ExprStruct(..) => {
|
||||
higher::range(expr)
|
||||
.map_or(false, |r| r.end.is_none())
|
||||
.into()
|
||||
ExprBox(ref e) | ExprAddrOf(_, ref e) => is_infinite(cx, e),
|
||||
ExprCall(ref path, _) => if let ExprPath(ref qpath) = path.node {
|
||||
match_qpath(qpath, &paths::REPEAT).into()
|
||||
} else {
|
||||
Finite
|
||||
},
|
||||
ExprStruct(..) => higher::range(expr)
|
||||
.map_or(false, |r| r.end.is_none())
|
||||
.into(),
|
||||
_ => Finite,
|
||||
}
|
||||
}
|
||||
@ -220,23 +217,18 @@ fn complete_infinite_iter(cx: &LateContext, expr: &Expr) -> Finiteness {
|
||||
}
|
||||
}
|
||||
if method.name == "last" && args.len() == 1 {
|
||||
let not_double_ended = get_trait_def_id(cx,
|
||||
&paths::DOUBLE_ENDED_ITERATOR)
|
||||
.map_or(false, |id| {
|
||||
!implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[])
|
||||
});
|
||||
let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR)
|
||||
.map_or(false, |id| !implements_trait(cx, cx.tables.expr_ty(&args[0]), id, &[]));
|
||||
if not_double_ended {
|
||||
return is_infinite(cx, &args[0]);
|
||||
return is_infinite(cx, &args[0]);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprBinary(op, ref l, ref r) => {
|
||||
if op.node.is_comparison() {
|
||||
return is_infinite(cx, l).and(is_infinite(cx, r)).and(
|
||||
MaybeInfinite,
|
||||
);
|
||||
}
|
||||
}, //TODO: ExprLoop + Match
|
||||
ExprBinary(op, ref l, ref r) => if op.node.is_comparison() {
|
||||
return is_infinite(cx, l)
|
||||
.and(is_infinite(cx, r))
|
||||
.and(MaybeInfinite);
|
||||
}, // TODO: ExprLoop + Match
|
||||
_ => (),
|
||||
}
|
||||
Finite
|
||||
|
@ -100,12 +100,10 @@ impl EarlyLintPass for UnitExpr {
|
||||
}
|
||||
fn is_unit_expr(expr: &Expr) -> Option<Span> {
|
||||
match expr.node {
|
||||
ExprKind::Block(ref block) => {
|
||||
if check_last_stmt_in_block(block) {
|
||||
Some(block.stmts[block.stmts.len() - 1].span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ExprKind::Block(ref block) => if check_last_stmt_in_block(block) {
|
||||
Some(block.stmts[block.stmts.len() - 1].span)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
ExprKind::If(_, ref then, ref else_) => {
|
||||
let check_then = check_last_stmt_in_block(then);
|
||||
@ -115,7 +113,11 @@ fn is_unit_expr(expr: &Expr) -> Option<Span> {
|
||||
return Some(*expr_else);
|
||||
}
|
||||
}
|
||||
if check_then { Some(expr.span) } else { None }
|
||||
if check_then {
|
||||
Some(expr.span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
ExprKind::Match(ref _pattern, ref arms) => {
|
||||
for arm in arms {
|
||||
@ -137,12 +139,9 @@ fn check_last_stmt_in_block(block: &Block) -> bool {
|
||||
// like `panic!()`
|
||||
match final_stmt.node {
|
||||
StmtKind::Expr(_) => false,
|
||||
StmtKind::Semi(ref expr) => {
|
||||
match expr.node {
|
||||
ExprKind::Break(_, _) |
|
||||
ExprKind::Ret(_) => false,
|
||||
_ => true,
|
||||
}
|
||||
StmtKind::Semi(ref expr) => match expr.node {
|
||||
ExprKind::Break(_, _) | ExprKind::Ret(_) => false,
|
||||
_ => true,
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
|
@ -47,9 +47,10 @@ impl EarlyLintPass for ItemsAfterStatements {
|
||||
}
|
||||
|
||||
// skip initial items
|
||||
let stmts = item.stmts.iter().map(|stmt| &stmt.node).skip_while(|s| {
|
||||
matches!(**s, StmtKind::Item(..))
|
||||
});
|
||||
let stmts = item.stmts
|
||||
.iter()
|
||||
.map(|stmt| &stmt.node)
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
|
||||
|
||||
// lint on all further items
|
||||
for stmt in stmts {
|
||||
@ -66,7 +67,7 @@ impl EarlyLintPass for ItemsAfterStatements {
|
||||
ITEMS_AFTER_STATEMENTS,
|
||||
it.span,
|
||||
"adding items after statements is confusing, since items exist from the \
|
||||
start of the scope",
|
||||
start of the scope",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{span_lint_and_then, snippet_opt, type_size};
|
||||
use utils::{snippet_opt, span_lint_and_then, type_size};
|
||||
use rustc::ty::TypeFoldable;
|
||||
|
||||
/// **What it does:** Checks for large size differences between variants on
|
||||
@ -34,7 +34,9 @@ pub struct LargeEnumVariant {
|
||||
|
||||
impl LargeEnumVariant {
|
||||
pub fn new(maximum_size_difference_allowed: u64) -> Self {
|
||||
Self { maximum_size_difference_allowed: maximum_size_difference_allowed }
|
||||
Self {
|
||||
maximum_size_difference_allowed: maximum_size_difference_allowed,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,9 +51,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
|
||||
let did = cx.tcx.hir.local_def_id(item.id);
|
||||
if let ItemEnum(ref def, _) = item.node {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
let adt = ty.ty_adt_def().expect(
|
||||
"already checked whether this is an enum",
|
||||
);
|
||||
let adt = ty.ty_adt_def()
|
||||
.expect("already checked whether this is an enum");
|
||||
|
||||
let mut smallest_variant: Option<(_, _)> = None;
|
||||
let mut largest_variant: Option<(_, _)> = None;
|
||||
@ -90,15 +91,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
|
||||
|db| {
|
||||
if variant.fields.len() == 1 {
|
||||
let span = match def.variants[i].node.data {
|
||||
VariantData::Struct(ref fields, _) |
|
||||
VariantData::Tuple(ref fields, _) => fields[0].ty.span,
|
||||
VariantData::Struct(ref fields, _) | VariantData::Tuple(ref fields, _) => {
|
||||
fields[0].ty.span
|
||||
},
|
||||
VariantData::Unit(_) => unreachable!(),
|
||||
};
|
||||
if let Some(snip) = snippet_opt(cx, span) {
|
||||
db.span_suggestion(
|
||||
span,
|
||||
"consider boxing the large fields to reduce the total size of the \
|
||||
enum",
|
||||
enum",
|
||||
format!("Box<{}>", snip),
|
||||
);
|
||||
return;
|
||||
@ -112,7 +114,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,16 +91,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
|
||||
|
||||
fn check_trait_items(cx: &LateContext, visited_trait: &Item, trait_items: &[TraitItemRef]) {
|
||||
fn is_named_self(cx: &LateContext, item: &TraitItemRef, name: &str) -> bool {
|
||||
item.name == name &&
|
||||
if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self &&
|
||||
{
|
||||
let did = cx.tcx.hir.local_def_id(item.id.node_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
false
|
||||
item.name == name && if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self && {
|
||||
let did = cx.tcx.hir.local_def_id(item.id.node_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// fill the set with current and super traits
|
||||
@ -121,10 +119,8 @@ fn check_trait_items(cx: &LateContext, visited_trait: &Item, trait_items: &[Trai
|
||||
.iter()
|
||||
.flat_map(|&i| cx.tcx.associated_items(i))
|
||||
.any(|i| {
|
||||
i.kind == ty::AssociatedKind::Method &&
|
||||
i.method_has_self_argument &&
|
||||
i.name == "is_empty" &&
|
||||
cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
|
||||
i.kind == ty::AssociatedKind::Method && i.method_has_self_argument && i.name == "is_empty" &&
|
||||
cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
|
||||
});
|
||||
|
||||
if !is_empty_method_found {
|
||||
@ -143,16 +139,14 @@ fn check_trait_items(cx: &LateContext, visited_trait: &Item, trait_items: &[Trai
|
||||
|
||||
fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItemRef]) {
|
||||
fn is_named_self(cx: &LateContext, item: &ImplItemRef, name: &str) -> bool {
|
||||
item.name == name &&
|
||||
if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self &&
|
||||
{
|
||||
let did = cx.tcx.hir.local_def_id(item.id.node_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
false
|
||||
item.name == name && if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self && {
|
||||
let did = cx.tcx.hir.local_def_id(item.id.node_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
|
||||
@ -197,7 +191,11 @@ fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str)
|
||||
}
|
||||
|
||||
fn check_len_zero(cx: &LateContext, span: Span, name: Name, args: &[Expr], lit: &Lit, op: &str) {
|
||||
if let Spanned { node: LitKind::Int(0, _), .. } = *lit {
|
||||
if let Spanned {
|
||||
node: LitKind::Int(0, _),
|
||||
..
|
||||
} = *lit
|
||||
{
|
||||
if name == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
@ -231,25 +229,19 @@ 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(id).iter().any(|imp| {
|
||||
cx.tcx.associated_items(*imp).any(
|
||||
|item| is_is_empty(cx, &item),
|
||||
)
|
||||
cx.tcx
|
||||
.associated_items(*imp)
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
})
|
||||
}
|
||||
|
||||
let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr));
|
||||
match ty.sty {
|
||||
ty::TyDynamic(..) => {
|
||||
cx.tcx
|
||||
.associated_items(ty.ty_to_def_id().expect("trait impl not found"))
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
},
|
||||
ty::TyProjection(_) => {
|
||||
ty.ty_to_def_id().map_or(
|
||||
false,
|
||||
|id| has_is_empty_impl(cx, id),
|
||||
)
|
||||
},
|
||||
ty::TyDynamic(..) => cx.tcx
|
||||
.associated_items(ty.ty_to_def_id().expect("trait impl not found"))
|
||||
.any(|item| is_is_empty(cx, &item)),
|
||||
ty::TyProjection(_) => ty.ty_to_def_id()
|
||||
.map_or(false, |id| has_is_empty_impl(cx, id)),
|
||||
ty::TyAdt(id, _) => has_is_empty_impl(cx, id.did),
|
||||
ty::TyArray(..) | ty::TySlice(..) | ty::TyStr => true,
|
||||
_ => false,
|
||||
|
@ -8,39 +8,42 @@
|
||||
#![feature(slice_patterns)]
|
||||
#![feature(stmt_expr_attributes)]
|
||||
#![feature(conservative_impl_trait)]
|
||||
|
||||
#![allow(unknown_lints, indexing_slicing, shadow_reuse, missing_docs_in_private_items)]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate syntax_pos;
|
||||
#[macro_use]
|
||||
extern crate rustc;
|
||||
extern crate syntax;
|
||||
extern crate syntax_pos;
|
||||
|
||||
extern crate toml;
|
||||
|
||||
// for unicode nfc normalization
|
||||
|
||||
extern crate unicode_normalization;
|
||||
|
||||
// for semver check in attrs.rs
|
||||
|
||||
extern crate semver;
|
||||
|
||||
// for regex checking
|
||||
|
||||
extern crate regex_syntax;
|
||||
|
||||
// for finding minimal boolean expressions
|
||||
|
||||
extern crate quine_mc_cluskey;
|
||||
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_plugin;
|
||||
extern crate rustc_const_eval;
|
||||
extern crate rustc_const_math;
|
||||
extern crate rustc_errors;
|
||||
extern crate rustc_plugin;
|
||||
|
||||
#[macro_use]
|
||||
extern crate matches as matches_macro;
|
||||
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
@ -2,10 +2,10 @@ use reexport::*;
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl, walk_generics, NestedVisitorMap};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use rustc::hir::intravisit::{walk_fn_decl, walk_generics, walk_ty, walk_ty_param_bound, NestedVisitorMap, Visitor};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use syntax::codemap::Span;
|
||||
use utils::{in_external_macro, span_lint, last_path_segment};
|
||||
use utils::{in_external_macro, last_path_segment, span_lint};
|
||||
use syntax::symbol::keywords;
|
||||
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
@ -171,7 +171,9 @@ fn could_use_elision<'a, 'tcx: 'a>(
|
||||
};
|
||||
|
||||
if let Some(body_id) = body {
|
||||
let mut checker = BodyLifetimeChecker { lifetimes_used_in_body: false };
|
||||
let mut checker = BodyLifetimeChecker {
|
||||
lifetimes_used_in_body: false,
|
||||
};
|
||||
checker.visit_expr(&cx.tcx.hir.body(body_id).value);
|
||||
if checker.lifetimes_used_in_body {
|
||||
return false;
|
||||
@ -192,9 +194,9 @@ fn could_use_elision<'a, 'tcx: 'a>(
|
||||
// no output lifetimes, check distinctness of input lifetimes
|
||||
|
||||
// only unnamed and static, ok
|
||||
let unnamed_and_static = input_lts.iter().all(|lt| {
|
||||
*lt == RefLt::Unnamed || *lt == RefLt::Static
|
||||
});
|
||||
let unnamed_and_static = input_lts
|
||||
.iter()
|
||||
.all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
|
||||
if unnamed_and_static {
|
||||
return false;
|
||||
}
|
||||
@ -210,8 +212,8 @@ fn could_use_elision<'a, 'tcx: 'a>(
|
||||
match (&input_lts[0], &output_lts[0]) {
|
||||
(&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
|
||||
(&RefLt::Named(_), &RefLt::Unnamed) => true,
|
||||
_ => false, // already elided, different named lifetimes
|
||||
// or something static going on
|
||||
_ => false, /* already elided, different named lifetimes
|
||||
* or something static going on */
|
||||
}
|
||||
} else {
|
||||
false
|
||||
@ -277,7 +279,11 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Option<Vec<RefLt>> {
|
||||
if self.abort { None } else { Some(self.lts) }
|
||||
if self.abort {
|
||||
None
|
||||
} else {
|
||||
Some(self.lts)
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
|
||||
@ -285,8 +291,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
||||
if !last_path_segment.parenthesized && last_path_segment.lifetimes.is_empty() {
|
||||
let hir_id = self.cx.tcx.hir.node_to_hir_id(ty.id);
|
||||
match self.cx.tables.qpath_def(qpath, hir_id) {
|
||||
Def::TyAlias(def_id) |
|
||||
Def::Struct(def_id) => {
|
||||
Def::TyAlias(def_id) | Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.generics_of(def_id);
|
||||
for _ in generics.regions.as_slice() {
|
||||
self.record(&None);
|
||||
@ -318,11 +323,9 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||
TyPath(ref path) => {
|
||||
self.collect_anonymous_lifetimes(path, ty);
|
||||
},
|
||||
TyImplTrait(ref param_bounds) => {
|
||||
for bound in param_bounds {
|
||||
if let RegionTyParamBound(_) = *bound {
|
||||
self.record(&None);
|
||||
}
|
||||
TyImplTrait(ref param_bounds) => for bound in param_bounds {
|
||||
if let RegionTyParamBound(_) = *bound {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
TyTraitObject(ref bounds, ref lt) => {
|
||||
@ -366,11 +369,9 @@ fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &
|
||||
// and check that all lifetimes are allowed
|
||||
match visitor.into_vec() {
|
||||
None => return false,
|
||||
Some(lts) => {
|
||||
for lt in lts {
|
||||
if !allowed_lts.contains(<) {
|
||||
return true;
|
||||
}
|
||||
Some(lts) => for lt in lts {
|
||||
if !allowed_lts.contains(<) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::*;
|
||||
use syntax_pos;
|
||||
use utils::{span_help_and_lint, snippet_opt, in_external_macro};
|
||||
use utils::{in_external_macro, snippet_opt, span_help_and_lint};
|
||||
|
||||
/// **What it does:** Warns if a long integral or floating-point constant does
|
||||
/// not contain underscores.
|
||||
@ -195,33 +195,27 @@ enum WarningType {
|
||||
impl WarningType {
|
||||
pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
|
||||
match *self {
|
||||
WarningType::UnreadableLiteral => {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
UNREADABLE_LITERAL,
|
||||
*span,
|
||||
"long literal lacking separators",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
)
|
||||
},
|
||||
WarningType::LargeDigitGroups => {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
*span,
|
||||
"digit groups should be smaller",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
)
|
||||
},
|
||||
WarningType::InconsistentDigitGrouping => {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
*span,
|
||||
"digits grouped inconsistently by underscores",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
)
|
||||
},
|
||||
WarningType::UnreadableLiteral => span_help_and_lint(
|
||||
cx,
|
||||
UNREADABLE_LITERAL,
|
||||
*span,
|
||||
"long literal lacking separators",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
),
|
||||
WarningType::LargeDigitGroups => span_help_and_lint(
|
||||
cx,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
*span,
|
||||
"digit groups should be smaller",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
),
|
||||
WarningType::InconsistentDigitGrouping => span_help_and_lint(
|
||||
cx,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
*span,
|
||||
"digits grouped inconsistently by underscores",
|
||||
&format!("consider: {}", grouping_hint),
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
use itertools::Itertools;
|
||||
use reexport::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl, walk_pat, walk_stmt, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_block, walk_decl, walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::map::Node::{NodeBlock, NodeExpr, NodeStmt};
|
||||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
@ -14,11 +15,30 @@ use std::collections::{HashMap, HashSet};
|
||||
use syntax::ast;
|
||||
use utils::sugg;
|
||||
|
||||
use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro,
|
||||
is_refutable, span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher,
|
||||
last_path_segment, span_lint_and_sugg};
|
||||
use utils::{get_enclosing_block, get_parent_expr, higher, in_external_macro, is_integer_literal, is_refutable,
|
||||
last_path_segment, match_trait_method, match_type, multispan_sugg, snippet, snippet_opt,
|
||||
span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then};
|
||||
use utils::paths;
|
||||
|
||||
/// **What it does:** Checks for for loops that manually copy items between
|
||||
/// slices that could be optimized by having a memcpy.
|
||||
///
|
||||
/// **Why is this bad?** It is not as fast as a memcpy.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// for i in 0..src.len() {
|
||||
/// dst[i + 64] = src[i];
|
||||
/// }
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub MANUAL_MEMCPY,
|
||||
Warn,
|
||||
"manually copying items between slices"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for looping over the range of `0..len` of some
|
||||
/// collection just to get the values by index.
|
||||
///
|
||||
@ -314,6 +334,7 @@ pub struct Pass;
|
||||
impl LintPass for Pass {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(
|
||||
MANUAL_MEMCPY,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
EXPLICIT_ITER_LOOP,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
@ -340,11 +361,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
|
||||
// check for never_loop
|
||||
match expr.node {
|
||||
ExprWhile(_, ref block, _) |
|
||||
ExprLoop(ref block, _, _) => {
|
||||
if never_loop(block, &expr.id) {
|
||||
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
|
||||
}
|
||||
ExprWhile(_, ref block, _) | ExprLoop(ref block, _, _) => if never_loop(block, &expr.id) {
|
||||
span_lint(cx, NEVER_LOOP, expr.span, "this loop never actually loops");
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
@ -360,7 +378,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
EMPTY_LOOP,
|
||||
expr.span,
|
||||
"empty `loop {}` detected. You may want to either use `panic!()` or add \
|
||||
`std::thread::sleep(..);` to the loop body.",
|
||||
`std::thread::sleep(..);` to the loop body.",
|
||||
);
|
||||
}
|
||||
|
||||
@ -371,8 +389,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
if let ExprMatch(ref matchexpr, ref arms, ref source) = inner.node {
|
||||
// ensure "if let" compatible match structure
|
||||
match *source {
|
||||
MatchSource::Normal |
|
||||
MatchSource::IfLetDesugar { .. } => {
|
||||
MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
|
||||
if arms.len() == 2 && arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
|
||||
arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
|
||||
is_break_expr(&arms[1].body)
|
||||
@ -407,8 +424,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
}
|
||||
if let ExprMatch(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
|
||||
let pat = &arms[0].pats[0].node;
|
||||
if let (&PatKind::TupleStruct(ref qpath, ref pat_args, _),
|
||||
&ExprMethodCall(ref method_path, _, ref method_args)) = (pat, &match_expr.node)
|
||||
if let (
|
||||
&PatKind::TupleStruct(ref qpath, ref pat_args, _),
|
||||
&ExprMethodCall(ref method_path, _, ref method_args),
|
||||
) = (pat, &match_expr.node)
|
||||
{
|
||||
let iter_expr = &method_args[0];
|
||||
let lhs_constructor = last_path_segment(qpath);
|
||||
@ -441,7 +460,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
UNUSED_COLLECT,
|
||||
expr.span,
|
||||
"you are collect()ing an iterator and throwing away the result. \
|
||||
Consider using an explicit for loop to exhaust the iterator",
|
||||
Consider using an explicit for loop to exhaust the iterator",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -455,28 +474,25 @@ fn never_loop(block: &Block, id: &NodeId) -> bool {
|
||||
|
||||
fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
|
||||
block.stmts.iter().any(|e| contains_continue_stmt(e, dest)) ||
|
||||
block.expr.as_ref().map_or(
|
||||
false,
|
||||
|e| contains_continue_expr(e, dest),
|
||||
)
|
||||
block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or(false, |e| contains_continue_expr(e, dest))
|
||||
}
|
||||
|
||||
fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
|
||||
match stmt.node {
|
||||
StmtSemi(ref e, _) |
|
||||
StmtExpr(ref e, _) => contains_continue_expr(e, dest),
|
||||
StmtSemi(ref e, _) | StmtExpr(ref e, _) => contains_continue_expr(e, dest),
|
||||
StmtDecl(ref d, _) => contains_continue_decl(d, dest),
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
|
||||
match decl.node {
|
||||
DeclLocal(ref local) => {
|
||||
local.init.as_ref().map_or(
|
||||
false,
|
||||
|e| contains_continue_expr(e, dest),
|
||||
)
|
||||
},
|
||||
DeclLocal(ref local) => local
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(false, |e| contains_continue_expr(e, dest)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -492,9 +508,9 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
|
||||
ExprTupField(ref e, _) |
|
||||
ExprAddrOf(_, ref e) |
|
||||
ExprRepeat(ref e, _) => contains_continue_expr(e, dest),
|
||||
ExprArray(ref es) |
|
||||
ExprMethodCall(_, _, ref es) |
|
||||
ExprTup(ref es) => es.iter().any(|e| contains_continue_expr(e, dest)),
|
||||
ExprArray(ref es) | ExprMethodCall(_, _, ref es) | ExprTup(ref es) => {
|
||||
es.iter().any(|e| contains_continue_expr(e, dest))
|
||||
},
|
||||
ExprCall(ref e, ref es) => {
|
||||
contains_continue_expr(e, dest) || es.iter().any(|e| contains_continue_expr(e, dest))
|
||||
},
|
||||
@ -502,22 +518,17 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
|
||||
ExprAssign(ref e1, ref e2) |
|
||||
ExprAssignOp(_, ref e1, ref e2) |
|
||||
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| contains_continue_expr(e, dest)),
|
||||
ExprIf(ref e, ref e2, ref e3) => {
|
||||
[e, e2].iter().chain(e3.as_ref().iter()).any(|e| {
|
||||
contains_continue_expr(e, dest)
|
||||
})
|
||||
},
|
||||
ExprIf(ref e, ref e2, ref e3) => [e, e2]
|
||||
.iter()
|
||||
.chain(e3.as_ref().iter())
|
||||
.any(|e| contains_continue_expr(e, dest)),
|
||||
ExprWhile(ref e, ref b, _) => contains_continue_expr(e, dest) || contains_continue_block(b, dest),
|
||||
ExprMatch(ref e, ref arms, _) => {
|
||||
contains_continue_expr(e, dest) || arms.iter().any(|a| contains_continue_expr(&a.body, dest))
|
||||
},
|
||||
ExprBlock(ref block) => contains_continue_block(block, dest),
|
||||
ExprStruct(_, _, ref base) => {
|
||||
base.as_ref().map_or(
|
||||
false,
|
||||
|e| contains_continue_expr(e, dest),
|
||||
)
|
||||
},
|
||||
ExprStruct(_, _, ref base) => base.as_ref()
|
||||
.map_or(false, |e| contains_continue_expr(e, dest)),
|
||||
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
|
||||
_ => false,
|
||||
}
|
||||
@ -529,8 +540,7 @@ fn loop_exit_block(block: &Block) -> bool {
|
||||
|
||||
fn loop_exit_stmt(stmt: &Stmt) -> bool {
|
||||
match stmt.node {
|
||||
StmtSemi(ref e, _) |
|
||||
StmtExpr(ref e, _) => loop_exit_expr(e),
|
||||
StmtSemi(ref e, _) | StmtExpr(ref e, _) => loop_exit_expr(e),
|
||||
StmtDecl(ref d, _) => loop_exit_decl(d),
|
||||
}
|
||||
}
|
||||
@ -552,9 +562,7 @@ fn loop_exit_expr(expr: &Expr) -> bool {
|
||||
ExprTupField(ref e, _) |
|
||||
ExprAddrOf(_, ref e) |
|
||||
ExprRepeat(ref e, _) => loop_exit_expr(e),
|
||||
ExprArray(ref es) |
|
||||
ExprMethodCall(_, _, ref es) |
|
||||
ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
|
||||
ExprArray(ref es) | ExprMethodCall(_, _, ref es) | ExprTup(ref es) => es.iter().any(|e| loop_exit_expr(e)),
|
||||
ExprCall(ref e, ref es) => loop_exit_expr(e) || es.iter().any(|e| loop_exit_expr(e)),
|
||||
ExprBinary(_, ref e1, ref e2) |
|
||||
ExprAssign(ref e1, ref e2) |
|
||||
@ -583,6 +591,249 @@ fn check_for_loop<'a, 'tcx>(
|
||||
check_for_loop_arg(cx, pat, arg, expr);
|
||||
check_for_loop_explicit_counter(cx, arg, body, expr);
|
||||
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
|
||||
detect_manual_memcpy(cx, pat, arg, body, expr);
|
||||
}
|
||||
|
||||
fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: DefId) -> bool {
|
||||
if_let_chain! {[
|
||||
let ExprPath(ref qpath) = expr.node,
|
||||
let QPath::Resolved(None, ref path) = *qpath,
|
||||
path.segments.len() == 1,
|
||||
// our variable!
|
||||
cx.tables.qpath_def(qpath, expr.hir_id).def_id() == var
|
||||
], {
|
||||
return true;
|
||||
}}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
struct Offset {
|
||||
value: String,
|
||||
negate: bool,
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
fn negative(s: String) -> Self {
|
||||
Self {
|
||||
value: s,
|
||||
negate: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn positive(s: String) -> Self {
|
||||
Self {
|
||||
value: s,
|
||||
negate: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FixedOffsetVar {
|
||||
var_name: String,
|
||||
offset: Offset,
|
||||
}
|
||||
|
||||
fn is_slice_like<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty) -> bool {
|
||||
let is_slice = match ty.sty {
|
||||
ty::TyRef(_, ref subty) => is_slice_like(cx, subty.ty),
|
||||
ty::TySlice(..) | ty::TyArray(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_slice || match_type(cx, ty, &paths::VEC) || match_type(cx, ty, &paths::VEC_DEQUE)
|
||||
}
|
||||
|
||||
fn get_fixed_offset_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: DefId) -> Option<FixedOffsetVar> {
|
||||
fn extract_offset<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, e: &Expr, var: DefId) -> Option<String> {
|
||||
match e.node {
|
||||
ExprLit(ref l) => match l.node {
|
||||
ast::LitKind::Int(x, _ty) => Some(x.to_string()),
|
||||
_ => None,
|
||||
},
|
||||
ExprPath(..) if !same_var(cx, e, var) => Some(snippet_opt(cx, e.span).unwrap_or_else(|| "??".into())),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
if let ExprIndex(ref seqexpr, ref idx) = expr.node {
|
||||
let ty = cx.tables.expr_ty(seqexpr);
|
||||
if !is_slice_like(cx, ty) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let offset = match idx.node {
|
||||
ExprBinary(op, ref lhs, ref rhs) => match op.node {
|
||||
BinOp_::BiAdd => {
|
||||
let offset_opt = if same_var(cx, lhs, var) {
|
||||
extract_offset(cx, rhs, var)
|
||||
} else if same_var(cx, rhs, var) {
|
||||
extract_offset(cx, lhs, var)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
offset_opt.map(Offset::positive)
|
||||
},
|
||||
BinOp_::BiSub if same_var(cx, lhs, var) => extract_offset(cx, rhs, var).map(Offset::negative),
|
||||
_ => None,
|
||||
},
|
||||
ExprPath(..) => if same_var(cx, idx, var) {
|
||||
Some(Offset::positive("0".into()))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
offset.map(|o| {
|
||||
FixedOffsetVar {
|
||||
var_name: snippet_opt(cx, seqexpr.span).unwrap_or_else(|| "???".into()),
|
||||
offset: o,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_indexed_assignments<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
body: &Expr,
|
||||
var: DefId,
|
||||
) -> Vec<(FixedOffsetVar, FixedOffsetVar)> {
|
||||
fn get_assignment<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
e: &Expr,
|
||||
var: DefId,
|
||||
) -> Option<(FixedOffsetVar, FixedOffsetVar)> {
|
||||
if let Expr_::ExprAssign(ref lhs, ref rhs) = e.node {
|
||||
match (get_fixed_offset_var(cx, lhs, var), get_fixed_offset_var(cx, rhs, var)) {
|
||||
(Some(offset_left), Some(offset_right)) => Some((offset_left, offset_right)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
if let Expr_::ExprBlock(ref b) = body.node {
|
||||
let Block {
|
||||
ref stmts,
|
||||
ref expr,
|
||||
..
|
||||
} = **b;
|
||||
|
||||
stmts
|
||||
.iter()
|
||||
.map(|stmt| match stmt.node {
|
||||
Stmt_::StmtDecl(..) => None,
|
||||
Stmt_::StmtExpr(ref e, _node_id) | Stmt_::StmtSemi(ref e, _node_id) => Some(get_assignment(cx, e, var)),
|
||||
})
|
||||
.chain(
|
||||
expr.as_ref()
|
||||
.into_iter()
|
||||
.map(|e| Some(get_assignment(cx, &*e, var))),
|
||||
)
|
||||
.filter_map(|op| op)
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_else(|| vec![])
|
||||
} else {
|
||||
get_assignment(cx, body, var).into_iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for for loops that sequentially copy items from one slice-like
|
||||
/// object to another.
|
||||
fn detect_manual_memcpy<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &'tcx Pat,
|
||||
arg: &'tcx Expr,
|
||||
body: &'tcx Expr,
|
||||
expr: &'tcx Expr,
|
||||
) {
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
ref end,
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, def_id, _, _) = pat.node {
|
||||
let print_sum = |arg1: &Offset, arg2: &Offset| -> String {
|
||||
match (&arg1.value[..], arg1.negate, &arg2.value[..], arg2.negate) {
|
||||
("0", _, "0", _) => "".into(),
|
||||
("0", _, x, false) | (x, false, "0", false) => x.into(),
|
||||
("0", _, x, true) | (x, false, "0", true) => format!("-{}", x),
|
||||
(x, false, y, false) => format!("({} + {})", x, y),
|
||||
(x, false, y, true) => format!("({} - {})", x, y),
|
||||
(x, true, y, false) => format!("({} - {})", y, x),
|
||||
(x, true, y, true) => format!("-({} + {})", x, y),
|
||||
}
|
||||
};
|
||||
|
||||
let print_limit = |end: &Option<&Expr>, offset: Offset, var_name: &str| if let Some(end) = *end {
|
||||
if_let_chain! {[
|
||||
let ExprMethodCall(ref method, _, ref len_args) = end.node,
|
||||
method.name == "len",
|
||||
len_args.len() == 1,
|
||||
let Some(arg) = len_args.get(0),
|
||||
snippet(cx, arg.span, "??") == var_name,
|
||||
], {
|
||||
return if offset.negate {
|
||||
format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
|
||||
} else {
|
||||
"".to_owned()
|
||||
};
|
||||
}}
|
||||
|
||||
let end_str = match limits {
|
||||
ast::RangeLimits::Closed => {
|
||||
let end = sugg::Sugg::hir(cx, end, "<count>");
|
||||
format!("{}", end + sugg::ONE)
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => format!("{}", snippet(cx, end.span, "..")),
|
||||
};
|
||||
|
||||
print_sum(&Offset::positive(end_str), &offset)
|
||||
} else {
|
||||
"..".into()
|
||||
};
|
||||
|
||||
// The only statements in the for loops can be indexed assignments from
|
||||
// indexed retrievals.
|
||||
let manual_copies = get_indexed_assignments(cx, body, def_id);
|
||||
|
||||
let big_sugg = manual_copies
|
||||
.into_iter()
|
||||
.map(|(dst_var, src_var)| {
|
||||
let start_str = Offset::positive(snippet_opt(cx, start.span).unwrap_or_else(|| "".into()));
|
||||
let dst_offset = print_sum(&start_str, &dst_var.offset);
|
||||
let dst_limit = print_limit(end, dst_var.offset, &dst_var.var_name);
|
||||
let src_offset = print_sum(&start_str, &src_var.offset);
|
||||
let src_limit = print_limit(end, src_var.offset, &src_var.var_name);
|
||||
let dst = if dst_offset == "" && dst_limit == "" {
|
||||
dst_var.var_name
|
||||
} else {
|
||||
format!("{}[{}..{}]", dst_var.var_name, dst_offset, dst_limit)
|
||||
};
|
||||
|
||||
format!("{}.clone_from_slice(&{}[{}..{}])", dst, src_var.var_name, src_offset, src_limit)
|
||||
})
|
||||
.join("\n ");
|
||||
|
||||
if !big_sugg.is_empty() {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_MEMCPY,
|
||||
expr.span,
|
||||
"it looks like you're manually copying between slices",
|
||||
"try replacing the loop by",
|
||||
big_sugg,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check for looping over a range and then indexing a sequence with it.
|
||||
@ -595,10 +846,10 @@ fn check_for_loop_range<'a, 'tcx>(
|
||||
expr: &'tcx Expr,
|
||||
) {
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
ref end,
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
start: Some(start),
|
||||
ref end,
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
{
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, def_id, ref ident, _) = pat.node {
|
||||
@ -613,9 +864,11 @@ fn check_for_loop_range<'a, 'tcx>(
|
||||
|
||||
// linting condition: we only indexed one variable
|
||||
if visitor.indexed.len() == 1 {
|
||||
let (indexed, indexed_extent) = visitor.indexed.into_iter().next().expect(
|
||||
"already checked that we have exactly 1 element",
|
||||
);
|
||||
let (indexed, indexed_extent) = visitor
|
||||
.indexed
|
||||
.into_iter()
|
||||
.next()
|
||||
.expect("already checked that we have exactly 1 element");
|
||||
|
||||
// ensure that the indexed variable was declared before the loop, see #601
|
||||
if let Some(indexed_extent) = indexed_extent {
|
||||
@ -659,16 +912,22 @@ fn check_for_loop_range<'a, 'tcx>(
|
||||
};
|
||||
|
||||
if visitor.nonindex {
|
||||
span_lint_and_then(cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
expr.span,
|
||||
&format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
|
||||
|db| {
|
||||
multispan_sugg(db,
|
||||
"consider using an iterator".to_string(),
|
||||
vec![(pat.span, format!("({}, <item>)", ident.node)),
|
||||
(arg.span, format!("{}.iter().enumerate(){}{}", indexed, take, skip))]);
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
expr.span,
|
||||
&format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
|
||||
|db| {
|
||||
multispan_sugg(
|
||||
db,
|
||||
"consider using an iterator".to_string(),
|
||||
vec![
|
||||
(pat.span, format!("({}, <item>)", ident.node)),
|
||||
(arg.span, format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let repl = if starts_at_zero && take.is_empty() {
|
||||
format!("&{}", indexed)
|
||||
@ -676,17 +935,19 @@ fn check_for_loop_range<'a, 'tcx>(
|
||||
format!("{}.iter(){}{}", indexed, take, skip)
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
expr.span,
|
||||
&format!("the loop variable `{}` is only used to index `{}`.",
|
||||
ident.node,
|
||||
indexed),
|
||||
|db| {
|
||||
multispan_sugg(db,
|
||||
"consider using an iterator".to_string(),
|
||||
vec![(pat.span, "<item>".to_string()), (arg.span, repl)]);
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
expr.span,
|
||||
&format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
|
||||
|db| {
|
||||
multispan_sugg(
|
||||
db,
|
||||
"consider using an iterator".to_string(),
|
||||
vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -711,10 +972,10 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
|
||||
fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
|
||||
// if this for loop is iterating over a two-sided range...
|
||||
if let Some(higher::Range {
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
start: Some(start),
|
||||
end: Some(end),
|
||||
limits,
|
||||
}) = higher::range(arg)
|
||||
{
|
||||
// ...and both sides are compile-time constant integers...
|
||||
let parent_item = cx.tcx.hir.get_parent(arg.id);
|
||||
@ -743,19 +1004,25 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
|
||||
".."
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
REVERSE_RANGE_LOOP,
|
||||
expr.span,
|
||||
"this range is empty so this for loop will never run",
|
||||
|db| {
|
||||
db.span_suggestion(arg.span,
|
||||
"consider using the following if you are attempting to iterate over this \
|
||||
range in reverse",
|
||||
format!("({end}{dots}{start}).rev()",
|
||||
end = end_snippet,
|
||||
dots = dots,
|
||||
start = start_snippet));
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
REVERSE_RANGE_LOOP,
|
||||
expr.span,
|
||||
"this range is empty so this for loop will never run",
|
||||
|db| {
|
||||
db.span_suggestion(
|
||||
arg.span,
|
||||
"consider using the following if you are attempting to iterate over this \
|
||||
range in reverse",
|
||||
format!(
|
||||
"({end}{dots}{start}).rev()",
|
||||
end = end_snippet,
|
||||
dots = dots,
|
||||
start = start_snippet
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if eq && limits != ast::RangeLimits::Closed {
|
||||
// if they are equal, it's also problematic - this loop
|
||||
// will never run.
|
||||
@ -783,7 +1050,7 @@ fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, method_name: &s
|
||||
EXPLICIT_ITER_LOOP,
|
||||
arg.span,
|
||||
"it is more idiomatic to loop over references to containers instead of using explicit \
|
||||
iteration methods",
|
||||
iteration methods",
|
||||
"to write this more concisely, try",
|
||||
format!("&{}{}", muta, object),
|
||||
)
|
||||
@ -816,7 +1083,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
arg.span,
|
||||
"it is more idiomatic to loop over containers instead of using explicit \
|
||||
iteration methods`",
|
||||
iteration methods`",
|
||||
"to write this more concisely, try",
|
||||
object.to_string(),
|
||||
);
|
||||
@ -827,7 +1094,7 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
|
||||
ITER_NEXT_LOOP,
|
||||
expr.span,
|
||||
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
|
||||
probably not what you want",
|
||||
probably not what you want",
|
||||
);
|
||||
next_loop_linted = true;
|
||||
}
|
||||
@ -848,7 +1115,7 @@ fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
|
||||
arg.span,
|
||||
&format!(
|
||||
"for loop over `{0}`, which is an `Option`. This is more readably written as an \
|
||||
`if let` statement.",
|
||||
`if let` statement.",
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
&format!(
|
||||
@ -864,7 +1131,7 @@ fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
|
||||
arg.span,
|
||||
&format!(
|
||||
"for loop over `{0}`, which is a `Result`. This is more readably written as an \
|
||||
`if let` statement.",
|
||||
`if let` statement.",
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
&format!(
|
||||
@ -894,14 +1161,14 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
|
||||
// For each candidate, check the parent block to see if
|
||||
// it's initialized to zero at the start of the loop.
|
||||
let map = &cx.tcx.hir;
|
||||
let parent_scope = map.get_enclosing_scope(expr.id).and_then(|id| {
|
||||
map.get_enclosing_scope(id)
|
||||
});
|
||||
let parent_scope = map.get_enclosing_scope(expr.id)
|
||||
.and_then(|id| map.get_enclosing_scope(id));
|
||||
if let Some(parent_id) = parent_scope {
|
||||
if let NodeBlock(block) = map.get(parent_id) {
|
||||
for (id, _) in visitor.states.iter().filter(
|
||||
|&(_, v)| *v == VarState::IncrOnce,
|
||||
)
|
||||
for (id, _) in visitor
|
||||
.states
|
||||
.iter()
|
||||
.filter(|&(_, v)| *v == VarState::IncrOnce)
|
||||
{
|
||||
let mut visitor2 = InitializeVisitor {
|
||||
cx: cx,
|
||||
@ -922,7 +1189,7 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
|
||||
expr.span,
|
||||
&format!(
|
||||
"the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
|
||||
item) in {1}.enumerate()` or similar iterators",
|
||||
item) in {1}.enumerate()` or similar iterators",
|
||||
name,
|
||||
snippet(cx, arg.span, "_")
|
||||
),
|
||||
@ -948,12 +1215,10 @@ fn check_for_loop_over_map_kv<'a, 'tcx>(
|
||||
if pat.len() == 2 {
|
||||
let arg_span = arg.span;
|
||||
let (new_pat_span, kind, ty, mutbl) = match cx.tables.expr_ty(arg).sty {
|
||||
ty::TyRef(_, ref tam) => {
|
||||
match (&pat[0].node, &pat[1].node) {
|
||||
(key, _) if pat_is_wild(key, body) => (pat[1].span, "value", tam.ty, tam.mutbl),
|
||||
(_, value) if pat_is_wild(value, body) => (pat[0].span, "key", tam.ty, MutImmutable),
|
||||
_ => return,
|
||||
}
|
||||
ty::TyRef(_, ref tam) => match (&pat[0].node, &pat[1].node) {
|
||||
(key, _) if pat_is_wild(key, body) => (pat[1].span, "value", tam.ty, tam.mutbl),
|
||||
(_, value) if pat_is_wild(value, body) => (pat[0].span, "key", tam.ty, MutImmutable),
|
||||
_ => return,
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
@ -967,21 +1232,26 @@ fn check_for_loop_over_map_kv<'a, 'tcx>(
|
||||
};
|
||||
|
||||
if match_type(cx, ty, &paths::HASHMAP) || match_type(cx, ty, &paths::BTREEMAP) {
|
||||
span_lint_and_then(cx,
|
||||
FOR_KV_MAP,
|
||||
expr.span,
|
||||
&format!("you seem to want to iterate on a map's {}s", kind),
|
||||
|db| {
|
||||
let map = sugg::Sugg::hir(cx, arg, "map");
|
||||
multispan_sugg(db,
|
||||
"use the corresponding method".into(),
|
||||
vec![(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
|
||||
(arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl))]);
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FOR_KV_MAP,
|
||||
expr.span,
|
||||
&format!("you seem to want to iterate on a map's {}s", kind),
|
||||
|db| {
|
||||
let map = sugg::Sugg::hir(cx, arg, "map");
|
||||
multispan_sugg(
|
||||
db,
|
||||
"use the corresponding method".into(),
|
||||
vec![
|
||||
(pat_span, snippet(cx, new_pat_span, kind).into_owned()),
|
||||
(arg_span, format!("{}.{}s{}()", map.maybe_par(), kind, mutbl)),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Return true if the pattern is a `PatWild` or an ident prefixed with `'_'`.
|
||||
@ -1011,16 +1281,36 @@ fn match_var(expr: &Expr, var: Name) -> bool {
|
||||
|
||||
struct UsedVisitor {
|
||||
var: ast::Name, // var to look for
|
||||
used: bool, // has the var been used otherwise?
|
||||
used: bool, // has the var been used otherwise?
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for UsedVisitor {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if match_var(expr, self.var) {
|
||||
self.used = true;
|
||||
return;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
struct DefIdUsedVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for DefIdUsedVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if same_var(self.cx, expr, self.def_id) {
|
||||
self.used = true;
|
||||
} else {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
@ -1048,40 +1338,46 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
if_let_chain! {[
|
||||
// an index op
|
||||
let ExprIndex(ref seqexpr, ref idx) = expr.node,
|
||||
// directly indexing a variable
|
||||
let ExprPath(ref qpath) = idx.node,
|
||||
let QPath::Resolved(None, ref path) = *qpath,
|
||||
path.segments.len() == 1,
|
||||
// our variable!
|
||||
self.cx.tables.qpath_def(qpath, expr.hir_id).def_id() == self.var,
|
||||
// the indexed container is referenced by a name
|
||||
let ExprPath(ref seqpath) = seqexpr.node,
|
||||
let QPath::Resolved(None, ref seqvar) = *seqpath,
|
||||
seqvar.segments.len() == 1,
|
||||
], {
|
||||
let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
|
||||
match def {
|
||||
Def::Local(..) | Def::Upvar(..) => {
|
||||
let def_id = def.def_id();
|
||||
let node_id = self.cx.tcx.hir.as_local_node_id(def_id).expect("local/upvar are local nodes");
|
||||
let hir_id = self.cx.tcx.hir.node_to_hir_id(node_id);
|
||||
let index_used = same_var(self.cx, idx, self.var) || {
|
||||
let mut used_visitor = DefIdUsedVisitor {
|
||||
cx: self.cx,
|
||||
def_id: self.var,
|
||||
used: false,
|
||||
};
|
||||
walk_expr(&mut used_visitor, idx);
|
||||
used_visitor.used
|
||||
};
|
||||
|
||||
let parent_id = self.cx.tcx.hir.get_parent(expr.id);
|
||||
let parent_def_id = self.cx.tcx.hir.local_def_id(parent_id);
|
||||
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
|
||||
self.indexed.insert(seqvar.segments[0].name, Some(extent));
|
||||
return; // no need to walk further
|
||||
if index_used {
|
||||
let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
|
||||
match def {
|
||||
Def::Local(..) | Def::Upvar(..) => {
|
||||
let def_id = def.def_id();
|
||||
let node_id = self.cx.tcx.hir.as_local_node_id(def_id).expect("local/upvar are local nodes");
|
||||
let hir_id = self.cx.tcx.hir.node_to_hir_id(node_id);
|
||||
|
||||
let parent_id = self.cx.tcx.hir.get_parent(expr.id);
|
||||
let parent_def_id = self.cx.tcx.hir.local_def_id(parent_id);
|
||||
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
|
||||
self.indexed.insert(seqvar.segments[0].name, Some(extent));
|
||||
return; // no need to walk further *on the variable*
|
||||
}
|
||||
Def::Static(..) | Def::Const(..) => {
|
||||
self.indexed.insert(seqvar.segments[0].name, None);
|
||||
return; // no need to walk further *on the variable*
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Def::Static(..) | Def::Const(..) => {
|
||||
self.indexed.insert(seqvar.segments[0].name, None);
|
||||
return; // no need to walk further
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}}
|
||||
|
||||
if_let_chain! {[
|
||||
// directly indexing a variable
|
||||
// directly using a variable
|
||||
let ExprPath(ref qpath) = expr.node,
|
||||
let QPath::Resolved(None, ref path) = *qpath,
|
||||
path.segments.len() == 1,
|
||||
@ -1196,12 +1492,9 @@ fn extract_expr_from_first_stmt(block: &Block) -> Option<&Expr> {
|
||||
fn extract_first_expr(block: &Block) -> Option<&Expr> {
|
||||
match block.expr {
|
||||
Some(ref expr) if block.stmts.is_empty() => Some(expr),
|
||||
None if !block.stmts.is_empty() => {
|
||||
match block.stmts[0].node {
|
||||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => Some(expr),
|
||||
StmtDecl(..) => None,
|
||||
}
|
||||
None if !block.stmts.is_empty() => match block.stmts[0].node {
|
||||
StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => Some(expr),
|
||||
StmtDecl(..) => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
@ -1211,11 +1504,9 @@ fn extract_first_expr(block: &Block) -> Option<&Expr> {
|
||||
fn is_break_expr(expr: &Expr) -> bool {
|
||||
match expr.node {
|
||||
ExprBreak(dest, _) if dest.ident.is_none() => true,
|
||||
ExprBlock(ref b) => {
|
||||
match extract_first_expr(b) {
|
||||
Some(subexpr) => is_break_expr(subexpr),
|
||||
None => false,
|
||||
}
|
||||
ExprBlock(ref b) => match extract_first_expr(b) {
|
||||
Some(subexpr) => is_break_expr(subexpr),
|
||||
None => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
@ -1226,7 +1517,7 @@ fn is_break_expr(expr: &Expr) -> bool {
|
||||
// at the start of the loop.
|
||||
#[derive(PartialEq)]
|
||||
enum VarState {
|
||||
Initial, // Not examined yet
|
||||
Initial, // Not examined yet
|
||||
IncrOnce, // Incremented exactly once, may be a loop counter
|
||||
Declared, // Declared but not (yet) initialized to zero
|
||||
Warn,
|
||||
@ -1235,9 +1526,9 @@ enum VarState {
|
||||
|
||||
/// Scan a for loop for variables that are incremented exactly once.
|
||||
struct IncrementVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
states: HashMap<NodeId, VarState>, // incremented variables
|
||||
depth: u32, // depth of conditional expressions
|
||||
depth: u32, // depth of conditional expressions
|
||||
done: bool,
|
||||
}
|
||||
|
||||
@ -1291,7 +1582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
|
||||
/// Check whether a variable is initialized to zero at the start of a loop.
|
||||
struct InitializeVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
end_expr: &'tcx Expr, // the for loop. Stop scanning here.
|
||||
end_expr: &'tcx Expr, // the for loop. Stop scanning here.
|
||||
var_id: NodeId,
|
||||
state: VarState,
|
||||
name: Option<Name>,
|
||||
@ -1379,9 +1670,10 @@ fn var_def_id(cx: &LateContext, expr: &Expr) -> Option<NodeId> {
|
||||
if let ExprPath(ref qpath) = expr.node {
|
||||
let path_res = cx.tables.qpath_def(qpath, expr.hir_id);
|
||||
if let Def::Local(def_id) = path_res {
|
||||
let node_id = cx.tcx.hir.as_local_node_id(def_id).expect(
|
||||
"That DefId should be valid",
|
||||
);
|
||||
let node_id = cx.tcx
|
||||
.hir
|
||||
.as_local_node_id(def_id)
|
||||
.expect("That DefId should be valid");
|
||||
return Some(node_id);
|
||||
}
|
||||
}
|
||||
@ -1425,13 +1717,11 @@ fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool
|
||||
return false;
|
||||
}
|
||||
match cx.tcx.hir.find(parent) {
|
||||
Some(NodeExpr(expr)) => {
|
||||
match expr.node {
|
||||
ExprLoop(..) | ExprWhile(..) => {
|
||||
return true;
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
Some(NodeExpr(expr)) => match expr.node {
|
||||
ExprLoop(..) | ExprWhile(..) => {
|
||||
return true;
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Some(NodeBlock(block)) => {
|
||||
let mut block_visitor = LoopNestVisitor {
|
||||
@ -1455,12 +1745,12 @@ fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum Nesting {
|
||||
Unknown, // no nesting detected yet
|
||||
RuledOut, // the iterator is initialized or assigned within scope
|
||||
Unknown, // no nesting detected yet
|
||||
RuledOut, // the iterator is initialized or assigned within scope
|
||||
LookFurther, // no nesting detected, no further walk required
|
||||
}
|
||||
|
||||
use self::Nesting::{Unknown, RuledOut, LookFurther};
|
||||
use self::Nesting::{LookFurther, RuledOut, Unknown};
|
||||
|
||||
struct LoopNestVisitor {
|
||||
id: NodeId,
|
||||
@ -1486,11 +1776,8 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
|
||||
return;
|
||||
}
|
||||
match expr.node {
|
||||
ExprAssign(ref path, _) |
|
||||
ExprAssignOp(_, ref path, _) => {
|
||||
if match_var(path, self.iterator) {
|
||||
self.nesting = RuledOut;
|
||||
}
|
||||
ExprAssign(ref path, _) | ExprAssignOp(_, ref path, _) => if match_var(path, self.iterator) {
|
||||
self.nesting = RuledOut;
|
||||
},
|
||||
_ => walk_expr(self, expr),
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::ty;
|
||||
use syntax::ast;
|
||||
use utils::{is_adjusted, match_qpath, match_trait_method, match_type, remove_blocks, paths, snippet,
|
||||
span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
|
||||
use utils::{is_adjusted, iter_input_pats, match_qpath, match_trait_method, match_type, paths, remove_blocks, snippet,
|
||||
span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
|
||||
|
||||
/// **What it does:** Checks for mapping `clone()` over an iterator.
|
||||
///
|
||||
@ -73,21 +73,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
}
|
||||
}}
|
||||
},
|
||||
ExprPath(ref path) => {
|
||||
if match_qpath(path, &paths::CLONE) {
|
||||
let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
expr.span,
|
||||
&format!(
|
||||
"you seem to be using .map() to clone the contents of an \
|
||||
{}, consider using `.cloned()`",
|
||||
type_name
|
||||
),
|
||||
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
|
||||
);
|
||||
}
|
||||
ExprPath(ref path) => if match_qpath(path, &paths::CLONE) {
|
||||
let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
MAP_CLONE,
|
||||
expr.span,
|
||||
&format!(
|
||||
"you seem to be using .map() to clone the contents of an \
|
||||
{}, consider using `.cloned()`",
|
||||
type_name
|
||||
),
|
||||
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -11,8 +11,8 @@ use syntax::ast::LitKind;
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::codemap::Span;
|
||||
use utils::paths;
|
||||
use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, span_lint_and_sugg, in_external_macro,
|
||||
expr_block, walk_ptrs_ty, is_expn_of, remove_blocks, is_allowed};
|
||||
use utils::{expr_block, in_external_macro, is_allowed, is_expn_of, match_type, remove_blocks, snippet,
|
||||
span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty};
|
||||
use utils::sugg::Sugg;
|
||||
|
||||
/// **What it does:** Checks for matches with a single arm where an `if let`
|
||||
@ -219,7 +219,7 @@ fn report_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm],
|
||||
lint,
|
||||
expr.span,
|
||||
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
|
||||
let`",
|
||||
let`",
|
||||
"try this",
|
||||
format!(
|
||||
"if let {} = {} {}{}",
|
||||
@ -290,21 +290,17 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
||||
|
||||
if let Some((true_expr, false_expr)) = exprs {
|
||||
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
|
||||
(false, false) => {
|
||||
Some(format!(
|
||||
"if {} {} else {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, ".."),
|
||||
expr_block(cx, false_expr, None, "..")
|
||||
))
|
||||
},
|
||||
(false, true) => {
|
||||
Some(format!(
|
||||
"if {} {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, "..")
|
||||
))
|
||||
},
|
||||
(false, false) => Some(format!(
|
||||
"if {} {} else {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, ".."),
|
||||
expr_block(cx, false_expr, None, "..")
|
||||
)),
|
||||
(false, true) => Some(format!(
|
||||
"if {} {}",
|
||||
snippet(cx, ex.span, "b"),
|
||||
expr_block(cx, true_expr, None, "..")
|
||||
)),
|
||||
(true, false) => {
|
||||
let test = Sugg::hir(cx, ex, "..");
|
||||
Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
|
||||
@ -317,7 +313,6 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -384,15 +379,17 @@ fn is_panic_block(block: &Block) -> bool {
|
||||
fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: MatchSource, expr: &Expr) {
|
||||
if has_only_ref_pats(arms) {
|
||||
if let ExprAddrOf(Mutability::MutImmutable, ref inner) = ex.node {
|
||||
span_lint_and_then(cx,
|
||||
MATCH_REF_PATS,
|
||||
expr.span,
|
||||
"you don't need to add `&` to both the expression and the patterns",
|
||||
|db| {
|
||||
let inner = Sugg::hir(cx, inner, "..");
|
||||
let template = match_template(expr.span, source, &inner);
|
||||
db.span_suggestion(expr.span, "try", template);
|
||||
});
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MATCH_REF_PATS,
|
||||
expr.span,
|
||||
"you don't need to add `&` to both the expression and the patterns",
|
||||
|db| {
|
||||
let inner = Sugg::hir(cx, inner, "..");
|
||||
let template = match_template(expr.span, source, &inner);
|
||||
db.span_suggestion(expr.span, "try", template);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
@ -471,24 +468,18 @@ fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges {
|
||||
ranges
|
||||
.iter()
|
||||
.filter_map(|range| match range.node {
|
||||
(ConstVal::Integral(start), Bound::Included(ConstVal::Integral(end))) => {
|
||||
Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Included(end)),
|
||||
})
|
||||
},
|
||||
(ConstVal::Integral(start), Bound::Excluded(ConstVal::Integral(end))) => {
|
||||
Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Excluded(end)),
|
||||
})
|
||||
},
|
||||
(ConstVal::Integral(start), Bound::Unbounded) => {
|
||||
Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Unbounded),
|
||||
})
|
||||
},
|
||||
(ConstVal::Integral(start), Bound::Included(ConstVal::Integral(end))) => Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Included(end)),
|
||||
}),
|
||||
(ConstVal::Integral(start), Bound::Excluded(ConstVal::Integral(end))) => Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Excluded(end)),
|
||||
}),
|
||||
(ConstVal::Integral(start), Bound::Unbounded) => Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, Bound::Unbounded),
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
@ -507,9 +498,9 @@ fn has_only_ref_pats(arms: &[Arm]) -> bool {
|
||||
.flat_map(|a| &a.pats)
|
||||
.map(|p| {
|
||||
match p.node {
|
||||
PatKind::Ref(..) => Some(true), // &-patterns
|
||||
PatKind::Ref(..) => Some(true), // &-patterns
|
||||
PatKind::Wild => Some(false), // an "anything" wildcard is also fine
|
||||
_ => None, // any other pattern is not fine
|
||||
_ => None, // any other pattern is not fine
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<bool>>>();
|
||||
@ -540,8 +531,7 @@ where
|
||||
impl<'a, T: Copy> Kind<'a, T> {
|
||||
fn range(&self) -> &'a SpannedRange<T> {
|
||||
match *self {
|
||||
Kind::Start(_, r) |
|
||||
Kind::End(_, r) => r,
|
||||
Kind::Start(_, r) | Kind::End(_, r) => r,
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,22 +552,16 @@ where
|
||||
impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
match (self.value(), other.value()) {
|
||||
(Bound::Included(a), Bound::Included(b)) |
|
||||
(Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
|
||||
(Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
|
||||
// Range patterns cannot be unbounded (yet)
|
||||
(Bound::Unbounded, _) |
|
||||
(_, Bound::Unbounded) => unimplemented!(),
|
||||
(Bound::Included(a), Bound::Excluded(b)) => {
|
||||
match a.cmp(&b) {
|
||||
Ordering::Equal => Ordering::Greater,
|
||||
other => other,
|
||||
}
|
||||
(Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
|
||||
(Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
|
||||
Ordering::Equal => Ordering::Greater,
|
||||
other => other,
|
||||
},
|
||||
(Bound::Excluded(a), Bound::Included(b)) => {
|
||||
match a.cmp(&b) {
|
||||
Ordering::Equal => Ordering::Less,
|
||||
other => other,
|
||||
}
|
||||
(Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
|
||||
Ordering::Equal => Ordering::Less,
|
||||
other => other,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -594,10 +578,8 @@ where
|
||||
|
||||
for (a, b) in values.iter().zip(values.iter().skip(1)) {
|
||||
match (a, b) {
|
||||
(&Kind::Start(_, ra), &Kind::End(_, rb)) => {
|
||||
if ra.node != rb.node {
|
||||
return Some((ra, rb));
|
||||
}
|
||||
(&Kind::Start(_, ra), &Kind::End(_, rb)) => if ra.node != rb.node {
|
||||
return Some((ra, rb));
|
||||
},
|
||||
(&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
|
||||
_ => return Some((a.range(), b.range())),
|
||||
|
@ -39,8 +39,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
|
||||
if match forgot_ty.ty_adt_def() {
|
||||
Some(def) => def.has_dtor(cx.tcx),
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
} {
|
||||
span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type");
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,10 @@ use rustc_const_eval::ConstContext;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, is_copy, match_qpath, match_trait_method,
|
||||
match_type, method_chain_args, return_ty, same_tys, snippet, span_lint, span_lint_and_then,
|
||||
span_lint_and_sugg, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, last_path_segment,
|
||||
single_segment_path, match_def_path, is_self, is_self_ty, iter_input_pats, match_path};
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, is_copy, is_self, is_self_ty,
|
||||
iter_input_pats, last_path_segment, match_def_path, match_path, match_qpath, match_trait_method,
|
||||
match_type, method_chain_args, return_ty, same_tys, single_segment_path, snippet, span_lint,
|
||||
span_lint_and_sugg, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
|
||||
use utils::paths;
|
||||
use utils::sugg;
|
||||
|
||||
@ -618,11 +618,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
}
|
||||
|
||||
match self_ty.sty {
|
||||
ty::TyRef(_, ty) if ty.ty.sty == ty::TyStr => {
|
||||
for &(method, pos) in &PATTERN_METHODS {
|
||||
if method_call.name == method && args.len() > pos {
|
||||
lint_single_char_pattern(cx, expr, &args[pos]);
|
||||
}
|
||||
ty::TyRef(_, ty) if ty.ty.sty == ty::TyStr => for &(method, pos) in &PATTERN_METHODS {
|
||||
if method_call.name == method && args.len() > pos {
|
||||
lint_single_char_pattern(cx, expr, &args[pos]);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
@ -723,12 +721,11 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
||||
|
||||
if ["default", "new"].contains(&path) {
|
||||
let arg_ty = cx.tables.expr_ty(arg);
|
||||
let default_trait_id =
|
||||
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
|
||||
default_trait_id
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let default_trait_id = if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
|
||||
default_trait_id
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
|
||||
if implements_trait(cx, arg_ty, default_trait_id, &[]) {
|
||||
span_lint_and_sugg(
|
||||
@ -771,13 +768,12 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
||||
}
|
||||
|
||||
// (path, fn_has_argument, methods, suffix)
|
||||
let know_types: &[(&[_], _, &[_], _)] =
|
||||
&[
|
||||
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
|
||||
];
|
||||
let know_types: &[(&[_], _, &[_], _)] = &[
|
||||
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
|
||||
];
|
||||
|
||||
let self_ty = cx.tables.expr_ty(self_expr);
|
||||
|
||||
@ -835,7 +831,7 @@ fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, arg_t
|
||||
CLONE_DOUBLE_REF,
|
||||
expr.span,
|
||||
"using `clone` on a double-reference; \
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
|db| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
db.span_suggestion(expr.span, "try dereferencing it", format!("({}).clone()", snip.deref()));
|
||||
},
|
||||
@ -919,7 +915,7 @@ fn lint_iter_cloned_collect(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir
|
||||
ITER_CLONED_COLLECT,
|
||||
expr.span,
|
||||
"called `cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
|
||||
more readable",
|
||||
more readable",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1021,12 +1017,10 @@ fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: Ty) -> Option<sugg::S
|
||||
match ty.sty {
|
||||
ty::TySlice(_) => sugg::Sugg::hir_opt(cx, expr),
|
||||
ty::TyAdt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => sugg::Sugg::hir_opt(cx, expr),
|
||||
ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) => {
|
||||
if may_slice(cx, inner) {
|
||||
sugg::Sugg::hir_opt(cx, expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) => if may_slice(cx, inner) {
|
||||
sugg::Sugg::hir_opt(cx, expr)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
@ -1052,8 +1046,8 @@ fn lint_unwrap(cx: &LateContext, expr: &hir::Expr, unwrap_args: &[hir::Expr]) {
|
||||
expr.span,
|
||||
&format!(
|
||||
"used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
|
||||
using expect() to provide a better panic \
|
||||
message",
|
||||
using expect() to provide a better panic \
|
||||
message",
|
||||
kind,
|
||||
none_value
|
||||
),
|
||||
@ -1222,7 +1216,7 @@ fn lint_search_is_some(
|
||||
if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
|
||||
let msg = format!(
|
||||
"called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
|
||||
expressed by calling `any()`.",
|
||||
expressed by calling `any()`.",
|
||||
search_method
|
||||
);
|
||||
let search_snippet = snippet(cx, search_args[1].span, "..");
|
||||
@ -1459,35 +1453,37 @@ fn is_as_ref_or_mut_trait(ty: &hir::Ty, self_ty: &hir::Ty, generics: &hir::Gener
|
||||
single_segment_ty(ty).map_or(false, |seg| {
|
||||
generics.ty_params.iter().any(|param| {
|
||||
param.name == seg.name &&
|
||||
param.bounds.iter().any(|bound| {
|
||||
if let hir::TyParamBound::TraitTyParamBound(ref ptr, ..) = *bound {
|
||||
param
|
||||
.bounds
|
||||
.iter()
|
||||
.any(|bound| if let hir::TyParamBound::TraitTyParamBound(ref ptr, ..) = *bound {
|
||||
let path = &ptr.trait_ref.path;
|
||||
match_path(path, name) &&
|
||||
path.segments.last().map_or(
|
||||
false,
|
||||
|s| if s.parameters.parenthesized {
|
||||
path.segments
|
||||
.last()
|
||||
.map_or(false, |s| if s.parameters.parenthesized {
|
||||
false
|
||||
} else {
|
||||
s.parameters.types.len() == 1 &&
|
||||
(is_self_ty(&s.parameters.types[0]) || is_ty(&*s.parameters.types[0], self_ty))
|
||||
},
|
||||
)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn is_ty(ty: &hir::Ty, self_ty: &hir::Ty) -> bool {
|
||||
match (&ty.node, &self_ty.node) {
|
||||
(&hir::TyPath(hir::QPath::Resolved(_, ref ty_path)),
|
||||
&hir::TyPath(hir::QPath::Resolved(_, ref self_ty_path))) => {
|
||||
ty_path.segments.iter().map(|seg| seg.name).eq(
|
||||
self_ty_path.segments.iter().map(|seg| seg.name),
|
||||
)
|
||||
},
|
||||
(
|
||||
&hir::TyPath(hir::QPath::Resolved(_, ref ty_path)),
|
||||
&hir::TyPath(hir::QPath::Resolved(_, ref self_ty_path)),
|
||||
) => ty_path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|seg| seg.name)
|
||||
.eq(self_ty_path.segments.iter().map(|seg| seg.name)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use consts::{Constant, constant_simple};
|
||||
use consts::{constant_simple, Constant};
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use std::cmp::{PartialOrd, Ordering};
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
use utils::{match_def_path, paths, span_lint};
|
||||
|
||||
/// **What it does:** Checks for expressions where `std::cmp::min` and `max` are
|
||||
@ -41,9 +41,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass {
|
||||
return;
|
||||
}
|
||||
match (outer_max, outer_c.partial_cmp(&inner_c)) {
|
||||
(_, None) |
|
||||
(MinMax::Max, Some(Ordering::Less)) |
|
||||
(MinMax::Min, Some(Ordering::Greater)) => (),
|
||||
(_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
|
||||
_ => {
|
||||
span_lint(cx, MIN_MAX, expr.span, "this min/max combination leads to constant result");
|
||||
},
|
||||
|
@ -7,12 +7,12 @@ use rustc::ty;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc_const_eval::ConstContext;
|
||||
use rustc_const_math::ConstFloat;
|
||||
use syntax::codemap::{Span, ExpnFormat};
|
||||
use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_qpath, snippet,
|
||||
span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment, iter_input_pats, in_constant,
|
||||
match_trait_method, paths};
|
||||
use syntax::codemap::{ExpnFormat, Span};
|
||||
use utils::{get_item_name, get_parent_expr, implements_trait, in_constant, in_macro, is_integer_literal,
|
||||
iter_input_pats, last_path_segment, match_qpath, match_trait_method, paths, snippet, span_lint,
|
||||
span_lint_and_then, walk_ptrs_ty};
|
||||
use utils::sugg::Sugg;
|
||||
use syntax::ast::{LitKind, CRATE_NODE_ID, FloatTy};
|
||||
use syntax::ast::{FloatTy, LitKind, CRATE_NODE_ID};
|
||||
|
||||
/// **What it does:** Checks for function arguments and let bindings denoted as
|
||||
/// `ref`.
|
||||
@ -242,7 +242,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
TOPLEVEL_REF_ARG,
|
||||
arg.pat.span,
|
||||
"`ref` directly on a function argument is ignored. Consider using a reference type \
|
||||
instead.",
|
||||
instead.",
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
@ -385,7 +385,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
expr.span,
|
||||
&format!(
|
||||
"used binding `{}` which is prefixed with an underscore. A leading \
|
||||
underscore signals that a binding will not be used.",
|
||||
underscore signals that a binding will not be used.",
|
||||
binding
|
||||
),
|
||||
);
|
||||
@ -484,16 +484,14 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
ExprCall(ref path, ref v) if v.len() == 1 => {
|
||||
if let ExprPath(ref path) = path.node {
|
||||
if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
|
||||
(cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
ExprCall(ref path, ref v) if v.len() == 1 => if let ExprPath(ref path) = path.node {
|
||||
if match_qpath(path, &["String", "from_str"]) || match_qpath(path, &["String", "from"]) {
|
||||
(cx.tables.expr_ty_adjusted(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
@ -554,8 +552,7 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr) {
|
||||
fn is_used(cx: &LateContext, expr: &Expr) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
match parent.node {
|
||||
ExprAssign(_, ref rhs) |
|
||||
ExprAssignOp(_, _, ref rhs) => **rhs == *expr,
|
||||
ExprAssign(_, ref rhs) | ExprAssignOp(_, _, ref rhs) => **rhs == *expr,
|
||||
_ => is_used(cx, parent),
|
||||
}
|
||||
} else {
|
||||
@ -567,20 +564,21 @@ fn is_used(cx: &LateContext, expr: &Expr) -> bool {
|
||||
/// generated by
|
||||
/// `#[derive(...)`] or the like).
|
||||
fn in_attributes_expansion(expr: &Expr) -> bool {
|
||||
expr.span.ctxt().outer().expn_info().map_or(
|
||||
false,
|
||||
|info| matches!(info.callee.format, ExpnFormat::MacroAttribute(_)),
|
||||
)
|
||||
expr.span
|
||||
.ctxt()
|
||||
.outer()
|
||||
.expn_info()
|
||||
.map_or(false, |info| matches!(info.callee.format, ExpnFormat::MacroAttribute(_)))
|
||||
}
|
||||
|
||||
/// Test whether `def` is a variable defined outside a macro.
|
||||
fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
|
||||
match *def {
|
||||
def::Def::Local(def_id) |
|
||||
def::Def::Upvar(def_id, _, _) => {
|
||||
let id = cx.tcx.hir.as_local_node_id(def_id).expect(
|
||||
"local variables should be found in the same crate",
|
||||
);
|
||||
def::Def::Local(def_id) | def::Def::Upvar(def_id, _, _) => {
|
||||
let id = cx.tcx
|
||||
.hir
|
||||
.as_local_node_id(def_id)
|
||||
.expect("local variables should be found in the same crate");
|
||||
!in_macro(cx.tcx.hir.span(id))
|
||||
},
|
||||
_ => false,
|
||||
|
@ -4,7 +4,7 @@ use std::char;
|
||||
use syntax::ast::*;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::visit::FnKind;
|
||||
use utils::{constants, span_lint, span_help_and_lint, snippet, snippet_opt, span_lint_and_then, in_external_macro};
|
||||
use utils::{constants, in_external_macro, snippet, snippet_opt, span_help_and_lint, span_lint, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for structure field patterns bound to wildcards.
|
||||
///
|
||||
@ -251,7 +251,7 @@ impl EarlyLintPass for MiscEarly {
|
||||
UNNEEDED_FIELD_PATTERN,
|
||||
field.span,
|
||||
"You matched a field with a wildcard pattern. Consider using `..` \
|
||||
instead",
|
||||
instead",
|
||||
&format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
|
||||
);
|
||||
}
|
||||
@ -276,7 +276,7 @@ impl EarlyLintPass for MiscEarly {
|
||||
*correspondence,
|
||||
&format!(
|
||||
"`{}` already exists, having another argument having almost the same \
|
||||
name makes code comprehension and documentation more difficult",
|
||||
name makes code comprehension and documentation more difficult",
|
||||
arg_name[1..].to_owned()
|
||||
),
|
||||
);;
|
||||
@ -293,30 +293,28 @@ impl EarlyLintPass for MiscEarly {
|
||||
return;
|
||||
}
|
||||
match expr.node {
|
||||
ExprKind::Call(ref paren, _) => {
|
||||
if let ExprKind::Paren(ref closure) = paren.node {
|
||||
if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
|
||||
span_lint_and_then(cx,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
expr.span,
|
||||
"Try not to call a closure in the expression where it is declared.",
|
||||
|db| if decl.inputs.is_empty() {
|
||||
let hint = snippet(cx, block.span, "..").into_owned();
|
||||
db.span_suggestion(expr.span, "Try doing something like: ", hint);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Neg, ref inner) => {
|
||||
if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
|
||||
span_lint(
|
||||
ExprKind::Call(ref paren, _) => if let ExprKind::Paren(ref closure) = paren.node {
|
||||
if let ExprKind::Closure(_, ref decl, ref block, _) = closure.node {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DOUBLE_NEG,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
expr.span,
|
||||
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
|
||||
"Try not to call a closure in the expression where it is declared.",
|
||||
|db| if decl.inputs.is_empty() {
|
||||
let hint = snippet(cx, block.span, "..").into_owned();
|
||||
db.span_suggestion(expr.span, "Try doing something like: ", hint);
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Neg, ref inner) => if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
|
||||
span_lint(
|
||||
cx,
|
||||
DOUBLE_NEG,
|
||||
expr.span,
|
||||
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
|
||||
);
|
||||
},
|
||||
ExprKind::Lit(ref lit) => self.check_lit(cx, lit),
|
||||
_ => (),
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
// [`missing_doc`]:
|
||||
// https://github.
|
||||
// com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
@ -64,13 +64,15 @@ impl ::std::default::Default for MissingDoc {
|
||||
|
||||
impl MissingDoc {
|
||||
pub fn new() -> Self {
|
||||
Self { doc_hidden_stack: vec![false] }
|
||||
Self {
|
||||
doc_hidden_stack: vec![false],
|
||||
}
|
||||
}
|
||||
|
||||
fn doc_hidden(&self) -> bool {
|
||||
*self.doc_hidden_stack.last().expect(
|
||||
"empty doc_hidden_stack",
|
||||
)
|
||||
*self.doc_hidden_stack
|
||||
.last()
|
||||
.expect("empty doc_hidden_stack")
|
||||
}
|
||||
|
||||
fn check_missing_docs_attrs(&self, cx: &LateContext, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
|
||||
@ -89,9 +91,9 @@ impl MissingDoc {
|
||||
return;
|
||||
}
|
||||
|
||||
let has_doc = attrs.iter().any(|a| {
|
||||
a.is_value_str() && a.name().map_or(false, |n| n == "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,
|
||||
@ -110,14 +112,12 @@ impl LintPass for MissingDoc {
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
||||
fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
|
||||
let doc_hidden = self.doc_hidden() ||
|
||||
attrs.iter().any(|attr| {
|
||||
attr.check_name("doc") &&
|
||||
match attr.meta_item_list() {
|
||||
None => false,
|
||||
Some(l) => attr::list_contains_name(&l[..], "hidden"),
|
||||
}
|
||||
});
|
||||
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
|
||||
attr.check_name("doc") && match attr.meta_item_list() {
|
||||
None => false,
|
||||
Some(l) => attr::list_contains_name(&l[..], "hidden"),
|
||||
}
|
||||
});
|
||||
self.doc_hidden_stack.push(doc_hidden);
|
||||
}
|
||||
|
||||
@ -166,10 +166,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
||||
let def_id = cx.tcx.hir.local_def_id(impl_item.id);
|
||||
match cx.tcx.associated_item(def_id).container {
|
||||
ty::TraitContainer(_) => return,
|
||||
ty::ImplContainer(cid) => {
|
||||
if cx.tcx.impl_trait_ref(cid).is_some() {
|
||||
return;
|
||||
}
|
||||
ty::ImplContainer(cid) => if cx.tcx.impl_trait_ref(cid).is_some() {
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,14 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
expr.span,
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
} else if let ty::TyRef(_, ty::TypeAndMut { mutbl: hir::MutMutable, .. }) = self.cx.tables.expr_ty(e).sty {
|
||||
} else if let ty::TyRef(
|
||||
_,
|
||||
ty::TypeAndMut {
|
||||
mutbl: hir::MutMutable,
|
||||
..
|
||||
},
|
||||
) = self.cx.tables.expr_ty(e).sty
|
||||
{
|
||||
span_lint(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
@ -82,13 +89,22 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
|
||||
if let hir::TyRptr(_,
|
||||
hir::MutTy {
|
||||
ty: ref pty,
|
||||
mutbl: hir::MutMutable,
|
||||
}) = ty.node
|
||||
if let hir::TyRptr(
|
||||
_,
|
||||
hir::MutTy {
|
||||
ty: ref pty,
|
||||
mutbl: hir::MutMutable,
|
||||
},
|
||||
) = ty.node
|
||||
{
|
||||
if let hir::TyRptr(_, hir::MutTy { mutbl: hir::MutMutable, .. }) = pty.node {
|
||||
if let hir::TyRptr(
|
||||
_,
|
||||
hir::MutTy {
|
||||
mutbl: hir::MutMutable,
|
||||
..
|
||||
},
|
||||
) = pty.node
|
||||
{
|
||||
span_lint(
|
||||
self.cx,
|
||||
MUT_MUT,
|
||||
@ -96,7 +112,6 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
"generally you want to avoid `&mut &mut _` if possible",
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
intravisit::walk_ty(self, ty);
|
||||
|
@ -36,15 +36,13 @@ impl LintPass for UnnecessaryMutPassed {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
match e.node {
|
||||
ExprCall(ref fn_expr, ref arguments) => {
|
||||
if let ExprPath(ref path) = fn_expr.node {
|
||||
check_arguments(
|
||||
cx,
|
||||
arguments,
|
||||
cx.tables.expr_ty(fn_expr),
|
||||
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
|
||||
);
|
||||
}
|
||||
ExprCall(ref fn_expr, ref arguments) => if let ExprPath(ref path) = fn_expr.node {
|
||||
check_arguments(
|
||||
cx,
|
||||
arguments,
|
||||
cx.tables.expr_ty(fn_expr),
|
||||
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
|
||||
);
|
||||
},
|
||||
ExprMethodCall(ref path, _, ref arguments) => {
|
||||
let def_id = cx.tables.type_dependent_defs()[e.hir_id].def_id();
|
||||
@ -63,16 +61,23 @@ fn check_arguments<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arguments: &[Expr], typ
|
||||
let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
|
||||
for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
|
||||
match parameter.sty {
|
||||
ty::TyRef(_, ty::TypeAndMut { mutbl: MutImmutable, .. }) |
|
||||
ty::TyRawPtr(ty::TypeAndMut { mutbl: MutImmutable, .. }) => {
|
||||
if let ExprAddrOf(MutMutable, _) = argument.node {
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_MUT_PASSED,
|
||||
argument.span,
|
||||
&format!("The function/method `{}` doesn't need a mutable reference", name),
|
||||
);
|
||||
}
|
||||
ty::TyRef(
|
||||
_,
|
||||
ty::TypeAndMut {
|
||||
mutbl: MutImmutable,
|
||||
..
|
||||
},
|
||||
) |
|
||||
ty::TyRawPtr(ty::TypeAndMut {
|
||||
mutbl: MutImmutable,
|
||||
..
|
||||
}) => if let ExprAddrOf(MutMutable, _) = argument.node {
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_MUT_PASSED,
|
||||
argument.span,
|
||||
&format!("The function/method `{}` doesn't need a mutable reference", name),
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use rustc::lint::{LintPass, LintArray, LateLintPass, LateContext};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::hir::Expr;
|
||||
use syntax::ast;
|
||||
@ -65,7 +65,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutexAtomic {
|
||||
if let Some(atomic_name) = get_atomic_name(mutex_param) {
|
||||
let msg = format!(
|
||||
"Consider using an {} instead of a Mutex here. If you just want the locking \
|
||||
behaviour and not the internal type, consider using Mutex<()>.",
|
||||
behaviour and not the internal type, consider using Mutex<()>.",
|
||||
atomic_name
|
||||
);
|
||||
match mutex_param.sty {
|
||||
|
@ -6,7 +6,7 @@ use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::codemap::Spanned;
|
||||
use utils::{span_lint, span_lint_and_sugg, snippet};
|
||||
use utils::{snippet, span_lint, span_lint_and_sugg};
|
||||
use utils::sugg::Sugg;
|
||||
|
||||
/// **What it does:** Checks for expressions of the form `if c { true } else {
|
||||
@ -82,8 +82,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
|
||||
};
|
||||
if let ExprBlock(ref then_block) = then_block.node {
|
||||
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
|
||||
(RetBool(true), RetBool(true)) |
|
||||
(Bool(true), Bool(true)) => {
|
||||
(RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_BOOL,
|
||||
@ -91,8 +90,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
|
||||
"this if-then-else expression will always return true",
|
||||
);
|
||||
},
|
||||
(RetBool(false), RetBool(false)) |
|
||||
(Bool(false), Bool(false)) => {
|
||||
(RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_BOOL,
|
||||
@ -186,16 +184,14 @@ enum Expression {
|
||||
fn fetch_bool_block(block: &Block) -> Expression {
|
||||
match (&*block.stmts, block.expr.as_ref()) {
|
||||
(&[], Some(e)) => fetch_bool_expr(&**e),
|
||||
(&[ref e], None) => {
|
||||
if let StmtSemi(ref e, _) = e.node {
|
||||
if let ExprRet(_) = e.node {
|
||||
fetch_bool_expr(&**e)
|
||||
} else {
|
||||
Expression::Other
|
||||
}
|
||||
(&[ref e], None) => if let StmtSemi(ref e, _) = e.node {
|
||||
if let ExprRet(_) = e.node {
|
||||
fetch_bool_expr(&**e)
|
||||
} else {
|
||||
Expression::Other
|
||||
}
|
||||
} else {
|
||||
Expression::Other
|
||||
},
|
||||
_ => Expression::Other,
|
||||
}
|
||||
@ -204,18 +200,14 @@ fn fetch_bool_block(block: &Block) -> Expression {
|
||||
fn fetch_bool_expr(expr: &Expr) -> Expression {
|
||||
match expr.node {
|
||||
ExprBlock(ref block) => fetch_bool_block(block),
|
||||
ExprLit(ref lit_ptr) => {
|
||||
if let LitKind::Bool(value) = lit_ptr.node {
|
||||
Expression::Bool(value)
|
||||
} else {
|
||||
Expression::Other
|
||||
}
|
||||
ExprLit(ref lit_ptr) => if let LitKind::Bool(value) = lit_ptr.node {
|
||||
Expression::Bool(value)
|
||||
} else {
|
||||
Expression::Other
|
||||
},
|
||||
ExprRet(Some(ref expr)) => {
|
||||
match fetch_bool_expr(expr) {
|
||||
Expression::Bool(value) => Expression::RetBool(value),
|
||||
_ => Expression::Other,
|
||||
}
|
||||
ExprRet(Some(ref expr)) => match fetch_bool_expr(expr) {
|
||||
Expression::Bool(value) => Expression::RetBool(value),
|
||||
_ => Expression::Other,
|
||||
},
|
||||
_ => Expression::Other,
|
||||
}
|
||||
|
@ -3,10 +3,10 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::{ExprAddrOf, Expr, MutImmutable, Pat, PatKind, BindingAnnotation};
|
||||
use rustc::hir::{BindingAnnotation, Expr, ExprAddrOf, MutImmutable, Pat, PatKind};
|
||||
use rustc::ty;
|
||||
use rustc::ty::adjustment::{Adjustment, Adjust};
|
||||
use utils::{span_lint, in_macro};
|
||||
use rustc::ty::adjustment::{Adjust, Adjustment};
|
||||
use utils::{in_macro, span_lint};
|
||||
|
||||
/// **What it does:** Checks for address of operations (`&`) that are going to
|
||||
/// be dereferenced immediately by the compiler.
|
||||
@ -43,16 +43,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
|
||||
if let ExprAddrOf(MutImmutable, ref inner) = e.node {
|
||||
if let ty::TyRef(..) = cx.tables.expr_ty(inner).sty {
|
||||
for adj3 in cx.tables.expr_adjustments(e).windows(3) {
|
||||
if let [
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
Adjustment { kind: Adjust::Deref(_), .. },
|
||||
Adjustment { kind: Adjust::Borrow(_), .. }
|
||||
] = *adj3 {
|
||||
span_lint(cx,
|
||||
NEEDLESS_BORROW,
|
||||
e.span,
|
||||
"this expression borrows a reference that is immediately dereferenced by the \
|
||||
compiler");
|
||||
if let [Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
}, Adjustment {
|
||||
kind: Adjust::Deref(_),
|
||||
..
|
||||
}, Adjustment {
|
||||
kind: Adjust::Borrow(_),
|
||||
..
|
||||
}] = *adj3
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
NEEDLESS_BORROW,
|
||||
e.span,
|
||||
"this expression borrows a reference that is immediately dereferenced by the \
|
||||
compiler",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@
|
||||
//! This lint is **warn** by default
|
||||
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::{MutImmutable, Pat, PatKind, BindingAnnotation};
|
||||
use utils::{span_lint_and_then, in_macro, snippet};
|
||||
use rustc::hir::{BindingAnnotation, MutImmutable, Pat, PatKind};
|
||||
use utils::{in_macro, snippet, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for useless borrowed references.
|
||||
///
|
||||
|
@ -32,7 +32,7 @@ use syntax::ast;
|
||||
use syntax::codemap::{original_sp, DUMMY_SP};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use utils::{in_macro, span_help_and_lint, snippet_block, snippet, trim_multiline};
|
||||
use utils::{in_macro, snippet, snippet_block, span_help_and_lint, trim_multiline};
|
||||
|
||||
/// **What it does:** The lint checks for `if`-statements appearing in loops
|
||||
/// that contain a `continue` statement in either their main blocks or their
|
||||
@ -181,13 +181,10 @@ fn needless_continue_in_else(else_expr: &ast::Expr) -> bool {
|
||||
|
||||
fn is_first_block_stmt_continue(block: &ast::Block) -> bool {
|
||||
block.stmts.get(0).map_or(false, |stmt| match stmt.node {
|
||||
ast::StmtKind::Semi(ref e) |
|
||||
ast::StmtKind::Expr(ref e) => {
|
||||
if let ast::ExprKind::Continue(_) = e.node {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => if let ast::ExprKind::Continue(_) = e.node {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
@ -222,8 +219,7 @@ where
|
||||
F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
|
||||
{
|
||||
match stmt.node {
|
||||
ast::StmtKind::Semi(ref e) |
|
||||
ast::StmtKind::Expr(ref e) => {
|
||||
ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
|
||||
if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.node {
|
||||
func(e, cond, if_block, else_expr);
|
||||
}
|
||||
@ -269,25 +265,20 @@ const DROP_ELSE_BLOCK_MSG: &'static str = "Consider dropping the else clause, an
|
||||
|
||||
|
||||
fn emit_warning<'a>(ctx: &EarlyContext, data: &'a LintData, header: &str, typ: LintType) {
|
||||
|
||||
// snip is the whole *help* message that appears after the warning.
|
||||
// message is the warning message.
|
||||
// expr is the expression which the lint warning message refers to.
|
||||
let (snip, message, expr) = match typ {
|
||||
LintType::ContinueInsideElseBlock => {
|
||||
(
|
||||
suggestion_snippet_for_continue_inside_else(ctx, data, header),
|
||||
MSG_REDUNDANT_ELSE_BLOCK,
|
||||
data.else_expr,
|
||||
)
|
||||
},
|
||||
LintType::ContinueInsideThenBlock => {
|
||||
(
|
||||
suggestion_snippet_for_continue_inside_if(ctx, data, header),
|
||||
MSG_ELSE_BLOCK_NOT_NEEDED,
|
||||
data.if_expr,
|
||||
)
|
||||
},
|
||||
LintType::ContinueInsideElseBlock => (
|
||||
suggestion_snippet_for_continue_inside_else(ctx, data, header),
|
||||
MSG_REDUNDANT_ELSE_BLOCK,
|
||||
data.else_expr,
|
||||
),
|
||||
LintType::ContinueInsideThenBlock => (
|
||||
suggestion_snippet_for_continue_inside_if(ctx, data, header),
|
||||
MSG_ELSE_BLOCK_NOT_NEEDED,
|
||||
data.if_expr,
|
||||
),
|
||||
};
|
||||
span_help_and_lint(ctx, NEEDLESS_CONTINUE, expr.span, message, &snip);
|
||||
}
|
||||
|
@ -9,9 +9,9 @@ use rustc::middle::mem_categorization as mc;
|
||||
use syntax::ast::NodeId;
|
||||
use syntax_pos::Span;
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, match_type, snippet, span_lint_and_then,
|
||||
multispan_sugg, paths};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use utils::{get_trait_def_id, implements_trait, in_macro, is_copy, is_self, match_type, multispan_sugg, paths,
|
||||
snippet, span_lint_and_then};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
/// **What it does:** Checks for functions taking arguments by value, but not
|
||||
/// consuming them in its
|
||||
@ -62,16 +62,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
||||
}
|
||||
|
||||
match kind {
|
||||
FnKind::ItemFn(.., attrs) => {
|
||||
for a in attrs {
|
||||
if_let_chain!{[
|
||||
a.meta_item_list().is_some(),
|
||||
let Some(name) = a.name(),
|
||||
name == "proc_macro_derive",
|
||||
], {
|
||||
return;
|
||||
}}
|
||||
}
|
||||
FnKind::ItemFn(.., attrs) => for a in attrs {
|
||||
if_let_chain!{[
|
||||
a.meta_item_list().is_some(),
|
||||
let Some(name) = a.name(),
|
||||
name == "proc_macro_derive",
|
||||
], {
|
||||
return;
|
||||
}}
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
@ -106,7 +104,6 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
||||
let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
|
||||
|
||||
for ((input, &ty), arg) in decl.inputs.iter().zip(fn_sig.inputs()).zip(&body.arguments) {
|
||||
|
||||
// Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
|
||||
// This is needed due to the `Borrow<T> for T` blanket impl.
|
||||
let implements_borrow_trait = preds
|
||||
@ -118,9 +115,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
||||
})
|
||||
.filter(|tpred| tpred.def_id() == borrow_trait && tpred.self_ty() == ty)
|
||||
.any(|tpred| {
|
||||
tpred.input_types().nth(1).expect(
|
||||
"Borrow trait must have an parameter",
|
||||
) != ty
|
||||
tpred
|
||||
.input_types()
|
||||
.nth(1)
|
||||
.expect("Borrow trait must have an parameter") != ty
|
||||
});
|
||||
|
||||
if_let_chain! {[
|
||||
@ -299,8 +297,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
|
||||
fn unwrap_downcast_or_interior(mut cmt: mc::cmt) -> mc::cmt {
|
||||
loop {
|
||||
match cmt.cat.clone() {
|
||||
mc::Categorization::Downcast(c, _) |
|
||||
mc::Categorization::Interior(c, _) => {
|
||||
mc::Categorization::Downcast(c, _) | mc::Categorization::Interior(c, _) => {
|
||||
cmt = c;
|
||||
},
|
||||
_ => return cmt,
|
||||
|
@ -115,43 +115,42 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
|
||||
return;
|
||||
}
|
||||
if decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(id) {
|
||||
let self_ty = cx.tcx.type_of(
|
||||
cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id)),
|
||||
);
|
||||
let self_ty = cx.tcx
|
||||
.type_of(cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id)));
|
||||
if_let_chain!{[
|
||||
same_tys(cx, self_ty, return_ty(cx, id)),
|
||||
let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT),
|
||||
!implements_trait(cx, self_ty, default_trait_id, &[])
|
||||
], {
|
||||
if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
|
||||
span_lint_and_then(cx,
|
||||
NEW_WITHOUT_DEFAULT_DERIVE, span,
|
||||
&format!("you should consider deriving a \
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.suggest_item_with_attr(cx, sp, "try this", "#[derive(Default)]");
|
||||
});
|
||||
} else {
|
||||
span_lint_and_then(cx,
|
||||
NEW_WITHOUT_DEFAULT, span,
|
||||
&format!("you should consider adding a \
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.suggest_prepend_item(cx,
|
||||
span,
|
||||
"try this",
|
||||
&format!(
|
||||
same_tys(cx, self_ty, return_ty(cx, id)),
|
||||
let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT),
|
||||
!implements_trait(cx, self_ty, default_trait_id, &[])
|
||||
], {
|
||||
if let Some(sp) = can_derive_default(self_ty, cx, default_trait_id) {
|
||||
span_lint_and_then(cx,
|
||||
NEW_WITHOUT_DEFAULT_DERIVE, span,
|
||||
&format!("you should consider deriving a \
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.suggest_item_with_attr(cx, sp, "try this", "#[derive(Default)]");
|
||||
});
|
||||
} else {
|
||||
span_lint_and_then(cx,
|
||||
NEW_WITHOUT_DEFAULT, span,
|
||||
&format!("you should consider adding a \
|
||||
`Default` implementation for `{}`",
|
||||
self_ty),
|
||||
|db| {
|
||||
db.suggest_prepend_item(cx,
|
||||
span,
|
||||
"try this",
|
||||
&format!(
|
||||
"impl Default for {} {{
|
||||
fn default() -> Self {{
|
||||
Self::new()
|
||||
}}
|
||||
}}",
|
||||
self_ty));
|
||||
});
|
||||
}
|
||||
}}
|
||||
self_ty));
|
||||
});
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::{Expr, Expr_, Stmt, StmtSemi, BlockCheckMode, UnsafeSource, BiAnd, BiOr};
|
||||
use utils::{in_macro, span_lint, snippet_opt, span_lint_and_sugg};
|
||||
use rustc::hir::{BiAnd, BiOr, BlockCheckMode, Expr, Expr_, Stmt, StmtSemi, UnsafeSource};
|
||||
use utils::{in_macro, snippet_opt, span_lint, span_lint_and_sugg};
|
||||
use std::ops::Deref;
|
||||
|
||||
/// **What it does:** Checks for statements which have no effect.
|
||||
@ -45,13 +45,11 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
|
||||
return false;
|
||||
}
|
||||
match expr.node {
|
||||
Expr_::ExprLit(..) |
|
||||
Expr_::ExprClosure(.., _) |
|
||||
Expr_::ExprPath(..) => true,
|
||||
Expr_::ExprIndex(ref a, ref b) |
|
||||
Expr_::ExprBinary(_, ref a, ref b) => has_no_effect(cx, a) && has_no_effect(cx, b),
|
||||
Expr_::ExprArray(ref v) |
|
||||
Expr_::ExprTup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
|
||||
Expr_::ExprLit(..) | Expr_::ExprClosure(.., _) | Expr_::ExprPath(..) => true,
|
||||
Expr_::ExprIndex(ref a, ref b) | Expr_::ExprBinary(_, ref a, ref b) => {
|
||||
has_no_effect(cx, a) && has_no_effect(cx, b)
|
||||
},
|
||||
Expr_::ExprArray(ref v) | Expr_::ExprTup(ref v) => v.iter().all(|val| has_no_effect(cx, val)),
|
||||
Expr_::ExprRepeat(ref inner, _) |
|
||||
Expr_::ExprCast(ref inner, _) |
|
||||
Expr_::ExprType(ref inner, _) |
|
||||
@ -61,34 +59,29 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
|
||||
Expr_::ExprAddrOf(_, ref inner) |
|
||||
Expr_::ExprBox(ref inner) => has_no_effect(cx, inner),
|
||||
Expr_::ExprStruct(_, ref fields, ref base) => {
|
||||
fields.iter().all(|field| has_no_effect(cx, &field.expr)) &&
|
||||
match *base {
|
||||
Some(ref base) => has_no_effect(cx, base),
|
||||
None => true,
|
||||
}
|
||||
fields.iter().all(|field| has_no_effect(cx, &field.expr)) && match *base {
|
||||
Some(ref base) => has_no_effect(cx, base),
|
||||
None => true,
|
||||
}
|
||||
},
|
||||
Expr_::ExprCall(ref callee, ref args) => {
|
||||
if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tables.qpath_def(qpath, callee.hir_id);
|
||||
match def {
|
||||
Def::Struct(..) |
|
||||
Def::Variant(..) |
|
||||
Def::StructCtor(..) |
|
||||
Def::VariantCtor(..) => args.iter().all(|arg| has_no_effect(cx, arg)),
|
||||
_ => false,
|
||||
}
|
||||
Expr_::ExprCall(ref callee, ref args) => if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tables.qpath_def(qpath, callee.hir_id);
|
||||
match def {
|
||||
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
|
||||
args.iter().all(|arg| has_no_effect(cx, arg))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
},
|
||||
Expr_::ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() && if let Some(ref expr) = block.expr {
|
||||
has_no_effect(cx, expr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Expr_::ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() &&
|
||||
if let Some(ref expr) = block.expr {
|
||||
has_no_effect(cx, expr)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -143,8 +136,7 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
|
||||
Expr_::ExprBinary(ref binop, ref a, ref b) if binop.node != BiAnd && binop.node != BiOr => {
|
||||
Some(vec![&**a, &**b])
|
||||
},
|
||||
Expr_::ExprArray(ref v) |
|
||||
Expr_::ExprTup(ref v) => Some(v.iter().collect()),
|
||||
Expr_::ExprArray(ref v) | Expr_::ExprTup(ref v) => Some(v.iter().collect()),
|
||||
Expr_::ExprRepeat(ref inner, _) |
|
||||
Expr_::ExprCast(ref inner, _) |
|
||||
Expr_::ExprType(ref inner, _) |
|
||||
@ -153,29 +145,24 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
|
||||
Expr_::ExprTupField(ref inner, _) |
|
||||
Expr_::ExprAddrOf(_, ref inner) |
|
||||
Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
|
||||
Expr_::ExprStruct(_, ref fields, ref base) => {
|
||||
Some(
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| &f.expr)
|
||||
.chain(base)
|
||||
.map(Deref::deref)
|
||||
.collect(),
|
||||
)
|
||||
},
|
||||
Expr_::ExprCall(ref callee, ref args) => {
|
||||
if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tables.qpath_def(qpath, callee.hir_id);
|
||||
match def {
|
||||
Def::Struct(..) |
|
||||
Def::Variant(..) |
|
||||
Def::StructCtor(..) |
|
||||
Def::VariantCtor(..) => Some(args.iter().collect()),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
Expr_::ExprStruct(_, ref fields, ref base) => Some(
|
||||
fields
|
||||
.iter()
|
||||
.map(|f| &f.expr)
|
||||
.chain(base)
|
||||
.map(Deref::deref)
|
||||
.collect(),
|
||||
),
|
||||
Expr_::ExprCall(ref callee, ref args) => if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tables.qpath_def(qpath, callee.hir_id);
|
||||
match def {
|
||||
Def::Struct(..) | Def::Variant(..) | Def::StructCtor(..) | Def::VariantCtor(..) => {
|
||||
Some(args.iter().collect())
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Expr_::ExprBlock(ref block) => {
|
||||
if block.stmts.is_empty() {
|
||||
|
@ -3,8 +3,8 @@ use syntax::codemap::Span;
|
||||
use syntax::symbol::InternedString;
|
||||
use syntax::ast::*;
|
||||
use syntax::attr;
|
||||
use syntax::visit::{Visitor, walk_block, walk_pat, walk_expr};
|
||||
use utils::{span_lint_and_then, in_macro, span_lint};
|
||||
use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor};
|
||||
use utils::{in_macro, span_lint, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for names that are very similar and thus confusing.
|
||||
///
|
||||
@ -82,11 +82,9 @@ impl<'a, 'tcx: 'a, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat) {
|
||||
match pat.node {
|
||||
PatKind::Ident(_, id, _) => self.check_name(id.span, id.node.name),
|
||||
PatKind::Struct(_, ref fields, _) => {
|
||||
for field in fields {
|
||||
if !field.node.is_shorthand {
|
||||
self.visit_pat(&field.node.pat);
|
||||
}
|
||||
PatKind::Struct(_, ref fields, _) => for field in fields {
|
||||
if !field.node.is_shorthand {
|
||||
self.visit_pat(&field.node.pat);
|
||||
}
|
||||
},
|
||||
_ => walk_pat(self, pat),
|
||||
@ -104,9 +102,8 @@ fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
|
||||
}
|
||||
|
||||
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
|
||||
list.iter().any(|&name| {
|
||||
interned_name.starts_with(name) || interned_name.ends_with(name)
|
||||
})
|
||||
list.iter()
|
||||
.any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
@ -157,21 +154,21 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
} else {
|
||||
let mut interned_chars = interned_name.chars();
|
||||
let mut existing_chars = existing_name.interned.chars();
|
||||
let first_i = interned_chars.next().expect(
|
||||
"we know we have at least one char",
|
||||
);
|
||||
let first_e = existing_chars.next().expect(
|
||||
"we know we have at least one char",
|
||||
);
|
||||
let first_i = interned_chars
|
||||
.next()
|
||||
.expect("we know we have at least one char");
|
||||
let first_e = existing_chars
|
||||
.next()
|
||||
.expect("we know we have at least one char");
|
||||
let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
|
||||
|
||||
if eq_or_numeric((first_i, first_e)) {
|
||||
let last_i = interned_chars.next_back().expect(
|
||||
"we know we have at least two chars",
|
||||
);
|
||||
let last_e = existing_chars.next_back().expect(
|
||||
"we know we have at least two chars",
|
||||
);
|
||||
let last_i = interned_chars
|
||||
.next_back()
|
||||
.expect("we know we have at least two chars");
|
||||
let last_e = existing_chars
|
||||
.next_back()
|
||||
.expect("we know we have at least two chars");
|
||||
if eq_or_numeric((last_i, last_e)) {
|
||||
if interned_chars
|
||||
.zip(existing_chars)
|
||||
@ -181,12 +178,12 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
let second_last_i = interned_chars.next_back().expect(
|
||||
"we know we have at least three chars",
|
||||
);
|
||||
let second_last_e = existing_chars.next_back().expect(
|
||||
"we know we have at least three chars",
|
||||
);
|
||||
let second_last_i = interned_chars
|
||||
.next_back()
|
||||
.expect("we know we have at least three chars");
|
||||
let second_last_e = existing_chars
|
||||
.next_back()
|
||||
.expect("we know we have at least three chars");
|
||||
if !eq_or_numeric((second_last_i, second_last_e)) || second_last_i == '_' ||
|
||||
!interned_chars.zip(existing_chars).all(eq_or_numeric)
|
||||
{
|
||||
@ -197,12 +194,12 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
|
||||
}
|
||||
} else {
|
||||
let second_i = interned_chars.next().expect(
|
||||
"we know we have at least two chars",
|
||||
);
|
||||
let second_e = existing_chars.next().expect(
|
||||
"we know we have at least two chars",
|
||||
);
|
||||
let second_i = interned_chars
|
||||
.next()
|
||||
.expect("we know we have at least two chars");
|
||||
let second_e = existing_chars
|
||||
.next()
|
||||
.expect("we know we have at least two chars");
|
||||
if !eq_or_numeric((second_i, second_e)) || second_i == '_' ||
|
||||
!interned_chars.zip(existing_chars).all(eq_or_numeric)
|
||||
{
|
||||
@ -225,7 +222,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
span,
|
||||
&format!(
|
||||
"separate the discriminating character by an \
|
||||
underscore like: `{}_{}`",
|
||||
underscore like: `{}_{}`",
|
||||
&interned_name[..split],
|
||||
&interned_name[split..]
|
||||
),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{paths, method_chain_args, span_help_and_lint, match_type, snippet};
|
||||
use utils::{match_type, method_chain_args, paths, snippet, span_help_and_lint};
|
||||
|
||||
/// **What it does:*** Checks for unnecessary `ok()` in if let.
|
||||
///
|
||||
|
@ -1,4 +1,4 @@
|
||||
use rustc::hir::{Expr, ExprMethodCall, ExprLit};
|
||||
use rustc::hir::{Expr, ExprLit, ExprMethodCall};
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::codemap::{Span, Spanned};
|
||||
@ -67,11 +67,18 @@ fn get_open_options(cx: &LateContext, argument: &Expr, options: &mut Vec<(OpenOp
|
||||
|
||||
// Only proceed if this is a call on some object of type std::fs::OpenOptions
|
||||
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
|
||||
|
||||
let argument_option = match arguments[1].node {
|
||||
ExprLit(ref span) => {
|
||||
if let Spanned { node: LitKind::Bool(lit), .. } = **span {
|
||||
if lit { Argument::True } else { Argument::False }
|
||||
if let Spanned {
|
||||
node: LitKind::Bool(lit),
|
||||
..
|
||||
} = **span
|
||||
{
|
||||
if lit {
|
||||
Argument::True
|
||||
} else {
|
||||
Argument::False
|
||||
}
|
||||
} else {
|
||||
return; // The function is called with a literal
|
||||
// which is not a boolean literal. This is theoretically
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::LitKind;
|
||||
use utils::{is_direct_expn_of, match_def_path, resolve_node, paths, span_lint};
|
||||
use utils::{is_direct_expn_of, match_def_path, paths, resolve_node, span_lint};
|
||||
|
||||
/// **What it does:** Checks for missing parameters in `panic!`.
|
||||
///
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::*;
|
||||
use syntax::codemap::Spanned;
|
||||
use utils::{span_lint_and_sugg, snippet};
|
||||
use utils::{snippet, span_lint_and_sugg};
|
||||
|
||||
/// **What it does:** Checks for operations where precedence may be unclear
|
||||
/// and suggests to add parentheses. Currently it catches the following:
|
||||
@ -89,9 +89,7 @@ impl EarlyLintPass for Precedence {
|
||||
if let Some(slf) = args.first() {
|
||||
if let ExprKind::Lit(ref lit) = slf.node {
|
||||
match lit.node {
|
||||
LitKind::Int(..) |
|
||||
LitKind::Float(..) |
|
||||
LitKind::FloatUnsuffixed(..) => {
|
||||
LitKind::Int(..) | LitKind::Float(..) | LitKind::FloatUnsuffixed(..) => {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
PRECEDENCE,
|
||||
|
@ -1,8 +1,8 @@
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::map::Node::{NodeItem, NodeImplItem};
|
||||
use rustc::hir::map::Node::{NodeImplItem, NodeItem};
|
||||
use rustc::lint::*;
|
||||
use utils::paths;
|
||||
use utils::{is_expn_of, match_def_path, resolve_node, span_lint, match_path};
|
||||
use utils::{is_expn_of, match_def_path, match_path, resolve_node, span_lint};
|
||||
use format::get_argument_fmtstr_parts;
|
||||
|
||||
/// **What it does:** This lint warns when you using `print!()` with a format
|
||||
|
@ -128,11 +128,13 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
||||
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
|
||||
if let ty::TyRef(
|
||||
_,
|
||||
ty::TypeAndMut {
|
||||
ty,
|
||||
mutbl: MutImmutable,
|
||||
},
|
||||
) = ty.sty
|
||||
{
|
||||
if match_type(cx, ty, &paths::VEC) {
|
||||
span_lint(
|
||||
@ -140,7 +142,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
|
||||
with non-Vec-based slices. Consider changing the type to `&[...]`",
|
||||
with non-Vec-based slices. Consider changing the type to `&[...]`",
|
||||
);
|
||||
} else if match_type(cx, ty, &paths::STRING) {
|
||||
span_lint(
|
||||
@ -148,7 +150,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
||||
PTR_ARG,
|
||||
arg.span,
|
||||
"writing `&String` instead of `&str` involves a new object where a slice will do. \
|
||||
Consider changing the type to `&str`",
|
||||
Consider changing the type to `&str`",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -157,10 +159,10 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
||||
if let FunctionRetTy::Return(ref ty) = decl.output {
|
||||
if let Some((out, MutMutable, _)) = get_rptr_lm(ty) {
|
||||
let mut immutables = vec![];
|
||||
for (_, ref mutbl, ref argspan) in
|
||||
decl.inputs.iter().filter_map(|ty| get_rptr_lm(ty)).filter(
|
||||
|&(lt, _, _)| lt.name == out.name,
|
||||
)
|
||||
for (_, ref mutbl, ref argspan) in decl.inputs
|
||||
.iter()
|
||||
.filter_map(|ty| get_rptr_lm(ty))
|
||||
.filter(|&(lt, _, _)| lt.name == out.name)
|
||||
{
|
||||
if *mutbl == MutMutable {
|
||||
return;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{is_integer_literal, paths, snippet, span_lint};
|
||||
use utils::{higher, implements_trait, get_trait_def_id};
|
||||
use utils::{get_trait_def_id, higher, implements_trait};
|
||||
|
||||
/// **What it does:** Checks for calling `.step_by(0)` on iterators,
|
||||
/// which never terminates.
|
||||
@ -54,7 +54,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
|
||||
|
||||
// Range with step_by(0).
|
||||
if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) {
|
||||
use consts::{Constant, constant};
|
||||
use consts::{constant, Constant};
|
||||
use rustc_const_math::ConstInt::Usize;
|
||||
if let Some((Constant::Int(Usize(us)), _)) = constant(cx, &args[1]) {
|
||||
if us.as_u64(cx.sess().target.uint_type) == 0 {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use syntax::ast::{Expr, ExprKind, UnOp};
|
||||
use rustc::lint::*;
|
||||
use utils::{span_lint_and_sugg, snippet};
|
||||
use utils::{snippet, span_lint_and_sugg};
|
||||
|
||||
/// **What it does:** Checks for usage of `*&` and `*&mut` in expressions.
|
||||
///
|
||||
|
@ -7,9 +7,9 @@ use rustc::ty::subst::Substs;
|
||||
use std::collections::HashSet;
|
||||
use std::error::Error;
|
||||
use syntax::ast::{LitKind, NodeId};
|
||||
use syntax::codemap::{Span, BytePos};
|
||||
use syntax::codemap::{BytePos, Span};
|
||||
use syntax::symbol::InternedString;
|
||||
use utils::{is_expn_of, match_def_path, match_type, paths, span_lint, span_help_and_lint};
|
||||
use utils::{is_expn_of, match_def_path, match_type, paths, span_help_and_lint, span_lint};
|
||||
|
||||
/// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
|
||||
/// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
|
||||
@ -161,27 +161,19 @@ fn is_trivial_regex(s: ®ex_syntax::Expr) -> Option<&'static str> {
|
||||
match *s {
|
||||
Expr::Empty | Expr::StartText | Expr::EndText => Some("the regex is unlikely to be useful as it is"),
|
||||
Expr::Literal { .. } => Some("consider using `str::contains`"),
|
||||
Expr::Concat(ref exprs) => {
|
||||
match exprs.len() {
|
||||
2 => {
|
||||
match (&exprs[0], &exprs[1]) {
|
||||
(&Expr::StartText, &Expr::EndText) => Some("consider using `str::is_empty`"),
|
||||
(&Expr::StartText, &Expr::Literal { .. }) => Some("consider using `str::starts_with`"),
|
||||
(&Expr::Literal { .. }, &Expr::EndText) => Some("consider using `str::ends_with`"),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) =
|
||||
(&exprs[0], &exprs[1], &exprs[2])
|
||||
{
|
||||
Some("consider using `==` on `str`s")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Expr::Concat(ref exprs) => match exprs.len() {
|
||||
2 => match (&exprs[0], &exprs[1]) {
|
||||
(&Expr::StartText, &Expr::EndText) => Some("consider using `str::is_empty`"),
|
||||
(&Expr::StartText, &Expr::Literal { .. }) => Some("consider using `str::starts_with`"),
|
||||
(&Expr::Literal { .. }, &Expr::EndText) => Some("consider using `str::ends_with`"),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
3 => if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) = (&exprs[0], &exprs[1], &exprs[2]) {
|
||||
Some("consider using `==` on `str`s")
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
@ -205,16 +197,14 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
|
||||
if let LitKind::Str(ref r, _) = lit.node {
|
||||
let r = &r.as_str();
|
||||
match builder.parse(r) {
|
||||
Ok(r) => {
|
||||
if let Some(repl) = is_trivial_regex(&r) {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
TRIVIAL_REGEX,
|
||||
expr.span,
|
||||
"trivial regex",
|
||||
&format!("consider using {}", repl),
|
||||
);
|
||||
}
|
||||
Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
TRIVIAL_REGEX,
|
||||
expr.span,
|
||||
"trivial regex",
|
||||
&format!("consider using {}", repl),
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
span_lint(
|
||||
@ -228,16 +218,14 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
|
||||
}
|
||||
} else if let Some(r) = const_str(cx, expr) {
|
||||
match builder.parse(&r) {
|
||||
Ok(r) => {
|
||||
if let Some(repl) = is_trivial_regex(&r) {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
TRIVIAL_REGEX,
|
||||
expr.span,
|
||||
"trivial regex",
|
||||
&format!("consider using {}", repl),
|
||||
);
|
||||
}
|
||||
Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
TRIVIAL_REGEX,
|
||||
expr.span,
|
||||
"trivial regex",
|
||||
&format!("consider using {}", repl),
|
||||
);
|
||||
},
|
||||
Err(e) => {
|
||||
span_lint(
|
||||
|
@ -3,7 +3,7 @@ use syntax::ast;
|
||||
use syntax::codemap::{Span, Spanned};
|
||||
use syntax::visit::FnKind;
|
||||
|
||||
use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast, in_macro, in_external_macro};
|
||||
use utils::{in_external_macro, in_macro, match_path_ast, snippet_opt, span_lint_and_then, span_note_and_lint};
|
||||
|
||||
/// **What it does:** Checks for return statements at the end of a block.
|
||||
///
|
||||
@ -50,8 +50,7 @@ impl ReturnPass {
|
||||
fn check_block_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
|
||||
if let Some(stmt) = block.stmts.last() {
|
||||
match stmt.node {
|
||||
ast::StmtKind::Expr(ref expr) |
|
||||
ast::StmtKind::Semi(ref expr) => {
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
|
||||
self.check_final_expr(cx, expr, Some(stmt.span));
|
||||
},
|
||||
_ => (),
|
||||
@ -81,10 +80,8 @@ impl ReturnPass {
|
||||
self.check_final_expr(cx, elsexpr, None);
|
||||
},
|
||||
// a match expr, check all arms
|
||||
ast::ExprKind::Match(_, ref arms) => {
|
||||
for arm in arms {
|
||||
self.check_final_expr(cx, &arm.body, Some(arm.body.span));
|
||||
}
|
||||
ast::ExprKind::Match(_, ref arms) => for arm in arms {
|
||||
self.check_final_expr(cx, &arm.body, Some(arm.body.span));
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
@ -140,8 +137,7 @@ impl LintPass for ReturnPass {
|
||||
impl EarlyLintPass for ReturnPass {
|
||||
fn check_fn(&mut self, cx: &EarlyContext, kind: FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) {
|
||||
match kind {
|
||||
FnKind::ItemFn(.., block) |
|
||||
FnKind::Method(.., block) => self.check_block_return(cx, block),
|
||||
FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block),
|
||||
FnKind::Closure(body) => self.check_final_expr(cx, body, Some(body.span)),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{span_lint, get_trait_def_id, paths};
|
||||
use utils::{get_trait_def_id, paths, span_lint};
|
||||
|
||||
/// **What it does:** Checks for mis-uses of the serde API.
|
||||
///
|
||||
|
@ -4,7 +4,7 @@ use rustc::hir::*;
|
||||
use rustc::hir::intravisit::FnKind;
|
||||
use rustc::ty;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{contains_name, higher, in_external_macro, snippet, span_lint_and_then, iter_input_pats};
|
||||
use utils::{contains_name, higher, in_external_macro, iter_input_pats, snippet, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for bindings that shadow other bindings already in
|
||||
/// scope, while just changing reference level or mutability.
|
||||
@ -111,8 +111,7 @@ fn check_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, block: &'tcx Block, binding
|
||||
for stmt in &block.stmts {
|
||||
match stmt.node {
|
||||
StmtDecl(ref decl, _) => check_decl(cx, decl, bindings),
|
||||
StmtExpr(ref e, _) |
|
||||
StmtSemi(ref e, _) => check_expr(cx, e, bindings),
|
||||
StmtExpr(ref e, _) | StmtSemi(ref e, _) => check_expr(cx, e, bindings),
|
||||
}
|
||||
}
|
||||
if let Some(ref o) = block.expr {
|
||||
@ -185,54 +184,49 @@ fn check_pat<'a, 'tcx>(
|
||||
check_pat(cx, p, init, span, bindings);
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, ref pfields, _) => {
|
||||
if let Some(init_struct) = init {
|
||||
if let ExprStruct(_, ref efields, _) = init_struct.node {
|
||||
for field in pfields {
|
||||
let name = field.node.name;
|
||||
let efield = efields.iter().find(|f| f.name.node == name).map(
|
||||
|f| &*f.expr,
|
||||
);
|
||||
check_pat(cx, &field.node.pat, efield, span, bindings);
|
||||
}
|
||||
} else {
|
||||
for field in pfields {
|
||||
check_pat(cx, &field.node.pat, init, span, bindings);
|
||||
}
|
||||
PatKind::Struct(_, ref pfields, _) => if let Some(init_struct) = init {
|
||||
if let ExprStruct(_, ref efields, _) = init_struct.node {
|
||||
for field in pfields {
|
||||
let name = field.node.name;
|
||||
let efield = efields
|
||||
.iter()
|
||||
.find(|f| f.name.node == name)
|
||||
.map(|f| &*f.expr);
|
||||
check_pat(cx, &field.node.pat, efield, span, bindings);
|
||||
}
|
||||
} else {
|
||||
for field in pfields {
|
||||
check_pat(cx, &field.node.pat, None, span, bindings);
|
||||
check_pat(cx, &field.node.pat, init, span, bindings);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for field in pfields {
|
||||
check_pat(cx, &field.node.pat, None, span, bindings);
|
||||
}
|
||||
},
|
||||
PatKind::Tuple(ref inner, _) => {
|
||||
if let Some(init_tup) = init {
|
||||
if let ExprTup(ref tup) = init_tup.node {
|
||||
for (i, p) in inner.iter().enumerate() {
|
||||
check_pat(cx, p, Some(&tup[i]), p.span, bindings);
|
||||
}
|
||||
} else {
|
||||
for p in inner {
|
||||
check_pat(cx, p, init, span, bindings);
|
||||
}
|
||||
PatKind::Tuple(ref inner, _) => if let Some(init_tup) = init {
|
||||
if let ExprTup(ref tup) = init_tup.node {
|
||||
for (i, p) in inner.iter().enumerate() {
|
||||
check_pat(cx, p, Some(&tup[i]), p.span, bindings);
|
||||
}
|
||||
} else {
|
||||
for p in inner {
|
||||
check_pat(cx, p, None, span, bindings);
|
||||
check_pat(cx, p, init, span, bindings);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for p in inner {
|
||||
check_pat(cx, p, None, span, bindings);
|
||||
}
|
||||
},
|
||||
PatKind::Box(ref inner) => {
|
||||
if let Some(initp) = init {
|
||||
if let ExprBox(ref inner_init) = initp.node {
|
||||
check_pat(cx, inner, Some(&**inner_init), span, bindings);
|
||||
} else {
|
||||
check_pat(cx, inner, init, span, bindings);
|
||||
}
|
||||
PatKind::Box(ref inner) => if let Some(initp) = init {
|
||||
if let ExprBox(ref inner_init) = initp.node {
|
||||
check_pat(cx, inner, Some(&**inner_init), span, bindings);
|
||||
} else {
|
||||
check_pat(cx, inner, init, span, bindings);
|
||||
}
|
||||
} else {
|
||||
check_pat(cx, inner, init, span, bindings);
|
||||
},
|
||||
PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
|
||||
// PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
|
||||
@ -292,13 +286,14 @@ fn lint_shadow<'a, 'tcx: 'a>(
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
span_lint_and_then(cx,
|
||||
SHADOW_UNRELATED,
|
||||
span,
|
||||
&format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
|
||||
|db| { db.span_note(prev_span, "previous binding is here"); });
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
SHADOW_UNRELATED,
|
||||
span,
|
||||
&format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
|
||||
|db| { db.span_note(prev_span, "previous binding is here"); },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,19 +302,14 @@ fn check_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, bindings:
|
||||
return;
|
||||
}
|
||||
match expr.node {
|
||||
ExprUnary(_, ref e) |
|
||||
ExprField(ref e, _) |
|
||||
ExprTupField(ref e, _) |
|
||||
ExprAddrOf(_, ref e) |
|
||||
ExprBox(ref e) => check_expr(cx, e, bindings),
|
||||
ExprBlock(ref block) |
|
||||
ExprLoop(ref block, _, _) => check_block(cx, block, bindings),
|
||||
ExprUnary(_, ref e) | ExprField(ref e, _) | ExprTupField(ref e, _) | ExprAddrOf(_, ref e) | ExprBox(ref e) => {
|
||||
check_expr(cx, e, bindings)
|
||||
},
|
||||
ExprBlock(ref block) | ExprLoop(ref block, _, _) => check_block(cx, block, bindings),
|
||||
// ExprCall
|
||||
// ExprMethodCall
|
||||
ExprArray(ref v) | ExprTup(ref v) => {
|
||||
for e in v {
|
||||
check_expr(cx, e, bindings)
|
||||
}
|
||||
ExprArray(ref v) | ExprTup(ref v) => for e in v {
|
||||
check_expr(cx, e, bindings)
|
||||
},
|
||||
ExprIf(ref cond, ref then, ref otherwise) => {
|
||||
check_expr(cx, cond, bindings);
|
||||
@ -358,12 +348,9 @@ fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty, bindings: &mut V
|
||||
check_ty(cx, fty, bindings);
|
||||
check_expr(cx, &cx.tcx.hir.body(body_id).value, bindings);
|
||||
},
|
||||
TyPtr(MutTy { ty: ref mty, .. }) |
|
||||
TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
|
||||
TyTup(ref tup) => {
|
||||
for t in tup {
|
||||
check_ty(cx, t, bindings)
|
||||
}
|
||||
TyPtr(MutTy { ty: ref mty, .. }) | TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
|
||||
TyTup(ref tup) => for t in tup {
|
||||
check_ty(cx, t, bindings)
|
||||
},
|
||||
TyTypeof(body_id) => check_expr(cx, &cx.tcx.hir.body(body_id).value, bindings),
|
||||
_ => (),
|
||||
@ -372,14 +359,13 @@ fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty, bindings: &mut V
|
||||
|
||||
fn is_self_shadow(name: Name, expr: &Expr) -> bool {
|
||||
match expr.node {
|
||||
ExprBox(ref inner) |
|
||||
ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
|
||||
ExprBox(ref inner) | ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
|
||||
ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() &&
|
||||
block.expr.as_ref().map_or(
|
||||
false,
|
||||
|e| is_self_shadow(name, e),
|
||||
)
|
||||
block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or(false, |e| is_self_shadow(name, e))
|
||||
},
|
||||
ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
|
||||
ExprPath(QPath::Resolved(_, ref path)) => path_eq_name(name, path),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::{is_direct_expn_of, is_expn_of, implements_trait, span_lint};
|
||||
use utils::{implements_trait, is_direct_expn_of, is_expn_of, span_lint};
|
||||
|
||||
/// **What it does:** Checks for `assert!(x == y)` or `assert!(x != y)` which
|
||||
/// can be better written
|
||||
|
@ -2,7 +2,7 @@ use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use syntax::codemap::Spanned;
|
||||
use utils::SpanlessEq;
|
||||
use utils::{match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty, get_parent_expr, is_allowed};
|
||||
use utils::{get_parent_expr, is_allowed, match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty};
|
||||
|
||||
/// **What it does:** Checks for string appends of the form `x = x + y` (without
|
||||
/// `let`!).
|
||||
@ -108,7 +108,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
|
||||
STRING_ADD_ASSIGN,
|
||||
e.span,
|
||||
"you assigned the result of adding something to this string. Consider using \
|
||||
`String::push_str()` instead",
|
||||
`String::push_str()` instead",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -124,10 +124,10 @@ fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
|
||||
ExprBinary(Spanned { node: BiAdd, .. }, ref left, _) => SpanlessEq::new(cx).eq_expr(target, left),
|
||||
ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() &&
|
||||
block.expr.as_ref().map_or(
|
||||
false,
|
||||
|expr| is_add(cx, expr, target),
|
||||
)
|
||||
block
|
||||
.expr
|
||||
.as_ref()
|
||||
.map_or(false, |expr| is_add(cx, expr, target))
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
@ -146,7 +146,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
use std::ascii::AsciiExt;
|
||||
use syntax::ast::LitKind;
|
||||
use utils::{snippet, in_macro};
|
||||
use utils::{in_macro, snippet};
|
||||
|
||||
if let ExprMethodCall(ref path, _, ref args) = e.node {
|
||||
if path.name == "as_bytes" {
|
||||
|
@ -41,11 +41,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprAssign(ref target, _) = expr.node {
|
||||
match target.node {
|
||||
ExprField(ref base, _) |
|
||||
ExprTupField(ref base, _) => {
|
||||
if is_temporary(base) && !is_adjusted(cx, base) {
|
||||
span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
|
||||
}
|
||||
ExprField(ref base, _) | ExprTupField(ref base, _) => if is_temporary(base) && !is_adjusted(cx, base) {
|
||||
span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::hir::*;
|
||||
use utils::{match_def_path, paths, span_lint, span_lint_and_then, snippet, last_path_segment};
|
||||
use utils::{last_path_segment, match_def_path, paths, snippet, span_lint, span_lint_and_then};
|
||||
use utils::sugg;
|
||||
|
||||
/// **What it does:** Checks for transmutes that can't ever be correct on any
|
||||
@ -95,103 +95,88 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
let to_ty = cx.tables.expr_ty(e);
|
||||
|
||||
match (&from_ty.sty, &to_ty.sty) {
|
||||
_ if from_ty == to_ty => {
|
||||
span_lint(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to itself", from_ty),
|
||||
)
|
||||
},
|
||||
(&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from a reference to a pointer",
|
||||
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let sugg = if ptr_ty == rty {
|
||||
arg.as_ty(to_ty)
|
||||
} else {
|
||||
arg.as_ty(cx.tcx.mk_ptr(rty)).as_ty(to_ty)
|
||||
};
|
||||
_ if from_ty == to_ty => span_lint(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to itself", from_ty),
|
||||
),
|
||||
(&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from a reference to a pointer",
|
||||
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
let sugg = if ptr_ty == rty {
|
||||
arg.as_ty(to_ty)
|
||||
} else {
|
||||
arg.as_ty(cx.tcx.mk_ptr(rty)).as_ty(to_ty)
|
||||
};
|
||||
|
||||
db.span_suggestion(e.span, "try", sugg.to_string());
|
||||
},
|
||||
)
|
||||
},
|
||||
(&ty::TyInt(_), &ty::TyRawPtr(_)) |
|
||||
(&ty::TyUint(_), &ty::TyRawPtr(_)) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from an integer to a pointer",
|
||||
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
|
||||
},
|
||||
)
|
||||
},
|
||||
db.span_suggestion(e.span, "try", sugg.to_string());
|
||||
},
|
||||
),
|
||||
(&ty::TyInt(_), &ty::TyRawPtr(_)) | (&ty::TyUint(_), &ty::TyRawPtr(_)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from an integer to a pointer",
|
||||
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
|
||||
},
|
||||
),
|
||||
(&ty::TyFloat(_), &ty::TyRef(..)) |
|
||||
(&ty::TyFloat(_), &ty::TyRawPtr(_)) |
|
||||
(&ty::TyChar, &ty::TyRef(..)) |
|
||||
(&ty::TyChar, &ty::TyRawPtr(_)) => {
|
||||
span_lint(
|
||||
cx,
|
||||
WRONG_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a pointer", from_ty),
|
||||
)
|
||||
},
|
||||
(&ty::TyRawPtr(from_ptr), _) if from_ptr.ty == to_ty => {
|
||||
span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from a type (`{}`) to the type that it points to (`{}`)",
|
||||
from_ty,
|
||||
to_ty
|
||||
),
|
||||
)
|
||||
},
|
||||
(_, &ty::TyRawPtr(to_ptr)) if to_ptr.ty == from_ty => {
|
||||
span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)", from_ty, to_ty),
|
||||
)
|
||||
},
|
||||
(&ty::TyRawPtr(from_pty), &ty::TyRef(_, to_rty)) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from a pointer type (`{}`) to a reference type \
|
||||
(`{}`)",
|
||||
from_ty,
|
||||
to_ty
|
||||
),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
|
||||
("&mut *", "*mut")
|
||||
} else {
|
||||
("&*", "*const")
|
||||
};
|
||||
(&ty::TyChar, &ty::TyRawPtr(_)) => span_lint(
|
||||
cx,
|
||||
WRONG_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a pointer", from_ty),
|
||||
),
|
||||
(&ty::TyRawPtr(from_ptr), _) if from_ptr.ty == to_ty => span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from a type (`{}`) to the type that it points to (`{}`)",
|
||||
from_ty,
|
||||
to_ty
|
||||
),
|
||||
),
|
||||
(_, &ty::TyRawPtr(to_ptr)) if to_ptr.ty == from_ty => span_lint(
|
||||
cx,
|
||||
CROSSPOINTER_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)", from_ty, to_ty),
|
||||
),
|
||||
(&ty::TyRawPtr(from_pty), &ty::TyRef(_, to_rty)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
e.span,
|
||||
&format!(
|
||||
"transmute from a pointer type (`{}`) to a reference type \
|
||||
(`{}`)",
|
||||
from_ty,
|
||||
to_ty
|
||||
),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
|
||||
("&mut *", "*mut")
|
||||
} else {
|
||||
("&*", "*const")
|
||||
};
|
||||
|
||||
let arg = if from_pty.ty == to_rty.ty {
|
||||
arg
|
||||
} else {
|
||||
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_rty.ty)))
|
||||
};
|
||||
let arg = if from_pty.ty == to_rty.ty {
|
||||
arg
|
||||
} else {
|
||||
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_rty.ty)))
|
||||
};
|
||||
|
||||
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
|
||||
},
|
||||
)
|
||||
},
|
||||
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
|
||||
},
|
||||
),
|
||||
_ => return,
|
||||
};
|
||||
}
|
||||
|
@ -1,16 +1,16 @@
|
||||
use reexport::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_ty, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_ty, FnKind, NestedVisitorMap, Visitor};
|
||||
use rustc::lint::*;
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::ty::subst::Substs;
|
||||
use std::cmp::Ordering;
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
use syntax::ast::{FloatTy, IntTy, UintTy};
|
||||
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,
|
||||
span_lint_and_sugg, opt_def_id, last_path_segment, type_size, match_path};
|
||||
use utils::{comparisons, higher, in_external_macro, in_macro, last_path_segment, match_def_path, match_path,
|
||||
opt_def_id, snippet, span_help_and_lint, span_lint, span_lint_and_sugg, type_size};
|
||||
use utils::paths;
|
||||
|
||||
/// Handles all the linting of funky types
|
||||
@ -114,8 +114,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypePass {
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
match item.node {
|
||||
TraitItemKind::Const(ref ty, _) |
|
||||
TraitItemKind::Type(_, Some(ref ty)) => check_ty(cx, ty, false),
|
||||
TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => check_ty(cx, ty, false),
|
||||
TraitItemKind::Method(ref sig, _) => check_fn_decl(cx, &sig.decl),
|
||||
_ => (),
|
||||
}
|
||||
@ -182,20 +181,18 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
|
||||
match *qpath {
|
||||
QPath::Resolved(Some(ref ty), ref p) => {
|
||||
check_ty(cx, ty, is_local);
|
||||
for ty in p.segments.iter().flat_map(
|
||||
|seg| seg.parameters.types.iter(),
|
||||
)
|
||||
for ty in p.segments
|
||||
.iter()
|
||||
.flat_map(|seg| seg.parameters.types.iter())
|
||||
{
|
||||
check_ty(cx, ty, is_local);
|
||||
}
|
||||
},
|
||||
QPath::Resolved(None, ref p) => {
|
||||
for ty in p.segments.iter().flat_map(
|
||||
|seg| seg.parameters.types.iter(),
|
||||
)
|
||||
{
|
||||
check_ty(cx, ty, is_local);
|
||||
}
|
||||
QPath::Resolved(None, ref p) => for ty in p.segments
|
||||
.iter()
|
||||
.flat_map(|seg| seg.parameters.types.iter())
|
||||
{
|
||||
check_ty(cx, ty, is_local);
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref seg) => {
|
||||
check_ty(cx, ty, is_local);
|
||||
@ -248,13 +245,9 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
|
||||
}
|
||||
},
|
||||
// recurse
|
||||
TySlice(ref ty) |
|
||||
TyArray(ref ty, _) |
|
||||
TyPtr(MutTy { ref ty, .. }) => check_ty(cx, ty, is_local),
|
||||
TyTup(ref tys) => {
|
||||
for ty in tys {
|
||||
check_ty(cx, ty, is_local);
|
||||
}
|
||||
TySlice(ref ty) | TyArray(ref ty, _) | TyPtr(MutTy { ref ty, .. }) => check_ty(cx, ty, is_local),
|
||||
TyTup(ref tys) => for ty in tys {
|
||||
check_ty(cx, ty, is_local);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
@ -529,25 +522,21 @@ declare_lint! {
|
||||
/// Will return 0 if the type is not an int or uint variant
|
||||
fn int_ty_to_nbits(typ: Ty, tcx: TyCtxt) -> u64 {
|
||||
match typ.sty {
|
||||
ty::TyInt(i) => {
|
||||
match i {
|
||||
IntTy::Is => tcx.data_layout.pointer_size.bits(),
|
||||
IntTy::I8 => 8,
|
||||
IntTy::I16 => 16,
|
||||
IntTy::I32 => 32,
|
||||
IntTy::I64 => 64,
|
||||
IntTy::I128 => 128,
|
||||
}
|
||||
ty::TyInt(i) => match i {
|
||||
IntTy::Is => tcx.data_layout.pointer_size.bits(),
|
||||
IntTy::I8 => 8,
|
||||
IntTy::I16 => 16,
|
||||
IntTy::I32 => 32,
|
||||
IntTy::I64 => 64,
|
||||
IntTy::I128 => 128,
|
||||
},
|
||||
ty::TyUint(i) => {
|
||||
match i {
|
||||
UintTy::Us => tcx.data_layout.pointer_size.bits(),
|
||||
UintTy::U8 => 8,
|
||||
UintTy::U16 => 16,
|
||||
UintTy::U32 => 32,
|
||||
UintTy::U64 => 64,
|
||||
UintTy::U128 => 128,
|
||||
}
|
||||
ty::TyUint(i) => match i {
|
||||
UintTy::Us => tcx.data_layout.pointer_size.bits(),
|
||||
UintTy::U8 => 8,
|
||||
UintTy::U16 => 16,
|
||||
UintTy::U32 => 32,
|
||||
UintTy::U64 => 64,
|
||||
UintTy::U128 => 128,
|
||||
},
|
||||
_ => 0,
|
||||
}
|
||||
@ -555,8 +544,7 @@ fn int_ty_to_nbits(typ: Ty, tcx: TyCtxt) -> u64 {
|
||||
|
||||
fn is_isize_or_usize(typ: Ty) -> bool {
|
||||
match typ.sty {
|
||||
ty::TyInt(IntTy::Is) |
|
||||
ty::TyUint(UintTy::Us) => true,
|
||||
ty::TyInt(IntTy::Is) | ty::TyUint(UintTy::Us) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -578,7 +566,7 @@ fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: Ty, cast_t
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting {0} to {1} causes a loss of precision {2}({0} is {3} bits wide, but {1}'s mantissa \
|
||||
is only {4} bits wide)",
|
||||
is only {4} bits wide)",
|
||||
cast_from,
|
||||
if cast_to_f64 { "f64" } else { "f32" },
|
||||
if arch_dependent {
|
||||
@ -617,38 +605,32 @@ fn check_truncation_and_wrapping(cx: &LateContext, expr: &Expr, cast_from: Ty, c
|
||||
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
|
||||
let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) =
|
||||
match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
|
||||
(true, true) | (false, false) => {
|
||||
(
|
||||
to_nbits < from_nbits,
|
||||
ArchSuffix::None,
|
||||
to_nbits == from_nbits && cast_unsigned_to_signed,
|
||||
ArchSuffix::None,
|
||||
)
|
||||
},
|
||||
(true, false) => {
|
||||
(
|
||||
to_nbits <= 32,
|
||||
if to_nbits == 32 {
|
||||
ArchSuffix::_64
|
||||
} else {
|
||||
ArchSuffix::None
|
||||
},
|
||||
to_nbits <= 32 && cast_unsigned_to_signed,
|
||||
ArchSuffix::_32,
|
||||
)
|
||||
},
|
||||
(false, true) => {
|
||||
(
|
||||
from_nbits == 64,
|
||||
ArchSuffix::_32,
|
||||
cast_unsigned_to_signed,
|
||||
if from_nbits == 64 {
|
||||
ArchSuffix::_64
|
||||
} else {
|
||||
ArchSuffix::_32
|
||||
},
|
||||
)
|
||||
},
|
||||
(true, true) | (false, false) => (
|
||||
to_nbits < from_nbits,
|
||||
ArchSuffix::None,
|
||||
to_nbits == from_nbits && cast_unsigned_to_signed,
|
||||
ArchSuffix::None,
|
||||
),
|
||||
(true, false) => (
|
||||
to_nbits <= 32,
|
||||
if to_nbits == 32 {
|
||||
ArchSuffix::_64
|
||||
} else {
|
||||
ArchSuffix::None
|
||||
},
|
||||
to_nbits <= 32 && cast_unsigned_to_signed,
|
||||
ArchSuffix::_32,
|
||||
),
|
||||
(false, true) => (
|
||||
from_nbits == 64,
|
||||
ArchSuffix::_32,
|
||||
cast_unsigned_to_signed,
|
||||
if from_nbits == 64 {
|
||||
ArchSuffix::_64
|
||||
} else {
|
||||
ArchSuffix::_32
|
||||
},
|
||||
),
|
||||
};
|
||||
if span_truncation {
|
||||
span_lint(
|
||||
@ -690,8 +672,7 @@ fn check_lossless(cx: &LateContext, expr: &Expr, op: &Expr, cast_from: Ty, cast_
|
||||
let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
|
||||
let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
|
||||
if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits &&
|
||||
!cast_signed_to_unsigned
|
||||
if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned
|
||||
{
|
||||
span_lossless_lint(cx, expr, op, cast_from, cast_to);
|
||||
}
|
||||
@ -715,19 +696,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
|
||||
if let ExprCast(ref ex, _) = expr.node {
|
||||
let (cast_from, cast_to) = (cx.tables.expr_ty(ex), cx.tables.expr_ty(expr));
|
||||
if let ExprLit(ref lit) = ex.node {
|
||||
use syntax::ast::{LitKind, LitIntType};
|
||||
use syntax::ast::{LitIntType, LitKind};
|
||||
match lit.node {
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) |
|
||||
LitKind::FloatUnsuffixed(_) => {},
|
||||
_ => {
|
||||
if cast_from.sty == cast_to.sty && !in_external_macro(cx, expr.span) {
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting to the same type is unnecessary (`{}` -> `{}`)", cast_from, cast_to),
|
||||
);
|
||||
}
|
||||
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::FloatUnsuffixed(_) => {},
|
||||
_ => if cast_from.sty == cast_to.sty && !in_external_macro(cx, expr.span) {
|
||||
span_lint(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting to the same type is unnecessary (`{}` -> `{}`)", cast_from, cast_to),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -776,8 +754,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
|
||||
check_lossless(cx, expr, ex, cast_from, cast_to);
|
||||
},
|
||||
(false, false) => {
|
||||
if let (&ty::TyFloat(FloatTy::F64), &ty::TyFloat(FloatTy::F32)) =
|
||||
(&cast_from.sty, &cast_to.sty)
|
||||
if let (&ty::TyFloat(FloatTy::F64), &ty::TyFloat(FloatTy::F32)) = (&cast_from.sty, &cast_to.sty)
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
@ -786,8 +763,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
|
||||
"casting f64 to f32 may truncate the value",
|
||||
);
|
||||
}
|
||||
if let (&ty::TyFloat(FloatTy::F32), &ty::TyFloat(FloatTy::F64)) =
|
||||
(&cast_from.sty, &cast_to.sty)
|
||||
if let (&ty::TyFloat(FloatTy::F32), &ty::TyFloat(FloatTy::F64)) = (&cast_from.sty, &cast_to.sty)
|
||||
{
|
||||
span_lossless_lint(cx, expr, ex, cast_from, cast_to);
|
||||
}
|
||||
@ -823,7 +799,9 @@ pub struct TypeComplexityPass {
|
||||
|
||||
impl TypeComplexityPass {
|
||||
pub fn new(threshold: u64) -> Self {
|
||||
Self { threshold: threshold }
|
||||
Self {
|
||||
threshold: threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,8 +831,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexityPass {
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
match item.node {
|
||||
ItemStatic(ref ty, _, _) |
|
||||
ItemConst(ref ty, _) => self.check_type(cx, ty),
|
||||
ItemStatic(ref ty, _, _) | ItemConst(ref ty, _) => self.check_type(cx, ty),
|
||||
// functions, enums, structs, impls and traits are covered
|
||||
_ => (),
|
||||
}
|
||||
@ -862,8 +839,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexityPass {
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
match item.node {
|
||||
TraitItemKind::Const(ref ty, _) |
|
||||
TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
|
||||
TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
|
||||
TraitItemKind::Method(MethodSig { ref decl, .. }, TraitMethod::Required(_)) => self.check_fndecl(cx, decl),
|
||||
// methods with default impl are covered by check_fn
|
||||
_ => (),
|
||||
@ -872,8 +848,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexityPass {
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
match item.node {
|
||||
ImplItemKind::Const(ref ty, _) |
|
||||
ImplItemKind::Type(ref ty) => self.check_type(cx, ty),
|
||||
ImplItemKind::Const(ref ty, _) | ImplItemKind::Type(ref ty) => self.check_type(cx, ty),
|
||||
// methods are covered by check_fn
|
||||
_ => (),
|
||||
}
|
||||
@ -938,9 +913,9 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
|
||||
TyBareFn(..) => (50 * self.nest, 1),
|
||||
|
||||
TyTraitObject(ref param_bounds, _) => {
|
||||
let has_lifetime_parameters = param_bounds.iter().any(
|
||||
|bound| !bound.bound_lifetimes.is_empty(),
|
||||
);
|
||||
let has_lifetime_parameters = param_bounds
|
||||
.iter()
|
||||
.any(|bound| !bound.bound_lifetimes.is_empty());
|
||||
if has_lifetime_parameters {
|
||||
// complex trait bounds like A<'a, 'b>
|
||||
(50 * self.nest, 1)
|
||||
@ -1101,7 +1076,7 @@ fn detect_absurd_comparison<'a>(
|
||||
Rel::Le => {
|
||||
match (lx, rx) {
|
||||
(Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), //max <= x
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), // max <= x
|
||||
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
|
||||
(_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
|
||||
_ => return None,
|
||||
@ -1187,14 +1162,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons {
|
||||
let conclusion = match result {
|
||||
AlwaysFalse => "this comparison is always false".to_owned(),
|
||||
AlwaysTrue => "this comparison is always true".to_owned(),
|
||||
InequalityImpossible => {
|
||||
format!(
|
||||
"the case where the two sides are not equal never occurs, consider using {} == {} \
|
||||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs")
|
||||
)
|
||||
},
|
||||
InequalityImpossible => format!(
|
||||
"the case where the two sides are not equal never occurs, consider using {} == {} \
|
||||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs")
|
||||
),
|
||||
};
|
||||
|
||||
let help = format!(
|
||||
@ -1264,9 +1237,8 @@ impl FullInt {
|
||||
|
||||
impl PartialEq for FullInt {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.partial_cmp(other).expect(
|
||||
"partial_cmp only returns Some(_)",
|
||||
) == Ordering::Equal
|
||||
self.partial_cmp(other)
|
||||
.expect("partial_cmp only returns Some(_)") == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
@ -1282,9 +1254,8 @@ impl PartialOrd for FullInt {
|
||||
}
|
||||
impl Ord for FullInt {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.partial_cmp(other).expect(
|
||||
"partial_cmp for FullInt can never return None",
|
||||
)
|
||||
self.partial_cmp(other)
|
||||
.expect("partial_cmp for FullInt can never return None")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1301,44 +1272,40 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
|
||||
return None;
|
||||
}
|
||||
match pre_cast_ty.sty {
|
||||
ty::TyInt(int_ty) => {
|
||||
Some(match int_ty {
|
||||
IntTy::I8 => (FullInt::S(i128::from(i8::min_value())), FullInt::S(i128::from(i8::max_value()))),
|
||||
IntTy::I16 => (
|
||||
FullInt::S(i128::from(i16::min_value())),
|
||||
FullInt::S(i128::from(i16::max_value())),
|
||||
),
|
||||
IntTy::I32 => (
|
||||
FullInt::S(i128::from(i32::min_value())),
|
||||
FullInt::S(i128::from(i32::max_value())),
|
||||
),
|
||||
IntTy::I64 => (
|
||||
FullInt::S(i128::from(i64::min_value())),
|
||||
FullInt::S(i128::from(i64::max_value())),
|
||||
),
|
||||
IntTy::I128 => (FullInt::S(i128::min_value() as i128), FullInt::S(i128::max_value() as i128)),
|
||||
IntTy::Is => (FullInt::S(isize::min_value() as i128), FullInt::S(isize::max_value() as i128)),
|
||||
})
|
||||
},
|
||||
ty::TyUint(uint_ty) => {
|
||||
Some(match uint_ty {
|
||||
UintTy::U8 => (FullInt::U(u128::from(u8::min_value())), FullInt::U(u128::from(u8::max_value()))),
|
||||
UintTy::U16 => (
|
||||
FullInt::U(u128::from(u16::min_value())),
|
||||
FullInt::U(u128::from(u16::max_value())),
|
||||
),
|
||||
UintTy::U32 => (
|
||||
FullInt::U(u128::from(u32::min_value())),
|
||||
FullInt::U(u128::from(u32::max_value())),
|
||||
),
|
||||
UintTy::U64 => (
|
||||
FullInt::U(u128::from(u64::min_value())),
|
||||
FullInt::U(u128::from(u64::max_value())),
|
||||
),
|
||||
UintTy::U128 => (FullInt::U(u128::min_value() as u128), FullInt::U(u128::max_value() as u128)),
|
||||
UintTy::Us => (FullInt::U(usize::min_value() as u128), FullInt::U(usize::max_value() as u128)),
|
||||
})
|
||||
},
|
||||
ty::TyInt(int_ty) => Some(match int_ty {
|
||||
IntTy::I8 => (FullInt::S(i128::from(i8::min_value())), FullInt::S(i128::from(i8::max_value()))),
|
||||
IntTy::I16 => (
|
||||
FullInt::S(i128::from(i16::min_value())),
|
||||
FullInt::S(i128::from(i16::max_value())),
|
||||
),
|
||||
IntTy::I32 => (
|
||||
FullInt::S(i128::from(i32::min_value())),
|
||||
FullInt::S(i128::from(i32::max_value())),
|
||||
),
|
||||
IntTy::I64 => (
|
||||
FullInt::S(i128::from(i64::min_value())),
|
||||
FullInt::S(i128::from(i64::max_value())),
|
||||
),
|
||||
IntTy::I128 => (FullInt::S(i128::min_value() as i128), FullInt::S(i128::max_value() as i128)),
|
||||
IntTy::Is => (FullInt::S(isize::min_value() as i128), FullInt::S(isize::max_value() as i128)),
|
||||
}),
|
||||
ty::TyUint(uint_ty) => Some(match uint_ty {
|
||||
UintTy::U8 => (FullInt::U(u128::from(u8::min_value())), FullInt::U(u128::from(u8::max_value()))),
|
||||
UintTy::U16 => (
|
||||
FullInt::U(u128::from(u16::min_value())),
|
||||
FullInt::U(u128::from(u16::max_value())),
|
||||
),
|
||||
UintTy::U32 => (
|
||||
FullInt::U(u128::from(u32::min_value())),
|
||||
FullInt::U(u128::from(u32::max_value())),
|
||||
),
|
||||
UintTy::U64 => (
|
||||
FullInt::U(u128::from(u64::min_value())),
|
||||
FullInt::U(u128::from(u64::max_value())),
|
||||
),
|
||||
UintTy::U128 => (FullInt::U(u128::min_value() as u128), FullInt::U(u128::max_value() as u128)),
|
||||
UintTy::Us => (FullInt::U(usize::min_value() as u128), FullInt::U(usize::max_value() as u128)),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
@ -1355,15 +1322,13 @@ fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> {
|
||||
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
|
||||
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
|
||||
match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(expr) {
|
||||
Ok(val) => {
|
||||
if let Integral(const_int) = val {
|
||||
match const_int.int_type() {
|
||||
IntType::SignedInt(_) => Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
|
||||
IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
Ok(val) => if let Integral(const_int) = val {
|
||||
match const_int.int_type() {
|
||||
IntType::SignedInt(_) => Some(FullInt::S(const_int.to_u128_unchecked() as i128)),
|
||||
IntType::UnsignedInt(_) => Some(FullInt::U(const_int.to_u128_unchecked())),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Err(_) => None,
|
||||
}
|
||||
@ -1402,42 +1367,32 @@ fn upcast_comparison_bounds_err(
|
||||
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
|
||||
}
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val < lb
|
||||
} else {
|
||||
ub < norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val <= lb
|
||||
} else {
|
||||
ub <= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
}
|
||||
{
|
||||
Rel::Lt => if invert {
|
||||
norm_rhs_val < lb
|
||||
} else {
|
||||
ub < norm_rhs_val
|
||||
},
|
||||
Rel::Le => if invert {
|
||||
norm_rhs_val <= lb
|
||||
} else {
|
||||
ub <= norm_rhs_val
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, true)
|
||||
} else if match rel {
|
||||
Rel::Lt => {
|
||||
if invert {
|
||||
norm_rhs_val >= ub
|
||||
} else {
|
||||
lb >= norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
if invert {
|
||||
norm_rhs_val > ub
|
||||
} else {
|
||||
lb > norm_rhs_val
|
||||
}
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
}
|
||||
{
|
||||
Rel::Lt => if invert {
|
||||
norm_rhs_val >= ub
|
||||
} else {
|
||||
lb >= norm_rhs_val
|
||||
},
|
||||
Rel::Le => if invert {
|
||||
norm_rhs_val > ub
|
||||
} else {
|
||||
lb > norm_rhs_val
|
||||
},
|
||||
Rel::Eq | Rel::Ne => unreachable!(),
|
||||
} {
|
||||
err_upcast_comparison(cx, span, lhs, false)
|
||||
}
|
||||
}
|
||||
@ -1447,7 +1402,6 @@ fn upcast_comparison_bounds_err(
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprBinary(ref cmp, ref lhs, ref rhs) = expr.node {
|
||||
|
||||
let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
|
||||
let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
|
||||
val
|
||||
|
@ -3,7 +3,7 @@ use rustc::hir::*;
|
||||
use syntax::ast::{LitKind, NodeId};
|
||||
use syntax::codemap::Span;
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
use utils::{snippet, span_help_and_lint, is_allowed};
|
||||
use utils::{is_allowed, snippet, span_help_and_lint};
|
||||
|
||||
/// **What it does:** Checks for the Unicode zero-width space in the code.
|
||||
///
|
||||
|
@ -48,13 +48,11 @@ impl EarlyLintPass for UnsafeNameRemoval {
|
||||
&item.span,
|
||||
);
|
||||
},
|
||||
ViewPath_::ViewPathList(_, ref path_list_items) => {
|
||||
for path_list_item in path_list_items.iter() {
|
||||
let plid = path_list_item.node;
|
||||
if let Some(rename) = plid.rename {
|
||||
unsafe_to_safe_check(plid.name, rename, cx, &item.span);
|
||||
};
|
||||
}
|
||||
ViewPath_::ViewPathList(_, ref path_list_items) => for path_list_item in path_list_items.iter() {
|
||||
let plid = path_list_item.node;
|
||||
if let Some(rename) = plid.rename {
|
||||
unsafe_to_safe_check(plid.name, rename, cx, &item.span);
|
||||
};
|
||||
},
|
||||
ViewPath_::ViewPathGlob(_) => {},
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir;
|
||||
use utils::{span_lint, match_qpath, match_trait_method, is_try, paths};
|
||||
use utils::{is_try, match_qpath, match_trait_method, paths, span_lint};
|
||||
|
||||
/// **What it does:** Checks for unused written/read amount.
|
||||
///
|
||||
@ -40,8 +40,7 @@ impl LintPass for UnusedIoAmount {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount {
|
||||
fn check_stmt(&mut self, cx: &LateContext, s: &hir::Stmt) {
|
||||
let expr = match s.node {
|
||||
hir::StmtSemi(ref expr, _) |
|
||||
hir::StmtExpr(ref expr, _) => &**expr,
|
||||
hir::StmtSemi(ref expr, _) | hir::StmtExpr(ref expr, _) => &**expr,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
@ -58,13 +57,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedIoAmount {
|
||||
}
|
||||
},
|
||||
|
||||
hir::ExprMethodCall(ref path, _, ref args) => {
|
||||
match &*path.name.as_str() {
|
||||
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
|
||||
check_method_call(cx, &args[0], expr);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
hir::ExprMethodCall(ref path, _, ref args) => match &*path.name.as_str() {
|
||||
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
|
||||
check_method_call(cx, &args[0], expr);
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
|
||||
_ => (),
|
||||
|
@ -1,6 +1,6 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, NestedVisitorMap};
|
||||
use rustc::hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
|
||||
use std::collections::HashMap;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
@ -69,14 +69,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedLabel {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for UnusedLabelVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
|
||||
match expr.node {
|
||||
hir::ExprBreak(destination, _) |
|
||||
hir::ExprAgain(destination) => {
|
||||
if let Some(label) = destination.ident {
|
||||
self.labels.remove(&label.node.name.as_str());
|
||||
}
|
||||
hir::ExprBreak(destination, _) | hir::ExprAgain(destination) => if let Some(label) = destination.ident {
|
||||
self.labels.remove(&label.node.name.as_str());
|
||||
},
|
||||
hir::ExprLoop(_, Some(label), _) |
|
||||
hir::ExprWhile(_, _, Some(label)) => {
|
||||
hir::ExprLoop(_, Some(label), _) | hir::ExprWhile(_, _, Some(label)) => {
|
||||
self.labels.insert(label.node.as_str(), expr.span);
|
||||
},
|
||||
_ => (),
|
||||
|
@ -1,7 +1,7 @@
|
||||
use rustc::lint::{LintArray, LateLintPass, LateContext, LintPass};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_path, NestedVisitorMap};
|
||||
use utils::{span_lint_and_then, in_macro};
|
||||
use rustc::hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
|
||||
use utils::{in_macro, span_lint_and_then};
|
||||
use syntax::ast::NodeId;
|
||||
use syntax_pos::symbol::keywords::SelfType;
|
||||
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
use rustc::lint::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::{Expr, QPath, Expr_};
|
||||
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
|
||||
use syntax::ast::{self, Attribute, NodeId, LitKind, DUMMY_NODE_ID};
|
||||
use rustc::hir::{Expr, Expr_, QPath};
|
||||
use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use syntax::ast::{self, Attribute, LitKind, NodeId, DUMMY_NODE_ID};
|
||||
use syntax::codemap::Span;
|
||||
use std::collections::HashMap;
|
||||
|
||||
@ -386,15 +386,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
||||
println!("Again(ref {}) = {},", destination_pat, current);
|
||||
// FIXME: implement label printing
|
||||
},
|
||||
Expr_::ExprRet(ref opt_value) => {
|
||||
if let Some(ref value) = *opt_value {
|
||||
let value_pat = self.next("value");
|
||||
println!("Ret(Some(ref {})) = {},", value_pat, current);
|
||||
self.current = value_pat;
|
||||
self.visit_expr(value);
|
||||
} else {
|
||||
println!("Ret(None) = {},", current);
|
||||
}
|
||||
Expr_::ExprRet(ref opt_value) => if let Some(ref value) = *opt_value {
|
||||
let value_pat = self.next("value");
|
||||
println!("Ret(Some(ref {})) = {},", value_pat, current);
|
||||
self.current = value_pat;
|
||||
self.visit_expr(value);
|
||||
} else {
|
||||
println!("Ret(None) = {},", current);
|
||||
},
|
||||
Expr_::ExprInlineAsm(_, ref _input, ref _output) => {
|
||||
println!("InlineAsm(_, ref input, ref output) = {},", current);
|
||||
@ -445,42 +443,36 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
|
||||
|
||||
fn has_attr(attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| {
|
||||
attr.check_name("clippy") &&
|
||||
attr.meta_item_list().map_or(false, |list| {
|
||||
list.len() == 1 &&
|
||||
match list[0].node {
|
||||
ast::NestedMetaItemKind::MetaItem(ref it) => it.name == "author",
|
||||
ast::NestedMetaItemKind::Literal(_) => false,
|
||||
}
|
||||
})
|
||||
attr.check_name("clippy") && attr.meta_item_list().map_or(false, |list| {
|
||||
list.len() == 1 && match list[0].node {
|
||||
ast::NestedMetaItemKind::MetaItem(ref it) => it.name == "author",
|
||||
ast::NestedMetaItemKind::Literal(_) => false,
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn print_path(path: &QPath, first: &mut bool) {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) => {
|
||||
for segment in &path.segments {
|
||||
QPath::Resolved(_, ref path) => for segment in &path.segments {
|
||||
if *first {
|
||||
*first = false;
|
||||
} else {
|
||||
print!(", ");
|
||||
}
|
||||
print!("{:?}", segment.name.as_str());
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref segment) => match ty.node {
|
||||
hir::Ty_::TyPath(ref inner_path) => {
|
||||
print_path(inner_path, first);
|
||||
if *first {
|
||||
*first = false;
|
||||
} else {
|
||||
print!(", ");
|
||||
}
|
||||
print!("{:?}", segment.name.as_str());
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
match ty.node {
|
||||
hir::Ty_::TyPath(ref inner_path) => {
|
||||
print_path(inner_path, first);
|
||||
if *first {
|
||||
*first = false;
|
||||
} else {
|
||||
print!(", ");
|
||||
}
|
||||
print!("{:?}", segment.name.as_str());
|
||||
},
|
||||
ref other => print!("/* unimplemented: {:?}*/", other),
|
||||
}
|
||||
},
|
||||
ref other => print!("/* unimplemented: {:?}*/", other),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -15,14 +15,13 @@ pub fn file_from_args(
|
||||
for arg in args.iter().filter_map(|a| a.meta_item()) {
|
||||
if arg.name() == "conf_file" {
|
||||
return match arg.node {
|
||||
ast::MetaItemKind::Word |
|
||||
ast::MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
|
||||
ast::MetaItemKind::NameValue(ref value) => {
|
||||
if let ast::LitKind::Str(ref file, _) = value.node {
|
||||
Ok(Some(file.to_string().into()))
|
||||
} else {
|
||||
Err(("`conf_file` value must be a string", value.span))
|
||||
}
|
||||
ast::MetaItemKind::Word | ast::MetaItemKind::List(_) => {
|
||||
Err(("`conf_file` must be a named value", arg.span))
|
||||
},
|
||||
ast::MetaItemKind::NameValue(ref value) => if let ast::LitKind::Str(ref file, _) = value.node {
|
||||
Ok(Some(file.to_string().into()))
|
||||
} else {
|
||||
Err(("`conf_file` value must be a string", value.span))
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -45,7 +44,7 @@ pub enum Error {
|
||||
/// The expected type.
|
||||
&'static str,
|
||||
/// The type we got instead.
|
||||
&'static str
|
||||
&'static str,
|
||||
),
|
||||
/// There is an unknown key is the file.
|
||||
UnknownKey(String),
|
||||
@ -191,10 +190,8 @@ pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
|
||||
Ok(ref md) if md.is_file() => return Ok(Some(config_file)),
|
||||
// Return the error if it's something other than `NotFound`; otherwise we didn't
|
||||
// find the project file yet, and continue searching.
|
||||
Err(e) => {
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e);
|
||||
}
|
||||
Err(e) => if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
use rustc::hir;
|
||||
use rustc::lint::LateContext;
|
||||
use syntax::ast;
|
||||
use utils::{is_expn_of, match_qpath, match_def_path, resolve_node, paths};
|
||||
use utils::{is_expn_of, match_def_path, match_qpath, paths, resolve_node};
|
||||
|
||||
/// Convert a hir binary operator to the corresponding `ast` type.
|
||||
pub fn binop(op: hir::BinOp_) -> ast::BinOpKind {
|
||||
@ -73,42 +73,40 @@ pub fn range(expr: &hir::Expr) -> Option<Range> {
|
||||
None
|
||||
}
|
||||
},
|
||||
hir::ExprStruct(ref path, ref fields, None) => {
|
||||
if match_qpath(path, &paths::RANGE_FROM_STD) || match_qpath(path, &paths::RANGE_FROM) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: None,
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_INCLUSIVE) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) ||
|
||||
match_qpath(path, &paths::RANGE_TO_INCLUSIVE)
|
||||
{
|
||||
Some(Range {
|
||||
start: None,
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) {
|
||||
Some(Range {
|
||||
start: None,
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
hir::ExprStruct(ref path, ref fields, None) => if match_qpath(path, &paths::RANGE_FROM_STD) ||
|
||||
match_qpath(path, &paths::RANGE_FROM)
|
||||
{
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: None,
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_INCLUSIVE) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_STD) || match_qpath(path, &paths::RANGE) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_TO_INCLUSIVE_STD) || match_qpath(path, &paths::RANGE_TO_INCLUSIVE) {
|
||||
Some(Range {
|
||||
start: None,
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::Closed,
|
||||
})
|
||||
} else if match_qpath(path, &paths::RANGE_TO_STD) || match_qpath(path, &paths::RANGE_TO) {
|
||||
Some(Range {
|
||||
start: None,
|
||||
end: get_field("end", fields),
|
||||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
|
@ -46,8 +46,9 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
||||
false
|
||||
}
|
||||
},
|
||||
(&StmtExpr(ref l, _), &StmtExpr(ref r, _)) |
|
||||
(&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => self.eq_expr(l, r),
|
||||
(&StmtExpr(ref l, _), &StmtExpr(ref r, _)) | (&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => {
|
||||
self.eq_expr(l, r)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -107,11 +108,10 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
||||
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.node.as_str() == r.node.as_str())
|
||||
},
|
||||
(&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => {
|
||||
ls == rs && self.eq_expr(le, re) &&
|
||||
over(la, ra, |l, r| {
|
||||
self.eq_expr(&l.body, &r.body) && both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) &&
|
||||
over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r))
|
||||
})
|
||||
ls == rs && self.eq_expr(le, re) && over(la, ra, |l, r| {
|
||||
self.eq_expr(&l.body, &r.body) && both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) &&
|
||||
over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r))
|
||||
})
|
||||
},
|
||||
(&ExprMethodCall(ref l_path, _, ref l_args), &ExprMethodCall(ref r_path, _, ref r_args)) => {
|
||||
!self.ignore_fn && l_path == r_path && self.eq_exprs(l_args, r_args)
|
||||
@ -257,9 +257,8 @@ 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))
|
||||
})
|
||||
l.as_ref()
|
||||
.map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
|
||||
}
|
||||
|
||||
/// Check if two slices are equal as per `eq_fn`.
|
||||
|
@ -54,12 +54,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
match item.vis {
|
||||
hir::Visibility::Public => println!("public"),
|
||||
hir::Visibility::Crate => println!("visible crate wide"),
|
||||
hir::Visibility::Restricted { ref path, .. } => {
|
||||
println!(
|
||||
"visible in module `{}`",
|
||||
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
|
||||
)
|
||||
},
|
||||
hir::Visibility::Restricted { ref path, .. } => println!(
|
||||
"visible in module `{}`",
|
||||
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
|
||||
),
|
||||
hir::Visibility::Inherited => println!("visibility inherited from outer item"),
|
||||
}
|
||||
if item.defaultness.is_default() {
|
||||
@ -125,8 +123,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
}
|
||||
match stmt.node {
|
||||
hir::StmtDecl(ref decl, _) => print_decl(cx, decl),
|
||||
hir::StmtExpr(ref e, _) |
|
||||
hir::StmtSemi(ref e, _) => print_expr(cx, e, 0),
|
||||
hir::StmtExpr(ref e, _) | hir::StmtSemi(ref e, _) => print_expr(cx, e, 0),
|
||||
}
|
||||
}
|
||||
// fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
|
||||
@ -355,12 +352,10 @@ fn print_item(cx: &LateContext, item: &hir::Item) {
|
||||
match item.vis {
|
||||
hir::Visibility::Public => println!("public"),
|
||||
hir::Visibility::Crate => println!("visible crate wide"),
|
||||
hir::Visibility::Restricted { ref path, .. } => {
|
||||
println!(
|
||||
"visible in module `{}`",
|
||||
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
|
||||
)
|
||||
},
|
||||
hir::Visibility::Restricted { ref path, .. } => println!(
|
||||
"visible in module `{}`",
|
||||
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
|
||||
),
|
||||
hir::Visibility::Inherited => println!("visibility inherited from outer item"),
|
||||
}
|
||||
match item.node {
|
||||
|
@ -1,11 +1,11 @@
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use utils::{paths, match_qpath, span_lint};
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use utils::{match_qpath, paths, span_lint};
|
||||
use syntax::symbol::InternedString;
|
||||
use syntax::ast::{Name, NodeId, ItemKind, Crate as AstCrate};
|
||||
use syntax::ast::{Crate as AstCrate, ItemKind, Name, NodeId};
|
||||
use syntax::codemap::Span;
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
||||
/// **What it does:** Checks for various things we like to keep tidy in clippy.
|
||||
@ -63,14 +63,17 @@ impl LintPass for Clippy {
|
||||
|
||||
impl EarlyLintPass for Clippy {
|
||||
fn check_crate(&mut self, cx: &EarlyContext, krate: &AstCrate) {
|
||||
if let Some(utils) = krate.module.items.iter().find(
|
||||
|item| item.ident.name == "utils",
|
||||
)
|
||||
if let Some(utils) = krate
|
||||
.module
|
||||
.items
|
||||
.iter()
|
||||
.find(|item| item.ident.name == "utils")
|
||||
{
|
||||
if let ItemKind::Mod(ref utils_mod) = utils.node {
|
||||
if let Some(paths) = utils_mod.items.iter().find(
|
||||
|item| item.ident.name == "paths",
|
||||
)
|
||||
if let Some(paths) = utils_mod
|
||||
.items
|
||||
.iter()
|
||||
.find(|item| item.ident.name == "paths")
|
||||
{
|
||||
if let ItemKind::Mod(ref paths_mod) = paths.node {
|
||||
let mut last_name: Option<InternedString> = None;
|
||||
@ -83,7 +86,7 @@ impl EarlyLintPass for Clippy {
|
||||
CLIPPY_LINTS_INTERNAL,
|
||||
item.span,
|
||||
"this constant should be before the previous constant due to lexical \
|
||||
ordering",
|
||||
ordering",
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -157,11 +160,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
|
||||
|
||||
|
||||
fn is_lint_ref_type(ty: &Ty) -> bool {
|
||||
if let TyRptr(ref lt,
|
||||
MutTy {
|
||||
ty: ref inner,
|
||||
mutbl: MutImmutable,
|
||||
}) = ty.node
|
||||
if let TyRptr(
|
||||
ref lt,
|
||||
MutTy {
|
||||
ty: ref inner,
|
||||
mutbl: MutImmutable,
|
||||
},
|
||||
) = ty.node
|
||||
{
|
||||
if lt.is_elided() {
|
||||
return false;
|
||||
|
@ -5,10 +5,10 @@ use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::intravisit::{NestedVisitorMap, Visitor};
|
||||
use rustc::hir::map::Node;
|
||||
use rustc::lint::{LintContext, Level, LateContext, Lint};
|
||||
use rustc::lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc::session::Session;
|
||||
use rustc::traits;
|
||||
use rustc::ty::{self, TyCtxt, Ty};
|
||||
use rustc::ty::{self, Ty, TyCtxt};
|
||||
use rustc::mir::transform::MirSource;
|
||||
use rustc_errors;
|
||||
use std::borrow::Cow;
|
||||
@ -104,18 +104,16 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
|
||||
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::GeneratorDrop(_) |
|
||||
MirSource::Fn(_) => false,
|
||||
MirSource::Const(_) |
|
||||
MirSource::Static(..) |
|
||||
MirSource::Promoted(..) => true,
|
||||
MirSource::GeneratorDrop(_) | 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(span: Span) -> bool {
|
||||
span.ctxt().outer().expn_info().map_or(false, |info| {
|
||||
match info.callee.format {// don't treat range expressions desugared to structs as "in_macro"
|
||||
match info.callee.format {
|
||||
// don't treat range expressions desugared to structs as "in_macro"
|
||||
ExpnFormat::CompilerDesugaring(kind) => kind != CompilerDesugaringKind::DotFill,
|
||||
_ => true,
|
||||
}
|
||||
@ -138,18 +136,18 @@ pub fn in_external_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
|
||||
// no span for the callee = external macro
|
||||
info.callee.span.map_or(true, |span| {
|
||||
// no snippet = external macro or compiler-builtin expansion
|
||||
cx.sess().codemap().span_to_snippet(span).ok().map_or(
|
||||
true,
|
||||
|code| {
|
||||
!code.starts_with("macro_rules")
|
||||
},
|
||||
)
|
||||
cx.sess()
|
||||
.codemap()
|
||||
.span_to_snippet(span)
|
||||
.ok()
|
||||
.map_or(true, |code| !code.starts_with("macro_rules"))
|
||||
})
|
||||
}
|
||||
|
||||
span.ctxt().outer().expn_info().map_or(false, |info| {
|
||||
in_macro_ext(cx, &info)
|
||||
})
|
||||
span.ctxt()
|
||||
.outer()
|
||||
.expn_info()
|
||||
.map_or(false, |info| in_macro_ext(cx, &info))
|
||||
}
|
||||
|
||||
/// Check if a `DefId`'s path matches the given absolute type path usage.
|
||||
@ -183,9 +181,10 @@ pub fn match_def_path(tcx: TyCtxt, def_id: DefId, path: &[&str]) -> bool {
|
||||
tcx.push_item_path(&mut apb, def_id);
|
||||
|
||||
apb.names.len() == path.len() &&
|
||||
apb.names.into_iter().zip(path.iter()).all(
|
||||
|(a, &b)| *a == *b,
|
||||
)
|
||||
apb.names
|
||||
.into_iter()
|
||||
.zip(path.iter())
|
||||
.all(|(a, &b)| *a == *b)
|
||||
}
|
||||
|
||||
/// Check if type is struct, enum or union type with given def path.
|
||||
@ -220,11 +219,9 @@ pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool
|
||||
|
||||
pub fn last_path_segment(path: &QPath) -> &PathSegment {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) => {
|
||||
path.segments.last().expect(
|
||||
"A path must have at least one segment",
|
||||
)
|
||||
},
|
||||
QPath::Resolved(_, ref path) => path.segments
|
||||
.last()
|
||||
.expect("A path must have at least one segment"),
|
||||
QPath::TypeRelative(_, ref seg) => seg,
|
||||
}
|
||||
}
|
||||
@ -246,22 +243,22 @@ pub fn single_segment_path(path: &QPath) -> Option<&PathSegment> {
|
||||
pub fn match_qpath(path: &QPath, segments: &[&str]) -> bool {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) => match_path(path, segments),
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
match ty.node {
|
||||
TyPath(ref inner_path) => {
|
||||
!segments.is_empty() && match_qpath(inner_path, &segments[..(segments.len() - 1)]) &&
|
||||
segment.name == segments[segments.len() - 1]
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
QPath::TypeRelative(ref ty, ref segment) => match ty.node {
|
||||
TyPath(ref inner_path) => {
|
||||
!segments.is_empty() && match_qpath(inner_path, &segments[..(segments.len() - 1)]) &&
|
||||
segment.name == segments[segments.len() - 1]
|
||||
},
|
||||
_ => false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_path(path: &Path, segments: &[&str]) -> bool {
|
||||
path.segments.iter().rev().zip(segments.iter().rev()).all(
|
||||
|(a, b)| a.name == *b,
|
||||
)
|
||||
path.segments
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(segments.iter().rev())
|
||||
.all(|(a, b)| a.name == *b)
|
||||
}
|
||||
|
||||
/// Match a `Path` against a slice of segment string literals, e.g.
|
||||
@ -271,9 +268,11 @@ pub fn match_path(path: &Path, segments: &[&str]) -> bool {
|
||||
/// match_qpath(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
|
||||
path.segments.iter().rev().zip(segments.iter().rev()).all(
|
||||
|(a, b)| a.identifier.name == *b,
|
||||
)
|
||||
path.segments
|
||||
.iter()
|
||||
.rev()
|
||||
.zip(segments.iter().rev())
|
||||
.all(|(a, b)| a.identifier.name == *b)
|
||||
}
|
||||
|
||||
/// Get the definition associated to a path.
|
||||
@ -281,9 +280,9 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option<def::Def> {
|
||||
let cstore = &cx.tcx.sess.cstore;
|
||||
|
||||
let crates = cstore.crates();
|
||||
let krate = crates.iter().find(
|
||||
|&&krate| cstore.crate_name(krate) == path[0],
|
||||
);
|
||||
let krate = crates
|
||||
.iter()
|
||||
.find(|&&krate| cstore.crate_name(krate) == path[0]);
|
||||
if let Some(krate) = krate {
|
||||
let krate = DefId {
|
||||
krate: *krate,
|
||||
@ -336,14 +335,9 @@ pub fn implements_trait<'a, 'tcx>(
|
||||
ty_params: &[Ty<'tcx>],
|
||||
) -> bool {
|
||||
let ty = cx.tcx.erase_regions(&ty);
|
||||
let obligation = cx.tcx.predicate_for_trait_def(
|
||||
cx.param_env,
|
||||
traits::ObligationCause::dummy(),
|
||||
trait_id,
|
||||
0,
|
||||
ty,
|
||||
ty_params,
|
||||
);
|
||||
let obligation =
|
||||
cx.tcx
|
||||
.predicate_for_trait_def(cx.param_env, traits::ObligationCause::dummy(), trait_id, 0, ty, ty_params);
|
||||
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||
traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation)
|
||||
})
|
||||
@ -522,30 +516,27 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> {
|
||||
if node_id == parent_id {
|
||||
return None;
|
||||
}
|
||||
map.find(parent_id).and_then(
|
||||
|node| if let Node::NodeExpr(parent) =
|
||||
node
|
||||
{
|
||||
map.find(parent_id)
|
||||
.and_then(|node| if let Node::NodeExpr(parent) = node {
|
||||
Some(parent)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_enclosing_block<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, node: NodeId) -> Option<&'tcx Block> {
|
||||
let map = &cx.tcx.hir;
|
||||
let enclosing_node = map.get_enclosing_scope(node).and_then(|enclosing_id| {
|
||||
map.find(enclosing_id)
|
||||
});
|
||||
let enclosing_node = map.get_enclosing_scope(node)
|
||||
.and_then(|enclosing_id| map.find(enclosing_id));
|
||||
if let Some(node) = enclosing_node {
|
||||
match node {
|
||||
Node::NodeBlock(block) => Some(block),
|
||||
Node::NodeItem(&Item { node: ItemFn(_, _, _, _, _, eid), .. }) => {
|
||||
match cx.tcx.hir.body(eid).value.node {
|
||||
ExprBlock(ref block) => Some(block),
|
||||
_ => None,
|
||||
}
|
||||
Node::NodeItem(&Item {
|
||||
node: ItemFn(_, _, _, _, _, eid),
|
||||
..
|
||||
}) => match cx.tcx.hir.body(eid).value.node {
|
||||
ExprBlock(ref block) => Some(block),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
@ -704,9 +695,9 @@ impl LimitStack {
|
||||
Self { stack: vec![limit] }
|
||||
}
|
||||
pub fn limit(&self) -> u64 {
|
||||
*self.stack.last().expect(
|
||||
"there should always be a value in the stack",
|
||||
)
|
||||
*self.stack
|
||||
.last()
|
||||
.expect("there should always be a value in the stack")
|
||||
}
|
||||
pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
|
||||
let stack = &mut self.stack;
|
||||
@ -741,9 +732,10 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
||||
/// See also `is_direct_expn_of`.
|
||||
pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
|
||||
loop {
|
||||
let span_name_span = span.ctxt().outer().expn_info().map(|ei| {
|
||||
(ei.callee.name(), ei.call_site)
|
||||
});
|
||||
let span_name_span = span.ctxt()
|
||||
.outer()
|
||||
.expn_info()
|
||||
.map(|ei| (ei.callee.name(), ei.call_site));
|
||||
|
||||
match span_name_span {
|
||||
Some((mac_name, new_span)) if mac_name == name => return Some(new_span),
|
||||
@ -763,9 +755,10 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
|
||||
/// `bar!` by
|
||||
/// `is_direct_expn_of`.
|
||||
pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
|
||||
let span_name_span = span.ctxt().outer().expn_info().map(|ei| {
|
||||
(ei.callee.name(), ei.call_site)
|
||||
});
|
||||
let span_name_span = span.ctxt()
|
||||
.outer()
|
||||
.expn_info()
|
||||
.map(|ei| (ei.callee.name(), ei.call_site));
|
||||
|
||||
match span_name_span {
|
||||
Some((mac_name, new_span)) if mac_name == name => Some(new_span),
|
||||
@ -800,7 +793,11 @@ pub fn camel_case_until(s: &str) -> usize {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if up { last_i } else { s.len() }
|
||||
if up {
|
||||
last_i
|
||||
} else {
|
||||
s.len()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return index of the last camel-case component of `s`.
|
||||
@ -844,9 +841,9 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> Ty<'t
|
||||
// <'b> Foo<'b>` but
|
||||
// not for type parameters.
|
||||
pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
|
||||
cx.tcx.infer_ctxt().enter(|infcx| {
|
||||
infcx.can_eq(cx.param_env, a, b).is_ok()
|
||||
})
|
||||
cx.tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
|
||||
}
|
||||
|
||||
/// Return whether the given type is an `unsafe` function.
|
||||
@ -875,36 +872,28 @@ pub fn is_refutable(cx: &LateContext, pat: &Pat) -> bool {
|
||||
}
|
||||
|
||||
match pat.node {
|
||||
PatKind::Binding(..) |
|
||||
PatKind::Wild => false,
|
||||
PatKind::Box(ref pat) |
|
||||
PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) |
|
||||
PatKind::Range(..) => true,
|
||||
PatKind::Binding(..) | PatKind::Wild => false,
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) | PatKind::Range(..) => true,
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
|
||||
PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
|
||||
PatKind::Struct(ref qpath, ref fields, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, fields.iter().map(|field| &*field.node.pat))
|
||||
}
|
||||
PatKind::Struct(ref qpath, ref fields, _) => if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, fields.iter().map(|field| &*field.node.pat))
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, ref pats, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
}
|
||||
},
|
||||
PatKind::Slice(ref head, ref middle, ref tail) => {
|
||||
are_refutable(
|
||||
cx,
|
||||
head.iter().chain(middle).chain(tail.iter()).map(
|
||||
|pat| &**pat,
|
||||
),
|
||||
)
|
||||
PatKind::TupleStruct(ref qpath, ref pats, _) => if is_enum_variant(cx, qpath, pat.hir_id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
},
|
||||
PatKind::Slice(ref head, ref middle, ref tail) => are_refutable(
|
||||
cx,
|
||||
head.iter()
|
||||
.chain(middle)
|
||||
.chain(tail.iter())
|
||||
.map(|pat| &**pat),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1029,9 +1018,9 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
|
||||
}
|
||||
|
||||
pub fn type_size<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Option<u64> {
|
||||
ty.layout(cx.tcx, cx.param_env).ok().map(|layout| {
|
||||
layout.size(cx.tcx).bytes()
|
||||
})
|
||||
ty.layout(cx.tcx, cx.param_env)
|
||||
.ok()
|
||||
.map(|layout| layout.size(cx.tcx).bytes())
|
||||
}
|
||||
|
||||
/// Returns true if the lint is allowed in the current context
|
||||
|
@ -33,9 +33,7 @@ pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
|
||||
impl<'a> Display for Sugg<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match *self {
|
||||
Sugg::NonParen(ref s) |
|
||||
Sugg::MaybeParen(ref s) |
|
||||
Sugg::BinOp(_, ref s) => s.fmt(f),
|
||||
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,12 +176,10 @@ impl<'a> Sugg<'a> {
|
||||
match self {
|
||||
Sugg::NonParen(..) => self,
|
||||
// (x) and (x).y() both don't need additional parens
|
||||
Sugg::MaybeParen(sugg) => {
|
||||
if sugg.starts_with('(') && sugg.ends_with(')') {
|
||||
Sugg::MaybeParen(sugg)
|
||||
} else {
|
||||
Sugg::NonParen(format!("({})", sugg).into())
|
||||
}
|
||||
Sugg::MaybeParen(sugg) => if sugg.starts_with('(') && sugg.ends_with(')') {
|
||||
Sugg::MaybeParen(sugg)
|
||||
} else {
|
||||
Sugg::NonParen(format!("({})", sugg).into())
|
||||
},
|
||||
Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
|
||||
}
|
||||
@ -273,8 +269,8 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
|
||||
fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
|
||||
other.precedence() < op.precedence() ||
|
||||
(other.precedence() == op.precedence() &&
|
||||
((op != other && associativity(op) != dir) ||
|
||||
(op == other && associativity(op) != Associativity::Both))) ||
|
||||
((op != other && associativity(op) != dir) ||
|
||||
(op == other && associativity(op) != Associativity::Both))) ||
|
||||
is_shift(op) && is_arith(other) || is_shift(other) && is_arith(op)
|
||||
}
|
||||
|
||||
@ -293,12 +289,24 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
|
||||
let lhs = ParenHelper::new(lhs_paren, lhs);
|
||||
let rhs = ParenHelper::new(rhs_paren, rhs);
|
||||
let sugg = match op {
|
||||
AssocOp::Add | AssocOp::BitAnd | AssocOp::BitOr | AssocOp::BitXor | AssocOp::Divide | AssocOp::Equal |
|
||||
AssocOp::Greater | AssocOp::GreaterEqual | AssocOp::LAnd | AssocOp::LOr | AssocOp::Less |
|
||||
AssocOp::LessEqual | AssocOp::Modulus | AssocOp::Multiply | AssocOp::NotEqual | AssocOp::ShiftLeft |
|
||||
AssocOp::ShiftRight | AssocOp::Subtract => {
|
||||
format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs)
|
||||
},
|
||||
AssocOp::Add |
|
||||
AssocOp::BitAnd |
|
||||
AssocOp::BitOr |
|
||||
AssocOp::BitXor |
|
||||
AssocOp::Divide |
|
||||
AssocOp::Equal |
|
||||
AssocOp::Greater |
|
||||
AssocOp::GreaterEqual |
|
||||
AssocOp::LAnd |
|
||||
AssocOp::LOr |
|
||||
AssocOp::Less |
|
||||
AssocOp::LessEqual |
|
||||
AssocOp::Modulus |
|
||||
AssocOp::Multiply |
|
||||
AssocOp::NotEqual |
|
||||
AssocOp::ShiftLeft |
|
||||
AssocOp::ShiftRight |
|
||||
AssocOp::Subtract => format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs),
|
||||
AssocOp::Inplace => format!("in ({}) {}", lhs, rhs),
|
||||
AssocOp::Assign => format!("{} = {}", lhs, rhs),
|
||||
AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, token_to_string(&token::BinOp(op)), rhs),
|
||||
@ -343,7 +351,16 @@ fn associativity(op: &AssocOp) -> Associativity {
|
||||
match *op {
|
||||
Inplace | Assign | AssignOp(_) => Associativity::Right,
|
||||
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
|
||||
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight |
|
||||
Divide |
|
||||
Equal |
|
||||
Greater |
|
||||
GreaterEqual |
|
||||
Less |
|
||||
LessEqual |
|
||||
Modulus |
|
||||
NotEqual |
|
||||
ShiftLeft |
|
||||
ShiftRight |
|
||||
Subtract => Associativity::Left,
|
||||
DotDot | DotDotDot => Associativity::None,
|
||||
}
|
||||
@ -393,9 +410,8 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
|
||||
/// before it on its line.
|
||||
fn indentation<'a, T: LintContext<'a>>(cx: &T, span: Span) -> Option<String> {
|
||||
let lo = cx.sess().codemap().lookup_char_pos(span.lo());
|
||||
if let Some(line) = lo.file.get_line(
|
||||
lo.line - 1, /* line numbers in `Loc` are 1-based */
|
||||
)
|
||||
if let Some(line) = lo.file
|
||||
.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
|
||||
{
|
||||
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
|
||||
// we can mix char and byte positions here because we only consider `[ \t]`
|
||||
|
@ -72,14 +72,12 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
higher::VecArgs::Vec(args) => {
|
||||
if let Some(last) = args.iter().last() {
|
||||
let span = args[0].span.to(last.span);
|
||||
higher::VecArgs::Vec(args) => if let Some(last) = args.iter().last() {
|
||||
let span = args[0].span.to(last.span);
|
||||
|
||||
format!("&[{}]", snippet(cx, span, "..")).into()
|
||||
} else {
|
||||
"&[]".into()
|
||||
}
|
||||
format!("&[{}]", snippet(cx, span, "..")).into()
|
||||
} else {
|
||||
"&[]".into()
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use consts::{Constant, constant_simple, FloatWidth};
|
||||
use consts::{constant_simple, Constant, FloatWidth};
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use utils::span_help_and_lint;
|
||||
|
@ -1,13 +1,12 @@
|
||||
#![feature(plugin_registrar, rustc_private)]
|
||||
|
||||
extern crate syntax;
|
||||
extern crate rustc;
|
||||
extern crate rustc_plugin;
|
||||
extern crate syntax;
|
||||
|
||||
use syntax::codemap::Span;
|
||||
use syntax::tokenstream::TokenTree;
|
||||
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
|
||||
use syntax::ext::build::AstBuilder; // trait for expr_usize
|
||||
use syntax::ext::base::{ExtCtxt, MacEager, MacResult};
|
||||
use syntax::ext::build::AstBuilder; // trait for expr_usize
|
||||
use rustc_plugin::Registry;
|
||||
|
||||
fn expand_macro(cx: &mut ExtCtxt, sp: Span, _: &[TokenTree]) -> Box<MacResult + 'static> {
|
||||
|
45
src/main.rs
45
src/main.rs
@ -45,13 +45,8 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
||||
descriptions: &rustc_errors::registry::Registry,
|
||||
output: ErrorOutputType,
|
||||
) -> Compilation {
|
||||
self.default.early_callback(
|
||||
matches,
|
||||
sopts,
|
||||
cfg,
|
||||
descriptions,
|
||||
output,
|
||||
)
|
||||
self.default
|
||||
.early_callback(matches, sopts, cfg, descriptions, output)
|
||||
}
|
||||
fn no_input(
|
||||
&mut self,
|
||||
@ -62,14 +57,8 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
||||
ofile: &Option<PathBuf>,
|
||||
descriptions: &rustc_errors::registry::Registry,
|
||||
) -> Option<(Input, Option<PathBuf>)> {
|
||||
self.default.no_input(
|
||||
matches,
|
||||
sopts,
|
||||
cfg,
|
||||
odir,
|
||||
ofile,
|
||||
descriptions,
|
||||
)
|
||||
self.default
|
||||
.no_input(matches, sopts, cfg, odir, ofile, descriptions)
|
||||
}
|
||||
fn late_callback(
|
||||
&mut self,
|
||||
@ -79,13 +68,8 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
||||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>,
|
||||
) -> Compilation {
|
||||
self.default.late_callback(
|
||||
matches,
|
||||
sess,
|
||||
input,
|
||||
odir,
|
||||
ofile,
|
||||
)
|
||||
self.default
|
||||
.late_callback(matches, sess, input, odir, ofile)
|
||||
}
|
||||
fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
|
||||
let mut control = self.default.build_controller(sess, matches);
|
||||
@ -101,7 +85,7 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
||||
.as_ref()
|
||||
.expect(
|
||||
"at this compilation stage \
|
||||
the krate must be parsed",
|
||||
the krate must be parsed",
|
||||
)
|
||||
.span,
|
||||
);
|
||||
@ -203,13 +187,13 @@ pub fn main() {
|
||||
.skip(2)
|
||||
.find(|val| val.starts_with("--manifest-path="));
|
||||
|
||||
let mut metadata =
|
||||
if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
|
||||
metadata
|
||||
} else {
|
||||
let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata.\n"));
|
||||
process::exit(101);
|
||||
};
|
||||
let mut metadata = if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref))
|
||||
{
|
||||
metadata
|
||||
} else {
|
||||
let _ = io::stderr().write_fmt(format_args!("error: Could not obtain cargo metadata.\n"));
|
||||
process::exit(101);
|
||||
};
|
||||
|
||||
let manifest_path = manifest_path_arg.map(|arg| {
|
||||
Path::new(&arg["--manifest-path=".len()..])
|
||||
@ -359,7 +343,6 @@ fn process<I>(old_args: I) -> Result<(), i32>
|
||||
where
|
||||
I: Iterator<Item = String>,
|
||||
{
|
||||
|
||||
let mut args = vec!["rustc".to_owned()];
|
||||
|
||||
let mut found_dashes = false;
|
||||
|
@ -29,6 +29,8 @@ fn compile_test() {
|
||||
prepare_env();
|
||||
run_mode("run-pass", "run-pass");
|
||||
run_mode("ui", "ui");
|
||||
#[cfg(target_os = "windows")] run_mode("ui-windows", "ui");
|
||||
#[cfg(not(target_os = "windows"))] run_mode("ui-posix", "ui");
|
||||
#[cfg(target_os = "windows")]
|
||||
run_mode("ui-windows", "ui");
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
run_mode("ui-posix", "ui");
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
extern crate compiletest_rs as compiletest;
|
||||
extern crate test;
|
||||
|
||||
use std::env::{var, set_var};
|
||||
use std::env::{set_var, var};
|
||||
use std::path::PathBuf;
|
||||
use test::TestPaths;
|
||||
|
||||
|
@ -1,14 +1,12 @@
|
||||
#![feature(plugin)]
|
||||
#![plugin(clippy)]
|
||||
|
||||
#![allow(warnings)]
|
||||
|
||||
// this should compile in a reasonable amount of time
|
||||
fn rust_type_id(name: &str) {
|
||||
if "bool" == &name[..] || "uint" == &name[..] || "u8" == &name[..] || "u16" == &name[..] ||
|
||||
"u32" == &name[..] || "f32" == &name[..] || "f64" == &name[..] || "i8" == &name[..] ||
|
||||
"i16" == &name[..] || "i32" == &name[..] ||
|
||||
"i64" == &name[..] || "Self" == &name[..] || "str" == &name[..]
|
||||
if "bool" == &name[..] || "uint" == &name[..] || "u8" == &name[..] || "u16" == &name[..] || "u32" == &name[..] ||
|
||||
"f32" == &name[..] || "f64" == &name[..] || "i8" == &name[..] || "i16" == &name[..] ||
|
||||
"i32" == &name[..] || "i64" == &name[..] || "Self" == &name[..] || "str" == &name[..]
|
||||
{
|
||||
unreachable!();
|
||||
}
|
||||
|
@ -21,13 +21,11 @@ fn test_overlapping() {
|
||||
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))]));
|
||||
assert_eq!(
|
||||
None,
|
||||
overlapping(
|
||||
&[
|
||||
sp(1, Bound::Included(4)),
|
||||
sp(5, Bound::Included(6)),
|
||||
sp(10, Bound::Included(11)),
|
||||
],
|
||||
)
|
||||
overlapping(&[
|
||||
sp(1, Bound::Included(4)),
|
||||
sp(5, Bound::Included(6)),
|
||||
sp(10, Bound::Included(11))
|
||||
],)
|
||||
);
|
||||
assert_eq!(
|
||||
Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
|
||||
@ -35,12 +33,10 @@ fn test_overlapping() {
|
||||
);
|
||||
assert_eq!(
|
||||
Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
|
||||
overlapping(
|
||||
&[
|
||||
sp(1, Bound::Included(4)),
|
||||
sp(5, Bound::Included(6)),
|
||||
sp(6, Bound::Included(11)),
|
||||
],
|
||||
)
|
||||
overlapping(&[
|
||||
sp(1, Bound::Included(4)),
|
||||
sp(5, Bound::Included(6)),
|
||||
sp(6, Bound::Included(11))
|
||||
],)
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
// Tests for the various helper functions used by the needless_continue
|
||||
// lint that don't belong in utils.
|
||||
|
||||
extern crate clippy_lints;
|
||||
use clippy_lints::needless_continue::{erode_from_back, erode_block, erode_from_front};
|
||||
use clippy_lints::needless_continue::{erode_block, erode_from_back, erode_from_front};
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
|
@ -4,14 +4,14 @@
|
||||
use std::collections::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
static STATIC: [usize; 4] = [ 0, 1, 8, 16 ];
|
||||
const CONST: [usize; 4] = [ 0, 1, 8, 16 ];
|
||||
static STATIC: [usize; 4] = [0, 1, 8, 16];
|
||||
const CONST: [usize; 4] = [0, 1, 8, 16];
|
||||
|
||||
#[warn(clippy)]
|
||||
fn for_loop_over_option_and_result() {
|
||||
let option = Some(1);
|
||||
let result = option.ok_or("x not found");
|
||||
let v = vec![0,1,2];
|
||||
let v = vec![0, 1, 2];
|
||||
|
||||
// check FOR_LOOP_OVER_OPTION lint
|
||||
for x in option {
|
||||
@ -27,7 +27,8 @@ fn for_loop_over_option_and_result() {
|
||||
println!("{}", x);
|
||||
}
|
||||
|
||||
// make sure LOOP_OVER_NEXT lint takes precedence when next() is the last call in the chain
|
||||
// make sure LOOP_OVER_NEXT lint takes precedence when next() is the last call
|
||||
// in the chain
|
||||
for x in v.iter().next() {
|
||||
println!("{}", x);
|
||||
}
|
||||
@ -72,7 +73,8 @@ impl Unrelated {
|
||||
}
|
||||
}
|
||||
|
||||
#[warn(needless_range_loop, explicit_iter_loop, explicit_into_iter_loop, iter_next_loop, reverse_range_loop, explicit_counter_loop, for_kv_map)]
|
||||
#[warn(needless_range_loop, explicit_iter_loop, explicit_into_iter_loop, iter_next_loop, reverse_range_loop,
|
||||
explicit_counter_loop, for_kv_map)]
|
||||
#[warn(unused_collect)]
|
||||
#[allow(linkedlist, shadow_unrelated, unnecessary_mut_passed, cyclomatic_complexity, similar_names)]
|
||||
#[allow(many_single_char_names, unused_variables)]
|
||||
@ -90,7 +92,9 @@ fn main() {
|
||||
println!("{}", vec[i]); // ok, not the `i` of the for-loop
|
||||
}
|
||||
|
||||
for i in 0..vec.len() { let _ = vec[i]; }
|
||||
for i in 0..vec.len() {
|
||||
let _ = vec[i];
|
||||
}
|
||||
|
||||
// ICE #746
|
||||
for j in 0..4 {
|
||||
@ -104,7 +108,8 @@ fn main() {
|
||||
for i in 0..vec.len() {
|
||||
println!("{} {}", vec[i], i);
|
||||
}
|
||||
for i in 0..vec.len() { // not an error, indexing more than one variable
|
||||
for i in 0..vec.len() {
|
||||
// not an error, indexing more than one variable
|
||||
println!("{} {}", vec[i], vec2[i]);
|
||||
}
|
||||
|
||||
@ -156,97 +161,104 @@ fn main() {
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in 5...5 { // not an error, this is the range with only one element “5”
|
||||
for i in 5...5 {
|
||||
// not an error, this is the range with only one element “5”
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in 0..10 { // not an error, the start index is less than the end index
|
||||
for i in 0..10 {
|
||||
// not an error, the start index is less than the end index
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in -10..0 { // not an error
|
||||
for i in -10..0 {
|
||||
// not an error
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in (10..0).map(|x| x * 2) { // not an error, it can't be known what arbitrary methods do to a range
|
||||
for i in (10..0).map(|x| x * 2) {
|
||||
// not an error, it can't be known what arbitrary methods do to a range
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
// testing that the empty range lint folds constants
|
||||
for i in 10..5+4 {
|
||||
for i in 10..5 + 4 {
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in (5+2)..(3-1) {
|
||||
for i in (5 + 2)..(3 - 1) {
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in (5+2)..(8-1) {
|
||||
for i in (5 + 2)..(8 - 1) {
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
for i in (2*2)..(2*3) { // no error, 4..6 is fine
|
||||
for i in (2 * 2)..(2 * 3) {
|
||||
// no error, 4..6 is fine
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
let x = 42;
|
||||
for i in x..10 { // no error, not constant-foldable
|
||||
for i in x..10 {
|
||||
// no error, not constant-foldable
|
||||
println!("{}", i);
|
||||
}
|
||||
|
||||
// See #601
|
||||
for i in 0..10 { // no error, id_col does not exist outside the loop
|
||||
for i in 0..10 {
|
||||
// no error, id_col does not exist outside the loop
|
||||
let mut id_col = vec![0f64; 10];
|
||||
id_col[i] = 1f64;
|
||||
}
|
||||
|
||||
for _v in vec.iter() { }
|
||||
for _v in vec.iter() {}
|
||||
|
||||
for _v in vec.iter_mut() { }
|
||||
for _v in vec.iter_mut() {}
|
||||
|
||||
let out_vec = vec![1,2,3];
|
||||
for _v in out_vec.into_iter() { }
|
||||
let out_vec = vec![1, 2, 3];
|
||||
for _v in out_vec.into_iter() {}
|
||||
|
||||
let array = [1, 2, 3];
|
||||
for _v in array.into_iter() {}
|
||||
|
||||
for _v in &vec { } // these are fine
|
||||
for _v in &mut vec { } // these are fine
|
||||
for _v in &vec {} // these are fine
|
||||
for _v in &mut vec {} // these are fine
|
||||
|
||||
for _v in [1, 2, 3].iter() { }
|
||||
for _v in [1, 2, 3].iter() {}
|
||||
|
||||
for _v in (&mut [1, 2, 3]).iter() { } // no error
|
||||
for _v in (&mut [1, 2, 3]).iter() {} // no error
|
||||
|
||||
for _v in [0; 32].iter() {}
|
||||
|
||||
for _v in [0; 33].iter() {} // no error
|
||||
|
||||
let ll: LinkedList<()> = LinkedList::new();
|
||||
for _v in ll.iter() { }
|
||||
for _v in ll.iter() {}
|
||||
|
||||
let vd: VecDeque<()> = VecDeque::new();
|
||||
for _v in vd.iter() { }
|
||||
for _v in vd.iter() {}
|
||||
|
||||
let bh: BinaryHeap<()> = BinaryHeap::new();
|
||||
for _v in bh.iter() { }
|
||||
for _v in bh.iter() {}
|
||||
|
||||
let hm: HashMap<(), ()> = HashMap::new();
|
||||
for _v in hm.iter() { }
|
||||
for _v in hm.iter() {}
|
||||
|
||||
let bt: BTreeMap<(), ()> = BTreeMap::new();
|
||||
for _v in bt.iter() { }
|
||||
for _v in bt.iter() {}
|
||||
|
||||
let hs: HashSet<()> = HashSet::new();
|
||||
for _v in hs.iter() { }
|
||||
for _v in hs.iter() {}
|
||||
|
||||
let bs: BTreeSet<()> = BTreeSet::new();
|
||||
for _v in bs.iter() { }
|
||||
for _v in bs.iter() {}
|
||||
|
||||
for _v in vec.iter().next() { }
|
||||
for _v in vec.iter().next() {}
|
||||
|
||||
let u = Unrelated(vec![]);
|
||||
for _v in u.next() { } // no error
|
||||
for _v in u.iter() { } // no error
|
||||
for _v in u.next() {} // no error
|
||||
for _v in u.iter() {} // no error
|
||||
|
||||
let mut out = vec![];
|
||||
vec.iter().cloned().map(|x| out.push(x)).collect::<Vec<_>>();
|
||||
@ -254,82 +266,135 @@ fn main() {
|
||||
|
||||
// Loop with explicit counter variable
|
||||
let mut _index = 0;
|
||||
for _v in &vec { _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 1;
|
||||
_index = 0;
|
||||
for _v in &vec { _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
// Potential false positives
|
||||
let mut _index = 0;
|
||||
_index = 1;
|
||||
for _v in &vec { _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
_index += 1;
|
||||
for _v in &vec { _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
if true { _index = 1 }
|
||||
for _v in &vec { _index += 1 }
|
||||
if true {
|
||||
_index = 1
|
||||
}
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
let mut _index = 1;
|
||||
for _v in &vec { _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for _v in &vec { _index += 1; _index += 1 }
|
||||
for _v in &vec {
|
||||
_index += 1;
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for _v in &vec { _index *= 2; _index += 1 }
|
||||
for _v in &vec {
|
||||
_index *= 2;
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for _v in &vec { _index = 1; _index += 1 }
|
||||
for _v in &vec {
|
||||
_index = 1;
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
|
||||
for _v in &vec { let mut _index = 0; _index += 1 }
|
||||
for _v in &vec {
|
||||
let mut _index = 0;
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for _v in &vec { _index += 1; _index = 0; }
|
||||
for _v in &vec {
|
||||
_index += 1;
|
||||
_index = 0;
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for _v in &vec { for _x in 0..1 { _index += 1; }; _index += 1 }
|
||||
for _v in &vec {
|
||||
for _x in 0..1 {
|
||||
_index += 1;
|
||||
}
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
for x in &vec { if *x == 1 { _index += 1 } }
|
||||
for x in &vec {
|
||||
if *x == 1 {
|
||||
_index += 1
|
||||
}
|
||||
}
|
||||
|
||||
let mut _index = 0;
|
||||
if true { _index = 1 };
|
||||
for _v in &vec { _index += 1 }
|
||||
if true {
|
||||
_index = 1
|
||||
};
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut _index = 1;
|
||||
if false { _index = 0 };
|
||||
for _v in &vec { _index += 1 }
|
||||
if false {
|
||||
_index = 0
|
||||
};
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut index = 0;
|
||||
{ let mut _x = &mut index; }
|
||||
for _v in &vec { _index += 1 }
|
||||
{
|
||||
let mut _x = &mut index;
|
||||
}
|
||||
for _v in &vec {
|
||||
_index += 1
|
||||
}
|
||||
|
||||
let mut index = 0;
|
||||
for _v in &vec { index += 1 }
|
||||
for _v in &vec {
|
||||
index += 1
|
||||
}
|
||||
println!("index: {}", index);
|
||||
|
||||
for_loop_over_option_and_result();
|
||||
|
||||
let m : HashMap<u64, u64> = HashMap::new();
|
||||
let m: HashMap<u64, u64> = HashMap::new();
|
||||
for (_, v) in &m {
|
||||
let _v = v;
|
||||
}
|
||||
|
||||
let m : Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
|
||||
let m: Rc<HashMap<u64, u64>> = Rc::new(HashMap::new());
|
||||
for (_, v) in &*m {
|
||||
let _v = v;
|
||||
// Here the `*` is not actually necesarry, but the test tests that we don't suggest
|
||||
// Here the `*` is not actually necesarry, but the test tests that we don't
|
||||
// suggest
|
||||
// `in *m.values()` as we used to
|
||||
}
|
||||
|
||||
let mut m : HashMap<u64, u64> = HashMap::new();
|
||||
let mut m: HashMap<u64, u64> = HashMap::new();
|
||||
for (_, v) in &mut m {
|
||||
let _v = v;
|
||||
}
|
||||
@ -339,7 +404,7 @@ fn main() {
|
||||
let _v = v;
|
||||
}
|
||||
|
||||
let m : HashMap<u64, u64> = HashMap::new();
|
||||
let m: HashMap<u64, u64> = HashMap::new();
|
||||
let rm = &m;
|
||||
for (k, _value) in rm {
|
||||
let _k = k;
|
||||
@ -347,8 +412,12 @@ fn main() {
|
||||
|
||||
test_for_kv_map();
|
||||
|
||||
fn f<T>(_: &T, _: &T) -> bool { unimplemented!() }
|
||||
fn g<T>(_: &mut [T], _: usize, _: usize) { unimplemented!() }
|
||||
fn f<T>(_: &T, _: &T) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
fn g<T>(_: &mut [T], _: usize, _: usize) {
|
||||
unimplemented!()
|
||||
}
|
||||
for i in 1..vec.len() {
|
||||
if f(&vec[i - 1], &vec[i]) {
|
||||
g(&mut vec, i - 1, i);
|
||||
@ -362,7 +431,7 @@ fn main() {
|
||||
|
||||
#[allow(used_underscore_binding)]
|
||||
fn test_for_kv_map() {
|
||||
let m : HashMap<u64, u64> = HashMap::new();
|
||||
let m: HashMap<u64, u64> = HashMap::new();
|
||||
|
||||
// No error, _value is actually used
|
||||
for (k, _value) in &m {
|
||||
@ -372,7 +441,7 @@ fn test_for_kv_map() {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
|
||||
fn partition<T: PartialOrd + Send>(v: &mut [T]) -> usize {
|
||||
let pivot = v.len() - 1;
|
||||
let mut i = 0;
|
||||
for j in 0..pivot {
|
||||
@ -384,3 +453,91 @@ fn partition<T:PartialOrd+Send>(v: &mut [T]) -> usize {
|
||||
v.swap(i, pivot);
|
||||
i
|
||||
}
|
||||
|
||||
const LOOP_OFFSET: usize = 5000;
|
||||
|
||||
#[warn(needless_range_loop)]
|
||||
pub fn manual_copy(src: &[i32], dst: &mut [i32], dst2: &mut [i32]) {
|
||||
// plain manual memcpy
|
||||
for i in 0..src.len() {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// dst offset memcpy
|
||||
for i in 0..src.len() {
|
||||
dst[i + 10] = src[i];
|
||||
}
|
||||
|
||||
// src offset memcpy
|
||||
for i in 0..src.len() {
|
||||
dst[i] = src[i + 10];
|
||||
}
|
||||
|
||||
// src offset memcpy
|
||||
for i in 11..src.len() {
|
||||
dst[i] = src[i - 10];
|
||||
}
|
||||
|
||||
// overwrite entire dst
|
||||
for i in 0..dst.len() {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// manual copy with branch - can't easily convert to memcpy!
|
||||
for i in 0..src.len() {
|
||||
dst[i] = src[i];
|
||||
if dst[i] > 5 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// multiple copies - suggest two memcpy statements
|
||||
for i in 10..256 {
|
||||
dst[i] = src[i - 5];
|
||||
dst2[i + 500] = src[i]
|
||||
}
|
||||
|
||||
// this is a reversal - the copy lint shouldn't be triggered
|
||||
for i in 10..LOOP_OFFSET {
|
||||
dst[i + LOOP_OFFSET] = src[LOOP_OFFSET - i];
|
||||
}
|
||||
|
||||
let some_var = 5;
|
||||
// Offset in variable
|
||||
for i in 10..LOOP_OFFSET {
|
||||
dst[i + LOOP_OFFSET] = src[i - some_var];
|
||||
}
|
||||
|
||||
// Non continuous copy - don't trigger lint
|
||||
for i in 0..10 {
|
||||
dst[i + i] = src[i];
|
||||
}
|
||||
|
||||
let src_vec = vec![1, 2, 3, 4, 5];
|
||||
let mut dst_vec = vec![0, 0, 0, 0, 0];
|
||||
|
||||
// make sure vectors are supported
|
||||
for i in 0..src_vec.len() {
|
||||
dst_vec[i] = src_vec[i];
|
||||
}
|
||||
|
||||
// lint should not trigger when either
|
||||
// source or destination type is not
|
||||
// slice-like, like DummyStruct
|
||||
struct DummyStruct(i32);
|
||||
|
||||
impl ::std::ops::Index<usize> for DummyStruct {
|
||||
type Output = i32;
|
||||
|
||||
fn index(&self, _: usize) -> &i32 {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
let src = DummyStruct(5);
|
||||
let mut dst_vec = vec![0; 10];
|
||||
|
||||
for i in 0..10 {
|
||||
dst_vec[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user