Merge branch 'master' of github.com:rust-lang-nursery/rust-clippy

This commit is contained in:
cgm616 2017-10-28 12:55:16 -05:00
commit d094f98f0b
63 changed files with 2624 additions and 1863 deletions

View file

@ -180,6 +180,15 @@ transparently:
#[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))]
```
## Updating rustc
Sometimes, rustc moves forward without clippy catching up. Therefore updating
rustc may leave clippy a non-functional state until we fix the resulting
breakage.
You can use the [rust-update](rust-update) script to update rustc only if
clippy would also update correctly.
## License
Licensed under [MPL](https://www.mozilla.org/MPL/2.0/).

View file

@ -28,6 +28,7 @@ toml = "0.4"
unicode-normalization = "0.1"
pulldown-cmark = "0.0.15"
url = "1.5.0"
if_chain = "0.1"
[features]
debugging = []

View file

@ -140,12 +140,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
let parent_fn = cx.tcx.hir.get_parent(e.id);
let parent_impl = cx.tcx.hir.get_parent(parent_fn);
// the crate node is the only one that is not in the map
if_let_chain!{[
parent_impl != ast::CRATE_NODE_ID,
let hir::map::Node::NodeItem(item) = cx.tcx.hir.get(parent_impl),
let hir::Item_::ItemImpl(_, _, _, _, Some(ref trait_ref), _, _) = item.node,
trait_ref.path.def.def_id() == trait_id
], { return; }}
if_chain! {
if parent_impl != ast::CRATE_NODE_ID;
if let hir::map::Node::NodeItem(item) = cx.tcx.hir.get(parent_impl);
if let hir::Item_::ItemImpl(_, _, _, _, Some(ref trait_ref), _, _) = item.node;
if trait_ref.path.def.def_id() == trait_id;
then { return; }
}
implements_trait($cx, $ty, trait_id, &[$rty])
},)*
_ => false,

View file

@ -94,13 +94,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
return;
}
for item in items {
if_let_chain! {[
let NestedMetaItemKind::MetaItem(ref mi) = item.node,
let MetaItemKind::NameValue(ref lit) = mi.node,
mi.name() == "since",
], {
check_semver(cx, item.span, lit);
}}
if_chain! {
if let NestedMetaItemKind::MetaItem(ref mi) = item.node;
if let MetaItemKind::NameValue(ref lit) = mi.node;
if mi.name() == "since";
then {
check_semver(cx, item.span, lit);
}
}
}
}
}

View file

@ -119,27 +119,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
}
}
}
if_let_chain!{[
let Expr_::ExprBinary(ref op, ref left, ref right) = e.node,
BinOp_::BiEq == op.node,
let Expr_::ExprBinary(ref op1, ref left1, ref right1) = left.node,
BinOp_::BiBitAnd == op1.node,
let Expr_::ExprLit(ref lit) = right1.node,
let LitKind::Int(n, _) = lit.node,
let Expr_::ExprLit(ref lit1) = right.node,
let LitKind::Int(0, _) = lit1.node,
n.leading_zeros() == n.count_zeros(),
n > u128::from(self.verbose_bit_mask_threshold),
], {
span_lint_and_then(cx,
VERBOSE_BIT_MASK,
e.span,
"bit mask could be simplified with a call to `trailing_zeros`",
|db| {
let sugg = Sugg::hir(cx, left1, "...").maybe_par();
db.span_suggestion(e.span, "try", format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()));
});
}}
if_chain! {
if let Expr_::ExprBinary(ref op, ref left, ref right) = e.node;
if BinOp_::BiEq == op.node;
if let Expr_::ExprBinary(ref op1, ref left1, ref right1) = left.node;
if BinOp_::BiBitAnd == op1.node;
if let Expr_::ExprLit(ref lit) = right1.node;
if let LitKind::Int(n, _) = lit.node;
if let Expr_::ExprLit(ref lit1) = right.node;
if let LitKind::Int(0, _) = lit1.node;
if n.leading_zeros() == n.count_zeros();
if n > u128::from(self.verbose_bit_mask_threshold);
then {
span_lint_and_then(cx,
VERBOSE_BIT_MASK,
e.span,
"bit mask could be simplified with a call to `trailing_zeros`",
|db| {
let sugg = Sugg::hir(cx, left1, "...").maybe_par();
db.span_suggestion(e.span, "try", format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()));
});
}
}
}
}

View file

@ -37,56 +37,58 @@ impl LintPass for ByteCount {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ByteCount {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
if_let_chain!([
let ExprMethodCall(ref count, _, ref count_args) = expr.node,
count.name == "count",
count_args.len() == 1,
let ExprMethodCall(ref filter, _, ref filter_args) = count_args[0].node,
filter.name == "filter",
filter_args.len() == 2,
let ExprClosure(_, _, body_id, _, _) = filter_args[1].node,
], {
let body = cx.tcx.hir.body(body_id);
if_let_chain!([
body.arguments.len() == 1,
let Some(argname) = get_pat_name(&body.arguments[0].pat),
let ExprBinary(ref op, ref l, ref r) = body.value.node,
op.node == BiEq,
match_type(cx,
walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
&paths::SLICE_ITER),
], {
let needle = match get_path_name(l) {
Some(name) if check_arg(name, argname, r) => r,
_ => match get_path_name(r) {
Some(name) if check_arg(name, argname, l) => l,
_ => { return; }
if_chain! {
if let ExprMethodCall(ref count, _, ref count_args) = expr.node;
if count.name == "count";
if count_args.len() == 1;
if let ExprMethodCall(ref filter, _, ref filter_args) = count_args[0].node;
if filter.name == "filter";
if filter_args.len() == 2;
if let ExprClosure(_, _, body_id, _, _) = filter_args[1].node;
then {
let body = cx.tcx.hir.body(body_id);
if_chain! {
if body.arguments.len() == 1;
if let Some(argname) = get_pat_name(&body.arguments[0].pat);
if let ExprBinary(ref op, ref l, ref r) = body.value.node;
if op.node == BiEq;
if match_type(cx,
walk_ptrs_ty(cx.tables.expr_ty(&filter_args[0])),
&paths::SLICE_ITER);
then {
let needle = match get_path_name(l) {
Some(name) if check_arg(name, argname, r) => r,
_ => match get_path_name(r) {
Some(name) if check_arg(name, argname, l) => l,
_ => { return; }
}
};
if ty::TyUint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).sty {
return;
}
let haystack = if let ExprMethodCall(ref path, _, ref args) =
filter_args[0].node {
let p = path.name;
if (p == "iter" || p == "iter_mut") && args.len() == 1 {
&args[0]
} else {
&filter_args[0]
}
} else {
&filter_args[0]
};
span_lint_and_sugg(cx,
NAIVE_BYTECOUNT,
expr.span,
"You appear to be counting bytes the naive way",
"Consider using the bytecount crate",
format!("bytecount::count({}, {})",
snippet(cx, haystack.span, ".."),
snippet(cx, needle.span, "..")));
}
};
if ty::TyUint(UintTy::U8) != walk_ptrs_ty(cx.tables.expr_ty(needle)).sty {
return;
}
let haystack = if let ExprMethodCall(ref path, _, ref args) =
filter_args[0].node {
let p = path.name;
if (p == "iter" || p == "iter_mut") && args.len() == 1 {
&args[0]
} else {
&filter_args[0]
}
} else {
&filter_args[0]
};
span_lint_and_sugg(cx,
NAIVE_BYTECOUNT,
expr.span,
"You appear to be counting bytes the naive way",
"Consider using the bytecount crate",
format!("bytecount::count({}, {})",
snippet(cx, haystack.span, ".."),
snippet(cx, needle.span, "..")));
});
});
}
};
}
}

View file

@ -100,43 +100,45 @@ fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
}
fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
if_let_chain! {[
let ast::ExprKind::Block(ref block) = else_.node,
let Some(else_) = expr_block(block),
!in_macro(else_.span),
], {
match else_.node {
ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
span_lint_and_sugg(cx,
COLLAPSIBLE_IF,
block.span,
"this `else { if .. }` block can be collapsed",
"try",
snippet_block(cx, else_.span, "..").into_owned());
if_chain! {
if let ast::ExprKind::Block(ref block) = else_.node;
if let Some(else_) = expr_block(block);
if !in_macro(else_.span);
then {
match else_.node {
ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
span_lint_and_sugg(cx,
COLLAPSIBLE_IF,
block.span,
"this `else { if .. }` block can be collapsed",
"try",
snippet_block(cx, else_.span, "..").into_owned());
}
_ => (),
}
_ => (),
}
}}
}
}
fn check_collapsible_no_if_let(cx: &EarlyContext, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
if_let_chain! {[
let Some(inner) = expr_block(then),
let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
], {
if expr.span.ctxt() != inner.span.ctxt() {
return;
if_chain! {
if let Some(inner) = expr_block(then);
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node;
then {
if expr.span.ctxt() != inner.span.ctxt() {
return;
}
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
let lhs = Sugg::ast(cx, check, "..");
let rhs = Sugg::ast(cx, check_inner, "..");
db.span_suggestion(expr.span,
"try",
format!("if {} {}",
lhs.and(rhs),
snippet_block(cx, content.span, "..")));
});
}
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
let lhs = Sugg::ast(cx, check, "..");
let rhs = Sugg::ast(cx, check_inner, "..");
db.span_suggestion(expr.span,
"try",
format!("if {} {}",
lhs.and(rhs),
snippet_block(cx, content.span, "..")));
});
}}
}
}
/// If the block contains only one expression, return it.

View file

@ -91,43 +91,44 @@ fn check_hash_peq<'a, 'tcx>(
ty: Ty<'tcx>,
hash_is_automatically_derived: bool,
) {
if_let_chain! {[
match_path(&trait_ref.path, &paths::HASH),
let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
], {
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
if peq_is_automatically_derived == hash_is_automatically_derived {
return;
}
let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
if trait_ref.substs.type_at(1) == ty {
let mess = if peq_is_automatically_derived {
"you are implementing `Hash` explicitly but have derived `PartialEq`"
} else {
"you are deriving `Hash` but have implemented `PartialEq` explicitly"
};
span_lint_and_then(
cx, DERIVE_HASH_XOR_EQ, span,
mess,
|db| {
if let Some(node_id) = cx.tcx.hir.as_local_node_id(impl_id) {
db.span_note(
cx.tcx.hir.span(node_id),
"`PartialEq` implemented here"
);
}
});
}
});
}}
if_chain! {
if match_path(&trait_ref.path, &paths::HASH);
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
then {
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
if peq_is_automatically_derived == hash_is_automatically_derived {
return;
}
let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
if trait_ref.substs.type_at(1) == ty {
let mess = if peq_is_automatically_derived {
"you are implementing `Hash` explicitly but have derived `PartialEq`"
} else {
"you are deriving `Hash` but have implemented `PartialEq` explicitly"
};
span_lint_and_then(
cx, DERIVE_HASH_XOR_EQ, span,
mess,
|db| {
if let Some(node_id) = cx.tcx.hir.as_local_node_id(impl_id) {
db.span_note(
cx.tcx.hir.span(node_id),
"`PartialEq` implemented here"
);
}
});
}
});
}
}
}
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.

View file

@ -115,50 +115,51 @@ 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_chain!{[
let ExprCall(ref path, ref args) = expr.node,
let ExprPath(ref qpath) = path.node,
args.len() == 1,
let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id)),
], {
let lint;
let msg;
let arg = &args[0];
let arg_ty = cx.tables.expr_ty(arg);
if let ty::TyRef(..) = arg_ty.sty {
if match_def_path(cx.tcx, def_id, &paths::DROP) {
lint = DROP_REF;
msg = DROP_REF_SUMMARY.to_string();
} else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
lint = FORGET_REF;
msg = FORGET_REF_SUMMARY.to_string();
} else {
return;
if_chain! {
if let ExprCall(ref path, ref args) = expr.node;
if let ExprPath(ref qpath) = path.node;
if args.len() == 1;
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
then {
let lint;
let msg;
let arg = &args[0];
let arg_ty = cx.tables.expr_ty(arg);
if let ty::TyRef(..) = arg_ty.sty {
if match_def_path(cx.tcx, def_id, &paths::DROP) {
lint = DROP_REF;
msg = DROP_REF_SUMMARY.to_string();
} else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
lint = FORGET_REF;
msg = FORGET_REF_SUMMARY.to_string();
} else {
return;
}
span_note_and_lint(cx,
lint,
expr.span,
&msg,
arg.span,
&format!("argument has type {}", arg_ty));
} else if is_copy(cx, arg_ty) {
if match_def_path(cx.tcx, def_id, &paths::DROP) {
lint = DROP_COPY;
msg = DROP_COPY_SUMMARY.to_string();
} else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
lint = FORGET_COPY;
msg = FORGET_COPY_SUMMARY.to_string();
} else {
return;
}
span_note_and_lint(cx,
lint,
expr.span,
&msg,
arg.span,
&format!("argument has type {}", arg_ty));
}
span_note_and_lint(cx,
lint,
expr.span,
&msg,
arg.span,
&format!("argument has type {}", arg_ty));
} else if is_copy(cx, arg_ty) {
if match_def_path(cx.tcx, def_id, &paths::DROP) {
lint = DROP_COPY;
msg = DROP_COPY_SUMMARY.to_string();
} else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
lint = FORGET_COPY;
msg = FORGET_COPY_SUMMARY.to_string();
} else {
return;
}
span_note_and_lint(cx,
lint,
expr.span,
&msg,
arg.span,
&format!("argument has type {}", arg_ty));
}
}}
}
}
}

View file

@ -87,25 +87,26 @@ fn check_cond<'a, 'tcx, 'b>(
cx: &'a LateContext<'a, 'tcx>,
check: &'b Expr,
) -> Option<(&'static str, &'b Expr, &'b Expr)> {
if_let_chain! {[
let ExprMethodCall(ref path, _, ref params) = check.node,
params.len() >= 2,
path.name == "contains_key",
let ExprAddrOf(_, ref key) = params[1].node
], {
let map = &params[0];
let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(map));
return if match_type(cx, obj_ty, &paths::BTREEMAP) {
Some(("BTreeMap", map, key))
if_chain! {
if let ExprMethodCall(ref path, _, ref params) = check.node;
if params.len() >= 2;
if path.name == "contains_key";
if let ExprAddrOf(_, ref key) = params[1].node;
then {
let map = &params[0];
let obj_ty = walk_ptrs_ty(cx.tables.expr_ty(map));
return if match_type(cx, obj_ty, &paths::BTREEMAP) {
Some(("BTreeMap", map, key))
}
else if match_type(cx, obj_ty, &paths::HASHMAP) {
Some(("HashMap", map, key))
}
else {
None
};
}
else if match_type(cx, obj_ty, &paths::HASHMAP) {
Some(("HashMap", map, key))
}
else {
None
};
}}
}
None
}
@ -121,32 +122,33 @@ struct InsertVisitor<'a, 'tcx: 'a, 'b> {
impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
fn visit_expr(&mut self, expr: &'tcx Expr) {
if_let_chain! {[
let ExprMethodCall(ref path, _, ref params) = expr.node,
params.len() == 3,
path.name == "insert",
get_item_name(self.cx, self.map) == get_item_name(self.cx, &params[0]),
SpanlessEq::new(self.cx).eq_expr(self.key, &params[1])
], {
span_lint_and_then(self.cx, MAP_ENTRY, self.span,
&format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |db| {
if self.sole_expr {
let help = format!("{}.entry({}).or_insert({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."),
snippet(self.cx, params[2].span, ".."));
db.span_suggestion(self.span, "consider using", help);
}
else {
let help = format!("{}.entry({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."));
db.span_suggestion(self.span, "consider using", help);
}
});
}}
if_chain! {
if let ExprMethodCall(ref path, _, ref params) = expr.node;
if params.len() == 3;
if path.name == "insert";
if get_item_name(self.cx, self.map) == get_item_name(self.cx, &params[0]);
if SpanlessEq::new(self.cx).eq_expr(self.key, &params[1]);
then {
span_lint_and_then(self.cx, MAP_ENTRY, self.span,
&format!("usage of `contains_key` followed by `insert` on a `{}`", self.ty), |db| {
if self.sole_expr {
let help = format!("{}.entry({}).or_insert({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."),
snippet(self.cx, params[2].span, ".."));
db.span_suggestion(self.span, "consider using", help);
}
else {
let help = format!("{}.entry({})",
snippet(self.cx, self.map.span, "map"),
snippet(self.cx, params[1].span, ".."));
db.span_suggestion(self.span, "consider using", help);
}
});
}
}
if !self.sole_expr {
walk_expr(self, expr);

View file

@ -0,0 +1,63 @@
use consts::{constant_simple, Constant};
use rustc::hir::*;
use rustc::lint::*;
use syntax::codemap::Span;
use utils::{in_macro, span_lint};
/// **What it does:** Checks for erasing operations, e.g. `x * 0`.
///
/// **Why is this bad?** The whole expression can be replaced by zero.
/// This is most likely not the intended outcome and should probably be
/// corrected
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// 0 / x; 0 * x; x & 0
/// ```
declare_lint! {
pub ERASING_OP,
Warn,
"using erasing operations, e.g. `x * 0` or `y & 0`"
}
#[derive(Copy, Clone)]
pub struct ErasingOp;
impl LintPass for ErasingOp {
fn get_lints(&self) -> LintArray {
lint_array!(ERASING_OP)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ErasingOp {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
if in_macro(e.span) {
return;
}
if let ExprBinary(ref cmp, ref left, ref right) = e.node {
match cmp.node {
BiMul | BiBitAnd => {
check(cx, left, e.span);
check(cx, right, e.span);
},
BiDiv => check(cx, left, e.span),
_ => (),
}
}
}
}
fn check(cx: &LateContext, e: &Expr, span: Span) {
if let Some(Constant::Int(v)) = constant_simple(cx, e) {
if v.to_u128_unchecked() == 0 {
span_lint(
cx,
ERASING_OP,
span,
"this operation will always return zero. This is likely not the intended outcome",
);
}
}
}

View file

@ -298,23 +298,24 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
match expr.node {
ExprPath(ref qpath) => {
if_let_chain! {[
let QPath::Resolved(None, ref path) = *qpath,
path.segments.len() == 1,
let def::Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id),
local_id == self.var,
if_chain! {
if let QPath::Resolved(None, ref path) = *qpath;
if path.segments.len() == 1;
if let def::Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id);
if local_id == self.var;
// Check that this is a read, not a write.
!is_in_assignment_position(self.cx, expr),
], {
span_note_and_lint(
self.cx,
EVAL_ORDER_DEPENDENCE,
expr.span,
"unsequenced read of a variable",
self.write_expr.span,
"whether read occurs before this write depends on evaluation order"
);
}}
if !is_in_assignment_position(self.cx, expr);
then {
span_note_and_lint(
self.cx,
EVAL_ORDER_DEPENDENCE,
expr.span,
"unsequenced read of a variable",
self.write_expr.span,
"whether read occurs before this write depends on evaluation order"
);
}
}
}
// We're about to descend a closure. Since we don't know when (or
// if) the closure will be evaluated, any reads in it might not

View file

@ -33,69 +33,70 @@ 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_chain! {[
if_chain! {
// match call to unwrap
let ExprMethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.node,
unwrap_fun.name == "unwrap",
if let ExprMethodCall(ref unwrap_fun, _, ref unwrap_args) = expr.node;
if unwrap_fun.name == "unwrap";
// match call to write_fmt
unwrap_args.len() > 0,
let ExprMethodCall(ref write_fun, _, ref write_args) =
unwrap_args[0].node,
write_fun.name == "write_fmt",
if unwrap_args.len() > 0;
if let ExprMethodCall(ref write_fun, _, ref write_args) =
unwrap_args[0].node;
if write_fun.name == "write_fmt";
// match calls to std::io::stdout() / std::io::stderr ()
write_args.len() > 0,
let ExprCall(ref dest_fun, _) = write_args[0].node,
let ExprPath(ref qpath) = dest_fun.node,
let Some(dest_fun_id) =
opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id)),
let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
if write_args.len() > 0;
if let ExprCall(ref dest_fun, _) = write_args[0].node;
if let ExprPath(ref qpath) = dest_fun.node;
if let Some(dest_fun_id) =
opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id));
if let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
Some("stdout")
} else if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stderr"]) {
Some("stderr")
} else {
None
},
], {
let write_span = unwrap_args[0].span;
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
if is_expn_of(write_span, "writeln").is_some() {
Some("writeln")
} else if is_expn_of(write_span, "write").is_some() {
Some("write")
} else {
None
};
let prefix = if dest_name == "stderr" {
"e"
} else {
""
};
if let Some(macro_name) = calling_macro {
span_lint(
cx,
EXPLICIT_WRITE,
expr.span,
&format!(
"use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
macro_name,
dest_name,
prefix,
macro_name.replace("write", "print")
)
);
} else {
span_lint(
cx,
EXPLICIT_WRITE,
expr.span,
&format!(
"use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead",
dest_name,
prefix,
)
);
then {
let write_span = unwrap_args[0].span;
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
if is_expn_of(write_span, "writeln").is_some() {
Some("writeln")
} else if is_expn_of(write_span, "write").is_some() {
Some("write")
} else {
None
};
let prefix = if dest_name == "stderr" {
"e"
} else {
""
};
if let Some(macro_name) = calling_macro {
span_lint(
cx,
EXPLICIT_WRITE,
expr.span,
&format!(
"use of `{}!({}(), ...).unwrap()`. Consider using `{}{}!` instead",
macro_name,
dest_name,
prefix,
macro_name.replace("write", "print")
)
);
} else {
span_lint(
cx,
EXPLICIT_WRITE,
expr.span,
&format!(
"use of `{}().write_fmt(...).unwrap()`. Consider using `{}print!` instead",
dest_name,
prefix,
)
);
}
}
}}
}
}
}

View file

@ -0,0 +1,133 @@
use rustc::lint::*;
use rustc::hir;
use rustc::ty;
use syntax_pos::Span;
use utils::{method_chain_args, match_def_path, span_lint_and_then, walk_ptrs_ty};
use utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT, OPTION, RESULT};
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
///
/// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// struct Foo(i32);
/// impl From<String> for Foo {
/// fn from(s: String) -> Self {
/// Foo(s.parse().unwrap())
/// }
/// }
/// ```
declare_lint! {
pub FALLIBLE_IMPL_FROM, Allow,
"Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
}
pub struct FallibleImplFrom;
impl LintPass for FallibleImplFrom {
fn get_lints(&self) -> LintArray {
lint_array!(FALLIBLE_IMPL_FROM)
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom {
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
// check for `impl From<???> for ..`
let impl_def_id = cx.tcx.hir.local_def_id(item.id);
if_chain! {
if let hir::ItemImpl(.., ref impl_items) = item.node;
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
if match_def_path(cx.tcx, impl_trait_ref.def_id, &FROM_TRAIT);
then {
lint_impl_body(cx, item.span, impl_items);
}
}
}
}
fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_items: &hir::HirVec<hir::ImplItemRef>) {
use rustc::hir::*;
use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor};
struct FindPanicUnwrap<'a, 'tcx: 'a> {
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
tables: &'tcx ty::TypeckTables<'tcx>,
result: Vec<Span>,
}
impl<'a, 'tcx: 'a> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr) {
// check for `begin_panic`
if_chain! {
if let ExprCall(ref func_expr, _) = expr.node;
if let ExprPath(QPath::Resolved(_, ref path)) = func_expr.node;
if match_def_path(self.tcx, path.def.def_id(), &BEGIN_PANIC) ||
match_def_path(self.tcx, path.def.def_id(), &BEGIN_PANIC_FMT);
then {
self.result.push(expr.span);
}
}
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let reciever_ty = walk_ptrs_ty(self.tables.expr_ty(&arglists[0][0]));
if match_type(self.tcx, reciever_ty, &OPTION) ||
match_type(self.tcx, reciever_ty, &RESULT)
{
self.result.push(expr.span);
}
}
// and check sub-expressions
intravisit::walk_expr(self, expr);
}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None
}
}
for impl_item in impl_items {
if_chain! {
if impl_item.name == "from";
if let ImplItemKind::Method(_, body_id) =
cx.tcx.hir.impl_item(impl_item.id).node;
then {
// check the body for `begin_panic` or `unwrap`
let body = cx.tcx.hir.body(body_id);
let impl_item_def_id = cx.tcx.hir.local_def_id(impl_item.id.node_id);
let mut fpu = FindPanicUnwrap {
tcx: cx.tcx,
tables: cx.tcx.typeck_tables_of(impl_item_def_id),
result: Vec::new(),
};
fpu.visit_expr(&body.value);
// if we've found one, lint
if !fpu.result.is_empty() {
span_lint_and_then(
cx,
FALLIBLE_IMPL_FROM,
impl_span,
"consider implementing `TryFrom` instead",
move |db| {
db.help(
"`From` is intended for infallible conversions only. \
Use `TryFrom` if there's a possibility for the conversion to fail.");
db.span_note(fpu.result, "potential failure(s)");
});
}
}
}
}
}
fn match_type(tcx: ty::TyCtxt, ty: ty::Ty, path: &[&str]) -> bool {
match ty.sty {
ty::TyAdt(adt, _) => match_def_path(tcx, adt.did, path),
_ => false,
}
}

View file

@ -42,19 +42,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
match expr.node {
// `format!("{}", foo)` expansion
ExprCall(ref fun, ref args) => {
if_let_chain!{[
let ExprPath(ref qpath) = fun.node,
args.len() == 2,
let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id)),
match_def_path(cx.tcx, fun_def_id, &paths::FMT_ARGUMENTS_NEWV1),
if_chain! {
if let ExprPath(ref qpath) = fun.node;
if args.len() == 2;
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id));
if match_def_path(cx.tcx, fun_def_id, &paths::FMT_ARGUMENTS_NEWV1);
// ensure the format string is `"{..}"` with only one argument and no text
check_static_str(&args[0]),
if check_static_str(&args[0]);
// ensure the format argument is `{}` ie. Display with no fancy option
// and that the argument is a string
check_arg_is_display(cx, &args[1])
], {
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
}}
if check_arg_is_display(cx, &args[1]);
then {
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
}
}
},
// `format!("foo")` expansion contains `match () { () => [], }`
ExprMatch(ref matchee, _, _) => if let ExprTup(ref tup) = matchee.node {
@ -70,15 +71,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
/// Checks if the expressions matches `&[""]`
fn check_static_str(expr: &Expr) -> bool {
if_let_chain! {[
let ExprAddrOf(_, ref expr) = expr.node, // &[""]
let ExprArray(ref exprs) = expr.node, // [""]
exprs.len() == 1,
let ExprLit(ref lit) = exprs[0].node,
let LitKind::Str(ref lit, _) = lit.node,
], {
return lit.as_str().is_empty();
}}
if_chain! {
if let ExprAddrOf(_, ref expr) = expr.node; // &[""]
if let ExprArray(ref exprs) = expr.node; // [""]
if exprs.len() == 1;
if let ExprLit(ref lit) = exprs[0].node;
if let LitKind::Str(ref lit, _) = lit.node;
then {
return lit.as_str().is_empty();
}
}
false
}
@ -91,25 +93,26 @@ fn check_static_str(expr: &Expr) -> bool {
/// }
/// ```
fn check_arg_is_display(cx: &LateContext, expr: &Expr) -> bool {
if_let_chain! {[
let ExprAddrOf(_, ref expr) = expr.node,
let ExprMatch(_, ref arms, _) = expr.node,
arms.len() == 1,
arms[0].pats.len() == 1,
let PatKind::Tuple(ref pat, None) = arms[0].pats[0].node,
pat.len() == 1,
let ExprArray(ref exprs) = arms[0].body.node,
exprs.len() == 1,
let ExprCall(_, ref args) = exprs[0].node,
args.len() == 2,
let ExprPath(ref qpath) = args[1].node,
let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, args[1].hir_id)),
match_def_path(cx.tcx, fun_def_id, &paths::DISPLAY_FMT_METHOD),
], {
let ty = walk_ptrs_ty(cx.tables.pat_ty(&pat[0]));
return ty.sty == ty::TyStr || match_type(cx, ty, &paths::STRING);
}}
if_chain! {
if let ExprAddrOf(_, ref expr) = expr.node;
if let ExprMatch(_, ref arms, _) = expr.node;
if arms.len() == 1;
if arms[0].pats.len() == 1;
if let PatKind::Tuple(ref pat, None) = arms[0].pats[0].node;
if pat.len() == 1;
if let ExprArray(ref exprs) = arms[0].body.node;
if exprs.len() == 1;
if let ExprCall(_, ref args) = exprs[0].node;
if args.len() == 2;
if let ExprPath(ref qpath) = args[1].node;
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, args[1].hir_id));
if match_def_path(cx.tcx, fun_def_id, &paths::DISPLAY_FMT_METHOD);
then {
let ty = walk_ptrs_ty(cx.tables.pat_ty(&pat[0]));
return ty.sty == ty::TyStr || match_type(cx, ty, &paths::STRING);
}
}
false
}

View file

@ -1,9 +1,9 @@
use consts::{constant_simple, Constant};
use rustc::lint::*;
use rustc::hir::*;
use rustc::lint::*;
use rustc_const_math::ConstInt;
use syntax::codemap::Span;
use utils::{in_macro, snippet, span_lint};
use syntax::attr::IntType::{SignedInt, UnsignedInt};
/// **What it does:** Checks for identity operations, e.g. `x + 0`.
///
@ -58,15 +58,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
}
}
fn all_ones(v: &ConstInt) -> bool {
match *v {
ConstInt::I8(i) => i == !0,
ConstInt::I16(i) => i == !0,
ConstInt::I32(i) => i == !0,
ConstInt::I64(i) => i == !0,
ConstInt::I128(i) => i == !0,
ConstInt::U8(i) => i == !0,
ConstInt::U16(i) => i == !0,
ConstInt::U32(i) => i == !0,
ConstInt::U64(i) => i == !0,
ConstInt::U128(i) => i == !0,
_ => false
}
}
#[allow(cast_possible_wrap)]
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 => all_ones(&v),
1 => v.to_u128_unchecked() == 1,
_ => unreachable!(),
} {

View file

@ -4,7 +4,7 @@
use rustc::lint::*;
use syntax::ast::*;
use utils::span_help_and_lint;
use utils::{span_help_and_lint, in_external_macro};
/// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
/// else branch.
@ -47,6 +47,9 @@ impl LintPass for IfNotElse {
impl EarlyLintPass for IfNotElse {
fn check_expr(&mut self, cx: &EarlyContext, item: &Expr) {
if in_external_macro(cx, item.span) {
return;
}
if let ExprKind::If(ref cond, _, Some(ref els)) = item.node {
if let ExprKind::Block(..) = els.node {
match cond.node {

View file

@ -34,22 +34,23 @@ impl LintPass for InvalidRef {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidRef {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if_let_chain!{[
let ExprCall(ref path, ref args) = expr.node,
let ExprPath(ref qpath) = path.node,
args.len() == 0,
let ty::TyRef(..) = cx.tables.expr_ty(expr).sty,
let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id)),
], {
let msg = if match_def_path(cx.tcx, def_id, &paths::MEM_ZEROED) | match_def_path(cx.tcx, def_id, &paths::INIT) {
ZERO_REF_SUMMARY
} else if match_def_path(cx.tcx, def_id, &paths::MEM_UNINIT) | match_def_path(cx.tcx, def_id, &paths::UNINIT) {
UNINIT_REF_SUMMARY
} else {
return;
};
span_help_and_lint(cx, INVALID_REF, expr.span, msg, HELP);
}}
if_chain! {
if let ExprCall(ref path, ref args) = expr.node;
if let ExprPath(ref qpath) = path.node;
if args.len() == 0;
if let ty::TyRef(..) = cx.tables.expr_ty(expr).sty;
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
then {
let msg = if match_def_path(cx.tcx, def_id, &paths::MEM_ZEROED) | match_def_path(cx.tcx, def_id, &paths::INIT) {
ZERO_REF_SUMMARY
} else if match_def_path(cx.tcx, def_id, &paths::MEM_UNINIT) | match_def_path(cx.tcx, def_id, &paths::UNINIT) {
UNINIT_REF_SUMMARY
} else {
return;
};
span_help_and_lint(cx, INVALID_REF, expr.span, msg, HELP);
}
}
return;
}
}

View file

@ -63,69 +63,70 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block) {
let mut it = block.stmts.iter().peekable();
while let Some(stmt) = it.next() {
if_let_chain! {[
let Some(expr) = it.peek(),
let hir::StmtDecl(ref decl, _) = stmt.node,
let hir::DeclLocal(ref decl) = decl.node,
let hir::PatKind::Binding(mode, canonical_id, ref name, None) = decl.pat.node,
let hir::StmtExpr(ref if_, _) = expr.node,
let hir::ExprIf(ref cond, ref then, ref else_) = if_.node,
!used_in_expr(cx, canonical_id, cond),
let hir::ExprBlock(ref then) = then.node,
let Some(value) = check_assign(cx, canonical_id, &*then),
!used_in_expr(cx, canonical_id, value),
], {
let span = stmt.span.to(if_.span);
let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
if let hir::ExprBlock(ref else_) = else_.node {
if let Some(default) = check_assign(cx, canonical_id, else_) {
(else_.stmts.len() > 1, default)
} else if let Some(ref default) = decl.init {
(true, &**default)
if_chain! {
if let Some(expr) = it.peek();
if let hir::StmtDecl(ref decl, _) = stmt.node;
if let hir::DeclLocal(ref decl) = decl.node;
if let hir::PatKind::Binding(mode, canonical_id, ref name, None) = decl.pat.node;
if let hir::StmtExpr(ref if_, _) = expr.node;
if let hir::ExprIf(ref cond, ref then, ref else_) = if_.node;
if !used_in_expr(cx, canonical_id, cond);
if let hir::ExprBlock(ref then) = then.node;
if let Some(value) = check_assign(cx, canonical_id, &*then);
if !used_in_expr(cx, canonical_id, value);
then {
let span = stmt.span.to(if_.span);
let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
if let hir::ExprBlock(ref else_) = else_.node {
if let Some(default) = check_assign(cx, canonical_id, else_) {
(else_.stmts.len() > 1, default)
} else if let Some(ref default) = decl.init {
(true, &**default)
} else {
continue;
}
} else {
continue;
}
} else if let Some(ref default) = decl.init {
(false, &**default)
} else {
continue;
}
} else if let Some(ref default) = decl.init {
(false, &**default)
} else {
continue;
};
let mutability = match mode {
BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
_ => "",
};
// FIXME: this should not suggest `mut` if we can detect that the variable is not
// use mutably after the `if`
let sug = format!(
"let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
mut=mutability,
name=name.node,
cond=snippet(cx, cond.span, "_"),
then=if then.stmts.len() > 1 { " ..;" } else { "" },
else=if default_multi_stmts { " ..;" } else { "" },
value=snippet(cx, value.span, "<value>"),
default=snippet(cx, default.span, "<default>"),
);
span_lint_and_then(cx,
USELESS_LET_IF_SEQ,
span,
"`if _ { .. } else { .. }` is an expression",
|db| {
db.span_suggestion(span,
"it is more idiomatic to write",
sug);
if !mutability.is_empty() {
db.note("you might not need `mut` at all");
}
});
}}
};
let mutability = match mode {
BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
_ => "",
};
// FIXME: this should not suggest `mut` if we can detect that the variable is not
// use mutably after the `if`
let sug = format!(
"let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
mut=mutability,
name=name.node,
cond=snippet(cx, cond.span, "_"),
then=if then.stmts.len() > 1 { " ..;" } else { "" },
else=if default_multi_stmts { " ..;" } else { "" },
value=snippet(cx, value.span, "<value>"),
default=snippet(cx, default.span, "<default>"),
);
span_lint_and_then(cx,
USELESS_LET_IF_SEQ,
span,
"`if _ { .. } else { .. }` is an expression",
|db| {
db.span_suggestion(span,
"it is more idiomatic to write",
sug);
if !mutability.is_empty() {
db.note("you might not need `mut` at all");
}
});
}
}
}
}
}
@ -138,14 +139,15 @@ struct UsedVisitor<'a, 'tcx: 'a> {
impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
if_let_chain! {[
let hir::ExprPath(ref qpath) = expr.node,
let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id),
self.id == local_id,
], {
self.used = true;
return;
}}
if_chain! {
if let hir::ExprPath(ref qpath) = expr.node;
if let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id);
if self.id == local_id;
then {
self.used = true;
return;
}
}
hir::intravisit::walk_expr(self, expr);
}
fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
@ -158,31 +160,32 @@ fn check_assign<'a, 'tcx>(
decl: ast::NodeId,
block: &'tcx hir::Block,
) -> Option<&'tcx hir::Expr> {
if_let_chain! {[
block.expr.is_none(),
let Some(expr) = block.stmts.iter().last(),
let hir::StmtSemi(ref expr, _) = expr.node,
let hir::ExprAssign(ref var, ref value) = expr.node,
let hir::ExprPath(ref qpath) = var.node,
let Def::Local(local_id) = cx.tables.qpath_def(qpath, var.hir_id),
decl == local_id,
], {
let mut v = UsedVisitor {
cx: cx,
id: decl,
used: false,
};
for s in block.stmts.iter().take(block.stmts.len()-1) {
hir::intravisit::walk_stmt(&mut v, s);
if v.used {
return None;
if_chain! {
if block.expr.is_none();
if let Some(expr) = block.stmts.iter().last();
if let hir::StmtSemi(ref expr, _) = expr.node;
if let hir::ExprAssign(ref var, ref value) = expr.node;
if let hir::ExprPath(ref qpath) = var.node;
if let Def::Local(local_id) = cx.tables.qpath_def(qpath, var.hir_id);
if decl == local_id;
then {
let mut v = UsedVisitor {
cx: cx,
id: decl,
used: false,
};
for s in block.stmts.iter().take(block.stmts.len()-1) {
hir::intravisit::walk_stmt(&mut v, s);
if v.used {
return None;
}
}
return Some(value);
}
return Some(value);
}}
}
None
}

View file

@ -11,6 +11,8 @@
#![feature(inclusive_range_syntax, range_contains)]
#![allow(unknown_lints, indexing_slicing, shadow_reuse, missing_docs_in_private_items)]
#![recursion_limit="256"]
#[macro_use]
extern crate rustc;
extern crate rustc_typeck;
@ -54,6 +56,9 @@ extern crate itertools;
extern crate pulldown_cmark;
extern crate url;
#[macro_use]
extern crate if_chain;
macro_rules! declare_restriction_lint {
{ pub $name:tt, $description:tt } => {
declare_lint! { pub $name, Allow, $description }
@ -88,6 +93,7 @@ pub mod entry;
pub mod enum_clike;
pub mod enum_glob_use;
pub mod enum_variants;
pub mod erasing_op;
pub mod eq_op;
pub mod escape;
pub mod eta_reduction;
@ -100,6 +106,7 @@ pub mod identity_conversion;
pub mod identity_op;
pub mod if_let_redundant_pattern_matching;
pub mod if_not_else;
pub mod fallible_impl_from;
pub mod infinite_iter;
pub mod int_plus_one;
pub mod invalid_ref;
@ -252,6 +259,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
reg.register_early_lint_pass(box needless_continue::NeedlessContinue);
reg.register_late_lint_pass(box eta_reduction::EtaPass);
reg.register_late_lint_pass(box identity_op::IdentityOp);
reg.register_late_lint_pass(box erasing_op::ErasingOp);
reg.register_early_lint_pass(box items_after_statements::ItemsAfterStatements);
reg.register_late_lint_pass(box mut_mut::MutMut);
reg.register_late_lint_pass(box mut_reference::UnnecessaryMutPassed);
@ -341,6 +349,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default());
reg.register_late_lint_pass(box types::ImplicitHasher);
reg.register_early_lint_pass(box const_static_lifetime::StaticConst);
reg.register_late_lint_pass(box fallible_impl_from::FallibleImplFrom);
reg.register_lint_group("clippy_restrictions", vec![
arithmetic::FLOAT_ARITHMETIC,
@ -446,6 +455,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
identity_conversion::IDENTITY_CONVERSION,
identity_op::IDENTITY_OP,
if_let_redundant_pattern_matching::IF_LET_REDUNDANT_PATTERN_MATCHING,
fallible_impl_from::FALLIBLE_IMPL_FROM,
infinite_iter::INFINITE_ITER,
invalid_ref::INVALID_REF,
is_unit_expr::UNIT_EXPR,

View file

@ -244,58 +244,60 @@ impl EarlyLintPass for LiteralDigitGrouping {
impl LiteralDigitGrouping {
fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
// Lint integral literals.
if_let_chain! {[
let LitKind::Int(..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let digit_info = DigitInfo::new(&src, false);
let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
});
}}
if_chain! {
if let LitKind::Int(..) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(firstch) = src.chars().next();
if char::to_digit(firstch, 10).is_some();
then {
let digit_info = DigitInfo::new(&src, false);
let _ = Self::do_lint(digit_info.digits).map_err(|warning_type| {
warning_type.display(&digit_info.grouping_hint(), cx, &lit.span)
});
}
}
// Lint floating-point literals.
if_let_chain! {[
let LitKind::Float(..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let digit_info = DigitInfo::new(&src, true);
// Separate digits into integral and fractional parts.
let parts: Vec<&str> = digit_info
.digits
.split_terminator('.')
.collect();
// Lint integral and fractional parts separately, and then check consistency of digit
// groups if both pass.
let _ = Self::do_lint(parts[0])
.map(|integral_group_size| {
if parts.len() > 1 {
// Lint the fractional part of literal just like integral part, but reversed.
let fractional_part = &parts[1].chars().rev().collect::<String>();
let _ = Self::do_lint(fractional_part)
.map(|fractional_group_size| {
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
parts[0].len(),
parts[1].len());
if !consistent {
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
cx,
&lit.span);
}
})
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
cx,
&lit.span));
}
})
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
}}
if_chain! {
if let LitKind::Float(..) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(firstch) = src.chars().next();
if char::to_digit(firstch, 10).is_some();
then {
let digit_info = DigitInfo::new(&src, true);
// Separate digits into integral and fractional parts.
let parts: Vec<&str> = digit_info
.digits
.split_terminator('.')
.collect();
// Lint integral and fractional parts separately, and then check consistency of digit
// groups if both pass.
let _ = Self::do_lint(parts[0])
.map(|integral_group_size| {
if parts.len() > 1 {
// Lint the fractional part of literal just like integral part, but reversed.
let fractional_part = &parts[1].chars().rev().collect::<String>();
let _ = Self::do_lint(fractional_part)
.map(|fractional_group_size| {
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
parts[0].len(),
parts[1].len());
if !consistent {
WarningType::InconsistentDigitGrouping.display(&digit_info.grouping_hint(),
cx,
&lit.span);
}
})
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(),
cx,
&lit.span));
}
})
.map_err(|warning_type| warning_type.display(&digit_info.grouping_hint(), cx, &lit.span));
}
}
}
/// Given the sizes of the digit groups of both integral and fractional

View file

@ -2,7 +2,7 @@ use itertools::Itertools;
use reexport::*;
use rustc::hir::*;
use rustc::hir::def::Def;
use rustc::hir::def_id;
use rustc::hir::def_id;
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::*;
@ -363,7 +363,7 @@ impl LintPass for Pass {
EMPTY_LOOP,
WHILE_LET_ON_ITERATOR,
FOR_KV_MAP,
NEVER_LOOP,
NEVER_LOOP,
MUT_RANGE_BOUND
)
}
@ -611,16 +611,17 @@ fn check_for_loop<'a, 'tcx>(
}
fn same_var<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &Expr, var: ast::NodeId) -> bool {
if_let_chain! {[
let ExprPath(ref qpath) = expr.node,
let QPath::Resolved(None, ref path) = *qpath,
path.segments.len() == 1,
let Def::Local(local_id) = cx.tables.qpath_def(qpath, expr.hir_id),
if_chain! {
if let ExprPath(ref qpath) = expr.node;
if let QPath::Resolved(None, ref path) = *qpath;
if path.segments.len() == 1;
if let Def::Local(local_id) = cx.tables.qpath_def(qpath, expr.hir_id);
// our variable!
local_id == var
], {
return true;
}}
if local_id == var;
then {
return true;
}
}
false
}
@ -725,14 +726,15 @@ fn fetch_cloned_fixed_offset_var<'a, 'tcx>(
expr: &Expr,
var: ast::NodeId,
) -> Option<FixedOffsetVar> {
if_let_chain! {[
let ExprMethodCall(ref method, _, ref args) = expr.node,
method.name == "clone",
args.len() == 1,
let Some(arg) = args.get(0),
], {
return get_fixed_offset_var(cx, arg, var);
}}
if_chain! {
if let ExprMethodCall(ref method, _, ref args) = expr.node;
if method.name == "clone";
if args.len() == 1;
if let Some(arg) = args.get(0);
then {
return get_fixed_offset_var(cx, arg, var);
}
}
get_fixed_offset_var(cx, expr, var)
}
@ -821,19 +823,20 @@ fn detect_manual_memcpy<'a, 'tcx>(
};
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()
};
}}
if_chain! {
if let ExprMethodCall(ref method, _, ref len_args) = end.node;
if method.name == "len";
if len_args.len() == 1;
if let Some(arg) = len_args.get(0);
if snippet(cx, arg.span, "??") == var_name;
then {
return if offset.negate {
format!("({} - {})", snippet(cx, end.span, "<src>.len()"), offset.value)
} else {
"".to_owned()
};
}
}
let end_str = match limits {
ast::RangeLimits::Closed => {
@ -1003,16 +1006,17 @@ fn check_for_loop_range<'a, 'tcx>(
}
fn is_len_call(expr: &Expr, var: &Name) -> bool {
if_let_chain! {[
let ExprMethodCall(ref method, _, ref len_args) = expr.node,
len_args.len() == 1,
method.name == "len",
let ExprPath(QPath::Resolved(_, ref path)) = len_args[0].node,
path.segments.len() == 1,
path.segments[0].name == *var
], {
return true;
}}
if_chain! {
if let ExprMethodCall(ref method, _, ref len_args) = expr.node;
if len_args.len() == 1;
if method.name == "len";
if let ExprPath(QPath::Resolved(_, ref path)) = len_args[0].node;
if path.segments.len() == 1;
if path.segments[0].name == *var;
then {
return true;
}
}
false
}
@ -1124,7 +1128,12 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
let fn_arg_tys = method_type.fn_sig(cx.tcx).inputs();
assert_eq!(fn_arg_tys.skip_binder().len(), 1);
if fn_arg_tys.skip_binder()[0].is_region_ptr() {
lint_iter_method(cx, args, arg, method_name);
match cx.tables.expr_ty(&args[0]).sty {
// If the length is greater than 32 no traits are implemented for array and
// therefore we cannot use `&`.
ty::TypeVariants::TyArray(_, size) if const_to_u64(size) > 32 => (),
_ => lint_iter_method(cx, args, arg, method_name)
};
} else {
let object = snippet(cx, args[0].span, "_");
span_lint_and_sugg(
@ -1315,7 +1324,7 @@ struct MutateDelegate {
impl<'tcx> Delegate<'tcx> for MutateDelegate {
fn consume(&mut self, _: NodeId, _: Span, _: cmt<'tcx>, _: ConsumeMode) {
}
fn matched_pat(&mut self, _: &Pat, _: cmt<'tcx>, _: MatchMode) {
}
@ -1374,22 +1383,24 @@ fn mut_warn_with_span(cx: &LateContext, span: Option<Span>) {
}
fn check_for_mutability(cx: &LateContext, bound: &Expr) -> Option<NodeId> {
if_let_chain! {[
let ExprPath(ref qpath) = bound.node,
let QPath::Resolved(None, _) = *qpath,
], {
let def = cx.tables.qpath_def(qpath, bound.hir_id);
if let Def::Local(node_id) = def {
let node_str = cx.tcx.hir.get(node_id);
if_let_chain! {[
let map::Node::NodeBinding(pat) = node_str,
let PatKind::Binding(bind_ann, _, _, _) = pat.node,
let BindingAnnotation::Mutable = bind_ann,
], {
return Some(node_id);
}}
if_chain! {
if let ExprPath(ref qpath) = bound.node;
if let QPath::Resolved(None, _) = *qpath;
then {
let def = cx.tables.qpath_def(qpath, bound.hir_id);
if let Def::Local(node_id) = def {
let node_str = cx.tcx.hir.get(node_id);
if_chain! {
if let map::Node::NodeBinding(pat) = node_str;
if let PatKind::Binding(bind_ann, _, _, _) = pat.node;
if let BindingAnnotation::Mutable = bind_ann;
then {
return Some(node_id);
}
}
}
}
}}
}
None
}
@ -1476,67 +1487,69 @@ struct VarVisitor<'a, 'tcx: 'a> {
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr) {
if_let_chain! {[
if_chain! {
// an index op
let ExprIndex(ref seqexpr, ref idx) = expr.node,
if let ExprIndex(ref seqexpr, ref idx) = expr.node;
// 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 index_used_directly = same_var(self.cx, idx, self.var);
let index_used = index_used_directly || {
let mut used_visitor = LocalUsedVisitor {
cx: self.cx,
local: self.var,
used: false,
if let ExprPath(ref seqpath) = seqexpr.node;
if let QPath::Resolved(None, ref seqvar) = *seqpath;
if seqvar.segments.len() == 1;
then {
let index_used_directly = same_var(self.cx, idx, self.var);
let index_used = index_used_directly || {
let mut used_visitor = LocalUsedVisitor {
cx: self.cx,
local: self.var,
used: false,
};
walk_expr(&mut used_visitor, idx);
used_visitor.used
};
walk_expr(&mut used_visitor, idx);
used_visitor.used
};
if index_used {
let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
match def {
Def::Local(node_id) | Def::Upvar(node_id, ..) => {
let hir_id = self.cx.tcx.hir.node_to_hir_id(node_id);
if index_used {
let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
match def {
Def::Local(node_id) | Def::Upvar(node_id, ..) => {
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));
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].name, Some(extent));
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));
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].name, Some(extent));
}
return; // no need to walk further *on the variable*
}
return; // no need to walk further *on the variable*
}
Def::Static(..) | Def::Const(..) => {
self.indexed.insert(seqvar.segments[0].name, None);
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].name, None);
Def::Static(..) | Def::Const(..) => {
self.indexed.insert(seqvar.segments[0].name, None);
if index_used_directly {
self.indexed_directly.insert(seqvar.segments[0].name, None);
}
return; // no need to walk further *on the variable*
}
return; // no need to walk further *on the variable*
_ => (),
}
_ => (),
}
}
}}
}
if_let_chain! {[
if_chain! {
// directly using a variable
let ExprPath(ref qpath) = expr.node,
let QPath::Resolved(None, ref path) = *qpath,
path.segments.len() == 1,
let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id),
], {
if local_id == self.var {
// we are not indexing anything, record that
self.nonindex = true;
} else {
// not the correct variable, but still a variable
self.referenced.insert(path.segments[0].name);
if let ExprPath(ref qpath) = expr.node;
if let QPath::Resolved(None, ref path) = *qpath;
if path.segments.len() == 1;
if let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id);
then {
if local_id == self.var {
// we are not indexing anything, record that
self.nonindex = true;
} else {
// not the correct variable, but still a variable
self.referenced.insert(path.segments[0].name);
}
}
}}
}
walk_expr(self, expr);
}
@ -1845,12 +1858,13 @@ fn is_conditional(expr: &Expr) -> bool {
}
fn is_nested(cx: &LateContext, match_expr: &Expr, iter_expr: &Expr) -> bool {
if_let_chain! {[
let Some(loop_block) = get_enclosing_block(cx, match_expr.id),
let Some(map::Node::NodeExpr(loop_expr)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(loop_block.id)),
], {
return is_loop_nested(cx, loop_expr, iter_expr)
}}
if_chain! {
if let Some(loop_block) = get_enclosing_block(cx, match_expr.id);
if let Some(map::Node::NodeExpr(loop_expr)) = cx.tcx.hir.find(cx.tcx.hir.get_parent_node(loop_block.id));
then {
return is_loop_nested(cx, loop_expr, iter_expr)
}
}
false
}

View file

@ -35,22 +35,36 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
let body = cx.tcx.hir.body(closure_eid);
let closure_expr = remove_blocks(&body.value);
let ty = cx.tables.pat_ty(&body.arguments[0].pat);
if_let_chain! {[
if_chain! {
// nothing special in the argument, besides reference bindings
// (e.g. .map(|&x| x) )
let Some(first_arg) = iter_input_pats(decl, body).next(),
let Some(arg_ident) = get_arg_name(&first_arg.pat),
if let Some(first_arg) = iter_input_pats(decl, body).next();
if let Some(arg_ident) = get_arg_name(&first_arg.pat);
// the method is being called on a known type (option or iterator)
let Some(type_name) = get_type_name(cx, expr, &args[0])
], {
// look for derefs, for .map(|x| *x)
if only_derefs(cx, &*closure_expr, arg_ident) &&
// .cloned() only removes one level of indirection, don't lint on more
walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
{
// the argument is not an &mut T
if let ty::TyRef(_, tam) = ty.sty {
if tam.mutbl == MutImmutable {
if let Some(type_name) = get_type_name(cx, expr, &args[0]);
then {
// look for derefs, for .map(|x| *x)
if only_derefs(cx, &*closure_expr, arg_ident) &&
// .cloned() only removes one level of indirection, don't lint on more
walk_ptrs_ty_depth(cx.tables.pat_ty(&first_arg.pat)).1 == 1
{
// the argument is not an &mut T
if let ty::TyRef(_, tam) = ty.sty {
if tam.mutbl == MutImmutable {
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, "..")));
}
}
}
// explicit clone() calls ( .map(|x| x.clone()) )
else if let ExprMethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
if clone_call.name == "clone" &&
clone_args.len() == 1 &&
match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
expr_eq_name(&clone_args[0], arg_ident)
{
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),
@ -58,20 +72,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
}
}
// explicit clone() calls ( .map(|x| x.clone()) )
else if let ExprMethodCall(ref clone_call, _, ref clone_args) = closure_expr.node {
if clone_call.name == "clone" &&
clone_args.len() == 1 &&
match_trait_method(cx, closure_expr, &paths::CLONE_TRAIT) &&
expr_eq_name(&clone_args[0], arg_ident)
{
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("_");

View file

@ -343,21 +343,22 @@ fn check_wild_err_arm(cx: &LateContext, ex: &Expr, arms: &[Arm]) {
for arm in arms {
if let PatKind::TupleStruct(ref path, ref inner, _) = arm.pats[0].node {
let path_str = print::to_string(print::NO_ANN, |s| s.print_qpath(path, false));
if_let_chain! {[
path_str == "Err",
inner.iter().any(|pat| pat.node == PatKind::Wild),
let ExprBlock(ref block) = arm.body.node,
is_panic_block(block)
], {
// `Err(_)` arm with `panic!` found
span_note_and_lint(cx,
MATCH_WILD_ERR_ARM,
arm.pats[0].span,
"Err(_) will match all errors, maybe not a good idea",
arm.pats[0].span,
"to remove this warning, match each error seperately \
or use unreachable macro");
}}
if_chain! {
if path_str == "Err";
if inner.iter().any(|pat| pat.node == PatKind::Wild);
if let ExprBlock(ref block) = arm.body.node;
if is_panic_block(block);
then {
// `Err(_)` arm with `panic!` found
span_note_and_lint(cx,
MATCH_WILD_ERR_ARM,
arm.pats[0].span,
"Err(_) will match all errors, maybe not a good idea",
arm.pats[0].span,
"to remove this warning, match each error seperately \
or use unreachable macro");
}
}
}
}
}
@ -428,24 +429,26 @@ fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm], id: NodeI
} else {
[].iter()
}.filter_map(|pat| {
if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs),
let Ok(rhs) = constcx.eval(rhs)
], {
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
RangeEnd::Excluded => Bound::Excluded(rhs),
};
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}}
if_chain! {
if let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node;
if let Ok(lhs) = constcx.eval(lhs);
if let Ok(rhs) = constcx.eval(rhs);
then {
let rhs = match *range_end {
RangeEnd::Included => Bound::Included(rhs),
RangeEnd::Excluded => Bound::Excluded(rhs),
};
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}
}
if_let_chain! {[
let PatKind::Lit(ref value) = pat.node,
let Ok(value) = constcx.eval(value)
], {
return Some(SpannedRange { span: pat.span, node: (value, Bound::Included(value)) });
}}
if_chain! {
if let PatKind::Lit(ref value) = pat.node;
if let Ok(value) = constcx.eval(value);
then {
return Some(SpannedRange { span: pat.span, node: (value, Bound::Included(value)) });
}
}
None
})

View file

@ -581,6 +581,29 @@ declare_lint! {
"using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
}
/// **What it does:** Checks for usage of `.as_ref()` or `.as_mut()` where the
/// types before and after the call are the same.
///
/// **Why is this bad?** The call is unnecessary.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x: &[i32] = &[1,2,3,4,5];
/// do_stuff(x.as_ref());
/// ```
/// The correct use would be:
/// ```rust
/// let x: &[i32] = &[1,2,3,4,5];
/// do_stuff(x);
/// ```
declare_lint! {
pub USELESS_ASREF,
Warn,
"using `as_ref` where the types before and after the call are the same"
}
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(
@ -609,8 +632,8 @@ impl LintPass for Pass {
ITER_SKIP_NEXT,
GET_UNWRAP,
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT
)
ITER_CLONED_COLLECT,
USELESS_ASREF)
}
}
@ -669,6 +692,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
lint_iter_skip_next(cx, expr);
} else if let Some(arglists) = method_chain_args(expr, &["cloned", "collect"]) {
lint_iter_cloned_collect(cx, expr, arglists[0]);
} else if let Some(arglists) = method_chain_args(expr, &["as_ref"]) {
lint_asref(cx, expr, "as_ref", arglists[0]);
} else if let Some(arglists) = method_chain_args(expr, &["as_mut"]) {
lint_asref(cx, expr, "as_mut", arglists[0]);
}
lint_or_fun_call(cx, expr, &method_call.name.as_str(), args);
@ -708,63 +735,65 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
let name = implitem.name;
let parent = cx.tcx.hir.get_parent(implitem.id);
let item = cx.tcx.hir.expect_item(parent);
if_let_chain! {[
let hir::ImplItemKind::Method(ref sig, id) = implitem.node,
let Some(first_arg_ty) = sig.decl.inputs.get(0),
let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir.body(id)).next(),
let hir::ItemImpl(_, _, _, _, None, ref self_ty, _) = item.node,
], {
if implitem.vis == hir::Visibility::Public ||
implitem.vis.is_pub_restricted() {
// check missing trait implementations
for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
if name == method_name &&
sig.decl.inputs.len() == n_args &&
out_type.matches(&sig.decl.output) &&
self_kind.matches(first_arg_ty, first_arg, self_ty, false, &sig.generics) {
span_lint(cx, SHOULD_IMPLEMENT_TRAIT, implitem.span, &format!(
"defining a method called `{}` on this type; consider implementing \
the `{}` trait or choosing a less ambiguous name", name, trait_name));
if_chain! {[
if let hir::ImplItemKind::Method(ref sig, id) = implitem.node;
if let Some(first_arg_ty) = sig.decl.inputs.get(0);
if let Some(first_arg) = iter_input_pats(&sig.decl, cx.tcx.hir.body(id)).next();
if let hir::ItemImpl(_, _, _, _, None, ref self_ty, _) = item.node;
then {
if implitem.vis == hir::Visibility::Public ||
implitem.vis.is_pub_restricted() {
// check missing trait implementations
for &(method_name, n_args, self_kind, out_type, trait_name) in &TRAIT_METHODS {
if name == method_name &&
sig.decl.inputs.len() == n_args &&
out_type.matches(&sig.decl.output) &&
self_kind.matches(first_arg_ty, first_arg, self_ty, false, &sig.generics) {
span_lint(cx, SHOULD_IMPLEMENT_TRAIT, implitem.span, &format!(
"defining a method called `{}` on this type; consider implementing \
the `{}` trait or choosing a less ambiguous name", name, trait_name));
}
}
}
}
// check conventions w.r.t. conversion method names and predicates
let def_id = cx.tcx.hir.local_def_id(item.id);
let ty = cx.tcx.type_of(def_id);
let is_copy = is_copy(cx, ty);
for &(ref conv, self_kinds) in &CONVENTIONS {
if_let_chain! {[
conv.check(&name.as_str()),
!self_kinds.iter().any(|k| k.matches(first_arg_ty, first_arg, self_ty, is_copy, &sig.generics)),
], {
let lint = if item.vis == hir::Visibility::Public {
WRONG_PUB_SELF_CONVENTION
} else {
WRONG_SELF_CONVENTION
};
// check conventions w.r.t. conversion method names and predicates
let def_id = cx.tcx.hir.local_def_id(item.id);
let ty = cx.tcx.type_of(def_id);
let is_copy = is_copy(cx, ty);
for &(ref conv, self_kinds) in &CONVENTIONS {
if_chain! {
if conv.check(&name.as_str());
if !self_kinds.iter().any(|k| k.matches(first_arg_ty, first_arg, self_ty, is_copy, &sig.generics));
then {
let lint = if item.vis == hir::Visibility::Public {
WRONG_PUB_SELF_CONVENTION
} else {
WRONG_SELF_CONVENTION
};
span_lint(cx,
lint,
first_arg.pat.span,
&format!("methods called `{}` usually take {}; consider choosing a less \
ambiguous name",
conv,
&self_kinds.iter()
.map(|k| k.description())
.collect::<Vec<_>>()
.join(" or ")));
}
}
}
let ret_ty = return_ty(cx, implitem.id);
if name == "new" &&
!ret_ty.walk().any(|t| same_tys(cx, t, ty)) {
span_lint(cx,
lint,
first_arg.pat.span,
&format!("methods called `{}` usually take {}; consider choosing a less \
ambiguous name",
conv,
&self_kinds.iter()
.map(|k| k.description())
.collect::<Vec<_>>()
.join(" or ")));
}}
NEW_RET_NO_SELF,
implitem.span,
"methods called `new` usually return `Self`");
}
}
let ret_ty = return_ty(cx, implitem.id);
if name == "new" &&
!ret_ty.walk().any(|t| same_tys(cx, t, ty)) {
span_lint(cx,
NEW_RET_NO_SELF,
implitem.span,
"methods called `new` usually return `Self`");
}
}}
}
}
}
@ -990,20 +1019,21 @@ fn lint_extend(cx: &LateContext, expr: &hir::Expr, args: &[hir::Expr]) {
}
fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwrap: &hir::Expr) {
if_let_chain!{[
let hir::ExprCall(ref fun, ref args) = new.node,
args.len() == 1,
let hir::ExprPath(ref path) = fun.node,
let Def::Method(did) = cx.tables.qpath_def(path, fun.hir_id),
match_def_path(cx.tcx, did, &paths::CSTRING_NEW)
], {
span_lint_and_then(cx, TEMPORARY_CSTRING_AS_PTR, expr.span,
"you are getting the inner pointer of a temporary `CString`",
|db| {
db.note("that pointer will be invalid outside this expression");
db.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
});
}}
if_chain! {
if let hir::ExprCall(ref fun, ref args) = new.node;
if args.len() == 1;
if let hir::ExprPath(ref path) = fun.node;
if let Def::Method(did) = cx.tables.qpath_def(path, fun.hir_id);
if match_def_path(cx.tcx, did, &paths::CSTRING_NEW);
then {
span_lint_and_then(cx, TEMPORARY_CSTRING_AS_PTR, expr.span,
"you are getting the inner pointer of a temporary `CString`",
|db| {
db.note("that pointer will be invalid outside this expression");
db.span_help(unwrap.span, "assign the `CString` to a variable to extend its lifetime");
});
}
}
}
fn lint_iter_cloned_collect(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr]) {
@ -1403,33 +1433,34 @@ fn lint_binary_expr_with_method_call<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, i
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_NEXT_CMP` lints.
fn lint_chars_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo, chain_methods: &[&str], lint: &'static Lint, suggest: &str) -> bool {
if_let_chain! {[
let Some(args) = method_chain_args(info.chain, chain_methods),
let hir::ExprCall(ref fun, ref arg_char) = info.other.node,
arg_char.len() == 1,
let hir::ExprPath(ref qpath) = fun.node,
let Some(segment) = single_segment_path(qpath),
segment.name == "Some"
], {
let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
if self_ty.sty != ty::TyStr {
return false;
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprCall(ref fun, ref arg_char) = info.other.node;
if arg_char.len() == 1;
if let hir::ExprPath(ref qpath) = fun.node;
if let Some(segment) = single_segment_path(qpath);
if segment.name == "Some";
then {
let self_ty = walk_ptrs_ty(cx.tables.expr_ty_adjusted(&args[0][0]));
if self_ty.sty != ty::TyStr {
return false;
}
span_lint_and_sugg(cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
snippet(cx, args[0][0].span, "_"),
suggest,
snippet(cx, arg_char[0].span, "_")));
return true;
}
span_lint_and_sugg(cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
snippet(cx, args[0][0].span, "_"),
suggest,
snippet(cx, arg_char[0].span, "_")));
return true;
}}
}
false
}
@ -1450,26 +1481,27 @@ fn lint_chars_last_cmp<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprIn
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
fn lint_chars_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &BinaryExprInfo, chain_methods: &[&str], lint: &'static Lint, suggest: &str) -> bool {
if_let_chain! {[
let Some(args) = method_chain_args(info.chain, chain_methods),
let hir::ExprLit(ref lit) = info.other.node,
let ast::LitKind::Char(c) = lit.node,
], {
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
snippet(cx, args[0][0].span, "_"),
suggest,
c)
);
return true;
}}
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprLit(ref lit) = info.other.node;
if let ast::LitKind::Char(c) = lit.node;
then {
span_lint_and_sugg(
cx,
lint,
info.expr.span,
&format!("you should use the `{}` method", suggest),
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
snippet(cx, args[0][0].span, "_"),
suggest,
c)
);
return true;
}
}
false
}
@ -1507,6 +1539,30 @@ fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx hi
}
}
/// Checks for the `USELESS_ASREF` lint.
fn lint_asref(cx: &LateContext, expr: &hir::Expr, call_name: &str, as_ref_args: &[hir::Expr]) {
// when we get here, we've already checked that the call name is "as_ref" or "as_mut"
// check if the call is to the actual `AsRef` or `AsMut` trait
if match_trait_method(cx, expr, &paths::ASREF_TRAIT) || match_trait_method(cx, expr, &paths::ASMUT_TRAIT) {
// check if the type after `as_ref` or `as_mut` is the same as before
let recvr = &as_ref_args[0];
let rcv_ty = cx.tables.expr_ty(recvr);
let res_ty = cx.tables.expr_ty(expr);
let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
span_lint_and_sugg(
cx,
USELESS_ASREF,
expr.span,
&format!("this call to `{}` does nothing", call_name),
"try this",
snippet(cx, recvr.span, "_").into_owned(),
);
}
}
}
/// Given a `Result<T, E>` type, return its error type (`E`).
fn get_error_type<'a>(cx: &LateContext, ty: Ty<'a>) -> Option<Ty<'a>> {
if let ty::TyAdt(_, substs) = ty.sty {

View file

@ -251,55 +251,57 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, s: &'tcx Stmt) {
if_let_chain! {[
let StmtDecl(ref d, _) = s.node,
let DeclLocal(ref l) = d.node,
let PatKind::Binding(an, _, i, None) = l.pat.node,
let Some(ref init) = l.init
], {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
let init = Sugg::hir(cx, init, "..");
let (mutopt,initref) = if an == BindingAnnotation::RefMut {
("mut ", init.mut_addr())
} else {
("", init.addr())
};
let tyopt = if let Some(ref ty) = l.ty {
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
} else {
"".to_owned()
};
span_lint_and_then(cx,
TOPLEVEL_REF_ARG,
l.pat.span,
"`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
|db| {
db.span_suggestion(s.span,
"try",
format!("let {name}{tyopt} = {initref};",
name=snippet(cx, i.span, "_"),
tyopt=tyopt,
initref=initref));
}
);
if_chain! {
if let StmtDecl(ref d, _) = s.node;
if let DeclLocal(ref l) = d.node;
if let PatKind::Binding(an, _, i, None) = l.pat.node;
if let Some(ref init) = l.init;
then {
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut {
let init = Sugg::hir(cx, init, "..");
let (mutopt,initref) = if an == BindingAnnotation::RefMut {
("mut ", init.mut_addr())
} else {
("", init.addr())
};
let tyopt = if let Some(ref ty) = l.ty {
format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, "_"))
} else {
"".to_owned()
};
span_lint_and_then(cx,
TOPLEVEL_REF_ARG,
l.pat.span,
"`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
|db| {
db.span_suggestion(s.span,
"try",
format!("let {name}{tyopt} = {initref};",
name=snippet(cx, i.span, "_"),
tyopt=tyopt,
initref=initref));
}
);
}
}
}};
if_let_chain! {[
let StmtSemi(ref expr, _) = s.node,
let Expr_::ExprBinary(ref binop, ref a, ref b) = expr.node,
binop.node == BiAnd || binop.node == BiOr,
let Some(sugg) = Sugg::hir_opt(cx, a),
], {
span_lint_and_then(cx,
SHORT_CIRCUIT_STATEMENT,
s.span,
"boolean short circuit operator in statement may be clearer using an explicit test",
|db| {
let sugg = if binop.node == BiOr { !sugg } else { sugg };
db.span_suggestion(s.span, "replace it with",
format!("if {} {{ {}; }}", sugg, &snippet(cx, b.span, "..")));
});
}};
};
if_chain! {
if let StmtSemi(ref expr, _) = s.node;
if let Expr_::ExprBinary(ref binop, ref a, ref b) = expr.node;
if binop.node == BiAnd || binop.node == BiOr;
if let Some(sugg) = Sugg::hir_opt(cx, a);
then {
span_lint_and_then(cx,
SHORT_CIRCUIT_STATEMENT,
s.span,
"boolean short circuit operator in statement may be clearer using an explicit test",
|db| {
let sugg = if binop.node == BiOr { !sugg } else { sugg };
db.span_suggestion(s.span, "replace it with",
format!("if {} {{ {}; }}", sugg, &snippet(cx, b.span, "..")));
});
}
};
}
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
@ -582,17 +584,18 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
}
fn check_cast(cx: &LateContext, span: Span, e: &Expr, ty: &Ty) {
if_let_chain! {[
let TyPtr(MutTy { mutbl, .. }) = ty.node,
let ExprLit(ref lit) = e.node,
let LitKind::Int(value, ..) = lit.node,
value == 0,
!in_constant(cx, e.id)
], {
let msg = match mutbl {
Mutability::MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::MutImmutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}}
if_chain! {
if let TyPtr(MutTy { mutbl, .. }) = ty.node;
if let ExprLit(ref lit) = e.node;
if let LitKind::Int(value, ..) = lit.node;
if value == 0;
if !in_constant(cx, e.id);
then {
let msg = match mutbl {
Mutability::MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
Mutability::MutImmutable => "`0 as *const _` detected. Consider using `ptr::null()`",
};
span_lint(cx, ZERO_PTR, span, msg);
}
}
}

View file

@ -322,100 +322,103 @@ impl EarlyLintPass for MiscEarly {
fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
for w in block.stmts.windows(2) {
if_let_chain! {[
let StmtKind::Local(ref local) = w[0].node,
let Option::Some(ref t) = local.init,
let ExprKind::Closure(_, _, _, _) = t.node,
let PatKind::Ident(_, sp_ident, _) = local.pat.node,
let StmtKind::Semi(ref second) = w[1].node,
let ExprKind::Assign(_, ref call) = second.node,
let ExprKind::Call(ref closure, _) = call.node,
let ExprKind::Path(_, ref path) = closure.node
], {
if sp_ident.node == (&path.segments[0]).identifier {
span_lint(
cx,
REDUNDANT_CLOSURE_CALL,
second.span,
"Closure called just once immediately after it was declared",
);
if_chain! {
if let StmtKind::Local(ref local) = w[0].node;
if let Option::Some(ref t) = local.init;
if let ExprKind::Closure(_, _, _, _) = t.node;
if let PatKind::Ident(_, sp_ident, _) = local.pat.node;
if let StmtKind::Semi(ref second) = w[1].node;
if let ExprKind::Assign(_, ref call) = second.node;
if let ExprKind::Call(ref closure, _) = call.node;
if let ExprKind::Path(_, ref path) = closure.node;
then {
if sp_ident.node == (&path.segments[0]).identifier {
span_lint(
cx,
REDUNDANT_CLOSURE_CALL,
second.span,
"Closure called just once immediately after it was declared",
);
}
}
}}
}
}
}
}
impl MiscEarly {
fn check_lit(&self, cx: &EarlyContext, lit: &Lit) {
if_let_chain! {[
let LitKind::Int(value, ..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let mut prev = '\0';
for ch in src.chars() {
if ch == 'i' || ch == 'u' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"integer type suffix should be separated by an underscore");
}
break;
}
prev = ch;
}
if src.starts_with("0x") {
let mut seen = (false, false);
if_chain! {
if let LitKind::Int(value, ..) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(firstch) = src.chars().next();
if char::to_digit(firstch, 10).is_some();
then {
let mut prev = '\0';
for ch in src.chars() {
match ch {
'a' ... 'f' => seen.0 = true,
'A' ... 'F' => seen.1 = true,
'i' | 'u' => break, // start of suffix already
_ => ()
if ch == 'i' || ch == 'u' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"integer type suffix should be separated by an underscore");
}
break;
}
prev = ch;
}
if seen.0 && seen.1 {
span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
"inconsistent casing in hexadecimal literal");
}
} else if src.starts_with("0b") || src.starts_with("0o") {
/* nothing to do */
} else if value != 0 && src.starts_with('0') {
span_lint_and_then(cx,
ZERO_PREFIXED_LITERAL,
lit.span,
"this is a decimal constant",
|db| {
db.span_suggestion(
lit.span,
"if you mean to use a decimal constant, remove the `0` to remove confusion",
src.trim_left_matches(|c| c == '_' || c == '0').to_string(),
);
db.span_suggestion(
lit.span,
"if you mean to use an octal constant, use `0o`",
format!("0o{}", src.trim_left_matches(|c| c == '_' || c == '0')),
);
});
}
}}
if_let_chain! {[
let LitKind::Float(..) = lit.node,
let Some(src) = snippet_opt(cx, lit.span),
let Some(firstch) = src.chars().next(),
char::to_digit(firstch, 10).is_some()
], {
let mut prev = '\0';
for ch in src.chars() {
if ch == 'f' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"float type suffix should be separated by an underscore");
if src.starts_with("0x") {
let mut seen = (false, false);
for ch in src.chars() {
match ch {
'a' ... 'f' => seen.0 = true,
'A' ... 'F' => seen.1 = true,
'i' | 'u' => break, // start of suffix already
_ => ()
}
}
break;
if seen.0 && seen.1 {
span_lint(cx, MIXED_CASE_HEX_LITERALS, lit.span,
"inconsistent casing in hexadecimal literal");
}
} else if src.starts_with("0b") || src.starts_with("0o") {
/* nothing to do */
} else if value != 0 && src.starts_with('0') {
span_lint_and_then(cx,
ZERO_PREFIXED_LITERAL,
lit.span,
"this is a decimal constant",
|db| {
db.span_suggestion(
lit.span,
"if you mean to use a decimal constant, remove the `0` to remove confusion",
src.trim_left_matches(|c| c == '_' || c == '0').to_string(),
);
db.span_suggestion(
lit.span,
"if you mean to use an octal constant, use `0o`",
format!("0o{}", src.trim_left_matches(|c| c == '_' || c == '0')),
);
});
}
prev = ch;
}
}}
}
if_chain! {
if let LitKind::Float(..) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span);
if let Some(firstch) = src.chars().next();
if char::to_digit(firstch, 10).is_some();
then {
let mut prev = '\0';
for ch in src.chars() {
if ch == 'f' {
if prev != '_' {
span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
"float type suffix should be separated by an underscore");
}
break;
}
prev = ch;
}
}
}
}
}

View file

@ -75,25 +75,26 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
if in_macro(pat.span) {
return;
}
if_let_chain! {[
let PatKind::Binding(BindingAnnotation::Ref, _, name, _) = pat.node,
let ty::TyRef(_, ref tam) = cx.tables.pat_ty(pat).sty,
tam.mutbl == MutImmutable,
let ty::TyRef(_, ref tam) = tam.ty.sty,
if_chain! {
if let PatKind::Binding(BindingAnnotation::Ref, _, name, _) = pat.node;
if let ty::TyRef(_, ref tam) = cx.tables.pat_ty(pat).sty;
if tam.mutbl == MutImmutable;
if let ty::TyRef(_, ref tam) = tam.ty.sty;
// only lint immutable refs, because borrowed `&mut T` cannot be moved out
tam.mutbl == MutImmutable,
], {
span_lint_and_then(
cx,
NEEDLESS_BORROW,
pat.span,
"this pattern creates a reference to a reference",
|db| {
if let Some(snippet) = snippet_opt(cx, name.span) {
db.span_suggestion(pat.span, "change this to", snippet);
if tam.mutbl == MutImmutable;
then {
span_lint_and_then(
cx,
NEEDLESS_BORROW,
pat.span,
"this pattern creates a reference to a reference",
|db| {
if let Some(snippet) = snippet_opt(cx, name.span) {
db.span_suggestion(pat.span, "change this to", snippet);
}
}
}
)
}}
)
}
}
}
}

View file

@ -64,19 +64,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrowedRef {
return;
}
if_let_chain! {[
if_chain! {
// Only lint immutable refs, because `&mut ref T` may be useful.
let PatKind::Ref(ref sub_pat, MutImmutable) = pat.node,
if let PatKind::Ref(ref sub_pat, MutImmutable) = pat.node;
// Check sub_pat got a `ref` keyword (excluding `ref mut`).
let PatKind::Binding(BindingAnnotation::Ref, _, spanned_name, ..) = sub_pat.node,
], {
span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
"this pattern takes a reference on something that is being de-referenced",
|db| {
let hint = snippet(cx, spanned_name.span, "..").into_owned();
db.span_suggestion(pat.span, "try removing the `&ref` part and just keep", hint);
});
}}
if let PatKind::Binding(BindingAnnotation::Ref, _, spanned_name, ..) = sub_pat.node;
then {
span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, pat.span,
"this pattern takes a reference on something that is being de-referenced",
|db| {
let hint = snippet(cx, spanned_name.span, "..").into_owned();
db.span_suggestion(pat.span, "try removing the `&ref` part and just keep", hint);
});
}
}
}
}

View file

@ -64,13 +64,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;
}}
if_chain! {
if a.meta_item_list().is_some();
if let Some(name) = a.name();
if name == "proc_macro_derive";
then {
return;
}
}
},
_ => return,
}
@ -148,101 +149,103 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
)
};
if_let_chain! {[
!is_self(arg),
!ty.is_mutable_pointer(),
!is_copy(cx, ty),
!fn_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])),
!implements_borrow_trait,
!all_borrowable_trait,
if_chain! {
if !is_self(arg);
if !ty.is_mutable_pointer();
if !is_copy(cx, ty);
if !fn_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
if !implements_borrow_trait;
if !all_borrowable_trait;
let PatKind::Binding(mode, canonical_id, ..) = arg.pat.node,
!moved_vars.contains(&canonical_id),
], {
if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
continue;
}
// Dereference suggestion
let sugg = |db: &mut DiagnosticBuilder| {
let deref_span = spans_need_deref.get(&canonical_id);
if_let_chain! {[
match_type(cx, ty, &paths::VEC),
let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]),
let TyPath(QPath::Resolved(_, ref path)) = input.node,
let Some(elem_ty) = path.segments.iter()
.find(|seg| seg.name == "Vec")
.and_then(|ps| ps.parameters.as_ref())
.map(|params| &params.types[0]),
], {
let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
db.span_suggestion(input.span,
"consider changing the type to",
slice_ty);
for (span, suggestion) in clone_spans {
db.span_suggestion(
span,
&snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x)),
),
suggestion.into()
);
}
// cannot be destructured, no need for `*` suggestion
assert!(deref_span.is_none());
return;
}}
if match_type(cx, ty, &paths::STRING) {
if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());
for (span, suggestion) in clone_spans {
db.span_suggestion(
span,
&snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x))
),
suggestion.into(),
);
if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.node;
if !moved_vars.contains(&canonical_id);
then {
if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
continue;
}
// Dereference suggestion
let sugg = |db: &mut DiagnosticBuilder| {
let deref_span = spans_need_deref.get(&canonical_id);
if_chain! {
if match_type(cx, ty, &paths::VEC);
if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
if let TyPath(QPath::Resolved(_, ref path)) = input.node;
if let Some(elem_ty) = path.segments.iter()
.find(|seg| seg.name == "Vec")
.and_then(|ps| ps.parameters.as_ref())
.map(|params| &params.types[0]);
then {
let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
db.span_suggestion(input.span,
"consider changing the type to",
slice_ty);
for (span, suggestion) in clone_spans {
db.span_suggestion(
span,
&snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x)),
),
suggestion.into()
);
}
// cannot be destructured, no need for `*` suggestion
assert!(deref_span.is_none());
return;
}
assert!(deref_span.is_none());
return;
}
}
let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
// Suggests adding `*` to dereference the added reference.
if let Some(deref_span) = deref_span {
spans.extend(
deref_span
.iter()
.cloned()
.map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
);
spans.sort_by_key(|&(span, _)| span);
}
multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
};
span_lint_and_then(
cx,
NEEDLESS_PASS_BY_VALUE,
input.span,
"this argument is passed by value, but not consumed in the function body",
sugg,
);
}}
if match_type(cx, ty, &paths::STRING) {
if let Some(clone_spans) =
get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
db.span_suggestion(input.span, "consider changing the type to", "&str".to_string());
for (span, suggestion) in clone_spans {
db.span_suggestion(
span,
&snippet_opt(cx, span)
.map_or(
"change the call to".into(),
|x| Cow::from(format!("change `{}` to", x))
),
suggestion.into(),
);
}
assert!(deref_span.is_none());
return;
}
}
let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
// Suggests adding `*` to dereference the added reference.
if let Some(deref_span) = deref_span {
spans.extend(
deref_span
.iter()
.cloned()
.map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
);
spans.sort_by_key(|&(span, _)| span);
}
multispan_sugg(db, "consider taking a reference instead".to_string(), spans);
};
span_lint_and_then(
cx,
NEEDLESS_PASS_BY_VALUE,
input.span,
"this argument is passed by value, but not consumed in the function body",
sugg,
);
}
}
}
}
}
@ -299,18 +302,19 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
map::Node::NodeStmt(s) => {
// `let <pat> = x;`
if_let_chain! {[
let StmtDecl(ref decl, _) = s.node,
let DeclLocal(ref local) = decl.node,
], {
self.spans_need_deref
.entry(vid)
.or_insert_with(HashSet::new)
.insert(local.init
.as_ref()
.map(|e| e.span)
.expect("`let` stmt without init aren't caught by match_pat"));
}}
if_chain! {
if let StmtDecl(ref decl, _) = s.node;
if let DeclLocal(ref local) = decl.node;
then {
self.spans_need_deref
.entry(vid)
.or_insert_with(HashSet::new)
.insert(local.init
.as_ref()
.map(|e| e.span)
.expect("`let` stmt without init aren't caught by match_pat"));
}
}
},
_ => {},

View file

@ -45,16 +45,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
}
fn check_mul(cx: &LateContext, span: Span, lit: &Expr, exp: &Expr) {
if_let_chain!([
let ExprLit(ref l) = lit.node,
let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit)),
let Some(val) = ci.to_u64(),
val == 1,
cx.tables.expr_ty(exp).is_integral()
], {
span_lint(cx,
NEG_MULTIPLY,
span,
"Negation by multiplying with -1");
})
if_chain! {
if let ExprLit(ref l) = lit.node;
if let Constant::Int(ref ci) = consts::lit_to_constant(&l.node, cx.tcx, cx.tables.expr_ty(lit));
if let Some(val) = ci.to_u64();
if val == 1;
if cx.tables.expr_ty(exp).is_integral();
then {
span_lint(cx,
NEG_MULTIPLY,
span,
"Negation by multiplying with -1");
}
}
}

View file

@ -117,40 +117,41 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
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)));
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!(
if_chain! {
if same_tys(cx, self_ty, return_ty(cx, id));
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT);
if !implements_trait(cx, self_ty, default_trait_id, &[]);
then {
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));
});
}
}
}}
}
}
}
}

View file

@ -74,6 +74,9 @@ const WHITELIST: &[&[&str]] = &[
&["lhs", "rhs"],
&["tx", "rx"],
&["set", "get"],
&["args", "arms"],
&["qpath", "path"],
&["lit", "lint"],
];
struct SimilarNamesNameVisitor<'a: 'b, 'tcx: 'a, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

View file

@ -43,21 +43,22 @@ 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_chain! {[ //begin checking variables
let ExprMatch(ref op, ref body, ref source) = expr.node, //test if expr is a match
let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pats[0].node, //get operation
method_chain_args(op, &["ok"]).is_some() //test to see if using ok() methoduse std::marker::Sized;
], {
let is_result_type = match_type(cx, cx.tables.expr_ty(&result_types[0]), &paths::RESULT);
let some_expr_string = snippet(cx, y[0].span, "");
if print::to_string(print::NO_ANN, |s| s.print_path(x, false)) == "Some" && is_result_type {
span_help_and_lint(cx, IF_LET_SOME_RESULT, expr.span,
"Matching on `Some` with `ok()` is redundant",
&format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
if_chain! { //begin checking variables
if let ExprMatch(ref op, ref body, ref source) = expr.node; //test if expr is a match
if let MatchSource::IfLetDesugar { .. } = *source; //test if it is an If Let
if let ExprMethodCall(_, _, ref result_types) = op.node; //check is expr.ok() has type Result<T,E>.ok()
if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pats[0].node; //get operation
if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
then {
let is_result_type = match_type(cx, cx.tables.expr_ty(&result_types[0]), &paths::RESULT);
let some_expr_string = snippet(cx, y[0].span, "");
if print::to_string(print::NO_ANN, |s| s.print_path(x, false)) == "Some" && is_result_type {
span_help_and_lint(cx, IF_LET_SOME_RESULT, expr.span,
"Matching on `Some` with `ok()` is redundant",
&format!("Consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string));
}
}
}}
}
}
}

View file

@ -31,52 +31,54 @@ impl LintPass for OverflowCheckConditional {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OverflowCheckConditional {
// a + b < a, a > a + b, a < a - b, a - b > a
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if_let_chain! {[
let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node,
let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = first.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = second.node,
path1.segments[0] == path3.segments[0] || path2.segments[0] == path3.segments[0],
cx.tables.expr_ty(ident1).is_integral(),
cx.tables.expr_ty(ident2).is_integral()
], {
if let BinOp_::BiLt = op.node {
if let BinOp_::BiAdd = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
if_chain! {
if let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node;
if let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = first.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = second.node;
if path1.segments[0] == path3.segments[0] || path2.segments[0] == path3.segments[0];
if cx.tables.expr_ty(ident1).is_integral();
if cx.tables.expr_ty(ident2).is_integral();
then {
if let BinOp_::BiLt = op.node {
if let BinOp_::BiAdd = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
}
}
if let BinOp_::BiGt = op.node {
if let BinOp_::BiSub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
}
}
}
if let BinOp_::BiGt = op.node {
if let BinOp_::BiSub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
}
}
}}
}
if_let_chain! {[
let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node,
let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = second.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node,
let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = first.node,
path1.segments[0] == path3.segments[0] || path2.segments[0] == path3.segments[0],
cx.tables.expr_ty(ident1).is_integral(),
cx.tables.expr_ty(ident2).is_integral()
], {
if let BinOp_::BiGt = op.node {
if let BinOp_::BiAdd = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
if_chain! {
if let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node;
if let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = second.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node;
if let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = first.node;
if path1.segments[0] == path3.segments[0] || path2.segments[0] == path3.segments[0];
if cx.tables.expr_ty(ident1).is_integral();
if cx.tables.expr_ty(ident2).is_integral();
then {
if let BinOp_::BiGt = op.node {
if let BinOp_::BiAdd = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C overflow conditions that will fail in Rust.");
}
}
if let BinOp_::BiLt = op.node {
if let BinOp_::BiSub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
}
}
}
if let BinOp_::BiLt = op.node {
if let BinOp_::BiSub = op2.node {
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
"You are trying to use classic C underflow conditions that will fail in Rust.");
}
}
}}
}
}
}

View file

@ -34,23 +34,24 @@ 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_chain! {[
let ExprBlock(ref block) = expr.node,
let Some(ref ex) = block.expr,
let ExprCall(ref fun, ref params) = ex.node,
params.len() == 2,
let ExprPath(ref qpath) = fun.node,
let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id)),
match_def_path(cx.tcx, fun_def_id, &paths::BEGIN_PANIC),
let ExprLit(ref lit) = params[0].node,
is_direct_expn_of(expr.span, "panic").is_some(),
let LitKind::Str(ref string, _) = lit.node,
let Some(par) = string.as_str().find('{'),
string.as_str()[par..].contains('}'),
params[0].span.source_callee().is_none()
], {
span_lint(cx, PANIC_PARAMS, params[0].span,
"you probably are missing some parameter in your format string");
}}
if_chain! {
if let ExprBlock(ref block) = expr.node;
if let Some(ref ex) = block.expr;
if let ExprCall(ref fun, ref params) = ex.node;
if params.len() == 2;
if let ExprPath(ref qpath) = fun.node;
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id));
if match_def_path(cx.tcx, fun_def_id, &paths::BEGIN_PANIC);
if let ExprLit(ref lit) = params[0].node;
if is_direct_expn_of(expr.span, "panic").is_some();
if let LitKind::Str(ref string, _) = lit.node;
if let Some(par) = string.as_str().find('{');
if string.as_str()[par..].contains('}');
if params[0].span.source_callee().is_none();
then {
span_lint(cx, PANIC_PARAMS, params[0].span,
"you probably are missing some parameter in your format string");
}
}
}
}

View file

@ -37,20 +37,21 @@ impl LintPass for Pass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
if_let_chain! {[
let ItemImpl(_, _, _, _, Some(ref trait_ref), _, ref impl_items) = item.node,
!is_automatically_derived(&*item.attrs),
let Some(eq_trait) = cx.tcx.lang_items().eq_trait(),
trait_ref.path.def.def_id() == eq_trait
], {
for impl_item in impl_items {
if impl_item.name == "ne" {
span_lint(cx,
PARTIALEQ_NE_IMPL,
impl_item.span,
"re-implementing `PartialEq::ne` is unnecessary")
if_chain! {
if let ItemImpl(_, _, _, _, Some(ref trait_ref), _, ref impl_items) = item.node;
if !is_automatically_derived(&*item.attrs);
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
if trait_ref.path.def.def_id() == eq_trait;
then {
for impl_item in impl_items {
if impl_item.name == "ne" {
span_lint(cx,
PARTIALEQ_NE_IMPL,
impl_item.span,
"re-implementing `PartialEq::ne` is unnecessary")
}
}
}
}};
};
}
}

View file

@ -89,58 +89,60 @@ 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_chain! {[
let ExprCall(ref fun, ref args) = expr.node,
let ExprPath(ref qpath) = fun.node,
let Some(fun_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id)),
], {
// Search for `std::io::_print(..)` which is unique in a
// `print!` expansion.
if match_def_path(cx.tcx, fun_id, &paths::IO_PRINT) {
if let Some(span) = is_expn_of(expr.span, "print") {
// `println!` uses `print!`.
let (span, name) = match is_expn_of(span, "println") {
Some(span) => (span, "println"),
None => (span, "print"),
};
span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name));
if_let_chain!{[
// ensure we're calling Arguments::new_v1
args.len() == 1,
let ExprCall(ref args_fun, ref args_args) = args[0].node,
let ExprPath(ref qpath) = args_fun.node,
let Some(const_def_id) = opt_def_id(resolve_node(cx, qpath, args_fun.hir_id)),
match_def_path(cx.tcx, const_def_id, &paths::FMT_ARGUMENTS_NEWV1),
args_args.len() == 2,
let ExprAddrOf(_, ref match_expr) = args_args[1].node,
let ExprMatch(ref args, _, _) = match_expr.node,
let ExprTup(ref args) = args.node,
let Some((fmtstr, fmtlen)) = get_argument_fmtstr_parts(&args_args[0]),
], {
match name {
"print" => check_print(cx, span, args, fmtstr, fmtlen),
"println" => check_println(cx, span, fmtstr, fmtlen),
_ => (),
if_chain! {
if let ExprCall(ref fun, ref args) = expr.node;
if let ExprPath(ref qpath) = fun.node;
if let Some(fun_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id));
then {
// Search for `std::io::_print(..)` which is unique in a
// `print!` expansion.
if match_def_path(cx.tcx, fun_id, &paths::IO_PRINT) {
if let Some(span) = is_expn_of(expr.span, "print") {
// `println!` uses `print!`.
let (span, name) = match is_expn_of(span, "println") {
Some(span) => (span, "println"),
None => (span, "print"),
};
span_lint(cx, PRINT_STDOUT, span, &format!("use of `{}!`", name));
if_chain! {
// ensure we're calling Arguments::new_v1
if args.len() == 1;
if let ExprCall(ref args_fun, ref args_args) = args[0].node;
if let ExprPath(ref qpath) = args_fun.node;
if let Some(const_def_id) = opt_def_id(resolve_node(cx, qpath, args_fun.hir_id));
if match_def_path(cx.tcx, const_def_id, &paths::FMT_ARGUMENTS_NEWV1);
if args_args.len() == 2;
if let ExprAddrOf(_, ref match_expr) = args_args[1].node;
if let ExprMatch(ref args, _, _) = match_expr.node;
if let ExprTup(ref args) = args.node;
if let Some((fmtstr, fmtlen)) = get_argument_fmtstr_parts(&args_args[0]);
then {
match name {
"print" => check_print(cx, span, args, fmtstr, fmtlen),
"println" => check_println(cx, span, fmtstr, fmtlen),
_ => (),
}
}
}
}}
}
}
}
// Search for something like
// `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
else if args.len() == 2 && match_def_path(cx.tcx, fun_id, &paths::FMT_ARGUMENTV1_NEW) {
if let ExprPath(ref qpath) = args[1].node {
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, args[1].hir_id)) {
if match_def_path(cx.tcx, def_id, &paths::DEBUG_FMT_METHOD)
&& !is_in_debug_impl(cx, expr) && is_expn_of(expr.span, "panic").is_none() {
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
// Search for something like
// `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
else if args.len() == 2 && match_def_path(cx.tcx, fun_id, &paths::FMT_ARGUMENTV1_NEW) {
if let ExprPath(ref qpath) = args[1].node {
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, args[1].hir_id)) {
if match_def_path(cx.tcx, def_id, &paths::DEBUG_FMT_METHOD)
&& !is_in_debug_impl(cx, expr) && is_expn_of(expr.span, "panic").is_none() {
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
}
}
}
}
}
}}
}
}
}
@ -152,36 +154,38 @@ fn check_print<'a, 'tcx>(
fmtstr: InternedString,
fmtlen: usize,
) {
if_let_chain!{[
if_chain! {
// check the final format string part
let Some('\n') = fmtstr.chars().last(),
if let Some('\n') = fmtstr.chars().last();
// "foo{}bar" is made into two strings + one argument,
// if the format string starts with `{}` (eg. "{}foo"),
// the string array is prepended an empty string "".
// We only want to check the last string after any `{}`:
args.len() < fmtlen,
], {
span_lint(cx, PRINT_WITH_NEWLINE, span,
"using `print!()` with a format string that ends in a \
newline, consider using `println!()` instead");
}}
if args.len() < fmtlen;
then {
span_lint(cx, PRINT_WITH_NEWLINE, span,
"using `print!()` with a format string that ends in a \
newline, consider using `println!()` instead");
}
}
}
/// Check for println!("")
fn check_println<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, span: Span, fmtstr: InternedString, fmtlen: usize) {
if_let_chain!{[
if_chain! {
// check that the string is empty
fmtlen == 1,
fmtstr.deref() == "\n",
if fmtlen == 1;
if fmtstr.deref() == "\n";
// check the presence of that string
let Ok(snippet) = cx.sess().codemap().span_to_snippet(span),
snippet.contains("\"\""),
], {
span_lint(cx, PRINT_WITH_NEWLINE, span,
"using `println!(\"\")`, consider using `println!()` instead");
}}
if let Ok(snippet) = cx.sess().codemap().span_to_snippet(span);
if snippet.contains("\"\"");
then {
span_lint(cx, PRINT_WITH_NEWLINE, span,
"using `println!(\"\")`, consider using `println!()` instead");
}
}
}
fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
@ -202,14 +206,15 @@ fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
/// Returns the slice of format string parts in an `Arguments::new_v1` call.
fn get_argument_fmtstr_parts(expr: &Expr) -> Option<(InternedString, usize)> {
if_let_chain! {[
let ExprAddrOf(_, ref expr) = expr.node, // &["…", "…", …]
let ExprArray(ref exprs) = expr.node,
let Some(expr) = exprs.last(),
let ExprLit(ref lit) = expr.node,
let LitKind::Str(ref lit, _) = lit.node,
], {
return Some((lit.as_str(), exprs.len()));
}}
if_chain! {
if let ExprAddrOf(_, ref expr) = expr.node; // &["…", "…", …]
if let ExprArray(ref exprs) = expr.node;
if let Some(expr) = exprs.last();
if let ExprLit(ref lit) = expr.node;
if let LitKind::Str(ref lit, _) = lit.node;
then {
return Some((lit.as_str(), exprs.len()));
}
}
None
}

View file

@ -156,13 +156,14 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId, opt_body_id: Option<
{
if match_type(cx, ty, &paths::VEC) {
let mut ty_snippet = None;
if_let_chain!([
let TyPath(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).node,
let Some(&PathSegment{parameters: Some(ref parameters), ..}) = path.segments.last(),
parameters.types.len() == 1,
], {
ty_snippet = snippet_opt(cx, parameters.types[0].span);
});
if_chain! {
if let TyPath(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).node;
if let Some(&PathSegment{parameters: Some(ref parameters), ..}) = path.segments.last();
if parameters.types.len() == 1;
then {
ty_snippet = snippet_opt(cx, parameters.types[0].span);
}
};
if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
span_lint_and_then(
cx,

View file

@ -113,69 +113,72 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
} else if name == "zip" && args.len() == 2 {
let iter = &args[0].node;
let zip_arg = &args[1];
if_let_chain! {[
if_chain! {
// .iter() call
let ExprMethodCall(ref iter_path, _, ref iter_args ) = *iter,
iter_path.name == "iter",
if let ExprMethodCall(ref iter_path, _, ref iter_args ) = *iter;
if iter_path.name == "iter";
// range expression in .zip() call: 0..x.len()
let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg),
is_integer_literal(start, 0),
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
if is_integer_literal(start, 0);
// .len() call
let ExprMethodCall(ref len_path, _, ref len_args) = end.node,
len_path.name == "len" && len_args.len() == 1,
if let ExprMethodCall(ref len_path, _, ref len_args) = end.node;
if len_path.name == "len" && len_args.len() == 1;
// .iter() and .len() called on same Path
let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node,
let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node,
iter_path.segments == len_path.segments
], {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
expr.span,
&format!("It is more idiomatic to use {}.iter().enumerate()",
snippet(cx, iter_args[0].span, "_")));
}}
if let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node;
if let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node;
if iter_path.segments == len_path.segments;
then {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
expr.span,
&format!("It is more idiomatic to use {}.iter().enumerate()",
snippet(cx, iter_args[0].span, "_")));
}
}
}
}
// exclusive range plus one: x..(y+1)
if_let_chain! {[
let Some(higher::Range { start, end: Some(end), limits: RangeLimits::HalfOpen }) = higher::range(expr),
let Some(y) = y_plus_one(end),
], {
span_lint_and_then(
cx,
RANGE_PLUS_ONE,
expr.span,
"an inclusive range would be more readable",
|db| {
let start = start.map_or("".to_owned(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
db.span_suggestion(expr.span,
"use",
format!("{}..={}", start, end));
},
);
}}
if_chain! {
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::HalfOpen }) = higher::range(expr);
if let Some(y) = y_plus_one(end);
then {
span_lint_and_then(
cx,
RANGE_PLUS_ONE,
expr.span,
"an inclusive range would be more readable",
|db| {
let start = start.map_or("".to_owned(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
db.span_suggestion(expr.span,
"use",
format!("{}..={}", start, end));
},
);
}
}
// inclusive range minus one: x..=(y-1)
if_let_chain! {[
let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr),
let Some(y) = y_minus_one(end),
], {
span_lint_and_then(
cx,
RANGE_MINUS_ONE,
expr.span,
"an exclusive range would be more readable",
|db| {
let start = start.map_or("".to_owned(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
db.span_suggestion(expr.span,
"use",
format!("{}..{}", start, end));
},
);
}}
if_chain! {
if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr);
if let Some(y) = y_minus_one(end);
then {
span_lint_and_then(
cx,
RANGE_MINUS_ONE,
expr.span,
"an exclusive range would be more readable",
|db| {
let start = start.map_or("".to_owned(), |x| Sugg::hir(cx, x, "x").to_string());
let end = Sugg::hir(cx, y, "y");
db.span_suggestion(expr.span,
"use",
format!("{}..{}", start, end));
},
);
}
}
}
}

View file

@ -86,22 +86,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
if_let_chain!{[
self.last.is_none(),
let Some(ref expr) = block.expr,
match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX),
let Some(span) = is_expn_of(expr.span, "regex"),
], {
if !self.spans.contains(&span) {
span_lint(cx,
REGEX_MACRO,
span,
"`regex!(_)` found. \
Please use `Regex::new(_)`, which is faster for now.");
self.spans.insert(span);
if_chain! {
if self.last.is_none();
if let Some(ref expr) = block.expr;
if match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX);
if let Some(span) = is_expn_of(expr.span, "regex");
then {
if !self.spans.contains(&span) {
span_lint(cx,
REGEX_MACRO,
span,
"`regex!(_)` found. \
Please use `Regex::new(_)`, which is faster for now.");
self.spans.insert(span);
}
self.last = Some(block.id);
}
self.last = Some(block.id);
}}
}
}
fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block) {
@ -111,24 +112,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
}
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if_let_chain!{[
let ExprCall(ref fun, ref args) = expr.node,
let ExprPath(ref qpath) = fun.node,
args.len() == 1,
let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, fun.hir_id)),
], {
if match_def_path(cx.tcx, def_id, &paths::REGEX_NEW) ||
match_def_path(cx.tcx, def_id, &paths::REGEX_BUILDER_NEW) {
check_regex(cx, &args[0], true);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_NEW) ||
match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
check_regex(cx, &args[0], false);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_SET_NEW) {
check_set(cx, &args[0], true);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_SET_NEW) {
check_set(cx, &args[0], false);
if_chain! {
if let ExprCall(ref fun, ref args) = expr.node;
if let ExprPath(ref qpath) = fun.node;
if args.len() == 1;
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, fun.hir_id));
then {
if match_def_path(cx.tcx, def_id, &paths::REGEX_NEW) ||
match_def_path(cx.tcx, def_id, &paths::REGEX_BUILDER_NEW) {
check_regex(cx, &args[0], true);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_NEW) ||
match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
check_regex(cx, &args[0], false);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_SET_NEW) {
check_set(cx, &args[0], true);
} else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_SET_NEW) {
check_set(cx, &args[0], false);
}
}
}}
}
}
}
@ -179,14 +181,15 @@ fn is_trivial_regex(s: &regex_syntax::Expr) -> Option<&'static str> {
}
fn check_set<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, utf8: bool) {
if_let_chain! {[
let ExprAddrOf(_, ref expr) = expr.node,
let ExprArray(ref exprs) = expr.node,
], {
for expr in exprs {
check_regex(cx, expr, utf8);
if_chain! {
if let ExprAddrOf(_, ref expr) = expr.node;
if let ExprArray(ref exprs) = expr.node;
then {
for expr in exprs {
check_regex(cx, expr, utf8);
}
}
}}
}
}
fn check_regex<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, utf8: bool) {

View file

@ -103,28 +103,29 @@ impl ReturnPass {
let mut it = block.stmts.iter();
// we need both a let-binding stmt and an expr
if_let_chain! {[
let Some(retexpr) = it.next_back(),
let ast::StmtKind::Expr(ref retexpr) = retexpr.node,
let Some(stmt) = it.next_back(),
let ast::StmtKind::Local(ref local) = stmt.node,
if_chain! {
if let Some(retexpr) = it.next_back();
if let ast::StmtKind::Expr(ref retexpr) = retexpr.node;
if let Some(stmt) = it.next_back();
if let ast::StmtKind::Local(ref local) = stmt.node;
// don't lint in the presence of type inference
local.ty.is_none(),
!local.attrs.iter().any(attr_is_cfg),
let Some(ref initexpr) = local.init,
let ast::PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node,
let ast::ExprKind::Path(_, ref path) = retexpr.node,
match_path_ast(path, &[&id.name.as_str()]),
!in_external_macro(cx, initexpr.span),
], {
span_note_and_lint(cx,
LET_AND_RETURN,
retexpr.span,
"returning the result of a let binding from a block. \
Consider returning the expression directly.",
initexpr.span,
"this expression can be directly returned");
}}
if local.ty.is_none();
if !local.attrs.iter().any(attr_is_cfg);
if let Some(ref initexpr) = local.init;
if let ast::PatKind::Ident(_, Spanned { node: id, .. }, _) = local.pat.node;
if let ast::ExprKind::Path(_, ref path) = retexpr.node;
if match_path_ast(path, &[&id.name.as_str()]);
if !in_external_macro(cx, initexpr.span);
then {
span_note_and_lint(cx,
LET_AND_RETURN,
retexpr.span,
"returning the result of a let binding from a block. \
Consider returning the expression directly.",
initexpr.span,
"this expression can be directly returned");
}
}
}
}

View file

@ -59,120 +59,122 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
/// Implementation of the `MANUAL_SWAP` lint.
fn check_manual_swap(cx: &LateContext, block: &Block) {
for w in block.stmts.windows(3) {
if_let_chain!{[
if_chain! {
// let t = foo();
let StmtDecl(ref tmp, _) = w[0].node,
let DeclLocal(ref tmp) = tmp.node,
let Some(ref tmp_init) = tmp.init,
let PatKind::Binding(_, _, ref tmp_name, None) = tmp.pat.node,
if let StmtDecl(ref tmp, _) = w[0].node;
if let DeclLocal(ref tmp) = tmp.node;
if let Some(ref tmp_init) = tmp.init;
if let PatKind::Binding(_, _, ref tmp_name, None) = tmp.pat.node;
// foo() = bar();
let StmtSemi(ref first, _) = w[1].node,
let ExprAssign(ref lhs1, ref rhs1) = first.node,
if let StmtSemi(ref first, _) = w[1].node;
if let ExprAssign(ref lhs1, ref rhs1) = first.node;
// bar() = t;
let StmtSemi(ref second, _) = w[2].node,
let ExprAssign(ref lhs2, ref rhs2) = second.node,
let ExprPath(QPath::Resolved(None, ref rhs2)) = rhs2.node,
rhs2.segments.len() == 1,
if let StmtSemi(ref second, _) = w[2].node;
if let ExprAssign(ref lhs2, ref rhs2) = second.node;
if let ExprPath(QPath::Resolved(None, ref rhs2)) = rhs2.node;
if rhs2.segments.len() == 1;
tmp_name.node.as_str() == rhs2.segments[0].name.as_str(),
SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1),
SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2)
], {
fn check_for_slice<'a>(
cx: &LateContext,
lhs1: &'a Expr,
lhs2: &'a Expr,
) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
if let ExprIndex(ref lhs1, ref idx1) = lhs1.node {
if let ExprIndex(ref lhs2, ref idx2) = lhs2.node {
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));
if matches!(ty.sty, ty::TySlice(_)) ||
matches!(ty.sty, ty::TyArray(_, _)) ||
match_type(cx, ty, &paths::VEC) ||
match_type(cx, ty, &paths::VEC_DEQUE) {
return Some((lhs1, idx1, idx2));
if tmp_name.node.as_str() == rhs2.segments[0].name.as_str();
if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
then {
fn check_for_slice<'a>(
cx: &LateContext,
lhs1: &'a Expr,
lhs2: &'a Expr,
) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
if let ExprIndex(ref lhs1, ref idx1) = lhs1.node {
if let ExprIndex(ref lhs2, ref idx2) = lhs2.node {
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));
if matches!(ty.sty, ty::TySlice(_)) ||
matches!(ty.sty, ty::TyArray(_, _)) ||
match_type(cx, ty, &paths::VEC) ||
match_type(cx, ty, &paths::VEC_DEQUE) {
return Some((lhs1, idx1, idx2));
}
}
}
}
None
}
None
}
let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
if let Some(slice) = Sugg::hir_opt(cx, slice) {
(false,
format!(" elements of `{}`", slice),
format!("{}.swap({}, {})",
slice.maybe_par(),
snippet(cx, idx1.span, ".."),
snippet(cx, idx2.span, "..")))
let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
if let Some(slice) = Sugg::hir_opt(cx, slice) {
(false,
format!(" elements of `{}`", slice),
format!("{}.swap({}, {})",
slice.maybe_par(),
snippet(cx, idx1.span, ".."),
snippet(cx, idx2.span, "..")))
} else {
(false, "".to_owned(), "".to_owned())
}
} else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
(true, format!(" `{}` and `{}`", first, second),
format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
} else {
(false, "".to_owned(), "".to_owned())
}
} else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
(true, format!(" `{}` and `{}`", first, second),
format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
} else {
(true, "".to_owned(), "".to_owned())
};
let span = w[0].span.to(second.span);
span_lint_and_then(cx,
MANUAL_SWAP,
span,
&format!("this looks like you are swapping{} manually", what),
|db| {
if !sugg.is_empty() {
db.span_suggestion(span, "try", sugg);
if replace {
db.note("or maybe you should use `std::mem::replace`?");
(true, "".to_owned(), "".to_owned())
};
let span = w[0].span.to(second.span);
span_lint_and_then(cx,
MANUAL_SWAP,
span,
&format!("this looks like you are swapping{} manually", what),
|db| {
if !sugg.is_empty() {
db.span_suggestion(span, "try", sugg);
if replace {
db.note("or maybe you should use `std::mem::replace`?");
}
}
}
});
}}
});
}
}
}
}
/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext, block: &Block) {
for w in block.stmts.windows(2) {
if_let_chain!{[
let StmtSemi(ref first, _) = w[0].node,
let StmtSemi(ref second, _) = w[1].node,
!differing_macro_contexts(first.span, second.span),
let ExprAssign(ref lhs0, ref rhs0) = first.node,
let ExprAssign(ref lhs1, ref rhs1) = second.node,
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
], {
let lhs0 = Sugg::hir_opt(cx, lhs0);
let rhs0 = Sugg::hir_opt(cx, rhs0);
let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
(format!(" `{}` and `{}`", first, second), first.mut_addr().to_string(), second.mut_addr().to_string())
} else {
("".to_owned(), "".to_owned(), "".to_owned())
};
let span = first.span.to(second.span);
span_lint_and_then(cx,
ALMOST_SWAPPED,
span,
&format!("this looks like you are trying to swap{}", what),
|db| {
if !what.is_empty() {
db.span_suggestion(span, "try",
format!("std::mem::swap({}, {})", lhs, rhs));
db.note("or maybe you should use `std::mem::replace`?");
}
});
}}
if_chain! {
if let StmtSemi(ref first, _) = w[0].node;
if let StmtSemi(ref second, _) = w[1].node;
if !differing_macro_contexts(first.span, second.span);
if let ExprAssign(ref lhs0, ref rhs0) = first.node;
if let ExprAssign(ref lhs1, ref rhs1) = second.node;
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
then {
let lhs0 = Sugg::hir_opt(cx, lhs0);
let rhs0 = Sugg::hir_opt(cx, rhs0);
let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
(format!(" `{}` and `{}`", first, second), first.mut_addr().to_string(), second.mut_addr().to_string())
} else {
("".to_owned(), "".to_owned(), "".to_owned())
};
let span = first.span.to(second.span);
span_lint_and_then(cx,
ALMOST_SWAPPED,
span,
&format!("this looks like you are trying to swap{}", what),
|db| {
if !what.is_empty() {
db.span_suggestion(span, "try",
format!("std::mem::swap({}, {})", lhs, rhs));
db.note("or maybe you should use `std::mem::replace`?");
}
});
}
}
}
}

View file

@ -214,7 +214,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for 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(
(&ty::TyRawPtr(from_pty), &ty::TyRef(_, to_ref_ty)) => span_lint_and_then(
cx,
TRANSMUTE_PTR_TO_REF,
e.span,
@ -226,16 +226,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
),
|db| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
let (deref, cast) = if to_ref_ty.mutbl == Mutability::MutMutable {
("&mut *", "*mut")
} else {
("&*", "*const")
};
let arg = if from_pty.ty == to_rty.ty {
let arg = if from_pty.ty == to_ref_ty.ty {
arg
} else {
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_rty.ty)))
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty.ty)))
};
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
@ -299,16 +299,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
/// the type's `ToString` implementation. In weird cases it could lead to types
/// with invalid `'_`
/// lifetime, but it should be rare.
fn get_type_snippet(cx: &LateContext, path: &QPath, to_rty: Ty) -> String {
fn get_type_snippet(cx: &LateContext, path: &QPath, to_ref_ty: Ty) -> String {
let seg = last_path_segment(path);
if_let_chain!{[
let Some(ref params) = seg.parameters,
!params.parenthesized,
let Some(to_ty) = params.types.get(1),
let TyRptr(_, ref to_ty) = to_ty.node,
], {
return snippet(cx, to_ty.ty.span, &to_rty.to_string()).to_string();
}}
if_chain! {
if let Some(ref params) = seg.parameters;
if !params.parenthesized;
if let Some(to_ty) = params.types.get(1);
if let TyRptr(_, ref to_ty) = to_ty.node;
then {
return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
}
}
to_rty.to_string()
to_ref_ty.to_string()
}

View file

@ -158,21 +158,22 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
if let Some(def_id) = opt_def_id(def) {
if Some(def_id) == cx.tcx.lang_items().owned_box() {
let last = last_path_segment(qpath);
if_let_chain! {[
let Some(ref params) = last.parameters,
!params.parenthesized,
let Some(vec) = params.types.get(0),
let TyPath(ref qpath) = vec.node,
let Some(did) = opt_def_id(cx.tables.qpath_def(qpath, cx.tcx.hir.node_to_hir_id(vec.id))),
match_def_path(cx.tcx, did, &paths::VEC),
], {
span_help_and_lint(cx,
BOX_VEC,
ast_ty.span,
"you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.");
return; // don't recurse into the type
}}
if_chain! {
if let Some(ref params) = last.parameters;
if !params.parenthesized;
if let Some(vec) = params.types.get(0);
if let TyPath(ref qpath) = vec.node;
if let Some(did) = opt_def_id(cx.tables.qpath_def(qpath, cx.tcx.hir.node_to_hir_id(vec.id)));
if match_def_path(cx.tcx, did, &paths::VEC);
then {
span_help_and_lint(cx,
BOX_VEC,
ast_ty.span,
"you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.");
return; // don't recurse into the type
}
}
} else if match_def_path(cx.tcx, def_id, &paths::LINKED_LIST) {
span_help_and_lint(
cx,
@ -227,39 +228,40 @@ fn check_ty_rptr(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool, lt: &Lifeti
TyPath(ref qpath) => {
let hir_id = cx.tcx.hir.node_to_hir_id(mut_ty.ty.id);
let def = cx.tables.qpath_def(qpath, hir_id);
if_let_chain! {[
let Some(def_id) = opt_def_id(def),
Some(def_id) == cx.tcx.lang_items().owned_box(),
let QPath::Resolved(None, ref path) = *qpath,
let [ref bx] = *path.segments,
let Some(ref params) = bx.parameters,
!params.parenthesized,
let [ref inner] = *params.types
], {
if is_any_trait(inner) {
// Ignore `Box<Any>` types, see #1884 for details.
return;
if_chain! {
if let Some(def_id) = opt_def_id(def);
if Some(def_id) == cx.tcx.lang_items().owned_box();
if let QPath::Resolved(None, ref path) = *qpath;
if let [ref bx] = *path.segments;
if let Some(ref params) = bx.parameters;
if !params.parenthesized;
if let [ref inner] = *params.types;
then {
if is_any_trait(inner) {
// Ignore `Box<Any>` types, see #1884 for details.
return;
}
let ltopt = if lt.is_elided() {
"".to_owned()
} else {
format!("{} ", lt.name.name().as_str())
};
let mutopt = if mut_ty.mutbl == Mutability::MutMutable {
"mut "
} else {
""
};
span_lint_and_sugg(cx,
BORROWED_BOX,
ast_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try",
format!("&{}{}{}", ltopt, mutopt, &snippet(cx, inner.span, ".."))
);
return; // don't recurse into the type
}
let ltopt = if lt.is_elided() {
"".to_owned()
} else {
format!("{} ", lt.name.name().as_str())
};
let mutopt = if mut_ty.mutbl == Mutability::MutMutable {
"mut "
} else {
""
};
span_lint_and_sugg(cx,
BORROWED_BOX,
ast_ty.span,
"you seem to be trying to use `&Box<T>`. Consider using just `&T`",
"try",
format!("&{}{}{}", ltopt, mutopt, &snippet(cx, inner.span, ".."))
);
return; // don't recurse into the type
}};
};
check_ty(cx, &mut_ty.ty, is_local);
},
_ => check_ty(cx, &mut_ty.ty, is_local),
@ -268,15 +270,16 @@ fn check_ty_rptr(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool, lt: &Lifeti
// Returns true if given type is `Any` trait.
fn is_any_trait(t: &hir::Ty) -> bool {
if_let_chain! {[
let TyTraitObject(ref traits, _) = t.node,
traits.len() >= 1,
if_chain! {
if let TyTraitObject(ref traits, _) = t.node;
if traits.len() >= 1;
// Only Send/Sync can be used as additional traits, so it is enough to
// check only the first trait.
match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT)
], {
return true;
}}
if match_path(&traits[0].trait_ref.path, &paths::ANY_TRAIT);
then {
return true;
}
}
false
}
@ -1719,43 +1722,44 @@ impl<'a, 'b, 'tcx: 'a + 'b> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'
}
fn visit_expr(&mut self, e: &'tcx Expr) {
if_let_chain!{[
let ExprCall(ref fun, ref args) = e.node,
let ExprPath(QPath::TypeRelative(ref ty, ref method)) = fun.node,
let TyPath(QPath::Resolved(None, ref ty_path)) = ty.node,
], {
if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) {
return;
}
if match_path(ty_path, &paths::HASHMAP) {
if method.name == "new" {
self.suggestions
.insert(e.span, "HashMap::default()".to_string());
} else if method.name == "with_capacity" {
self.suggestions.insert(
e.span,
format!(
"HashMap::with_capacity_and_hasher({}, Default::default())",
snippet(self.cx, args[0].span, "capacity"),
),
);
if_chain! {
if let ExprCall(ref fun, ref args) = e.node;
if let ExprPath(QPath::TypeRelative(ref ty, ref method)) = fun.node;
if let TyPath(QPath::Resolved(None, ref ty_path)) = ty.node;
then {
if !same_tys(self.cx, self.target.ty(), self.body.expr_ty(e)) {
return;
}
} else if match_path(ty_path, &paths::HASHSET) {
if method.name == "new" {
self.suggestions
.insert(e.span, "HashSet::default()".to_string());
} else if method.name == "with_capacity" {
self.suggestions.insert(
e.span,
format!(
"HashSet::with_capacity_and_hasher({}, Default::default())",
snippet(self.cx, args[0].span, "capacity"),
),
);
if match_path(ty_path, &paths::HASHMAP) {
if method.name == "new" {
self.suggestions
.insert(e.span, "HashMap::default()".to_string());
} else if method.name == "with_capacity" {
self.suggestions.insert(
e.span,
format!(
"HashMap::with_capacity_and_hasher({}, Default::default())",
snippet(self.cx, args[0].span, "capacity"),
),
);
}
} else if match_path(ty_path, &paths::HASHSET) {
if method.name == "new" {
self.suggestions
.insert(e.span, "HashSet::default()".to_string());
} else if method.name == "with_capacity" {
self.suggestions.insert(
e.span,
format!(
"HashSet::with_capacity_and_hasher({}, Default::default())",
snippet(self.cx, args[0].span, "capacity"),
),
);
}
}
}
}}
}
walk_expr(self, e);
}

View file

@ -54,26 +54,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UseSelf {
if in_macro(item.span) {
return;
}
if_let_chain!([
let ItemImpl(.., ref item_type, ref refs) = item.node,
let Ty_::TyPath(QPath::Resolved(_, ref item_path)) = item_type.node,
], {
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).parameters;
let should_check = if let Some(ref params) = *parameters {
!params.parenthesized && params.lifetimes.len() == 0
} else {
true
};
if should_check {
let visitor = &mut UseSelfVisitor {
item_path: item_path,
cx: cx,
if_chain! {
if let ItemImpl(.., ref item_type, ref refs) = item.node;
if let Ty_::TyPath(QPath::Resolved(_, ref item_path)) = item_type.node;
then {
let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).parameters;
let should_check = if let Some(ref params) = *parameters {
!params.parenthesized && params.lifetimes.len() == 0
} else {
true
};
for impl_item_ref in refs {
visitor.visit_impl_item(cx.tcx.hir.impl_item(impl_item_ref.id));
if should_check {
let visitor = &mut UseSelfVisitor {
item_path: item_path,
cx: cx,
};
for impl_item_ref in refs {
visitor.visit_impl_item(cx.tcx.hir.impl_item(impl_item_ref.id));
}
}
}
})
}
}
}

View file

@ -28,15 +28,16 @@ use std::collections::HashMap;
/// prints
///
/// ```
/// if_let_chain!{[
/// let Expr_::ExprIf(ref cond, ref then, None) = item.node,
/// let Expr_::ExprBinary(BinOp::Eq, ref left, ref right) = cond.node,
/// let Expr_::ExprPath(ref path) = left.node,
/// let Expr_::ExprLit(ref lit) = right.node,
/// let LitKind::Int(42, _) = lit.node,
/// ], {
/// // report your lint here
/// }}
/// if_chain!{
/// if let Expr_::ExprIf(ref cond, ref then, None) = item.node,
/// if let Expr_::ExprBinary(BinOp::Eq, ref left, ref right) = cond.node,
/// if let Expr_::ExprPath(ref path) = left.node,
/// if let Expr_::ExprLit(ref lit) = right.node,
/// if let LitKind::Int(42, _) = lit.node,
/// then {
/// // report your lint here
/// }
/// }
/// ```
declare_lint! {
pub LINT_AUTHOR,
@ -53,13 +54,14 @@ impl LintPass for Pass {
}
fn prelude() {
println!("if_let_chain!{{[");
println!("if_chain! {{");
}
fn done() {
println!("], {{");
println!(" // report your lint here");
println!("}}}}");
println!(" then {{");
println!(" // report your lint here");
println!(" }}");
println!("}}");
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
@ -181,36 +183,36 @@ struct PrintVisitor {
impl<'tcx> Visitor<'tcx> for PrintVisitor {
fn visit_expr(&mut self, expr: &Expr) {
print!(" let Expr_::Expr");
print!(" if let Expr_::Expr");
let current = format!("{}.node", self.current);
match expr.node {
Expr_::ExprBox(ref inner) => {
let inner_pat = self.next("inner");
println!("Box(ref {}) = {},", inner_pat, current);
println!("Box(ref {}) = {};", inner_pat, current);
self.current = inner_pat;
self.visit_expr(inner);
},
Expr_::ExprArray(ref elements) => {
let elements_pat = self.next("elements");
println!("Array(ref {}) = {},", elements_pat, current);
println!(" {}.len() == {},", elements_pat, elements.len());
println!("Array(ref {}) = {};", elements_pat, current);
println!(" if {}.len() == {};", elements_pat, elements.len());
for (i, element) in elements.iter().enumerate() {
self.current = format!("{}[{}]", elements_pat, i);
self.visit_expr(element);
}
},
Expr_::ExprCall(ref _func, ref _args) => {
println!("Call(ref func, ref args) = {},", current);
println!("Call(ref func, ref args) = {};", current);
println!(" // unimplemented: `ExprCall` is not further destructured at the moment");
},
Expr_::ExprMethodCall(ref _method_name, ref _generics, ref _args) => {
println!("MethodCall(ref method_name, ref generics, ref args) = {},", current);
println!("MethodCall(ref method_name, ref generics, ref args) = {};", current);
println!(" // unimplemented: `ExprMethodCall` is not further destructured at the moment");
},
Expr_::ExprTup(ref elements) => {
let elements_pat = self.next("elements");
println!("Tup(ref {}) = {},", elements_pat, current);
println!(" {}.len() == {},", elements_pat, elements.len());
println!("Tup(ref {}) = {};", elements_pat, current);
println!(" if {}.len() == {};", elements_pat, elements.len());
for (i, element) in elements.iter().enumerate() {
self.current = format!("{}[{}]", elements_pat, i);
self.visit_expr(element);
@ -220,8 +222,8 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
let op_pat = self.next("op");
let left_pat = self.next("left");
let right_pat = self.next("right");
println!("Binary(ref {}, ref {}, ref {}) = {},", op_pat, left_pat, right_pat, current);
println!(" BinOp_::{:?} == {}.node,", op.node, op_pat);
println!("Binary(ref {}, ref {}, ref {}) = {};", op_pat, left_pat, right_pat, current);
println!(" if BinOp_::{:?} == {}.node;", op.node, op_pat);
self.current = left_pat;
self.visit_expr(left);
self.current = right_pat;
@ -229,42 +231,42 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
},
Expr_::ExprUnary(ref op, ref inner) => {
let inner_pat = self.next("inner");
println!("Unary(UnOp::{:?}, ref {}) = {},", op, inner_pat, current);
println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
self.current = inner_pat;
self.visit_expr(inner);
},
Expr_::ExprLit(ref lit) => {
let lit_pat = self.next("lit");
println!("Lit(ref {}) = {},", lit_pat, current);
println!("Lit(ref {}) = {};", lit_pat, current);
match lit.node {
LitKind::Bool(val) => println!(" let LitKind::Bool({:?}) = {}.node,", val, lit_pat),
LitKind::Char(c) => println!(" let LitKind::Char({:?}) = {}.node,", c, lit_pat),
LitKind::Byte(b) => println!(" let LitKind::Byte({}) = {}.node,", b, lit_pat),
LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat),
// FIXME: also check int type
LitKind::Int(i, _) => println!(" let LitKind::Int({}, _) = {}.node,", i, lit_pat),
LitKind::Float(..) => println!(" let LitKind::Float(..) = {}.node,", lit_pat),
LitKind::FloatUnsuffixed(_) => println!(" let LitKind::FloatUnsuffixed(_) = {}.node,", lit_pat),
LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
LitKind::Float(..) => println!(" if let LitKind::Float(..) = {}.node;", lit_pat),
LitKind::FloatUnsuffixed(_) => println!(" if let LitKind::FloatUnsuffixed(_) = {}.node;", lit_pat),
LitKind::ByteStr(ref vec) => {
let vec_pat = self.next("vec");
println!(" let LitKind::ByteStr(ref {}) = {}.node,", vec_pat, lit_pat);
println!(" let [{:?}] = **{},", vec, vec_pat);
println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
println!(" if let [{:?}] = **{};", vec, vec_pat);
},
LitKind::Str(ref text, _) => {
let str_pat = self.next("s");
println!(" let LitKind::Str(ref {}) = {}.node,", str_pat, lit_pat);
println!(" {}.as_str() == {:?}", str_pat, &*text.as_str())
println!(" if let LitKind::Str(ref {}) = {}.node;", str_pat, lit_pat);
println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str())
},
}
},
Expr_::ExprCast(ref expr, ref _ty) => {
let cast_pat = self.next("expr");
println!("Cast(ref {}, _) = {},", cast_pat, current);
println!("Cast(ref {}, _) = {};", cast_pat, current);
self.current = cast_pat;
self.visit_expr(expr);
},
Expr_::ExprType(ref expr, ref _ty) => {
let cast_pat = self.next("expr");
println!("Type(ref {}, _) = {},", cast_pat, current);
println!("Type(ref {}, _) = {};", cast_pat, current);
self.current = cast_pat;
self.visit_expr(expr);
},
@ -273,11 +275,11 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
let then_pat = self.next("then");
if let Some(ref else_) = *opt_else {
let else_pat = self.next("else_");
println!("If(ref {}, ref {}, Some(ref {})) = {},", cond_pat, then_pat, else_pat, current);
println!("If(ref {}, ref {}, Some(ref {})) = {};", cond_pat, then_pat, else_pat, current);
self.current = else_pat;
self.visit_expr(else_);
} else {
println!("If(ref {}, ref {}, None) = {},", cond_pat, then_pat, current);
println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
}
self.current = cond_pat;
self.visit_expr(cond);
@ -285,37 +287,37 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
self.visit_expr(then);
},
Expr_::ExprWhile(ref _cond, ref _body, ref _opt_label) => {
println!("While(ref cond, ref body, ref opt_label) = {},", current);
println!("While(ref cond, ref body, ref opt_label) = {};", current);
println!(" // unimplemented: `ExprWhile` is not further destructured at the moment");
},
Expr_::ExprLoop(ref _body, ref _opt_label, ref _desuraging) => {
println!("Loop(ref body, ref opt_label, ref desugaring) = {},", current);
println!("Loop(ref body, ref opt_label, ref desugaring) = {};", current);
println!(" // unimplemented: `ExprLoop` is not further destructured at the moment");
},
Expr_::ExprMatch(ref _expr, ref _arms, ref _desugaring) => {
println!("Match(ref expr, ref arms, ref desugaring) = {},", current);
println!("Match(ref expr, ref arms, ref desugaring) = {};", current);
println!(" // unimplemented: `ExprMatch` is not further destructured at the moment");
},
Expr_::ExprClosure(ref _capture_clause, ref _func, _, _, _) => {
println!("Closure(ref capture_clause, ref func, _, _, _) = {},", current);
println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
println!(" // unimplemented: `ExprClosure` is not further destructured at the moment");
},
Expr_::ExprYield(ref sub) => {
let sub_pat = self.next("sub");
println!("Yield(ref sub) = {},", current);
println!("Yield(ref sub) = {};", current);
self.current = sub_pat;
self.visit_expr(sub);
},
Expr_::ExprBlock(ref block) => {
let block_pat = self.next("block");
println!("Block(ref {}) = {},", block_pat, current);
println!("Block(ref {}) = {};", block_pat, current);
self.current = block_pat;
self.visit_block(block);
},
Expr_::ExprAssign(ref target, ref value) => {
let target_pat = self.next("target");
let value_pat = self.next("value");
println!("Assign(ref {}, ref {}) = {},", target_pat, value_pat, current);
println!("Assign(ref {}, ref {}) = {};", target_pat, value_pat, current);
self.current = target_pat;
self.visit_expr(target);
self.current = value_pat;
@ -325,8 +327,8 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
let op_pat = self.next("op");
let target_pat = self.next("target");
let value_pat = self.next("value");
println!("AssignOp(ref {}, ref {}, ref {}) = {},", op_pat, target_pat, value_pat, current);
println!(" BinOp_::{:?} == {}.node,", op.node, op_pat);
println!("AssignOp(ref {}, ref {}, ref {}) = {};", op_pat, target_pat, value_pat, current);
println!(" if BinOp_::{:?} == {}.node;", op.node, op_pat);
self.current = target_pat;
self.visit_expr(target);
self.current = value_pat;
@ -335,23 +337,23 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
Expr_::ExprField(ref object, ref field_name) => {
let obj_pat = self.next("object");
let field_name_pat = self.next("field_name");
println!("Field(ref {}, ref {}) = {},", obj_pat, field_name_pat, current);
println!(" {}.node.as_str() == {:?}", field_name_pat, field_name.node.as_str());
println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
println!(" if {}.node.as_str() == {:?}", field_name_pat, field_name.node.as_str());
self.current = obj_pat;
self.visit_expr(object);
},
Expr_::ExprTupField(ref object, ref field_id) => {
let obj_pat = self.next("object");
let field_id_pat = self.next("field_id");
println!("TupField(ref {}, ref {}) = {},", obj_pat, field_id_pat, current);
println!(" {}.node == {}", field_id_pat, field_id.node);
println!("TupField(ref {}, ref {}) = {};", obj_pat, field_id_pat, current);
println!(" if {}.node == {}", field_id_pat, field_id.node);
self.current = obj_pat;
self.visit_expr(object);
},
Expr_::ExprIndex(ref object, ref index) => {
let object_pat = self.next("object");
let index_pat = self.next("index");
println!("Index(ref {}, ref {}) = {},", object_pat, index_pat, current);
println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
self.current = object_pat;
self.visit_expr(object);
self.current = index_pat;
@ -359,13 +361,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
},
Expr_::ExprPath(ref path) => {
let path_pat = self.next("path");
println!("Path(ref {}) = {},", path_pat, current);
println!("Path(ref {}) = {};", path_pat, current);
self.current = path_pat;
self.visit_qpath(path, expr.id, expr.span);
},
Expr_::ExprAddrOf(mutability, ref inner) => {
let inner_pat = self.next("inner");
println!("AddrOf({:?}, ref {}) = {},", mutability, inner_pat, current);
println!("AddrOf({:?}, ref {}) = {};", mutability, inner_pat, current);
self.current = inner_pat;
self.visit_expr(inner);
},
@ -373,29 +375,29 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
let destination_pat = self.next("destination");
if let Some(ref value) = *opt_value {
let value_pat = self.next("value");
println!("Break(ref {}, Some(ref {})) = {},", destination_pat, value_pat, current);
println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
self.current = value_pat;
self.visit_expr(value);
} else {
println!("Break(ref {}, None) = {},", destination_pat, current);
println!("Break(ref {}, None) = {};", destination_pat, current);
}
// FIXME: implement label printing
},
Expr_::ExprAgain(ref _destination) => {
let destination_pat = self.next("destination");
println!("Again(ref {}) = {},", destination_pat, current);
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);
println!("Ret(Some(ref {})) = {};", value_pat, current);
self.current = value_pat;
self.visit_expr(value);
} else {
println!("Ret(None) = {},", current);
println!("Ret(None) = {};", current);
},
Expr_::ExprInlineAsm(_, ref _input, ref _output) => {
println!("InlineAsm(_, ref input, ref output) = {},", current);
println!("InlineAsm(_, ref input, ref output) = {};", current);
println!(" // unimplemented: `ExprInlineAsm` is not further destructured at the moment");
},
Expr_::ExprStruct(ref path, ref fields, ref opt_base) => {
@ -404,7 +406,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
if let Some(ref base) = *opt_base {
let base_pat = self.next("base");
println!(
"Struct(ref {}, ref {}, Some(ref {})) = {},",
"Struct(ref {}, ref {}, Some(ref {})) = {};",
path_pat,
fields_pat,
base_pat,
@ -413,17 +415,17 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
self.current = base_pat;
self.visit_expr(base);
} else {
println!("Struct(ref {}, ref {}, None) = {},", path_pat, fields_pat, current);
println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
}
self.current = path_pat;
self.visit_qpath(path, expr.id, expr.span);
println!(" {}.len() == {},", fields_pat, fields.len());
println!(" if {}.len() == {};", fields_pat, fields.len());
println!(" // unimplemented: field checks");
},
// FIXME: compute length (needs type info)
Expr_::ExprRepeat(ref value, _) => {
let value_pat = self.next("value");
println!("Repeat(ref {}, _) = {},", value_pat, current);
println!("Repeat(ref {}, _) = {};", value_pat, current);
println!("// unimplemented: repeat count check");
self.current = value_pat;
self.visit_expr(value);
@ -432,9 +434,9 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
}
fn visit_qpath(&mut self, path: &QPath, _: NodeId, _: Span) {
print!(" match_qpath({}, &[", self.current);
print!(" if match_qpath({}, &[", self.current);
print_path(path, &mut true);
println!("]),");
println!("]);");
}
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
NestedVisitorMap::None

View file

@ -120,13 +120,14 @@ pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
// // do stuff
// }
// ```
if_let_chain! {[
let hir::DeclLocal(ref loc) = decl.node,
let Some(ref expr) = loc.init,
let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node,
], {
return true;
}}
if_chain! {
if let hir::DeclLocal(ref loc) = decl.node;
if let Some(ref expr) = loc.init;
if let hir::ExprMatch(_, _, hir::MatchSource::ForLoopDesugar) = expr.node;
then {
return true;
}
}
// This detects a variable binding in for loop to avoid `let_unit_value`
// lint (see issue #1964).
@ -136,12 +137,13 @@ pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
// // anything
// }
// ```
if_let_chain! {[
let hir::DeclLocal(ref loc) = decl.node,
let hir::LocalSource::ForLoopDesugar = loc.source,
], {
return true;
}}
if_chain! {
if let hir::DeclLocal(ref loc) = decl.node;
if let hir::LocalSource::ForLoopDesugar = loc.source;
then {
return true;
}
}
false
}
@ -149,19 +151,20 @@ pub fn is_from_for_desugar(decl: &hir::Decl) -> bool {
/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
pub fn for_loop(expr: &hir::Expr) -> Option<(&hir::Pat, &hir::Expr, &hir::Expr)> {
if_let_chain! {[
let hir::ExprMatch(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.node,
let hir::ExprCall(_, ref iterargs) = iterexpr.node,
iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
let hir::ExprLoop(ref block, _, _) = arms[0].body.node,
block.expr.is_none(),
let [ _, _, ref let_stmt, ref body ] = *block.stmts,
let hir::StmtDecl(ref decl, _) = let_stmt.node,
let hir::DeclLocal(ref decl) = decl.node,
let hir::StmtExpr(ref expr, _) = body.node,
], {
return Some((&*decl.pat, &iterargs[0], expr));
}}
if_chain! {
if let hir::ExprMatch(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.node;
if let hir::ExprCall(_, ref iterargs) = iterexpr.node;
if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
if let hir::ExprLoop(ref block, _, _) = arms[0].body.node;
if block.expr.is_none();
if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
if let hir::StmtDecl(ref decl, _) = let_stmt.node;
if let hir::DeclLocal(ref decl) = decl.node;
if let hir::StmtExpr(ref expr, _) = body.node;
then {
return Some((&*decl.pat, &iterargs[0], expr));
}
}
None
}
@ -176,31 +179,33 @@ pub enum VecArgs<'a> {
/// Returns the arguments of the `vec!` macro if this expression was expanded
/// from `vec!`.
pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
if_let_chain!{[
let hir::ExprCall(ref fun, ref args) = expr.node,
let hir::ExprPath(ref path) = fun.node,
is_expn_of(fun.span, "vec").is_some(),
let Some(fun_def_id) = opt_def_id(resolve_node(cx, path, fun.hir_id)),
], {
return if match_def_path(cx.tcx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
// `vec![elem; size]` case
Some(VecArgs::Repeat(&args[0], &args[1]))
if_chain! {
if let hir::ExprCall(ref fun, ref args) = expr.node;
if let hir::ExprPath(ref path) = fun.node;
if is_expn_of(fun.span, "vec").is_some();
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, path, fun.hir_id));
then {
return if match_def_path(cx.tcx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
// `vec![elem; size]` case
Some(VecArgs::Repeat(&args[0], &args[1]))
}
else if match_def_path(cx.tcx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
// `vec![a, b, c]` case
if_chain! {
if let hir::ExprBox(ref boxed) = args[0].node;
if let hir::ExprArray(ref args) = boxed.node;
then {
return Some(VecArgs::Vec(&*args));
}
}
None
}
else {
None
};
}
else if match_def_path(cx.tcx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
// `vec![a, b, c]` case
if_let_chain!{[
let hir::ExprBox(ref boxed) = args[0].node,
let hir::ExprArray(ref args) = boxed.node
], {
return Some(VecArgs::Vec(&*args));
}}
None
}
else {
None
};
}}
}
None
}

View file

@ -37,63 +37,6 @@ pub use self::hir_utils::{SpanlessEq, SpanlessHash};
pub type MethodArgs = HirVec<P<Expr>>;
/// Produce a nested chain of if-lets and ifs from the patterns:
///
/// ```rust,ignore
/// if_let_chain! {[
/// let Some(y) = x,
/// y.len() == 2,
/// let Some(z) = y,
/// ], {
/// block
/// }}
/// ```
///
/// becomes
///
/// ```rust,ignore
/// if let Some(y) = x {
/// if y.len() == 2 {
/// if let Some(z) = y {
/// block
/// }
/// }
/// }
/// ```
#[macro_export]
macro_rules! if_let_chain {
([let $pat:pat = $expr:expr, $($tt:tt)+], $block:block) => {
if let $pat = $expr {
if_let_chain!{ [$($tt)+], $block }
}
};
([let $pat:pat = $expr:expr], $block:block) => {
if let $pat = $expr {
$block
}
};
([let $pat:pat = $expr:expr,], $block:block) => {
if let $pat = $expr {
$block
}
};
([$expr:expr, $($tt:tt)+], $block:block) => {
if $expr {
if_let_chain!{ [$($tt)+], $block }
}
};
([$expr:expr], $block:block) => {
if $expr {
$block
}
};
([$expr:expr,], $block:block) => {
if $expr {
$block
}
};
}
pub mod higher;
/// Returns true if the two spans come from differing expansions (i.e. one is
@ -1004,13 +947,14 @@ pub fn is_self(slf: &Arg) -> bool {
}
pub fn is_self_ty(slf: &hir::Ty) -> bool {
if_let_chain! {[
let TyPath(ref qp) = slf.node,
let QPath::Resolved(None, ref path) = *qp,
let Def::SelfTy(..) = path.def,
], {
return true
}}
if_chain! {
if let TyPath(ref qp) = slf.node;
if let QPath::Resolved(None, ref path) = *qp;
if let Def::SelfTy(..) = path.def;
then {
return true
}
}
false
}
@ -1022,16 +966,17 @@ pub fn iter_input_pats<'tcx>(decl: &FnDecl, body: &'tcx Body) -> impl Iterator<I
/// expanded from `?` operator or `try` macro.
pub fn is_try(expr: &Expr) -> Option<&Expr> {
fn is_ok(arm: &Arm) -> bool {
if_let_chain! {[
let PatKind::TupleStruct(ref path, ref pat, None) = arm.pats[0].node,
match_qpath(path, &paths::RESULT_OK[1..]),
let PatKind::Binding(_, defid, _, None) = pat[0].node,
let ExprPath(QPath::Resolved(None, ref path)) = arm.body.node,
let Def::Local(lid) = path.def,
lid == defid,
], {
return true;
}}
if_chain! {
if let PatKind::TupleStruct(ref path, ref pat, None) = arm.pats[0].node;
if match_qpath(path, &paths::RESULT_OK[1..]);
if let PatKind::Binding(_, defid, _, None) = pat[0].node;
if let ExprPath(QPath::Resolved(None, ref path)) = arm.body.node;
if let Def::Local(lid) = path.def;
if lid == defid;
then {
return true;
}
}
false
}
@ -1049,15 +994,16 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
return Some(expr);
}
if_let_chain! {[
arms.len() == 2,
arms[0].pats.len() == 1 && arms[0].guard.is_none(),
arms[1].pats.len() == 1 && arms[1].guard.is_none(),
(is_ok(&arms[0]) && is_err(&arms[1])) ||
(is_ok(&arms[1]) && is_err(&arms[0])),
], {
return Some(expr);
}}
if_chain! {
if arms.len() == 2;
if arms[0].pats.len() == 1 && arms[0].guard.is_none();
if arms[1].pats.len() == 1 && arms[1].guard.is_none();
if (is_ok(&arms[0]) && is_err(&arms[1])) ||
(is_ok(&arms[1]) && is_err(&arms[0]));
then {
return Some(expr);
}
}
}
None

View file

@ -6,6 +6,7 @@ pub const ARC: [&str; 3] = ["alloc", "arc", "Arc"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 3] = ["alloc", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BOX: [&str; 3] = ["std", "boxed", "Box"];
@ -27,6 +28,7 @@ pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];

View file

@ -35,25 +35,27 @@ impl LintPass for Pass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
// search for `&vec![_]` expressions where the adjusted type is `&[_]`
if_let_chain!{[
let ty::TyRef(_, ref ty) = cx.tables.expr_ty_adjusted(expr).sty,
let ty::TySlice(..) = ty.ty.sty,
let ExprAddrOf(_, ref addressee) = expr.node,
let Some(vec_args) = higher::vec_macro(cx, addressee),
], {
check_vec_macro(cx, &vec_args, expr.span);
}}
if_chain! {
if let ty::TyRef(_, ref ty) = cx.tables.expr_ty_adjusted(expr).sty;
if let ty::TySlice(..) = ty.ty.sty;
if let ExprAddrOf(_, ref addressee) = expr.node;
if let Some(vec_args) = higher::vec_macro(cx, addressee);
then {
check_vec_macro(cx, &vec_args, expr.span);
}
}
// search for `for _ in vec![…]`
if_let_chain!{[
let Some((_, arg, _)) = higher::for_loop(expr),
let Some(vec_args) = higher::vec_macro(cx, arg),
is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg))),
], {
// report the error around the `vec!` not inside `<std macros>:`
let span = arg.span.ctxt().outer().expn_info().map(|info| info.call_site).expect("unable to get call_site");
check_vec_macro(cx, &vec_args, span);
}}
if_chain! {
if let Some((_, arg, _)) = higher::for_loop(expr);
if let Some(vec_args) = higher::vec_macro(cx, arg);
if is_copy(cx, vec_type(cx.tables.expr_ty_adjusted(arg)));
then {
// report the error around the `vec!` not inside `<std macros>:`
let span = arg.span.ctxt().outer().expn_info().map(|info| info.call_site).expect("unable to get call_site");
check_vec_macro(cx, &vec_args, span);
}
}
}
}

View file

@ -31,27 +31,28 @@ impl LintPass for Pass {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
// check for instances of 0.0/0.0
if_let_chain! {[
let ExprBinary(ref op, ref left, ref right) = expr.node,
let BinOp_::BiDiv = op.node,
if_chain! {
if let ExprBinary(ref op, ref left, ref right) = expr.node;
if let BinOp_::BiDiv = op.node;
// TODO - constant_simple does not fold many operations involving floats.
// That's probably fine for this lint - it's pretty unlikely that someone would
// do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left),
let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right),
Ok(0.0) == lhs_value.parse(),
Ok(0.0) == rhs_value.parse()
], {
// since we're about to suggest a use of std::f32::NaN or std::f64::NaN,
// match the precision of the literals that are given.
let float_type = match (lhs_width, rhs_width) {
(FloatWidth::F64, _)
| (_, FloatWidth::F64) => "f64",
_ => "f32"
};
span_help_and_lint(cx, ZERO_DIVIDED_BY_ZERO, expr.span,
"constant division of 0.0 with 0.0 will always result in NaN",
&format!("Consider using `std::{}::NAN` if you would like a constant representing NaN", float_type));
}}
if let Some(Constant::Float(ref lhs_value, lhs_width)) = constant_simple(cx, left);
if let Some(Constant::Float(ref rhs_value, rhs_width)) = constant_simple(cx, right);
if Ok(0.0) == lhs_value.parse();
if Ok(0.0) == rhs_value.parse();
then {
// since we're about to suggest a use of std::f32::NaN or std::f64::NaN,
// match the precision of the literals that are given.
let float_type = match (lhs_width, rhs_width) {
(FloatWidth::F64, _)
| (_, FloatWidth::F64) => "f64",
_ => "f32"
};
span_help_and_lint(cx, ZERO_DIVIDED_BY_ZERO, expr.span,
"constant division of 0.0 with 0.0 will always result in NaN",
&format!("Consider using `std::{}::NAN` if you would like a constant representing NaN", float_type));
}
}
}
}

31
rust-update Executable file
View file

@ -0,0 +1,31 @@
#!/bin/sh
if [ "$1" = '-h' ] ; then
echo 'Updates rustc & clippy'
echo 'It first checks if clippy would compile at currentl nightly and if so, it updates.'
echo 'Options:'
echo '-h: This help message'
echo '-f: Skips the check and just updates'
exit
fi
set -ex
renice -n 10 -p $$
export CARGO_INCREMENTAL=0
export RUSTFLAGS='-C target-cpu=native'
try_out() {
export RUSTUP_HOME=$HOME/.rustup-attempt
test -d $RUSTUP_HOME || (rustup toolchain add nightly && rustup default nightly)
rustup update
cargo +nightly install --force clippy
unset RUSTUP_HOME
export RUSTUP_HOME
}
[ "$1" = '-f' ] || try_out
rustup update
cargo +nightly install --force clippy

View file

@ -6,6 +6,14 @@ error: &-masking with zero
|
= note: `-D bad-bit-mask` implied by `-D warnings`
error: this operation will always return zero. This is likely not the intended outcome
--> $DIR/bit_masks.rs:12:5
|
12 | x & 0 == 0;
| ^^^^^
|
= note: `-D erasing-op` implied by `-D warnings`
error: incompatible bit mask: `_ & 2` can never be equal to `1`
--> $DIR/bit_masks.rs:15:5
|
@ -48,6 +56,12 @@ error: &-masking with zero
35 | 0 & x == 0;
| ^^^^^^^^^^
error: this operation will always return zero. This is likely not the intended outcome
--> $DIR/bit_masks.rs:35:5
|
35 | 0 & x == 0;
| ^^^^^
error: incompatible bit mask: `_ | 2` will always be higher than `1`
--> $DIR/bit_masks.rs:39:5
|

12
tests/ui/erasing_op.rs Normal file
View file

@ -0,0 +1,12 @@
#[allow(no_effect)]
#[warn(erasing_op)]
fn main() {
let x: u8 = 0;
x * 0;
0 & x;
0 / x;
}

View file

@ -0,0 +1,20 @@
error: this operation will always return zero. This is likely not the intended outcome
--> $DIR/erasing_op.rs:9:5
|
9 | x * 0;
| ^^^^^
|
= note: `-D erasing-op` implied by `-D warnings`
error: this operation will always return zero. This is likely not the intended outcome
--> $DIR/erasing_op.rs:10:5
|
10 | 0 & x;
| ^^^^^
error: this operation will always return zero. This is likely not the intended outcome
--> $DIR/erasing_op.rs:11:5
|
11 | 0 / x;
| ^^^^^

View file

@ -0,0 +1,64 @@
#![deny(fallible_impl_from)]
// docs example
struct Foo(i32);
impl From<String> for Foo {
fn from(s: String) -> Self {
Foo(s.parse().unwrap())
}
}
struct Valid(Vec<u8>);
impl<'a> From<&'a str> for Valid {
fn from(s: &'a str) -> Valid {
Valid(s.to_owned().into_bytes())
}
}
impl From<usize> for Valid {
fn from(i: usize) -> Valid {
Valid(Vec::with_capacity(i))
}
}
struct Invalid;
impl From<usize> for Invalid {
fn from(i: usize) -> Invalid {
if i != 42 {
panic!();
}
Invalid
}
}
impl From<Option<String>> for Invalid {
fn from(s: Option<String>) -> Invalid {
let s = s.unwrap();
if !s.is_empty() {
panic!(42);
} else if s.parse::<u32>().unwrap() != 42 {
panic!("{:?}", s);
}
Invalid
}
}
trait ProjStrTrait {
type ProjString;
}
impl<T> ProjStrTrait for Box<T> {
type ProjString = String;
}
impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
if s.parse::<u32>().ok().unwrap() != 42 {
panic!("{:?}", s);
}
Invalid
}
}
fn main() {}

View file

@ -0,0 +1,91 @@
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:5:1
|
5 | / impl From<String> for Foo {
6 | | fn from(s: String) -> Self {
7 | | Foo(s.parse().unwrap())
8 | | }
9 | | }
| |_^
|
note: lint level defined here
--> $DIR/fallible_impl_from.rs:1:9
|
1 | #![deny(fallible_impl_from)]
| ^^^^^^^^^^^^^^^^^^
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:7:13
|
7 | Foo(s.parse().unwrap())
| ^^^^^^^^^^^^^^^^^^
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:28:1
|
28 | / impl From<usize> for Invalid {
29 | | fn from(i: usize) -> Invalid {
30 | | if i != 42 {
31 | | panic!();
... |
34 | | }
35 | | }
| |_^
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:31:13
|
31 | panic!();
| ^^^^^^^^^
= note: this error originates in a macro outside of the current crate
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:37:1
|
37 | / impl From<Option<String>> for Invalid {
38 | | fn from(s: Option<String>) -> Invalid {
39 | | let s = s.unwrap();
40 | | if !s.is_empty() {
... |
46 | | }
47 | | }
| |_^
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:39:17
|
39 | let s = s.unwrap();
| ^^^^^^^^^^
40 | if !s.is_empty() {
41 | panic!(42);
| ^^^^^^^^^^^
42 | } else if s.parse::<u32>().unwrap() != 42 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
43 | panic!("{:?}", s);
| ^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate
error: consider implementing `TryFrom` instead
--> $DIR/fallible_impl_from.rs:55:1
|
55 | / impl<'a> From<&'a mut <Box<u32> as ProjStrTrait>::ProjString> for Invalid {
56 | | fn from(s: &'a mut <Box<u32> as ProjStrTrait>::ProjString) -> Invalid {
57 | | if s.parse::<u32>().ok().unwrap() != 42 {
58 | | panic!("{:?}", s);
... |
61 | | }
62 | | }
| |_^
|
= help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail.
note: potential failure(s)
--> $DIR/fallible_impl_from.rs:57:12
|
57 | if s.parse::<u32>().ok().unwrap() != 42 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
58 | panic!("{:?}", s);
| ^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate

View file

@ -27,4 +27,7 @@ fn main() {
x & NEG_ONE; //no error, as we skip lookups (for now)
-1 & x;
let u : u8 = 0;
u & 255;
}

View file

@ -42,3 +42,9 @@ error: the operation is ineffective. Consider reducing it to `x`
29 | -1 & x;
| ^^^^^^
error: the operation is ineffective. Consider reducing it to `u`
--> $DIR/identity_op.rs:32:5
|
32 | u & 255;
| ^^^^^^^

View file

@ -1,14 +1,15 @@
if_let_chain!{[
let Expr_::ExprBinary(ref op, ref left, ref right) = expr.node,
BinOp_::BiEq == op.node,
let Expr_::ExprBinary(ref op1, ref left1, ref right1) = left.node,
BinOp_::BiBitAnd == op1.node,
let Expr_::ExprPath(ref path) = left1.node,
match_qpath(path, &["x"]),
let Expr_::ExprLit(ref lit) = right1.node,
let LitKind::Int(15, _) = lit.node,
let Expr_::ExprLit(ref lit1) = right.node,
let LitKind::Int(0, _) = lit1.node,
], {
// report your lint here
}}
if_chain! {
if let Expr_::ExprBinary(ref op, ref left, ref right) = expr.node;
if BinOp_::BiEq == op.node;
if let Expr_::ExprBinary(ref op1, ref left1, ref right1) = left.node;
if BinOp_::BiBitAnd == op1.node;
if let Expr_::ExprPath(ref path) = left1.node;
if match_qpath(path, &["x"]);
if let Expr_::ExprLit(ref lit) = right1.node;
if let LitKind::Int(15, _) = lit.node;
if let Expr_::ExprLit(ref lit1) = right.node;
if let LitKind::Int(0, _) = lit1.node;
then {
// report your lint here
}
}

118
tests/ui/useless_asref.rs Normal file
View file

@ -0,0 +1,118 @@
#![deny(useless_asref)]
use std::fmt::Debug;
struct FakeAsRef;
#[allow(should_implement_trait)]
impl FakeAsRef {
fn as_ref(&self) -> &Self { self }
}
struct MoreRef;
impl<'a, 'b, 'c> AsRef<&'a &'b &'c MoreRef> for MoreRef {
fn as_ref(&self) -> &&'a &'b &'c MoreRef {
&&&&MoreRef
}
}
fn foo_rstr(x: &str) { println!("{:?}", x); }
fn foo_rslice(x: &[i32]) { println!("{:?}", x); }
fn foo_mrslice(x: &mut [i32]) { println!("{:?}", x); }
fn foo_rrrrmr(_: &&&&MoreRef) { println!("so many refs"); }
fn not_ok() {
let rstr: &str = "hello";
let mut mrslice: &mut [i32] = &mut [1,2,3];
{
let rslice: &[i32] = &*mrslice;
foo_rstr(rstr.as_ref());
foo_rstr(rstr);
foo_rslice(rslice.as_ref());
foo_rslice(rslice);
}
{
foo_mrslice(mrslice.as_mut());
foo_mrslice(mrslice);
foo_rslice(mrslice.as_ref());
foo_rslice(mrslice);
}
{
let rrrrrstr = &&&&rstr;
let rrrrrslice = &&&&&*mrslice;
foo_rslice(rrrrrslice.as_ref());
foo_rslice(rrrrrslice);
foo_rstr(rrrrrstr.as_ref());
foo_rstr(rrrrrstr);
}
{
let mrrrrrslice = &mut &mut &mut &mut mrslice;
foo_mrslice(mrrrrrslice.as_mut());
foo_mrslice(mrrrrrslice);
foo_rslice(mrrrrrslice.as_ref());
foo_rslice(mrrrrrslice);
}
foo_rrrrmr((&&&&MoreRef).as_ref());
generic_not_ok(mrslice);
generic_ok(mrslice);
}
fn ok() {
let string = "hello".to_owned();
let mut arr = [1,2,3];
let mut vec = vec![1,2,3];
{
foo_rstr(string.as_ref());
foo_rslice(arr.as_ref());
foo_rslice(vec.as_ref());
}
{
foo_mrslice(arr.as_mut());
foo_mrslice(vec.as_mut());
}
{
let rrrrstring = &&&&string;
let rrrrarr = &&&&arr;
let rrrrvec = &&&&vec;
foo_rstr(rrrrstring.as_ref());
foo_rslice(rrrrarr.as_ref());
foo_rslice(rrrrvec.as_ref());
}
{
let mrrrrarr = &mut &mut &mut &mut arr;
let mrrrrvec = &mut &mut &mut &mut vec;
foo_mrslice(mrrrrarr.as_mut());
foo_mrslice(mrrrrvec.as_mut());
}
FakeAsRef.as_ref();
foo_rrrrmr(MoreRef.as_ref());
generic_not_ok(arr.as_mut());
generic_ok(&mut arr);
}
fn foo_mrt<T: Debug + ?Sized>(t: &mut T) { println!("{:?}", t); }
fn foo_rt<T: Debug + ?Sized>(t: &T) { println!("{:?}", t); }
fn generic_not_ok<T: AsMut<T> + AsRef<T> + Debug + ?Sized>(mrt: &mut T) {
foo_mrt(mrt.as_mut());
foo_mrt(mrt);
foo_rt(mrt.as_ref());
foo_rt(mrt);
}
fn generic_ok<U: AsMut<T> + AsRef<T> + ?Sized, T: Debug + ?Sized>(mru: &mut U) {
foo_mrt(mru.as_mut());
foo_rt(mru.as_ref());
}
fn main() {
not_ok();
ok();
}

View file

@ -0,0 +1,72 @@
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:31:18
|
31 | foo_rstr(rstr.as_ref());
| ^^^^^^^^^^^^^ help: try this: `rstr`
|
note: lint level defined here
--> $DIR/useless_asref.rs:1:9
|
1 | #![deny(useless_asref)]
| ^^^^^^^^^^^^^
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:33:20
|
33 | foo_rslice(rslice.as_ref());
| ^^^^^^^^^^^^^^^ help: try this: `rslice`
error: this call to `as_mut` does nothing
--> $DIR/useless_asref.rs:37:21
|
37 | foo_mrslice(mrslice.as_mut());
| ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:39:20
|
39 | foo_rslice(mrslice.as_ref());
| ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:46:20
|
46 | foo_rslice(rrrrrslice.as_ref());
| ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:48:18
|
48 | foo_rstr(rrrrrstr.as_ref());
| ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
error: this call to `as_mut` does nothing
--> $DIR/useless_asref.rs:53:21
|
53 | foo_mrslice(mrrrrrslice.as_mut());
| ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:55:20
|
55 | foo_rslice(mrrrrrslice.as_ref());
| ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:58:16
|
58 | foo_rrrrmr((&&&&MoreRef).as_ref());
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
error: this call to `as_mut` does nothing
--> $DIR/useless_asref.rs:104:13
|
104 | foo_mrt(mrt.as_mut());
| ^^^^^^^^^^^^ help: try this: `mrt`
error: this call to `as_ref` does nothing
--> $DIR/useless_asref.rs:106:12
|
106 | foo_rt(mrt.as_ref());
| ^^^^^^^^^^^^ help: try this: `mrt`