This commit is contained in:
Oliver Schneider 2017-08-09 09:30:56 +02:00
parent 705c6ec2a4
commit b25b6b3355
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
95 changed files with 3090 additions and 2022 deletions

View File

@ -7,9 +7,11 @@ use utils::span_lint;
/// **What it does:** Checks for floating point literals that approximate
/// constants which are defined in
/// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
/// [`std::f32::consts`](https://doc.rust-lang.
/// org/stable/std/f32/consts/#constants)
/// or
/// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
/// [`std::f64::consts`](https://doc.rust-lang.
/// org/stable/std/f64/consts/#constants),
/// respectively, suggesting to use the predefined constant.
///
/// **Why is this bad?** Usually, the definition in the standard library is more
@ -33,7 +35,8 @@ declare_lint! {
}
// Tuples are of the form (constant, name, min_digits)
const KNOWN_CONSTS: &'static [(f64, &'static str, usize)] = &[(f64::E, "E", 4),
const KNOWN_CONSTS: &'static [(f64, &'static str, usize)] = &[
(f64::E, "E", 4),
(f64::FRAC_1_PI, "FRAC_1_PI", 4),
(f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5),
(f64::FRAC_2_PI, "FRAC_2_PI", 5),
@ -48,7 +51,8 @@ const KNOWN_CONSTS: &'static [(f64, &'static str, usize)] = &[(f64::E, "E", 4),
(f64::LOG10_E, "LOG10_E", 5),
(f64::LOG2_E, "LOG2_E", 5),
(f64::PI, "PI", 3),
(f64::SQRT_2, "SQRT_2", 5)];
(f64::SQRT_2, "SQRT_2", 5),
];
#[derive(Copy, Clone)]
pub struct Pass;
@ -81,13 +85,17 @@ fn check_known_consts(cx: &LateContext, e: &Expr, s: &symbol::Symbol, module: &s
if s.parse::<f64>().is_ok() {
for &(constant, name, min_digits) in KNOWN_CONSTS {
if is_approx_const(constant, &s, min_digits) {
span_lint(cx,
span_lint(
cx,
APPROX_CONSTANT,
e.span,
&format!("approximate value of `{}::consts::{}` found. \
&format!(
"approximate value of `{}::consts::{}` found. \
Consider using it directly",
module,
&name));
&name
),
);
return;
}
}

View File

@ -8,7 +8,8 @@ use rustc::hir;
use syntax::ast::RangeLimits;
use utils::{self, higher};
/// **What it does:** Checks for out of bounds array indexing with a constant index.
/// **What it does:** Checks for out of bounds array indexing with a constant
/// index.
///
/// **Why is this bad?** This will always panic at runtime.
///
@ -61,8 +62,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Array with known size can be checked statically
let ty = cx.tables.expr_ty(array);
if let ty::TyArray(_, size) = ty.sty {
let size = ConstInt::Usize(ConstUsize::new(size as u64, cx.sess().target.uint_type)
.expect("array size is invalid"));
let size = ConstInt::Usize(
ConstUsize::new(size as u64, cx.sess().target.uint_type).expect("array size is invalid"),
);
let parent_item = cx.tcx.hir.get_parent(e.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
@ -80,12 +82,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
// Index is a constant range
if let Some(range) = higher::range(index) {
let start = range.start
.map(|start| constcx.eval(start))
.map(|v| v.ok());
let end = range.end
.map(|end| constcx.eval(end))
.map(|v| v.ok());
let start = range.start.map(|start| constcx.eval(start)).map(|v| v.ok());
let end = range.end.map(|end| constcx.eval(end)).map(|v| v.ok());
if let Some((start, end)) = to_const_range(&start, &end, range.limits, size) {
if start > size || end > size {
@ -111,12 +109,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
}
}
/// Returns an option containing a tuple with the start and end (exclusive) of the range.
/// Returns an option containing a tuple with the start and end (exclusive) of
/// the range.
fn to_const_range(
start: &Option<Option<ConstVal>>,
end: &Option<Option<ConstVal>>,
limits: RangeLimits,
array_size: ConstInt
array_size: ConstInt,
) -> Option<(ConstInt, ConstInt)> {
let start = match *start {
Some(Some(ConstVal::Integral(x))) => x,
@ -144,8 +143,7 @@ fn to_const_range(
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}
.expect("such a big array is not realistic")
}.expect("such a big array is not realistic")
} else {
x
}

View File

@ -4,12 +4,14 @@ use syntax::ast;
use utils::{span_lint_and_then, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
use utils::{higher, sugg};
/// **What it does:** Checks for compound assignment operations (`+=` and similar).
/// **What it does:** Checks for compound assignment operations (`+=` and
/// similar).
///
/// **Why is this bad?** Projects with many developers from languages without
/// those operations may find them unreadable and not worth their weight.
///
/// **Known problems:** Types implementing `OpAssign` don't necessarily implement `Op`.
/// **Known problems:** Types implementing `OpAssign` don't necessarily
/// implement `Op`.
///
/// **Example:**
/// ```rust
@ -20,7 +22,8 @@ declare_restriction_lint! {
"any compound assignment operation"
}
/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a` patterns.
/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
/// patterns.
///
/// **Why is this bad?** These can be written as the shorter `a op= b`.
///
@ -41,7 +44,8 @@ declare_lint! {
/// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
///
/// **Why is this bad?** Most likely these are bugs where one meant to write `a op= b`.
/// **Why is this bad?** Most likely these are bugs where one meant to write `a
/// op= b`.
///
/// **Known problems:** Someone might actually mean `a op= a op b`, but that
/// should rather be written as `a = (2 * a) op b` where applicable.
@ -75,9 +79,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
let lhs = &sugg::Sugg::hir(cx, lhs, "..");
let rhs = &sugg::Sugg::hir(cx, rhs, "..");
db.span_suggestion(expr.span,
db.span_suggestion(
expr.span,
"replace it with",
format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)));
format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)),
);
});
if let hir::ExprBinary(binop, ref l, ref r) = rhs.node {
if op.node == binop.node {
@ -144,7 +150,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
}
}
}
if ops!(op.node,
if ops!(
op.node,
cx,
ty,
rty,
@ -159,20 +166,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
BitOr: BiBitOr,
BitXor: BiBitXor,
Shr: BiShr,
Shl: BiShl) {
span_lint_and_then(cx,
Shl: BiShl
)
{
span_lint_and_then(
cx,
ASSIGN_OP_PATTERN,
expr.span,
"manual implementation of an assign operation",
|db| if let (Some(snip_a), Some(snip_r)) =
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) {
db.span_suggestion(expr.span,
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
{
db.span_suggestion(
expr.span,
"replace it with",
format!("{} {}= {}",
snip_a,
op.node.as_str(),
snip_r));
});
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
);
},
);
}
};
// a = a op b

View File

@ -35,12 +35,15 @@ declare_lint! {
"use of `#[inline(always)]`"
}
/// **What it does:** Checks for `extern crate` and `use` items annotated with lint attributes
/// **What it does:** Checks for `extern crate` and `use` items annotated with
/// lint attributes
///
/// **Why is this bad?** Lint attributes have no effect on crate imports. Most likely a `!` was
/// **Why is this bad?** Lint attributes have no effect on crate imports. Most
/// likely a `!` was
/// forgotten
///
/// **Known problems:** Technically one might allow `unused_import` on a `use` item,
/// **Known problems:** Technically one might allow `unused_import` on a `use`
/// item,
/// but it's easier to remove the unused item.
///
/// **Example:**
@ -124,14 +127,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
}
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
if sugg.len() > 1 {
span_lint_and_then(cx,
span_lint_and_then(
cx,
USELESS_ATTRIBUTE,
attr.span,
"useless lint attribute",
|db| {
sugg.insert(1, '!');
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
});
db.span_suggestion(
attr.span,
"if you just forgot a `!`, use",
sugg,
);
},
);
}
}
},
@ -191,7 +200,10 @@ fn is_relevant_block(tcx: TyCtxt, tables: &ty::TypeckTables, block: &Block) -> b
StmtSemi(ref expr, _) => is_relevant_expr(tcx, tables, expr),
}
} else {
block.expr.as_ref().map_or(false, |e| is_relevant_expr(tcx, tables, e))
block.expr.as_ref().map_or(
false,
|e| is_relevant_expr(tcx, tables, e),
)
}
}
@ -224,11 +236,15 @@ fn check_attrs(cx: &LateContext, span: Span, name: &Name, attrs: &[Attribute]) {
continue;
}
if is_word(&values[0], "always") {
span_lint(cx,
span_lint(
cx,
INLINE_ALWAYS,
attr.span,
&format!("you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
name));
&format!(
"you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
name
),
);
}
}
}
@ -240,10 +256,12 @@ fn check_semver(cx: &LateContext, span: Span, lit: &Lit) {
return;
}
}
span_lint(cx,
span_lint(
cx,
DEPRECATED_SEMVER,
span,
"the since field must contain a semver-compliant version");
"the since field must contain a semver-compliant version",
);
}
fn is_word(nmi: &NestedMetaItem, expected: &str) -> bool {

View File

@ -74,7 +74,8 @@ declare_lint! {
/// **What it does:** Checks for bit masks that can be replaced by a call
/// to `trailing_zeros`
///
/// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15 == 0`
/// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
/// == 0`
///
/// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
///
@ -162,12 +163,16 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
BiBitAnd => {
if mask_value & cmp_value != cmp_value {
if cmp_value != 0 {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` can never be equal to `{}`",
&format!(
"incompatible bit mask: `_ & {}` can never be equal to `{}`",
mask_value,
cmp_value));
cmp_value
),
);
}
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
@ -175,12 +180,16 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
},
BiBitOr => {
if mask_value | cmp_value != cmp_value {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` can never be equal to `{}`",
&format!(
"incompatible bit mask: `_ | {}` can never be equal to `{}`",
mask_value,
cmp_value));
cmp_value
),
);
}
},
_ => (),
@ -190,24 +199,32 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
match bit_op {
BiBitAnd => {
if mask_value < cmp_value {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` will always be lower than `{}`",
&format!(
"incompatible bit mask: `_ & {}` will always be lower than `{}`",
mask_value,
cmp_value));
cmp_value
),
);
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
}
},
BiBitOr => {
if mask_value >= cmp_value {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` will never be lower than `{}`",
&format!(
"incompatible bit mask: `_ | {}` will never be lower than `{}`",
mask_value,
cmp_value));
cmp_value
),
);
} else {
check_ineffective_lt(cx, *span, mask_value, cmp_value, "|");
}
@ -220,24 +237,32 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
match bit_op {
BiBitAnd => {
if mask_value <= cmp_value {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` will never be higher than `{}`",
&format!(
"incompatible bit mask: `_ & {}` will never be higher than `{}`",
mask_value,
cmp_value));
cmp_value
),
);
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
}
},
BiBitOr => {
if mask_value > cmp_value {
span_lint(cx,
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` will always be higher than `{}`",
&format!(
"incompatible bit mask: `_ | {}` will always be higher than `{}`",
mask_value,
cmp_value));
cmp_value
),
);
} else {
check_ineffective_gt(cx, *span, mask_value, cmp_value, "|");
}
@ -252,25 +277,33 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
fn check_ineffective_lt(cx: &LateContext, span: Span, m: u128, c: u128, op: &str) {
if c.is_power_of_two() && m < c {
span_lint(cx,
span_lint(
cx,
INEFFECTIVE_BIT_MASK,
span,
&format!("ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
&format!(
"ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
c));
c
),
);
}
}
fn check_ineffective_gt(cx: &LateContext, span: Span, m: u128, c: u128, op: &str) {
if (c + 1).is_power_of_two() && m <= c {
span_lint(cx,
span_lint(
cx,
INEFFECTIVE_BIT_MASK,
span,
&format!("ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
&format!(
"ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
c));
c
),
);
}
}

View File

@ -41,10 +41,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlackListedName {
fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat) {
if let PatKind::Binding(_, _, ref ident, _) = pat.node {
if self.blacklist.iter().any(|s| ident.node == *s) {
span_lint(cx,
span_lint(
cx,
BLACKLISTED_NAME,
ident.span,
&format!("use of a blacklisted/placeholder name `{}`", ident.node));
&format!("use of a blacklisted/placeholder name `{}`", ident.node),
);
}
}
}

View File

@ -87,27 +87,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
if in_macro(expr.span) || differing_macro_contexts(expr.span, ex.span) {
return;
}
span_help_and_lint(cx,
span_help_and_lint(
cx,
BLOCK_IN_IF_CONDITION_EXPR,
check.span,
BRACED_EXPR_MESSAGE,
&format!("try\nif {} {} ... ",
snippet_block(cx, ex.span, ".."),
snippet_block(cx, then.span, "..")));
snippet_block(cx, then.span, "..")),
);
}
} else {
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
let span = block.expr.as_ref().map_or_else(
|| block.stmts[0].span,
|e| e.span,
);
if in_macro(span) || differing_macro_contexts(expr.span, span) {
return;
}
// move block higher
span_help_and_lint(cx,
span_help_and_lint(
cx,
BLOCK_IN_IF_CONDITION_STMT,
check.span,
COMPLEX_BLOCK_MESSAGE,
&format!("try\nlet res = {};\nif res {} ... ",
snippet_block(cx, block.span, ".."),
snippet_block(cx, then.span, "..")));
snippet_block(cx, then.span, "..")),
);
}
}
} else {

View File

@ -61,7 +61,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
_: &'tcx FnDecl,
body: &'tcx Body,
_: Span,
_: NodeId
_: NodeId,
) {
NonminimalBoolVisitor { cx: cx }.visit_body(body)
}
@ -115,8 +115,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
}
for (n, expr) in self.terminals.iter().enumerate() {
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(e, expr) {
#[allow(cast_possible_truncation)]
return Ok(Bool::Term(n as u8));
#[allow(cast_possible_truncation)] return Ok(Bool::Term(n as u8));
}
let negated = match e.node {
ExprBinary(binop, ref lhs, ref rhs) => {
@ -141,15 +140,13 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
_ => continue,
};
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(&negated, expr) {
#[allow(cast_possible_truncation)]
return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
#[allow(cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
}
}
let n = self.terminals.len();
self.terminals.push(e);
if n < 32 {
#[allow(cast_possible_truncation)]
Ok(Bool::Term(n as u8))
#[allow(cast_possible_truncation)] Ok(Bool::Term(n as u8))
} else {
Err("too many literals".to_owned())
}
@ -353,26 +350,31 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
continue 'simplified;
}
if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
span_lint_and_then(self.cx,
span_lint_and_then(
self.cx,
LOGIC_BUG,
e.span,
"this boolean expression contains a logic bug",
|db| {
db.span_help(h2q.terminals[i].span,
db.span_help(
h2q.terminals[i].span,
"this expression can be optimized out by applying boolean operations to the \
outer expression");
db.span_suggestion(e.span,
outer expression",
);
db.span_suggestion(
e.span,
"it would look like the following",
suggest(self.cx, suggestion, &h2q.terminals));
});
suggest(self.cx, suggestion, &h2q.terminals),
);
},
);
// don't also lint `NONMINIMAL_BOOL`
return;
}
// if the number of occurrences of a terminal decreases or any of the stats
// decreases while none increases
improvement |= (stats.terminals[i] > simplified_stats.terminals[i]) ||
(stats.negations > simplified_stats.negations &&
stats.ops == simplified_stats.ops) ||
(stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops) ||
(stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
}
if improvement {
@ -380,17 +382,22 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
}
}
if !improvements.is_empty() {
span_lint_and_then(self.cx,
span_lint_and_then(
self.cx,
NONMINIMAL_BOOL,
e.span,
"this boolean expression can be simplified",
|db| {
db.span_suggestions(e.span,
db.span_suggestions(
e.span,
"try",
improvements.into_iter()
improvements
.into_iter()
.map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
.collect());
});
.collect(),
);
},
);
}
}
}

View File

@ -19,7 +19,8 @@ use utils::{in_macro, snippet_block, span_lint_and_then, span_lint_and_sugg};
use utils::sugg::Sugg;
/// **What it does:** Checks for nested `if` statements which can be collapsed
/// by `&&`-combining their conditions and for `else { if ... }` expressions that
/// by `&&`-combining their conditions and for `else { if ... }` expressions
/// that
/// can be collapsed to `else if ...`.
///
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which

View File

@ -83,7 +83,8 @@ impl PartialEq for Constant {
impl Hash for Constant {
fn hash<H>(&self, state: &mut H)
where H: Hasher
where
H: Hasher,
{
match *self {
Constant::Str(ref s, ref k) => {

View File

@ -138,12 +138,14 @@ fn lint_same_then_else(cx: &LateContext, blocks: &[&Block]) {
let eq: &Fn(&&Block, &&Block) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).eq_block(lhs, rhs) };
if let Some((i, j)) = search_same(blocks, hash, eq) {
span_note_and_lint(cx,
span_note_and_lint(
cx,
IF_SAME_THEN_ELSE,
j.span,
"this `if` has identical blocks",
i.span,
"same as this");
"same as this",
);
}
}
@ -158,12 +160,14 @@ fn lint_same_cond(cx: &LateContext, conds: &[&Expr]) {
let eq: &Fn(&&Expr, &&Expr) -> bool = &|&lhs, &rhs| -> bool { SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, rhs) };
if let Some((i, j)) = search_same(conds, hash, eq) {
span_note_and_lint(cx,
span_note_and_lint(
cx,
IFS_SAME_COND,
j.span,
"this `if` has the same condition as a previous if",
i.span,
"same as this");
"same as this",
);
}
}
@ -185,17 +189,22 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
if let ExprMatch(_, ref arms, MatchSource::Normal) = expr.node {
if let Some((i, j)) = search_same(arms, hash, eq) {
span_lint_and_then(cx,
span_lint_and_then(
cx,
MATCH_SAME_ARMS,
j.body.span,
"this `match` has identical arm bodies",
|db| {
db.span_note(i.body.span, "same as this");
// Note: this does not use `span_suggestion` on purpose: there is no clean way to
// remove the other arm. Building a span and suggest to replace it to "" makes an
// even more confusing error message. Also in order not to make up a span for the
// whole pattern, the suggestion is only shown when there is only one pattern. The
// Note: this does not use `span_suggestion` on purpose: there is no clean way
// to
// remove the other arm. Building a span and suggest to replace it to "" makes
// an
// even more confusing error message. Also in order not to make up a span for
// the
// whole pattern, the suggestion is only shown when there is only one pattern.
// The
// user should know about `|` if they are already using it…
if i.pats.len() == 1 && j.pats.len() == 1 {
@ -206,19 +215,22 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
// if the last arm is _, then i could be integrated into _
// note that i.pats[0] cannot be _, because that would mean that we're
// hiding all the subsequent arms, and rust won't compile
db.span_note(i.body.span,
&format!("`{}` has the same arm body as the `_` wildcard, consider removing it`",
lhs));
db.span_note(
i.body.span,
&format!("`{}` has the same arm body as the `_` wildcard, consider removing it`", lhs),
);
} else {
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
}
}
});
},
);
}
}
}
/// Return the list of condition expressions and the list of blocks in a sequence of `if/else`.
/// Return the list of condition expressions and the list of blocks in a
/// sequence of `if/else`.
/// Eg. would return `([a, b], [c, d, e])` for the expression
/// `if a { c } else if b { d } else { e }`.
fn if_sequence(mut expr: &Expr) -> (SmallVector<&Expr>, SmallVector<&Block>) {
@ -303,8 +315,9 @@ fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<Interned
}
fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)>
where Hash: Fn(&T) -> u64,
Eq: Fn(&T, &T) -> bool
where
Hash: Fn(&T) -> u64,
Eq: Fn(&T, &T) -> bool,
{
// common cases
if exprs.len() < 2 {

View File

@ -15,7 +15,8 @@ use utils::{in_macro, LimitStack, span_help_and_lint, paths, match_type};
/// **Why is this bad?** Methods of high cyclomatic complexity tend to be badly
/// readable. Also LLVM will usually optimize small methods better.
///
/// **Known problems:** Sometimes it's hard to find a way to reduce the complexity.
/// **Known problems:** Sometimes it's hard to find a way to reduce the
/// complexity.
///
/// **Example:** No. You'll see it when you get the warning.
declare_lint! {
@ -63,7 +64,13 @@ impl CyclomaticComplexity {
cx: cx,
};
helper.visit_expr(expr);
let CCHelper { match_arms, divergence, short_circuits, returns, .. } = helper;
let CCHelper {
match_arms,
divergence,
short_circuits,
returns,
..
} = helper;
let ret_ty = cx.tables.node_id_to_type(expr.id);
let ret_adjust = if match_type(cx, ret_ty, &paths::RESULT) {
returns
@ -80,11 +87,13 @@ impl CyclomaticComplexity {
rust_cc -= ret_adjust;
}
if rust_cc > self.limit.limit() {
span_help_and_lint(cx,
span_help_and_lint(
cx,
CYCLOMATIC_COMPLEXITY,
span,
&format!("the function has a cyclomatic complexity of {}", rust_cc),
"you could split it up into multiple smaller functions");
"you could split it up into multiple smaller functions",
);
}
}
}
@ -98,7 +107,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
_: &'tcx FnDecl,
body: &'tcx Body,
span: Span,
node_id: NodeId
node_id: NodeId,
) {
let def_id = cx.tcx.hir.local_def_id(node_id);
if !cx.tcx.has_attr(def_id, "test") {
@ -107,10 +116,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
}
fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
self.limit.push_attrs(cx.sess(), attrs, "cyclomatic_complexity");
self.limit.push_attrs(
cx.sess(),
attrs,
"cyclomatic_complexity",
);
}
fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
self.limit.pop_attrs(cx.sess(), attrs, "cyclomatic_complexity");
self.limit.pop_attrs(
cx.sess(),
attrs,
"cyclomatic_complexity",
);
}
}
@ -164,20 +181,24 @@ impl<'a, 'tcx> Visitor<'tcx> for CCHelper<'a, 'tcx> {
#[cfg(feature = "debugging")]
fn report_cc_bug(_: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span) {
span_bug!(span,
span_bug!(
span,
"Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \
div = {}, shorts = {}, returns = {}. Please file a bug report.",
cc,
narms,
div,
shorts,
returns);
returns
);
}
#[cfg(not(feature = "debugging"))]
fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span) {
if cx.current_level(CYCLOMATIC_COMPLEXITY) != Level::Allow {
cx.sess().span_note_without_error(span,
&format!("Clippy encountered a bug calculating cyclomatic complexity \
cx.sess().span_note_without_error(
span,
&format!(
"Clippy encountered a bug calculating cyclomatic complexity \
(hide this message with `#[allow(cyclomatic_complexity)]`): \
cc = {}, arms = {}, div = {}, shorts = {}, returns = {}. \
Please file a bug report.",
@ -185,6 +206,8 @@ fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, r
narms,
div,
shorts,
returns));
returns
),
);
}
}

View File

@ -89,7 +89,7 @@ fn check_hash_peq<'a, 'tcx>(
span: Span,
trait_ref: &TraitRef,
ty: Ty<'tcx>,
hash_is_automatically_derived: bool
hash_is_automatically_derived: bool,
) {
if_let_chain! {[
match_path_old(&trait_ref.path, &paths::HASH),

View File

@ -19,7 +19,8 @@ use utils::span_lint;
///
/// **Examples:**
/// ```rust
/// /// Do something with the foo_bar parameter. See also that::other::module::foo.
/// /// Do something with the foo_bar parameter. See also
/// that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. }
/// ```
@ -78,7 +79,8 @@ impl<'a> Iterator for Parser<'a> {
/// Cleanup documentation decoration (`///` and such).
///
/// We can't use `syntax::attr::AttributeMethods::with_desugared_doc` or
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we need to keep track of
/// `syntax::parse::lexer::comments::strip_doc_comment_decoration` because we
/// need to keep track of
/// the spans but this function is inspired from the later.
#[allow(cast_possible_truncation)]
pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
@ -89,7 +91,18 @@ pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(
let doc = &comment[prefix.len()..];
let mut doc = doc.to_owned();
doc.push('\n');
return (doc.to_owned(), vec![(doc.len(), Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })]);
return (
doc.to_owned(),
vec![
(
doc.len(),
Span {
lo: span.lo + BytePos(prefix.len() as u32),
..span
}
),
],
);
}
}
@ -102,7 +115,13 @@ pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(
debug_assert_eq!(offset as u32 as usize, offset);
// +1 for the newline
sizes.push((line.len() + 1, Span { lo: span.lo + BytePos(offset as u32), ..span }));
sizes.push((
line.len() + 1,
Span {
lo: span.lo + BytePos(offset as u32),
..span
},
));
}
return (doc.to_string(), sizes);
@ -163,7 +182,7 @@ fn check_doc<'a, Events: Iterator<Item = (usize, pulldown_cmark::Event<'a>)>>(
cx: &EarlyContext,
valid_idents: &[String],
docs: Events,
spans: &[(usize, Span)]
spans: &[(usize, Span)],
) {
use pulldown_cmark::Event::*;
use pulldown_cmark::Tag::*;
@ -192,7 +211,10 @@ fn check_doc<'a, Events: Iterator<Item = (usize, pulldown_cmark::Event<'a>)>>(
let (begin, span) = spans[index];
// Adjust for the begining of the current `Event`
let span = Span { lo: span.lo + BytePos::from_usize(offset - begin), ..span };
let span = Span {
lo: span.lo + BytePos::from_usize(offset - begin),
..span
};
check_text(cx, valid_idents, &text, span);
}
@ -225,8 +247,10 @@ fn check_text(cx: &EarlyContext, valid_idents: &[String], text: &str, span: Span
}
fn check_word(cx: &EarlyContext, word: &str, span: Span) {
/// Checks if a string is camel-case, ie. contains at least two uppercase letter (`Clippy` is
/// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded (`IDs` is ok).
/// Checks if a string is camel-case, ie. contains at least two uppercase
/// letter (`Clippy` is
/// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded
/// (`IDs` is ok).
fn is_camel_case(s: &str) -> bool {
if s.starts_with(|c: char| c.is_digit(10)) {
return false;
@ -247,9 +271,11 @@ fn check_word(cx: &EarlyContext, word: &str, span: Span) {
}
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
span_lint(cx,
span_lint(
cx,
DOC_MARKDOWN,
span,
&format!("you should put `{}` between ticks in the documentation", word));
&format!("you should put `{}` between ticks in the documentation", word),
);
}
}

View File

@ -16,7 +16,8 @@ use utils::{match_def_path, paths, span_note_and_lint, is_copy};
/// **Example:**
/// ```rust
/// let mut lock_guard = mutex.lock();
/// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex still locked
/// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
/// still locked
/// operation_that_requires_mutex_to_be_unlocked();
/// ```
declare_lint! {
@ -29,7 +30,8 @@ declare_lint! {
/// instead of an owned value.
///
/// **Why is this bad?** Calling `forget` on a reference will only forget the
/// reference itself, which is a no-op. It will not forget the underlying referenced
/// reference itself, which is a no-op. It will not forget the underlying
/// referenced
/// value, which is likely what was intended.
///
/// **Known problems:** None.
@ -57,7 +59,8 @@ declare_lint! {
/// **Example:**
/// ```rust
/// let x:i32 = 42; // i32 implements Copy
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the original unaffected
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the
/// original unaffected
/// ```
declare_lint! {
pub DROP_COPY,
@ -72,8 +75,10 @@ declare_lint! {
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
/// value will be copied and moved into the function on invocation.
///
/// An alternative, but also valid, explanation is that Copy types do not implement
/// the Drop trait, which means they have no destructors. Without a destructor, there
/// An alternative, but also valid, explanation is that Copy types do not
/// implement
/// the Drop trait, which means they have no destructors. Without a destructor,
/// there
/// is nothing for `std::mem::forget` to ignore.
///
/// **Known problems:** None.
@ -81,7 +86,8 @@ declare_lint! {
/// **Example:**
/// ```rust
/// let x:i32 = 42; // i32 implements Copy
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the original unaffected
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the
/// original unaffected
/// ```
declare_lint! {
pub FORGET_COPY,

View File

@ -6,7 +6,8 @@ use utils::span_lint_and_then;
/// **What it does:** Checks for `enum`s with no variants.
///
/// **Why is this bad?** Enum's with no variants should be replaced with `!`, the uninhabited type,
/// **Why is this bad?** Enum's with no variants should be replaced with `!`,
/// the uninhabited type,
/// or a wrapper around it.
///
/// **Known problems:** None.
@ -35,7 +36,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
let did = cx.tcx.hir.local_def_id(item.id);
if let ItemEnum(..) = item.node {
let ty = cx.tcx.type_of(did);
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
let adt = ty.ty_adt_def().expect(
"already checked whether this is an enum",
);
if adt.variants.is_empty() {
span_lint_and_then(cx, EMPTY_ENUM, item.span, "enum with no variants", |db| {
db.span_help(item.span, "consider using the uninhabited type `!` or a wrapper around it");

View File

@ -86,7 +86,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapLint {
fn check_cond<'a, 'tcx, 'b>(
cx: &'a LateContext<'a, 'tcx>,
check: &'b Expr
check: &'b Expr,
) -> Option<(&'static str, &'b Expr, &'b Expr)> {
if_let_chain! {[
let ExprMethodCall(ref path, _, ref params) = check.node,

View File

@ -1,4 +1,5 @@
//! lint on C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`
//! lint on C-like enums that are `repr(isize/usize)` and have values that
//! don't fit into an `i32`
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
@ -50,16 +51,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
let did = cx.tcx.hir.body_owner_def_id(body_id);
let param_env = ty::ParamEnv::empty(Reveal::UserFacing);
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), did);
let bad = match cx.tcx.at(expr.span).const_eval(param_env.and((did, substs))) {
let bad = match cx.tcx.at(expr.span).const_eval(
param_env.and((did, substs)),
) {
Ok(ConstVal::Integral(Usize(Us64(i)))) => i as u32 as u64 != i,
Ok(ConstVal::Integral(Isize(Is64(i)))) => i as i32 as i64 != i,
_ => false,
};
if bad {
span_lint(cx,
span_lint(
cx,
ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span,
"Clike enum variant discriminant is not portable to 32-bit targets");
"Clike enum variant discriminant is not portable to 32-bit targets",
);
}
}
}

View File

@ -48,9 +48,14 @@ impl EnumGlobUse {
}
if let ItemUse(ref path, UseKind::Glob) = item.node {
// FIXME: ask jseyfried why the qpath.def for `use std::cmp::Ordering::*;`
// extracted through `ItemUse(ref qpath, UseKind::Glob)` is a `Mod` and not an `Enum`
// extracted through `ItemUse(ref qpath, UseKind::Glob)` is a `Mod` and not an
// `Enum`
// if let Def::Enum(_) = path.def {
if path.segments.last().and_then(|seg| seg.name.as_str().chars().next()).map_or(false, char::is_uppercase) {
if path.segments
.last()
.and_then(|seg| seg.name.as_str().chars().next())
.map_or(false, char::is_uppercase)
{
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
}
}

View File

@ -68,13 +68,16 @@ declare_lint! {
"type names prefixed/postfixed with their containing module's name"
}
/// **What it does:** Checks for modules that have the same name as their parent module
/// **What it does:** Checks for modules that have the same name as their
/// parent module
///
/// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and again `mod foo { ..
/// **Why is this bad?** A typical beginner mistake is to have `mod foo;` and
/// again `mod foo { ..
/// }` in `foo.rs`.
/// The expectation is that items inside the inner `mod foo { .. }` are then
/// available
/// through `foo::x`, but they are only available through `foo::foo::x`.
/// through `foo::x`, but they are only available through
/// `foo::foo::x`.
/// If this is done on purpose, it would be better to choose a more
/// representative module name.
///
@ -123,14 +126,21 @@ fn var2str(var: &Variant) -> InternedString {
fn partial_match(pre: &str, name: &str) -> usize {
let mut name_iter = name.chars();
let _ = name_iter.next_back(); // make sure the name is never fully matched
pre.chars().zip(name_iter).take_while(|&(l, r)| l == r).count()
pre.chars()
.zip(name_iter)
.take_while(|&(l, r)| l == r)
.count()
}
/// Returns the number of chars that match from the end
fn partial_rmatch(post: &str, name: &str) -> usize {
let mut name_iter = name.chars();
let _ = name_iter.next(); // make sure the name is never fully matched
post.chars().rev().zip(name_iter.rev()).take_while(|&(l, r)| l == r).count()
post.chars()
.rev()
.zip(name_iter.rev())
.take_while(|&(l, r)| l == r)
.count()
}
// FIXME: #600
@ -142,7 +152,7 @@ fn check_variant(
item_name: &str,
item_name_chars: usize,
span: Span,
lint: &'static Lint
lint: &'static Lint,
) {
if (def.variants.len() as u64) < threshold {
return;
@ -187,13 +197,17 @@ fn check_variant(
(false, _) => ("pre", pre),
(true, false) => ("post", post),
};
span_help_and_lint(cx,
span_help_and_lint(
cx,
lint,
span,
&format!("All variants have the same {}fix: `{}`", what, value),
&format!("remove the {}fixes and use full paths to \
&format!(
"remove the {}fixes and use full paths to \
the variants instead of glob imports",
what));
what
),
);
}
fn to_camel_case(item_name: &str) -> String {
@ -234,10 +248,12 @@ impl EarlyLintPass for EnumVariantNames {
if !mod_camel.is_empty() {
if *mod_name == item_name {
if let ItemKind::Mod(..) = item.node {
span_lint(cx,
span_lint(
cx,
MODULE_INCEPTION,
item.span,
"module has the same name as its containing module");
"module has the same name as its containing module",
);
}
}
if item.vis == Visibility::Public {

View File

@ -23,7 +23,8 @@ declare_lint! {
"equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
}
/// **What it does:** Checks for arguments to `==` which have their address taken to satisfy a bound
/// **What it does:** Checks for arguments to `==` which have their address
/// taken to satisfy a bound
/// and suggests to dereference the other argument instead
///
/// **Why is this bad?** It is more idiomatic to dereference the other argument.
@ -53,10 +54,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
if let ExprBinary(ref op, ref left, ref right) = e.node {
if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
span_lint(cx,
span_lint(
cx,
EQ_OP,
e.span,
&format!("equal expressions as operands to `{}`", op.node.as_str()));
&format!("equal expressions as operands to `{}`", op.node.as_str()),
);
return;
}
let (trait_id, requires_ref) = match op.node {
@ -89,31 +92,37 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
let rcpy = is_copy(cx, rty);
// either operator autorefs or both args are copyable
if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty]) {
span_lint_and_then(cx,
span_lint_and_then(
cx,
OP_REF,
e.span,
"needlessly taken reference of both operands",
|db| {
let lsnip = snippet(cx, l.span, "...").to_string();
let rsnip = snippet(cx, r.span, "...").to_string();
multispan_sugg(db,
multispan_sugg(
db,
"use the values directly".to_string(),
vec![(left.span, lsnip), (right.span, rsnip)]);
})
vec![(left.span, lsnip), (right.span, rsnip)],
);
},
)
} else if lcpy && !rcpy && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right)]) {
span_lint_and_then(cx, OP_REF, e.span, "needlessly taken reference of left operand", |db| {
let lsnip = snippet(cx, l.span, "...").to_string();
db.span_suggestion(left.span, "use the left value directly", lsnip);
})
} else if !lcpy && rcpy && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty]) {
span_lint_and_then(cx,
span_lint_and_then(
cx,
OP_REF,
e.span,
"needlessly taken reference of right operand",
|db| {
let rsnip = snippet(cx, r.span, "...").to_string();
db.span_suggestion(right.span, "use the right value directly", rsnip);
})
},
)
}
},
// &foo == bar

View File

@ -61,7 +61,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
_: &'tcx FnDecl,
body: &'tcx Body,
_: Span,
node_id: NodeId
node_id: NodeId,
) {
let fn_def_id = cx.tcx.hir.local_def_id(node_id);
let mut v = EscapeDelegate {
@ -74,10 +74,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
ExprUseVisitor::new(&mut v, cx.tcx, cx.param_env, region_maps, cx.tables).consume_body(body);
for node in v.set {
span_lint(cx,
span_lint(
cx,
BOXED_LOCAL,
cx.tcx.hir.span(node),
"local variable doesn't need to be boxed here");
"local variable doesn't need to be boxed here",
);
}
}
}

View File

@ -20,7 +20,8 @@ pub struct EtaPass;
/// ```rust
/// xs.map(|x| foo(x))
/// ```
/// where `foo(_)` is a plain function that takes the exact argument type of `x`.
/// where `foo(_)` is a plain function that takes the exact argument type of
/// `x`.
declare_lint! {
pub REDUNDANT_CLOSURE,
Warn,
@ -91,12 +92,10 @@ fn check_closure(cx: &LateContext, expr: &Expr) {
return;
}
}
span_lint_and_then(cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure found",
|db| if let Some(snippet) = snippet_opt(cx, caller.span) {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure found", |db| {
if let Some(snippet) = snippet_opt(cx, caller.span) {
db.span_suggestion(expr.span, "remove closure as shown", snippet);
}
});
}
}

View File

@ -27,12 +27,14 @@ declare_lint! {
"whether a variable read occurs before a write depends on sub-expression evaluation order"
}
/// **What it does:** Checks for diverging calls that are not match arms or statements.
/// **What it does:** Checks for diverging calls that are not match arms or
/// statements.
///
/// **Why is this bad?** It is often confusing to read. In addition, the
/// sub-expression evaluation order for Rust is not well documented.
///
/// **Known problems:** Someone might want to use `some_bool || panic!()` as a shorthand.
/// **Known problems:** Someone might want to use `some_bool || panic!()` as a
/// shorthand.
///
/// **Example:**
/// ```rust
@ -144,7 +146,8 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
}
},
_ => {
// do not lint expressions referencing objects of type `!`, as that required a diverging expression
// do not lint expressions referencing objects of type `!`, as that required a
// diverging expression
// to begin with
},
}
@ -271,8 +274,10 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt) -> St
DeclLocal(ref local) => Some(local),
_ => None,
};
local.and_then(|local| local.init.as_ref())
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr))
local.and_then(|local| local.init.as_ref()).map_or(
StopEarly::KeepGoing,
|expr| check_expr(vis, expr),
)
},
}
}

View File

@ -99,7 +99,8 @@ pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Exp
/// Checks if the expressions matches
/// ```rust, ignore
/// { static __STATIC_FMTSTR: &'static[&'static str] = &["a", "b", c]; __STATIC_FMTSTR }
/// { static __STATIC_FMTSTR: &'static[&'static str] = &["a", "b", c];
/// __STATIC_FMTSTR }
/// ```
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
if let Some(expr) = get_argument_fmtstr_parts(cx, expr) {
@ -112,7 +113,8 @@ fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
/// Checks if the expressions matches
/// ```rust,ignore
/// &match (&42,) {
/// (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)],
/// (__arg0,) => [::std::fmt::ArgumentV1::new(__arg0,
/// ::std::fmt::Display::fmt)],
/// }
/// ```
fn check_arg_is_display(cx: &LateContext, expr: &Expr) -> bool {

View File

@ -4,9 +4,11 @@ use syntax_pos::{Span, NO_EXPANSION};
use utils::{differing_macro_contexts, in_macro, snippet_opt, span_note_and_lint};
use syntax::ptr::P;
/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-` operators.
/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
/// operators.
///
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or confusing.
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
/// confusing.
///
/// **Known problems:** None.
///
@ -72,7 +74,11 @@ pub struct Formatting;
impl LintPass for Formatting {
fn get_lints(&self) -> LintArray {
lint_array![SUSPICIOUS_ASSIGNMENT_FORMATTING, SUSPICIOUS_ELSE_FORMATTING, POSSIBLE_MISSING_COMMA]
lint_array!(
SUSPICIOUS_ASSIGNMENT_FORMATTING,
SUSPICIOUS_ELSE_FORMATTING,
POSSIBLE_MISSING_COMMA
)
}
}
@ -114,14 +120,18 @@ fn check_assign(cx: &EarlyContext, expr: &ast::Expr) {
ctxt: NO_EXPANSION,
};
if eq_snippet.ends_with('=') {
span_note_and_lint(cx,
span_note_and_lint(
cx,
SUSPICIOUS_ASSIGNMENT_FORMATTING,
eqop_span,
&format!("this looks like you are trying to use `.. {op}= ..`, but you \
&format!(
"this looks like you are trying to use `.. {op}= ..`, but you \
really are doing `.. = ({op} ..)`",
op = op),
op = op
),
eqop_span,
&format!("to remove this lint, use either `{op}=` or `= {op}`", op = op));
&format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
);
}
}
}
@ -133,7 +143,8 @@ fn check_assign(cx: &EarlyContext, expr: &ast::Expr) {
fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
if unsugar_if(else_).is_some() && !differing_macro_contexts(then.span, else_.span) && !in_macro(then.span) {
// this will be a span from the closing } of the “then” block (excluding) to the
// this will be a span from the closing } of the “then” block (excluding) to
// the
// “if” of the “else if” block (excluding)
let else_span = Span {
lo: then.span.hi,
@ -144,16 +155,20 @@ fn check_else_if(cx: &EarlyContext, expr: &ast::Expr) {
// the snippet should look like " else \n " with maybe comments anywhere
// its bad when there is a \n after the “else”
if let Some(else_snippet) = snippet_opt(cx, else_span) {
let else_pos = else_snippet.find("else").expect("there must be a `else` here");
let else_pos = else_snippet.find("else").expect(
"there must be a `else` here",
);
if else_snippet[else_pos..].contains('\n') {
span_note_and_lint(cx,
span_note_and_lint(
cx,
SUSPICIOUS_ELSE_FORMATTING,
else_span,
"this is an `else if` but the formatting might hide it",
else_span,
"to remove this lint, remove the `else` or remove the new line between `else` \
and `if`");
and `if`",
);
}
}
}
@ -178,12 +193,14 @@ fn check_array(cx: &EarlyContext, expr: &ast::Expr) {
ctxt: NO_EXPANSION,
};
if space_snippet.contains('\n') {
span_note_and_lint(cx,
span_note_and_lint(
cx,
POSSIBLE_MISSING_COMMA,
lint_span,
"possibly missing a comma here",
lint_span,
"to remove this lint, add a comma or write the expr in a single line");
"to remove this lint, add a comma or write the expr in a single line",
);
}
}
}
@ -195,7 +212,8 @@ fn check_array(cx: &EarlyContext, expr: &ast::Expr) {
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for consecutive ifs.
fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Expr) {
if !differing_macro_contexts(first.span, second.span) && !in_macro(first.span) && unsugar_if(first).is_some() &&
unsugar_if(second).is_some() {
unsugar_if(second).is_some()
{
// where the else would be
let else_span = Span {
lo: first.span.hi,
@ -205,13 +223,15 @@ fn check_consecutive_ifs(cx: &EarlyContext, first: &ast::Expr, second: &ast::Exp
if let Some(else_snippet) = snippet_opt(cx, else_span) {
if !else_snippet.contains('\n') {
span_note_and_lint(cx,
span_note_and_lint(
cx,
SUSPICIOUS_ELSE_FORMATTING,
else_span,
"this looks like an `else if` but the `else` is missing",
else_span,
"to remove this lint, add the missing `else` or add a new line before the second \
`if`");
`if`",
);
}
}
}

View File

@ -17,7 +17,8 @@ use utils::{span_lint, type_is_unsafe_function, iter_input_pats};
///
/// **Example:**
/// ```rust
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { .. }
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b:
/// f32) { .. }
/// ```
declare_lint! {
pub TOO_MANY_ARGUMENTS,
@ -35,7 +36,8 @@ declare_lint! {
/// **Known problems:**
///
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't trigger.
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
@ -75,7 +77,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Body,
span: Span,
nodeid: ast::NodeId
nodeid: ast::NodeId,
) {
use rustc::hir::map::Node::*;
@ -123,10 +125,12 @@ impl<'a, 'tcx> Functions {
fn check_arg_number(&self, cx: &LateContext, decl: &hir::FnDecl, span: Span) {
let args = decl.inputs.len() as u64;
if args > self.threshold {
span_lint(cx,
span_lint(
cx,
TOO_MANY_ARGUMENTS,
span,
&format!("this function has too many arguments ({}/{})", args, self.threshold));
&format!("this function has too many arguments ({}/{})", args, self.threshold),
);
}
}
@ -136,7 +140,7 @@ impl<'a, 'tcx> Functions {
unsafety: hir::Unsafety,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Body,
nodeid: ast::NodeId
nodeid: ast::NodeId,
) {
let expr = &body.value;
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
@ -208,10 +212,12 @@ impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
if let hir::ExprPath(ref qpath) = ptr.node {
let def = self.cx.tables.qpath_def(qpath, ptr.id);
if self.ptrs.contains(&def.def_id()) {
span_lint(self.cx,
span_lint(
self.cx,
NOT_UNSAFE_PTR_ARG_DEREF,
ptr.span,
"this public function dereferences a raw pointer but is not marked `unsafe`");
"this public function dereferences a raw pointer but is not marked `unsafe`",
);
}
}
}

View File

@ -71,12 +71,17 @@ fn check(cx: &LateContext, e: &Expr, m: i8, span: Span, arg: Span) {
},
1 => v.to_u128_unchecked() == 1,
_ => unreachable!(),
} {
span_lint(cx,
}
{
span_lint(
cx,
IDENTITY_OP,
span,
&format!("the operation is ineffective. Consider reducing it to `{}`",
snippet(cx, arg, "..")));
&format!(
"the operation is ineffective. Consider reducing it to `{}`",
snippet(cx, arg, "..")
),
);
}
}
}

View File

@ -3,9 +3,11 @@ use rustc::hir::*;
use syntax::codemap::Span;
use utils::{paths, span_lint_and_then, match_path, snippet};
/// **What it does:*** Lint for redundant pattern matching over `Result` or `Option`
/// **What it does:*** Lint for redundant pattern matching over `Result` or
/// `Option`
///
/// **Why is this bad?** It's more concise and clear to just use the proper utility function
/// **Why is this bad?** It's more concise and clear to just use the proper
/// utility function
///
/// **Known problems:** None.
///

View File

@ -1,4 +1,5 @@
//! lint on if branches that could be swapped so no `!` operation is necessary on the condition
//! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition
use rustc::lint::*;
use syntax::ast::*;
@ -50,18 +51,22 @@ impl EarlyLintPass for IfNotElse {
if let ExprKind::Block(..) = els.node {
match cond.node {
ExprKind::Unary(UnOp::Not, _) => {
span_help_and_lint(cx,
span_help_and_lint(
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary boolean `not` operation",
"remove the `!` and swap the blocks of the if/else");
"remove the `!` and swap the blocks of the if/else",
);
},
ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
span_help_and_lint(cx,
span_help_and_lint(
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary `!=` operation",
"change to `==` and swap the blocks of the if/else");
"change to `==` and swap the blocks of the if/else",
);
},
_ => (),
}

View File

@ -47,10 +47,9 @@ impl EarlyLintPass for ItemsAfterStatements {
}
// skip initial items
let stmts = item.stmts
.iter()
.map(|stmt| &stmt.node)
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
let stmts = item.stmts.iter().map(|stmt| &stmt.node).skip_while(|s| {
matches!(**s, StmtKind::Item(..))
});
// lint on all further items
for stmt in stmts {
@ -62,11 +61,13 @@ impl EarlyLintPass for ItemsAfterStatements {
// do not lint `macro_rules`, but continue processing further statements
continue;
}
span_lint(cx,
span_lint(
cx,
ITEMS_AFTER_STATEMENTS,
it.span,
"adding items after statements is confusing, since items exist from the \
start of the scope");
start of the scope",
);
}
}
}

View File

@ -5,9 +5,11 @@ use rustc::hir::*;
use utils::{span_lint_and_then, snippet_opt, type_size};
use rustc::ty::TypeFoldable;
/// **What it does:** Checks for large size differences between variants on `enum`s.
/// **What it does:** Checks for large size differences between variants on
/// `enum`s.
///
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a large variant
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a
/// large variant
/// can penalize the memory layout of that enum.
///
/// **Known problems:** None.
@ -47,13 +49,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
let did = cx.tcx.hir.local_def_id(item.id);
if let ItemEnum(ref def, _) = item.node {
let ty = cx.tcx.type_of(did);
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
let adt = ty.ty_adt_def().expect(
"already checked whether this is an enum",
);
let mut smallest_variant: Option<(_, _)> = None;
let mut largest_variant: Option<(_, _)> = None;
for (i, variant) in adt.variants.iter().enumerate() {
let size: u64 = variant.fields
let size: u64 = variant
.fields
.iter()
.map(|f| {
let ty = cx.tcx.type_of(f.did);
@ -77,7 +82,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
if difference > self.maximum_size_difference_allowed {
let (i, variant) = largest.1;
span_lint_and_then(cx,
span_lint_and_then(
cx,
LARGE_ENUM_VARIANT,
def.variants[i].span,
"large size difference between variants",
@ -89,16 +95,21 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
VariantData::Unit(_) => unreachable!(),
};
if let Some(snip) = snippet_opt(cx, span) {
db.span_suggestion(span,
db.span_suggestion(
span,
"consider boxing the large fields to reduce the total size of the \
enum",
format!("Box<{}>", snip));
format!("Box<{}>", snip),
);
return;
}
}
db.span_help(def.variants[i].span,
"consider boxing the large fields to reduce the total size of the enum");
});
db.span_help(
def.variants[i].span,
"consider boxing the large fields to reduce the total size of the enum",
);
},
);
}
}
@ -107,7 +118,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
}
fn update_if<T, F>(old: &mut Option<T>, new: T, f: F)
where F: Fn(&T, &T) -> bool
where
F: Fn(&T, &T) -> bool,
{
if let Some(ref mut val) = *old {
if f(val, &new) {

View File

@ -105,10 +105,12 @@ fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItemRef]
if !trait_items.iter().any(|i| is_named_self(cx, i, "is_empty")) {
if let Some(i) = trait_items.iter().find(|i| is_named_self(cx, i, "len")) {
if cx.access_levels.is_exported(i.id.node_id) {
span_lint(cx,
span_lint(
cx,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("trait `{}` has a `len` method but no `is_empty` method", item.name));
&format!("trait `{}` has a `len` method but no `is_empty` method", item.name),
);
}
}
}
@ -143,10 +145,12 @@ fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItemRef]) {
let def_id = cx.tcx.hir.local_def_id(item.id);
let ty = cx.tcx.type_of(def_id);
span_lint(cx,
span_lint(
cx,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("item `{}` has a public `len` method but {} `is_empty` method", ty, is_empty));
&format!("item `{}` has a public `len` method but {} `is_empty` method", ty, is_empty),
);
}
}
}
@ -170,12 +174,14 @@ fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str)
fn check_len_zero(cx: &LateContext, span: Span, name: Name, args: &[Expr], lit: &Lit, op: &str) {
if let Spanned { node: LitKind::Int(0, _), .. } = *lit {
if name == "len" && args.len() == 1 && has_is_empty(cx, &args[0]) {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
LEN_ZERO,
span,
"length comparison to zero",
"using `is_empty` is more concise",
format!("{}{}.is_empty()", op, snippet(cx, args[0].span, "_")));
format!("{}{}.is_empty()", op, snippet(cx, args[0].span, "_")),
);
}
}
}
@ -199,10 +205,11 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
/// Check the inherent impl's items for an `is_empty(self)` method.
fn has_is_empty_impl(cx: &LateContext, id: DefId) -> bool {
cx.tcx
.inherent_impls(id)
.iter()
.any(|imp| cx.tcx.associated_items(*imp).any(|item| is_is_empty(cx, &item)))
cx.tcx.inherent_impls(id).iter().any(|imp| {
cx.tcx.associated_items(*imp).any(
|item| is_is_empty(cx, &item),
)
})
}
let ty = &walk_ptrs_ty(cx.tables.expr_ty(expr));
@ -212,7 +219,12 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
.associated_items(ty.ty_to_def_id().expect("trait impl not found"))
.any(|item| is_is_empty(cx, &item))
},
ty::TyProjection(_) => ty.ty_to_def_id().map_or(false, |id| has_is_empty_impl(cx, id)),
ty::TyProjection(_) => {
ty.ty_to_def_id().map_or(
false,
|id| has_is_empty_impl(cx, id),
)
},
ty::TyAdt(id, _) => has_is_empty_impl(cx, id.did),
ty::TyArray(..) | ty::TySlice(..) | ty::TyStr => true,
_ => false,

View File

@ -154,7 +154,7 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
fn check_assign<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
decl: hir::def_id::DefId,
block: &'tcx hir::Block
block: &'tcx hir::Block,
) -> Option<&'tcx hir::Expr> {
if_let_chain! {[
block.expr.is_none(),

View File

@ -94,7 +94,7 @@ fn check_fn_inner<'a, 'tcx>(
decl: &'tcx FnDecl,
body: Option<BodyId>,
generics: &'tcx Generics,
span: Span
span: Span,
) {
if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
return;
@ -104,7 +104,8 @@ fn check_fn_inner<'a, 'tcx>(
for typ in &generics.ty_params {
for bound in &typ.bounds {
if let TraitTyParamBound(ref trait_ref, _) = *bound {
let bounds = trait_ref.trait_ref
let bounds = trait_ref
.trait_ref
.path
.segments
.last()
@ -121,10 +122,12 @@ fn check_fn_inner<'a, 'tcx>(
}
}
if could_use_elision(cx, decl, body, &generics.lifetimes, bounds_lts) {
span_lint(cx,
span_lint(
cx,
NEEDLESS_LIFETIMES,
span,
"explicit lifetimes given in parameter types where they could be elided");
"explicit lifetimes given in parameter types where they could be elided",
);
}
report_extra_lifetimes(cx, decl, generics);
}
@ -134,7 +137,7 @@ fn could_use_elision<'a, 'tcx: 'a>(
func: &'tcx FnDecl,
body: Option<BodyId>,
named_lts: &'tcx [LifetimeDef],
bounds_lts: Vec<&'tcx Lifetime>
bounds_lts: Vec<&'tcx Lifetime>,
) -> bool {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
@ -189,7 +192,10 @@ fn could_use_elision<'a, 'tcx: 'a>(
// no output lifetimes, check distinctness of input lifetimes
// only unnamed and static, ok
if input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static) {
if input_lts.iter().all(|lt| {
*lt == RefLt::Unnamed || *lt == RefLt::Static
})
{
return false;
}
// we have no output reference, so we only need all distinct lifetimes
@ -406,7 +412,8 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker {
}
fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
let hs = generics.lifetimes
let hs = generics
.lifetimes
.iter()
.map(|lt| (lt.lifetime.name, lt.lifetime.span))
.collect();

View File

@ -146,7 +146,8 @@ impl<'a> DigitInfo<'a> {
let group_size = self.radix.suggest_grouping();
if self.digits.contains('.') {
let mut parts = self.digits.split('.');
let int_part_hint = parts.next()
let int_part_hint = parts
.next()
.expect("split always returns at least one element")
.chars()
.rev()
@ -157,7 +158,8 @@ impl<'a> DigitInfo<'a> {
.rev()
.collect::<Vec<String>>()
.join("_");
let frac_part_hint = parts.next()
let frac_part_hint = parts
.next()
.expect("already checked that there is a `.`")
.chars()
.filter(|&c| c != '_')
@ -194,25 +196,31 @@ impl WarningType {
pub fn display(&self, grouping_hint: &str, cx: &EarlyContext, span: &syntax_pos::Span) {
match *self {
WarningType::UnreadableLiteral => {
span_help_and_lint(cx,
span_help_and_lint(
cx,
UNREADABLE_LITERAL,
*span,
"long literal lacking separators",
&format!("consider: {}", grouping_hint))
&format!("consider: {}", grouping_hint),
)
},
WarningType::LargeDigitGroups => {
span_help_and_lint(cx,
span_help_and_lint(
cx,
LARGE_DIGIT_GROUPS,
*span,
"digit groups should be smaller",
&format!("consider: {}", grouping_hint))
&format!("consider: {}", grouping_hint),
)
},
WarningType::InconsistentDigitGrouping => {
span_help_and_lint(cx,
span_help_and_lint(
cx,
INCONSISTENT_DIGIT_GROUPING,
*span,
"digits grouped inconsistently by underscores",
&format!("consider: {}", grouping_hint))
&format!("consider: {}", grouping_hint),
)
},
};
}
@ -309,7 +317,8 @@ impl LiteralDigitGrouping {
/// size on success or `WarningType` when emitting a warning.
fn do_lint(digits: &str) -> Result<usize, WarningType> {
// Grab underscore indices with respect to the units digit.
let underscore_positions: Vec<usize> = digits.chars()
let underscore_positions: Vec<usize> = digits
.chars()
.rev()
.enumerate()
.filter_map(|(idx, digit)| if digit == '_' { Some(idx) } else { None })

View File

@ -83,7 +83,8 @@ declare_lint! {
/// implements `IntoIterator`, so that possibly one value will be iterated,
/// leading to some hard to find bugs. No one will want to write such code
/// [except to win an Underhanded Rust
/// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
/// Contest](https://www.reddit.
/// com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
///
/// **Known problems:** None.
///
@ -99,7 +100,8 @@ declare_lint! {
/// **What it does:** Checks for `for` loops over `Option` values.
///
/// **Why is this bad?** Readability. This is more clearly expressed as an `if let`.
/// **Why is this bad?** Readability. This is more clearly expressed as an `if
/// let`.
///
/// **Known problems:** None.
///
@ -120,7 +122,8 @@ declare_lint! {
/// **What it does:** Checks for `for` loops over `Result` values.
///
/// **Why is this bad?** Readability. This is more clearly expressed as an `if let`.
/// **Why is this bad?** Readability. This is more clearly expressed as an `if
/// let`.
///
/// **Known problems:** None.
///
@ -142,7 +145,8 @@ declare_lint! {
/// **What it does:** Detects `loop + match` combinations that are easier
/// written as a `while let` loop.
///
/// **Why is this bad?** The `while let` loop is usually shorter and more readable.
/// **Why is this bad?** The `while let` loop is usually shorter and more
/// readable.
///
/// **Known problems:** Sometimes the wrong binding is displayed (#383).
///
@ -309,7 +313,8 @@ pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(NEEDLESS_RANGE_LOOP,
lint_array!(
NEEDLESS_RANGE_LOOP,
EXPLICIT_ITER_LOOP,
EXPLICIT_INTO_ITER_LOOP,
ITER_NEXT_LOOP,
@ -322,7 +327,8 @@ impl LintPass for Pass {
EMPTY_LOOP,
WHILE_LET_ON_ITERATOR,
FOR_KV_MAP,
NEVER_LOOP)
NEVER_LOOP
)
}
}
@ -349,11 +355,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let ExprLoop(ref block, _, LoopSource::Loop) = expr.node {
// also check for empty `loop {}` statements
if block.stmts.is_empty() && block.expr.is_none() {
span_lint(cx,
span_lint(
cx,
EMPTY_LOOP,
expr.span,
"empty `loop {}` detected. You may want to either use `panic!()` or add \
`std::thread::sleep(..);` to the loop body.");
`std::thread::sleep(..);` to the loop body.",
);
}
// extract the expression from the first statement (if any) in a block
@ -367,7 +375,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
MatchSource::IfLetDesugar { .. } => {
if arms.len() == 2 && arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
is_break_expr(&arms[1].body) {
is_break_expr(&arms[1].body)
{
if in_external_macro(cx, expr.span) {
return;
}
@ -377,14 +386,18 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
// 1) it was ugly with big bodies;
// 2) it was not indented properly;
// 3) it wasnt very smart (see #675).
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
WHILE_LET_LOOP,
expr.span,
"this loop could be written as a `while let` loop",
"try",
format!("while let {} = {} {{ .. }}",
format!(
"while let {} = {} {{ .. }}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, matchexpr.span, "..")));
snippet(cx, matchexpr.span, "..")
),
);
}
},
_ => (),
@ -395,20 +408,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let ExprMatch(ref match_expr, ref arms, MatchSource::WhileLetDesugar) = expr.node {
let pat = &arms[0].pats[0].node;
if let (&PatKind::TupleStruct(ref qpath, ref pat_args, _),
&ExprMethodCall(ref method_path, _, ref method_args)) = (pat, &match_expr.node) {
&ExprMethodCall(ref method_path, _, ref method_args)) = (pat, &match_expr.node)
{
let iter_expr = &method_args[0];
let lhs_constructor = last_path_segment(qpath);
if method_path.name == "next" && match_trait_method(cx, match_expr, &paths::ITERATOR) &&
lhs_constructor.name == "Some" && !is_refutable(cx, &pat_args[0]) &&
!is_iterator_used_after_while_let(cx, iter_expr) && !is_nested(cx, expr, &method_args[0]) {
!is_iterator_used_after_while_let(cx, iter_expr) &&
!is_nested(cx, expr, &method_args[0])
{
let iterator = snippet(cx, method_args[0].span, "_");
let loop_var = snippet(cx, pat_args[0].span, "_");
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
WHILE_LET_ON_ITERATOR,
expr.span,
"this loop could be written as a `for` loop",
"try",
format!("for {} in {} {{ .. }}", loop_var, iterator));
format!("for {} in {} {{ .. }}", loop_var, iterator),
);
}
}
}
@ -418,11 +436,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let StmtSemi(ref expr, _) = stmt.node {
if let ExprMethodCall(ref method, _, ref args) = expr.node {
if args.len() == 1 && method.name == "collect" && match_trait_method(cx, expr, &paths::ITERATOR) {
span_lint(cx,
span_lint(
cx,
UNUSED_COLLECT,
expr.span,
"you are collect()ing an iterator and throwing away the result. \
Consider using an explicit for loop to exhaust the iterator");
Consider using an explicit for loop to exhaust the iterator",
);
}
}
}
@ -435,7 +455,10 @@ fn never_loop(block: &Block, id: &NodeId) -> bool {
fn contains_continue_block(block: &Block, dest: &NodeId) -> bool {
block.stmts.iter().any(|e| contains_continue_stmt(e, dest)) ||
block.expr.as_ref().map_or(false, |e| contains_continue_expr(e, dest))
block.expr.as_ref().map_or(
false,
|e| contains_continue_expr(e, dest),
)
}
fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
@ -448,7 +471,12 @@ fn contains_continue_stmt(stmt: &Stmt, dest: &NodeId) -> bool {
fn contains_continue_decl(decl: &Decl, dest: &NodeId) -> bool {
match decl.node {
DeclLocal(ref local) => local.init.as_ref().map_or(false, |e| contains_continue_expr(e, dest)),
DeclLocal(ref local) => {
local.init.as_ref().map_or(
false,
|e| contains_continue_expr(e, dest),
)
},
_ => false,
}
}
@ -475,14 +503,21 @@ fn contains_continue_expr(expr: &Expr, dest: &NodeId) -> bool {
ExprAssignOp(_, ref e1, ref e2) |
ExprIndex(ref e1, ref e2) => [e1, e2].iter().any(|e| contains_continue_expr(e, dest)),
ExprIf(ref e, ref e2, ref e3) => {
[e, e2].iter().chain(e3.as_ref().iter()).any(|e| contains_continue_expr(e, dest))
[e, e2].iter().chain(e3.as_ref().iter()).any(|e| {
contains_continue_expr(e, dest)
})
},
ExprWhile(ref e, ref b, _) => contains_continue_expr(e, dest) || contains_continue_block(b, dest),
ExprMatch(ref e, ref arms, _) => {
contains_continue_expr(e, dest) || arms.iter().any(|a| contains_continue_expr(&a.body, dest))
},
ExprBlock(ref block) => contains_continue_block(block, dest),
ExprStruct(_, _, ref base) => base.as_ref().map_or(false, |e| contains_continue_expr(e, dest)),
ExprStruct(_, _, ref base) => {
base.as_ref().map_or(
false,
|e| contains_continue_expr(e, dest),
)
},
ExprAgain(d) => d.target_id.opt_id().map_or(false, |id| id == *dest),
_ => false,
}
@ -541,7 +576,7 @@ fn check_for_loop<'a, 'tcx>(
pat: &'tcx Pat,
arg: &'tcx Expr,
body: &'tcx Expr,
expr: &'tcx Expr
expr: &'tcx Expr,
) {
check_for_loop_range(cx, pat, arg, body, expr);
check_for_loop_reverse_range(cx, arg, expr);
@ -557,9 +592,14 @@ fn check_for_loop_range<'a, 'tcx>(
pat: &'tcx Pat,
arg: &'tcx Expr,
body: &'tcx Expr,
expr: &'tcx Expr
expr: &'tcx Expr,
) {
if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::range(arg) {
if let Some(higher::Range {
start: Some(start),
ref end,
limits,
}) = higher::range(arg)
{
// the var must be a single name
if let PatKind::Binding(_, def_id, ref ident, _) = pat.node {
let mut visitor = VarVisitor {
@ -573,10 +613,9 @@ fn check_for_loop_range<'a, 'tcx>(
// linting condition: we only indexed one variable
if visitor.indexed.len() == 1 {
let (indexed, indexed_extent) = visitor.indexed
.into_iter()
.next()
.expect("already checked that we have exactly 1 element");
let (indexed, indexed_extent) = visitor.indexed.into_iter().next().expect(
"already checked that we have exactly 1 element",
);
// ensure that the indexed variable was declared before the loop, see #601
if let Some(indexed_extent) = indexed_extent {
@ -589,7 +628,8 @@ fn check_for_loop_range<'a, 'tcx>(
}
}
// don't lint if the container that is indexed into is also used without indexing
// don't lint if the container that is indexed into is also used without
// indexing
if visitor.referenced.contains(&indexed) {
return;
}
@ -670,7 +710,12 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
// if this for loop is iterating over a two-sided range...
if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(arg) {
if let Some(higher::Range {
start: Some(start),
end: Some(end),
limits,
}) = higher::range(arg)
{
// ...and both sides are compile-time constant integers...
let parent_item = cx.tcx.hir.get_parent(arg.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
@ -714,10 +759,12 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
} else if eq && limits != ast::RangeLimits::Closed {
// if they are equal, it's also problematic - this loop
// will never run.
span_lint(cx,
span_lint(
cx,
REVERSE_RANGE_LOOP,
expr.span,
"this range is empty so this for loop will never run");
"this range is empty so this for loop will never run",
);
}
}
}
@ -731,13 +778,15 @@ fn lint_iter_method(cx: &LateContext, args: &[Expr], arg: &Expr, method_name: &s
} else {
""
};
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
EXPLICIT_ITER_LOOP,
arg.span,
"it is more idiomatic to loop over references to containers instead of using explicit \
iteration methods",
"to write this more concisely, try",
format!("&{}{}", muta, object))
format!("&{}{}", muta, object),
)
}
fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
@ -762,20 +811,24 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
lint_iter_method(cx, args, arg, method_name);
} else {
let object = snippet(cx, args[0].span, "_");
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
EXPLICIT_INTO_ITER_LOOP,
arg.span,
"it is more idiomatic to loop over containers instead of using explicit \
iteration methods`",
"to write this more concisely, try",
object.to_string());
object.to_string(),
);
}
} else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
span_lint(cx,
span_lint(
cx,
ITER_NEXT_LOOP,
expr.span,
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
probably not what you want");
probably not what you want",
);
next_loop_linted = true;
}
}
@ -789,25 +842,37 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
let ty = cx.tables.expr_ty(arg);
if match_type(cx, ty, &paths::OPTION) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
FOR_LOOP_OVER_OPTION,
arg.span,
&format!("for loop over `{0}`, which is an `Option`. This is more readably written as an \
&format!(
"for loop over `{0}`, which is an `Option`. This is more readably written as an \
`if let` statement.",
snippet(cx, arg.span, "_")),
&format!("consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
snippet(cx, arg.span, "_")
),
&format!(
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
snippet(cx, pat.span, "_"),
snippet(cx, arg.span, "_")));
snippet(cx, arg.span, "_")
),
);
} else if match_type(cx, ty, &paths::RESULT) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
FOR_LOOP_OVER_RESULT,
arg.span,
&format!("for loop over `{0}`, which is a `Result`. This is more readably written as an \
&format!(
"for loop over `{0}`, which is a `Result`. This is more readably written as an \
`if let` statement.",
snippet(cx, arg.span, "_")),
&format!("consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
snippet(cx, arg.span, "_")
),
&format!(
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
snippet(cx, pat.span, "_"),
snippet(cx, arg.span, "_")));
snippet(cx, arg.span, "_")
),
);
}
}
@ -815,7 +880,7 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
arg: &'tcx Expr,
body: &'tcx Expr,
expr: &'tcx Expr
expr: &'tcx Expr,
) {
// Look for variables that are incremented once per loop iteration.
let mut visitor = IncrementVisitor {
@ -829,10 +894,15 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
// For each candidate, check the parent block to see if
// it's initialized to zero at the start of the loop.
let map = &cx.tcx.hir;
let parent_scope = map.get_enclosing_scope(expr.id).and_then(|id| map.get_enclosing_scope(id));
let parent_scope = map.get_enclosing_scope(expr.id).and_then(|id| {
map.get_enclosing_scope(id)
});
if let Some(parent_id) = parent_scope {
if let NodeBlock(block) = map.get(parent_id) {
for (id, _) in visitor.states.iter().filter(|&(_, v)| *v == VarState::IncrOnce) {
for (id, _) in visitor.states.iter().filter(
|&(_, v)| *v == VarState::IncrOnce,
)
{
let mut visitor2 = InitializeVisitor {
cx: cx,
end_expr: expr,
@ -846,13 +916,17 @@ fn check_for_loop_explicit_counter<'a, 'tcx>(
if visitor2.state == VarState::Warn {
if let Some(name) = visitor2.name {
span_lint(cx,
span_lint(
cx,
EXPLICIT_COUNTER_LOOP,
expr.span,
&format!("the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
&format!(
"the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
item) in {1}.enumerate()` or similar iterators",
name,
snippet(cx, arg.span, "_")));
snippet(cx, arg.span, "_")
),
);
}
}
}
@ -866,7 +940,7 @@ fn check_for_loop_over_map_kv<'a, 'tcx>(
pat: &'tcx Pat,
arg: &'tcx Expr,
body: &'tcx Expr,
expr: &'tcx Expr
expr: &'tcx Expr,
) {
let pat_span = pat.span;
@ -929,7 +1003,7 @@ fn pat_is_wild<'tcx>(pat: &'tcx PatKind, body: &'tcx Expr) -> bool {
fn match_var(expr: &Expr, var: Name) -> bool {
if let ExprPath(QPath::Resolved(None, ref path)) = expr.node {
if path.segments.len() == 1 && path.segments[0].name == var {
return true
return true;
}
}
false
@ -964,7 +1038,8 @@ struct VarVisitor<'a, 'tcx: 'a> {
/// Any names that are used outside an index operation.
/// Used to detect things like `&mut vec` used together with `vec[i]`
referenced: HashSet<Name>,
/// has the loop variable been used in expressions other than the index of an index op?
/// has the loop variable been used in expressions other than the index of
/// an index op?
nonindex: bool,
}
@ -1095,7 +1170,8 @@ fn is_iterable_array(ty: Ty) -> bool {
}
}
/// If a block begins with a statement (possibly a `let` binding) and has an expression, return it.
/// If a block begins with a statement (possibly a `let` binding) and has an
/// expression, return it.
fn extract_expr_from_first_stmt(block: &Block) -> Option<&Expr> {
if block.stmts.is_empty() {
return None;
@ -1302,7 +1378,9 @@ fn var_def_id(cx: &LateContext, expr: &Expr) -> Option<NodeId> {
if let ExprPath(ref qpath) = expr.node {
let path_res = cx.tables.qpath_def(qpath, expr.id);
if let Def::Local(def_id) = path_res {
let node_id = cx.tcx.hir.as_local_node_id(def_id).expect("That DefId should be valid");
let node_id = cx.tcx.hir.as_local_node_id(def_id).expect(
"That DefId should be valid",
);
return Some(node_id);
}
}
@ -1348,16 +1426,17 @@ fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool
match cx.tcx.hir.find(parent) {
Some(NodeExpr(expr)) => {
match expr.node {
ExprLoop(..) |
ExprWhile(..) => { return true; },
_ => ()
ExprLoop(..) | ExprWhile(..) => {
return true;
},
_ => (),
}
},
Some(NodeBlock(block)) => {
let mut block_visitor = LoopNestVisitor {
id: id,
iterator: iter_name,
nesting: Unknown
nesting: Unknown,
};
walk_block(&mut block_visitor, block);
if block_visitor.nesting == RuledOut {
@ -1367,7 +1446,7 @@ fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool
Some(NodeStmt(_)) => (),
_ => {
return false;
}
},
}
id = parent;
}
@ -1377,7 +1456,7 @@ fn is_loop_nested(cx: &LateContext, loop_expr: &Expr, iter_expr: &Expr) -> bool
enum Nesting {
Unknown, // no nesting detected yet
RuledOut, // the iterator is initialized or assigned within scope
LookFurther // no nesting detected, no further walk required
LookFurther, // no nesting detected, no further walk required
}
use self::Nesting::{Unknown, RuledOut, LookFurther};
@ -1385,7 +1464,7 @@ use self::Nesting::{Unknown, RuledOut, LookFurther};
struct LoopNestVisitor {
id: NodeId,
iterator: Name,
nesting: Nesting
nesting: Nesting,
}
impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
@ -1398,22 +1477,28 @@ impl<'tcx> Visitor<'tcx> for LoopNestVisitor {
}
fn visit_expr(&mut self, expr: &'tcx Expr) {
if self.nesting != Unknown { return; }
if self.nesting != Unknown {
return;
}
if expr.id == self.id {
self.nesting = LookFurther;
return;
}
match expr.node {
ExprAssign(ref path, _) |
ExprAssignOp(_, ref path, _) => if match_var(path, self.iterator) {
ExprAssignOp(_, ref path, _) => {
if match_var(path, self.iterator) {
self.nesting = RuledOut;
}
},
_ => walk_expr(self, expr)
_ => walk_expr(self, expr),
}
}
fn visit_pat(&mut self, pat: &'tcx Pat) {
if self.nesting != Unknown { return; }
if self.nesting != Unknown {
return;
}
if let PatKind::Binding(_, _, span_name, _) = pat.node {
if self.iterator == span_name.node {
self.nesting = RuledOut;

View File

@ -2,8 +2,8 @@ use rustc::lint::*;
use rustc::hir::*;
use rustc::ty;
use syntax::ast;
use utils::{is_adjusted, match_path, match_trait_method, match_type, remove_blocks, paths, snippet, span_help_and_lint,
walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
use utils::{is_adjusted, match_path, match_trait_method, match_type, remove_blocks, paths, snippet,
span_help_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, iter_input_pats};
/// **What it does:** Checks for mapping `clone()` over an iterator.
///
@ -76,13 +76,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
ExprPath(ref path) => {
if match_path(path, &paths::CLONE) {
let type_name = get_type_name(cx, expr, &args[0]).unwrap_or("_");
span_help_and_lint(cx,
span_help_and_lint(
cx,
MAP_CLONE,
expr.span,
&format!("you seem to be using .map() to clone the contents of an \
&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, "..")));
type_name
),
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")),
);
}
},
_ => (),
@ -95,10 +99,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
match expr.node {
ExprPath(QPath::Resolved(None, ref path)) => {
let arg_segment = [PathSegment {
let arg_segment = [
PathSegment {
name: id,
parameters: PathParameters::none(),
}];
},
];
!path.is_global() && path.segments[..] == arg_segment
},
_ => false,

View File

@ -150,12 +150,14 @@ pub struct MatchPass;
impl LintPass for MatchPass {
fn get_lints(&self) -> LintArray {
lint_array!(SINGLE_MATCH,
lint_array!(
SINGLE_MATCH,
MATCH_REF_PATS,
MATCH_BOOL,
SINGLE_MATCH_ELSE,
MATCH_OVERLAPPING_ARM,
MATCH_WILD_ERR_ARM)
MATCH_WILD_ERR_ARM
)
}
}
@ -212,28 +214,34 @@ fn report_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm],
SINGLE_MATCH
};
let els_str = els.map_or(String::new(), |els| format!(" else {}", expr_block(cx, els, None, "..")));
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
let`",
"try this",
format!("if let {} = {} {}{}",
format!(
"if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
els_str
),
);
}
fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, ty: Ty, els: Option<&Expr>) {
// list of candidate Enums we know will never get any more members
let candidates = &[(&paths::COW, "Borrowed"),
let candidates = &[
(&paths::COW, "Borrowed"),
(&paths::COW, "Cow::Borrowed"),
(&paths::COW, "Cow::Owned"),
(&paths::COW, "Owned"),
(&paths::OPTION, "None"),
(&paths::RESULT, "Err"),
(&paths::RESULT, "Ok")];
(&paths::RESULT, "Ok"),
];
let path = match arms[1].pats[0].node {
PatKind::TupleStruct(ref path, ref inner, _) => {
@ -258,7 +266,8 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr:
fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
// type of expression == bool
if cx.tables.expr_ty(ex).sty == ty::TyBool {
span_lint_and_then(cx,
span_lint_and_then(
cx,
MATCH_BOOL,
expr.span,
"you seem to be trying to match on a boolean expression",
@ -282,13 +291,19 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
if let Some((true_expr, false_expr)) = exprs {
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => {
Some(format!("if {} {} else {}",
Some(format!(
"if {} {} else {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, ".."),
expr_block(cx, false_expr, None, "..")))
expr_block(cx, false_expr, None, "..")
))
},
(false, true) => {
Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
Some(format!(
"if {} {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, "..")
))
},
(true, false) => {
let test = Sugg::hir(cx, ex, "..");
@ -303,7 +318,8 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
}
}
});
},
);
}
}
@ -313,12 +329,14 @@ fn check_overlapping_arms(cx: &LateContext, ex: &Expr, arms: &[Arm]) {
let type_ranges = type_ranges(&ranges);
if !type_ranges.is_empty() {
if let Some((start, end)) = overlapping(&type_ranges) {
span_note_and_lint(cx,
span_note_and_lint(
cx,
MATCH_OVERLAPPING_ARM,
start.span,
"some ranges overlap",
end.span,
"overlaps with this");
"overlaps with this",
);
}
}
}
@ -376,17 +394,21 @@ fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: Match
db.span_suggestion(expr.span, "try", template);
});
} else {
span_lint_and_then(cx,
span_lint_and_then(
cx,
MATCH_REF_PATS,
expr.span,
"you don't need to add `&` to all patterns",
|db| {
let ex = Sugg::hir(cx, ex, "..");
let template = match_template(expr.span, source, &ex.deref());
db.span_suggestion(expr.span,
db.span_suggestion(
expr.span,
"instead of prefixing all patterns with `&`, you can dereference the expression",
template);
});
template,
);
},
);
}
}
}
@ -399,12 +421,16 @@ fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &[Arm], id: NodeId) ->
let constcx = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables);
arms.iter()
.flat_map(|arm| {
if let Arm { ref pats, guard: None, .. } = *arm {
if let Arm {
ref pats,
guard: None,
..
} = *arm
{
pats.iter()
} else {
[].iter()
}
.filter_map(|pat| {
}.filter_map(|pat| {
if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs),
@ -438,10 +464,12 @@ pub struct SpannedRange<T> {
type TypedRanges = Vec<SpannedRange<ConstInt>>;
/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway and other types than
/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
/// and other types than
/// `Uint` and `Int` probably don't make sense.
fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges {
ranges.iter()
ranges
.iter()
.filter_map(|range| match range.node {
(ConstVal::Integral(start), Bound::Included(ConstVal::Integral(end))) => {
Some(SpannedRange {
@ -500,7 +528,8 @@ fn match_template(span: Span, source: MatchSource, expr: &Sugg) -> String {
}
pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
where T: Copy + Ord
where
T: Copy + Ord,
{
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum Kind<'a, T: 'a> {

View File

@ -2,7 +2,8 @@ use rustc::lint::*;
use rustc::hir::{Expr, ExprCall, ExprPath};
use utils::{match_def_path, paths, span_lint};
/// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is `Drop`.
/// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is
/// `Drop`.
///
/// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its
/// destructor, possibly causing leaks.
@ -38,7 +39,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
if match forgot_ty.ty_adt_def() {
Some(def) => def.has_dtor(cx.tcx),
_ => false,
} {
}
{
span_lint(cx, MEM_FORGET, e.span, "usage of mem::forget on Drop type");
}
}

View File

@ -65,7 +65,8 @@ declare_lint! {
/// information) instead of an inherent implementation.
///
/// **Why is this bad?** Implementing the traits improve ergonomics for users of
/// the code, often with very little cost. Also people seeing a `mul(...)` method
/// the code, often with very little cost. Also people seeing a `mul(...)`
/// method
/// may expect `*` to work equally, so you should have good reason to disappoint
/// them.
///
@ -368,7 +369,8 @@ declare_lint! {
`_.split(\"x\")`"
}
/// **What it does:** Checks for getting the inner pointer of a temporary `CString`.
/// **What it does:** Checks for getting the inner pointer of a temporary
/// `CString`.
///
/// **Why is this bad?** The inner pointer of a `CString` is only valid as long
/// as the `CString` is alive.
@ -500,7 +502,8 @@ declare_lint! {
"using `x.extend(s.chars())` where s is a `&str` or `String`"
}
/// **What it does:** Checks for the use of `.cloned().collect()` on slice to create a `Vec`.
/// **What it does:** Checks for the use of `.cloned().collect()` on slice to
/// create a `Vec`.
///
/// **Why is this bad?** `.to_vec()` is clearer
///
@ -524,7 +527,8 @@ declare_lint! {
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(OPTION_UNWRAP_USED,
lint_array!(
OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED,
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
@ -546,7 +550,8 @@ impl LintPass for Pass {
ITER_SKIP_NEXT,
GET_UNWRAP,
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT)
ITER_CLONED_COLLECT
)
}
}
@ -706,7 +711,7 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
self_expr: &hir::Expr,
arg: &hir::Expr,
or_has_args: bool,
span: Span
span: Span,
) -> bool {
if or_has_args {
return false;
@ -718,20 +723,22 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
if ["default", "new"].contains(&path) {
let arg_ty = cx.tables.expr_ty(arg);
let default_trait_id = if let Some(default_trait_id) =
get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
let default_trait_id =
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
default_trait_id
} else {
return false;
};
if implements_trait(cx, arg_ty, default_trait_id, &[]) {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
OR_FUN_CALL,
span,
&format!("use of `{}` followed by a call to `{}`", name, path),
"try this",
format!("{}.unwrap_or_default()", snippet(cx, self_expr.span, "_")));
format!("{}.unwrap_or_default()", snippet(cx, self_expr.span, "_")),
);
return true;
}
}
@ -749,7 +756,7 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
self_expr: &hir::Expr,
arg: &hir::Expr,
or_has_args: bool,
span: Span
span: Span,
) {
// don't lint for constant values
// FIXME: can we `expect` here instead of match?
@ -765,15 +772,18 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
// (path, fn_has_argument, methods, suffix)
let know_types: &[(&[_], _, &[_], _)] =
&[(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
&[
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
(&paths::RESULT, true, &["or", "unwrap_or"], "else")];
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
];
let self_ty = cx.tables.expr_ty(self_expr);
let (fn_has_arguments, poss, suffix) = if let Some(&(_, fn_has_arguments, poss, suffix)) =
know_types.iter().find(|&&i| match_type(cx, self_ty, i.0)) {
know_types.iter().find(|&&i| match_type(cx, self_ty, i.0))
{
(fn_has_arguments, poss, suffix)
} else {
return;
@ -789,12 +799,14 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
(false, true) => snippet(cx, fun_span, ".."),
};
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
OR_FUN_CALL,
span,
&format!("use of `{}` followed by a function call", name),
"try this",
format!("{}.{}_{}({})", snippet(cx, self_expr.span, "_"), name, suffix, sugg));
format!("{}.{}_{}({})", snippet(cx, self_expr.span, "_"), name, suffix, sugg),
);
}
if args.len() == 2 {
@ -818,31 +830,29 @@ fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, arg_t
let ty = cx.tables.expr_ty(expr);
if let ty::TyRef(_, ty::TypeAndMut { ty: inner, .. }) = arg_ty.sty {
if let ty::TyRef(..) = inner.sty {
span_lint_and_then(cx,
span_lint_and_then(
cx,
CLONE_DOUBLE_REF,
expr.span,
"using `clone` on a double-reference; \
this will copy the reference instead of cloning the inner type",
|db| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
db.span_suggestion(expr.span,
"try dereferencing it",
format!("({}).clone()", snip.deref()));
});
db.span_suggestion(expr.span, "try dereferencing it", format!("({}).clone()", snip.deref()));
},
);
return; // don't report clone_on_copy
}
}
if is_copy(cx, ty) {
span_lint_and_then(cx,
CLONE_ON_COPY,
expr.span,
"using `clone` on a `Copy` type",
|db| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
span_lint_and_then(cx, CLONE_ON_COPY, expr.span, "using `clone` on a `Copy` type", |db| {
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
if let ty::TyRef(..) = cx.tables.expr_ty(arg).sty {
db.span_suggestion(expr.span, "try dereferencing it", format!("{}", snip.deref()));
} else {
db.span_suggestion(expr.span, "try removing the `clone` call", format!("{}", snip));
}
}
});
}
}
@ -860,15 +870,19 @@ fn lint_string_extend(cx: &LateContext, expr: &hir::Expr, args: &[hir::Expr]) {
return;
};
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
"try this",
format!("{}.push_str({}{})",
format!(
"{}.push_str({}{})",
snippet(cx, args[0].span, "_"),
ref_str,
snippet(cx, target.span, "_")));
snippet(cx, target.span, "_")
),
);
}
}
@ -898,12 +912,15 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
fn lint_iter_cloned_collect(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr]) {
if match_type(cx, cx.tables.expr_ty(expr), &paths::VEC) &&
derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some() {
span_lint(cx,
derefs_to_slice(cx, &iter_args[0], cx.tables.expr_ty(&iter_args[0])).is_some()
{
span_lint(
cx,
ITER_CLONED_COLLECT,
expr.span,
"called `cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
more readable");
more readable",
);
}
}
@ -919,12 +936,16 @@ fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr], is
return; // caller is not a type that we want to lint
};
span_lint(cx,
span_lint(
cx,
ITER_NTH,
expr.span,
&format!("called `.iter{0}().nth()` on a {1}. Calling `.get{0}()` is both faster and more readable",
&format!(
"called `.iter{0}().nth()` on a {1}. Calling `.get{0}()` is both faster and more readable",
mut_str,
caller_type));
caller_type
),
);
}
fn lint_get_unwrap(cx: &LateContext, expr: &hir::Expr, get_args: &[hir::Expr], is_mut: bool) {
@ -947,26 +968,34 @@ fn lint_get_unwrap(cx: &LateContext, expr: &hir::Expr, get_args: &[hir::Expr], i
let mut_str = if is_mut { "_mut" } else { "" };
let borrow_str = if is_mut { "&mut " } else { "&" };
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
GET_UNWRAP,
expr.span,
&format!("called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
&format!(
"called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
mut_str,
caller_type),
caller_type
),
"try this",
format!("{}{}[{}]",
format!(
"{}{}[{}]",
borrow_str,
snippet(cx, get_args[0].span, "_"),
snippet(cx, get_args[1].span, "_")));
snippet(cx, get_args[1].span, "_")
),
);
}
fn lint_iter_skip_next(cx: &LateContext, expr: &hir::Expr) {
// lint if caller of skip is an Iterator
if match_trait_method(cx, expr, &paths::ITERATOR) {
span_lint(cx,
span_lint(
cx,
ITER_SKIP_NEXT,
expr.span,
"called `skip(x).next()` on an iterator. This is more succinctly expressed by calling `nth(x)`");
"called `skip(x).next()` on an iterator. This is more succinctly expressed by calling `nth(x)`",
);
}
}
@ -1017,14 +1046,18 @@ fn lint_unwrap(cx: &LateContext, expr: &hir::Expr, unwrap_args: &[hir::Expr]) {
};
if let Some((lint, kind, none_value)) = mess {
span_lint(cx,
span_lint(
cx,
lint,
expr.span,
&format!("used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
&format!(
"used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
using expect() to provide a better panic \
message",
kind,
none_value));
none_value
),
);
}
}
@ -1035,10 +1068,12 @@ fn lint_ok_expect(cx: &LateContext, expr: &hir::Expr, ok_args: &[hir::Expr]) {
let result_type = cx.tables.expr_ty(&ok_args[0]);
if let Some(error_type) = get_error_type(cx, result_type) {
if has_debug_impl(error_type, cx) {
span_lint(cx,
span_lint(
cx,
OK_EXPECT,
expr.span,
"called `ok().expect()` on a Result value. You can call `expect` directly on the `Result`");
"called `ok().expect()` on a Result value. You can call `expect` directly on the `Result`",
);
}
}
}
@ -1059,14 +1094,18 @@ fn lint_map_unwrap_or(cx: &LateContext, expr: &hir::Expr, map_args: &[hir::Expr]
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_args[1].span.ctxt == unwrap_args[1].span.ctxt;
if same_span && !multiline {
span_note_and_lint(cx,
span_note_and_lint(
cx,
OPTION_MAP_UNWRAP_OR,
expr.span,
msg,
expr.span,
&format!("replace `map({0}).unwrap_or({1})` with `map_or({1}, {0})`",
&format!(
"replace `map({0}).unwrap_or({1})` with `map_or({1}, {0})`",
map_snippet,
unwrap_snippet));
unwrap_snippet
),
);
} else if same_span && multiline {
span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
};
@ -1088,14 +1127,18 @@ fn lint_map_unwrap_or_else(cx: &LateContext, expr: &hir::Expr, map_args: &[hir::
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
let same_span = map_args[1].span.ctxt == unwrap_args[1].span.ctxt;
if same_span && !multiline {
span_note_and_lint(cx,
span_note_and_lint(
cx,
OPTION_MAP_UNWRAP_OR_ELSE,
expr.span,
msg,
expr.span,
&format!("replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
&format!(
"replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
map_snippet,
unwrap_snippet));
unwrap_snippet
),
);
} else if same_span && multiline {
span_lint(cx, OPTION_MAP_UNWRAP_OR_ELSE, expr.span, msg);
};
@ -1111,12 +1154,14 @@ fn lint_filter_next(cx: &LateContext, expr: &hir::Expr, filter_args: &[hir::Expr
let filter_snippet = snippet(cx, filter_args[1].span, "..");
if filter_snippet.lines().count() <= 1 {
// add note if not multi-line
span_note_and_lint(cx,
span_note_and_lint(
cx,
FILTER_NEXT,
expr.span,
msg,
expr.span,
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet));
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet),
);
} else {
span_lint(cx, FILTER_NEXT, expr.span, msg);
}
@ -1171,22 +1216,26 @@ fn lint_search_is_some(
expr: &hir::Expr,
search_method: &str,
search_args: &[hir::Expr],
is_some_args: &[hir::Expr]
is_some_args: &[hir::Expr],
) {
// lint if caller of search is an Iterator
if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
let msg = format!("called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
let msg = format!(
"called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
expressed by calling `any()`.",
search_method);
search_method
);
let search_snippet = snippet(cx, search_args[1].span, "..");
if search_snippet.lines().count() <= 1 {
// add note if not multi-line
span_note_and_lint(cx,
span_note_and_lint(
cx,
SEARCH_IS_SOME,
expr.span,
&msg,
expr.span,
&format!("replace `{0}({1}).is_some()` with `any({1})`", search_method, search_snippet));
&format!("replace `{0}({1}).is_some()` with `any({1})`", search_method, search_snippet),
);
} else {
span_lint(cx, SEARCH_IS_SOME, expr.span, &msg);
}
@ -1233,11 +1282,13 @@ fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr)
if let Ok(ConstVal::Str(r)) = ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(arg) {
if r.len() == 1 {
let hint = snippet(cx, expr.span, "..").replace(&format!("\"{}\"", r), &format!("'{}'", r));
span_lint_and_then(cx,
span_lint_and_then(
cx,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
|db| { db.span_suggestion(expr.span, "try using a char instead", hint); });
|db| { db.span_suggestion(expr.span, "try using a char instead", hint); },
);
}
}
}
@ -1349,13 +1400,17 @@ impl SelfKind {
arg: &hir::Arg,
self_ty: &hir::Ty,
allow_value_for_ref: bool,
generics: &hir::Generics
generics: &hir::Generics,
) -> bool {
// Self types in the HIR are desugared to explicit self types. So it will always be `self:
// Self types in the HIR are desugared to explicit self types. So it will
// always be `self:
// SomeType`,
// where SomeType can be `Self` or an explicit impl self type (e.g. `Foo` if the impl is on `Foo`)
// Thus, we only need to test equality against the impl self type or if it is an explicit
// `Self`. Furthermore, the only possible types for `self: ` are `&Self`, `Self`, `&mut Self`,
// where SomeType can be `Self` or an explicit impl self type (e.g. `Foo` if
// the impl is on `Foo`)
// Thus, we only need to test equality against the impl self type or if it is
// an explicit
// `Self`. Furthermore, the only possible types for `self: ` are `&Self`,
// `Self`, `&mut Self`,
// and `Box<Self>`, including the equivalent types with `Foo`.
let is_actually_self = |ty| is_self_ty(ty) || ty == self_ty;
@ -1404,17 +1459,21 @@ fn is_as_ref_or_mut_trait(ty: &hir::Ty, self_ty: &hir::Ty, generics: &hir::Gener
single_segment_ty(ty).map_or(false, |seg| {
generics.ty_params.iter().any(|param| {
param.name == seg.name &&
param.bounds.iter().any(|bound| if let hir::TyParamBound::TraitTyParamBound(ref ptr, ..) = *bound {
param.bounds.iter().any(|bound| {
if let hir::TyParamBound::TraitTyParamBound(ref ptr, ..) = *bound {
let path = &ptr.trait_ref.path;
match_path_old(path, name) &&
path.segments.last().map_or(false, |s| if let hir::PathParameters::AngleBracketedParameters(ref data) =
s.parameters {
data.types.len() == 1 && (is_self_ty(&data.types[0]) || is_ty(&*data.types[0], self_ty))
path.segments.last().map_or(false, |s| {
if let hir::PathParameters::AngleBracketedParameters(ref data) = s.parameters {
data.types.len() == 1 &&
(is_self_ty(&data.types[0]) || is_ty(&*data.types[0], self_ty))
} else {
false
}
})
} else {
false
}
})
})
})
@ -1424,7 +1483,9 @@ fn is_ty(ty: &hir::Ty, self_ty: &hir::Ty) -> bool {
match (&ty.node, &self_ty.node) {
(&hir::TyPath(hir::QPath::Resolved(_, ref ty_path)),
&hir::TyPath(hir::QPath::Resolved(_, ref self_ty_path))) => {
ty_path.segments.iter().map(|seg| seg.name).eq(self_ty_path.segments.iter().map(|seg| seg.name))
ty_path.segments.iter().map(|seg| seg.name).eq(
self_ty_path.segments.iter().map(|seg| seg.name),
)
},
_ => false,
}

View File

@ -14,7 +14,8 @@ use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integ
use utils::sugg::Sugg;
use syntax::ast::{LitKind, CRATE_NODE_ID, FloatTy};
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
/// **What it does:** Checks for function arguments and let bindings denoted as
/// `ref`.
///
/// **Why is this bad?** The `ref` declaration makes the function take an owned
/// value, but turns the argument into a reference (which means that the value
@ -118,7 +119,8 @@ declare_lint! {
/// **What it does:** Checks for patterns in the form `name @ _`.
///
/// **Why is this bad?** It's almost always more readable to just use direct bindings.
/// **Why is this bad?** It's almost always more readable to just use direct
/// bindings.
///
/// **Known problems:** None.
///
@ -135,7 +137,8 @@ declare_lint! {
"using `name @ _` in a pattern"
}
/// **What it does:** Checks for the use of bindings with a single leading underscore.
/// **What it does:** Checks for the use of bindings with a single leading
/// underscore.
///
/// **Why is this bad?** A single leading underscore is usually used to indicate
/// that a binding will not be used. Using such a binding breaks this
@ -147,7 +150,8 @@ declare_lint! {
/// **Example:**
/// ```rust
/// let _x = 0;
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading underscore.
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading
/// underscore.
/// // We should rename `_x` to `x`
/// ```
declare_lint! {
@ -156,11 +160,14 @@ declare_lint! {
"using a binding which is prefixed with an underscore"
}
/// **What it does:** Checks for the use of short circuit boolean conditions as a
/// **What it does:** Checks for the use of short circuit boolean conditions as
/// a
/// statement.
///
/// **Why is this bad?** Using a short circuit boolean condition as a statement may
/// hide the fact that the second part is executed or not depending on the outcome of
/// **Why is this bad?** Using a short circuit boolean condition as a statement
/// may
/// hide the fact that the second part is executed or not depending on the
/// outcome of
/// the first part.
///
/// **Known problems:** None.
@ -198,7 +205,8 @@ pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(TOPLEVEL_REF_ARG,
lint_array!(
TOPLEVEL_REF_ARG,
CMP_NAN,
FLOAT_CMP,
CMP_OWNED,
@ -206,7 +214,8 @@ impl LintPass for Pass {
REDUNDANT_PATTERN,
USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT,
ZERO_PTR)
ZERO_PTR
)
}
}
@ -218,7 +227,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
decl: &'tcx FnDecl,
body: &'tcx Body,
_: Span,
_: NodeId
_: NodeId,
) {
if let FnKind::Closure(_) = k {
// Does not apply to closures
@ -228,11 +237,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
match arg.pat.node {
PatKind::Binding(BindingAnnotation::Ref, _, _, _) |
PatKind::Binding(BindingAnnotation::RefMut, _, _, _) => {
span_lint(cx,
span_lint(
cx,
TOPLEVEL_REF_ARG,
arg.pat.span,
"`ref` directly on a function argument is ignored. Consider using a reference type \
instead.");
instead.",
);
},
_ => {},
}
@ -316,7 +327,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
if let Some(name) = get_item_name(cx, expr) {
let name = name.as_str();
if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") ||
name.ends_with("_eq") {
name.ends_with("_eq")
{
return;
}
}
@ -324,9 +336,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
let lhs = Sugg::hir(cx, left, "..");
let rhs = Sugg::hir(cx, right, "..");
db.span_suggestion(expr.span,
db.span_suggestion(
expr.span,
"consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs));
format!("({}).abs() < error", lhs - rhs),
);
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
});
} else if op == BiRem && is_integer_literal(right, 1) {
@ -347,7 +361,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
binding != "_result" && // FIXME: #944
is_used(cx, expr) &&
// don't lint if the declaration is in a macro
non_macro_local(cx, &cx.tables.qpath_def(qpath, expr.id)) {
non_macro_local(cx, &cx.tables.qpath_def(qpath, expr.id))
{
Some(binding)
} else {
None
@ -364,22 +379,28 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
_ => None,
};
if let Some(binding) = binding {
span_lint(cx,
span_lint(
cx,
USED_UNDERSCORE_BINDING,
expr.span,
&format!("used binding `{}` which is prefixed with an underscore. A leading \
&format!(
"used binding `{}` which is prefixed with an underscore. A leading \
underscore signals that a binding will not be used.",
binding));
binding
),
);
}
}
fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat) {
if let PatKind::Binding(_, _, ref ident, Some(ref right)) = pat.node {
if right.node == PatKind::Wild {
span_lint(cx,
span_lint(
cx,
REDUNDANT_PATTERN,
pat.span,
&format!("the `{} @ _` pattern can be written as just `{}`", ident.node, ident.node));
&format!("the `{} @ _` pattern can be written as just `{}`", ident.node, ident.node),
);
}
}
}
@ -388,10 +409,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_nan(cx: &LateContext, path: &Path, expr: &Expr) {
if !in_constant(cx, expr.id) {
path.segments.last().map(|seg| if seg.name == "NAN" {
span_lint(cx,
span_lint(
cx,
CMP_NAN,
expr.span,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead",
);
});
}
}
@ -490,16 +513,19 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr) {
.builtin_deref(true, ty::LvaluePreference::NoPreference)
.map_or(false, |tam| implements_trait(cx, arg_ty, partial_eq_trait_id, &[tam.ty]))
// arg impls PartialEq<other>
&& !implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty]) {
&& !implements_trait(cx, arg_ty, partial_eq_trait_id, &[other_ty])
{
return;
}
span_lint_and_then(cx,
span_lint_and_then(
cx,
CMP_OWNED,
expr.span,
"this creates an owned instance just for comparison",
|db| {
// this is as good as our recursion check can get, we can't prove that the current function is
// this is as good as our recursion check can get, we can't prove that the
// current function is
// called by
// PartialEq::eq, but we can at least ensure that this code is not part of it
let parent_fn = cx.tcx.hir.get_parent(expr.id);
@ -508,7 +534,8 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr) {
if let map::NodeItem(item) = cx.tcx.hir.get(parent_impl) {
if let ItemImpl(.., Some(ref trait_ref), _, _) = item.node {
if trait_ref.path.def.def_id() == partial_eq_trait_id {
// we are implementing PartialEq, don't suggest not doing `to_owned`, otherwise we go into
// we are implementing PartialEq, don't suggest not doing `to_owned`, otherwise
// we go into
// recursion
db.span_label(expr.span, "try calling implementing the comparison without allocating");
return;
@ -517,10 +544,12 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr) {
}
}
db.span_suggestion(expr.span, "try", snip.to_string());
});
},
);
}
/// Heuristic to see if an expression is used. Should be compatible with `unused_variables`'s idea
/// Heuristic to see if an expression is used. Should be compatible with
/// `unused_variables`'s idea
/// of what it means for an expression to be "used".
fn is_used(cx: &LateContext, expr: &Expr) -> bool {
if let Some(parent) = get_parent_expr(cx, expr) {
@ -534,10 +563,14 @@ fn is_used(cx: &LateContext, expr: &Expr) -> bool {
}
}
/// Test whether an expression is in a macro expansion (e.g. something generated by
/// Test whether an expression is in a macro expansion (e.g. something
/// generated by
/// `#[derive(...)`] or the like).
fn in_attributes_expansion(expr: &Expr) -> bool {
expr.span.ctxt.outer().expn_info().map_or(false, |info| matches!(info.callee.format, ExpnFormat::MacroAttribute(_)))
expr.span.ctxt.outer().expn_info().map_or(
false,
|info| matches!(info.callee.format, ExpnFormat::MacroAttribute(_)),
)
}
/// Test whether `def` is a variable defined outside a macro.
@ -545,10 +578,9 @@ fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
match *def {
def::Def::Local(def_id) |
def::Def::Upvar(def_id, _, _) => {
let id = cx.tcx
.hir
.as_local_node_id(def_id)
.expect("local variables should be found in the same crate");
let id = cx.tcx.hir.as_local_node_id(def_id).expect(
"local variables should be found in the same crate",
);
!in_macro(cx.tcx.hir.span(id))
},
_ => false,

View File

@ -40,9 +40,11 @@ declare_lint! {
"function arguments having names which only differ by an underscore"
}
/// **What it does:** Detects closures called in the same expression where they are defined.
/// **What it does:** Detects closures called in the same expression where they
/// are defined.
///
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
/// **Why is this bad?** It is unnecessarily adding to the expression's
/// complexity.
///
/// **Known problems:** None.
///
@ -73,7 +75,8 @@ declare_lint! {
"`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
}
/// **What it does:** Warns on hexadecimal literals with mixed-case letter digits.
/// **What it does:** Warns on hexadecimal literals with mixed-case letter
/// digits.
///
/// **Why is this bad?** It looks confusing.
///
@ -89,7 +92,8 @@ declare_lint! {
"hex literals whose letter digits are not consistently upper- or lowercased"
}
/// **What it does:** Warns if literal suffixes are not separated by an underscore.
/// **What it does:** Warns if literal suffixes are not separated by an
/// underscore.
///
/// **Why is this bad?** It is much less readable.
///
@ -107,8 +111,10 @@ declare_lint! {
/// **What it does:** Warns if an integral constant literal starts with `0`.
///
/// **Why is this bad?** In some languages (including the infamous C language and most of its
/// family), this marks an octal constant. In Rust however, this is a decimal constant. This could
/// **Why is this bad?** In some languages (including the infamous C language
/// and most of its
/// family), this marks an octal constant. In Rust however, this is a decimal
/// constant. This could
/// be confusing for both the writer and a reader of the constant.
///
/// **Known problems:** None.
@ -167,14 +173,16 @@ pub struct MiscEarly;
impl LintPass for MiscEarly {
fn get_lints(&self) -> LintArray {
lint_array!(UNNEEDED_FIELD_PATTERN,
lint_array!(
UNNEEDED_FIELD_PATTERN,
DUPLICATE_UNDERSCORE_ARGUMENT,
REDUNDANT_CLOSURE_CALL,
DOUBLE_NEG,
MIXED_CASE_HEX_LITERALS,
UNSEPARATED_LITERAL_SUFFIX,
ZERO_PREFIXED_LITERAL,
BUILTIN_TYPE_SHADOW)
BUILTIN_TYPE_SHADOW
)
}
}
@ -183,10 +191,12 @@ impl EarlyLintPass for MiscEarly {
for ty in &gen.ty_params {
let name = ty.ident.name.as_str();
if constants::BUILTIN_TYPES.contains(&&*name) {
span_lint(cx,
span_lint(
cx,
BUILTIN_TYPE_SHADOW,
ty.span,
&format!("This generic shadows the built-in type `{}`", name));
&format!("This generic shadows the built-in type `{}`", name),
);
}
}
}
@ -194,7 +204,11 @@ impl EarlyLintPass for MiscEarly {
fn check_pat(&mut self, cx: &EarlyContext, pat: &Pat) {
if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
let mut wilds = 0;
let type_name = npat.segments.last().expect("A path must have at least one segment").identifier.name;
let type_name = npat.segments
.last()
.expect("A path must have at least one segment")
.identifier
.name;
for field in pfields {
if field.node.pat.node == PatKind::Wild {
@ -202,11 +216,13 @@ impl EarlyLintPass for MiscEarly {
}
}
if !pfields.is_empty() && wilds == pfields.len() {
span_help_and_lint(cx,
span_help_and_lint(
cx,
UNNEEDED_FIELD_PATTERN,
pat.span,
"All the struct fields are matched to a wildcard pattern, consider using `..`.",
&format!("Try with `{} {{ .. }}` instead", type_name));
&format!("Try with `{} {{ .. }}` instead", type_name),
);
return;
}
if wilds > 0 {
@ -223,19 +239,21 @@ impl EarlyLintPass for MiscEarly {
if field.node.pat.node == PatKind::Wild {
wilds -= 1;
if wilds > 0 {
span_lint(cx,
span_lint(
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` instead");
"You matched a field with a wildcard pattern. Consider using `..` instead",
);
} else {
span_help_and_lint(cx,
span_help_and_lint(
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` \
instead",
&format!("Try with `{} {{ {}, .. }}`",
type_name,
normal[..].join(", ")));
&format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
);
}
}
}
@ -252,12 +270,16 @@ impl EarlyLintPass for MiscEarly {
if arg_name.starts_with('_') {
if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
span_lint(cx,
span_lint(
cx,
DUPLICATE_UNDERSCORE_ARGUMENT,
*correspondence,
&format!("`{}` already exists, having another argument having almost the same \
&format!(
"`{}` already exists, having another argument having almost the same \
name makes code comprehension and documentation more difficult",
arg_name[1..].to_owned()));;
arg_name[1..].to_owned()
),
);;
}
} else {
registered_names.insert(arg_name, arg.pat.span);
@ -287,10 +309,12 @@ impl EarlyLintPass for MiscEarly {
},
ExprKind::Unary(UnOp::Neg, ref inner) => {
if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
span_lint(cx,
span_lint(
cx,
DOUBLE_NEG,
expr.span,
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
);
}
},
ExprKind::Lit(ref lit) => self.check_lit(cx, lit),

View File

@ -11,12 +11,16 @@
// except according to those terms.
//
// Note: More specifically this lint is largely inspired (aka copied) from *rustc*'s
// Note: More specifically this lint is largely inspired (aka copied) from
// *rustc*'s
// [`missing_doc`].
//
// [`missing_doc`]:
// https://github.
// com/rust-lang/rust/blob/d6d05904697d89099b55da3331155392f1db9c00/src/librustc_lint/builtin.
//
//
//
// rs#L246
//
@ -28,10 +32,13 @@ use syntax::attr;
use syntax::codemap::Span;
use utils::in_macro;
/// **What it does:** Warns if there is missing doc for any documentable item (public or private).
/// **What it does:** Warns if there is missing doc for any documentable item
/// (public or private).
///
/// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS` allowed-by-default lint for
/// public members, but has no way to enforce documentation of private items. This lint fixes that.
/// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
/// allowed-by-default lint for
/// public members, but has no way to enforce documentation of private items.
/// This lint fixes that.
///
/// **Known problems:** None.
declare_lint! {
@ -58,7 +65,9 @@ impl MissingDoc {
}
fn doc_hidden(&self) -> bool {
*self.doc_hidden_stack.last().expect("empty doc_hidden_stack")
*self.doc_hidden_stack.last().expect(
"empty doc_hidden_stack",
)
}
fn check_missing_docs_attrs(&self, cx: &LateContext, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
@ -77,11 +86,15 @@ impl MissingDoc {
return;
}
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name().map_or(false, |n| n == "doc"));
let has_doc = attrs.iter().any(|a| {
a.is_value_str() && a.name().map_or(false, |n| n == "doc")
});
if !has_doc {
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS,
cx.span_lint(
MISSING_DOCS_IN_PRIVATE_ITEMS,
sp,
&format!("missing documentation for {}", desc));
&format!("missing documentation for {}", desc),
);
}
}
}

View File

@ -64,26 +64,37 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
intravisit::walk_expr(self, body);
} else if let hir::ExprAddrOf(hir::MutMutable, ref e) = expr.node {
if let hir::ExprAddrOf(hir::MutMutable, _) = e.node {
span_lint(self.cx,
span_lint(
self.cx,
MUT_MUT,
expr.span,
"generally you want to avoid `&mut &mut _` if possible");
"generally you want to avoid `&mut &mut _` if possible",
);
} else if let ty::TyRef(_, ty::TypeAndMut { mutbl: hir::MutMutable, .. }) = self.cx.tables.expr_ty(e).sty {
span_lint(self.cx,
span_lint(
self.cx,
MUT_MUT,
expr.span,
"this expression mutably borrows a mutable reference. Consider reborrowing");
"this expression mutably borrows a mutable reference. Consider reborrowing",
);
}
}
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
if let hir::TyRptr(_, hir::MutTy { ty: ref pty, mutbl: hir::MutMutable }) = ty.node {
if let hir::TyRptr(_,
hir::MutTy {
ty: ref pty,
mutbl: hir::MutMutable,
}) = ty.node
{
if let hir::TyRptr(_, hir::MutTy { mutbl: hir::MutMutable, .. }) = pty.node {
span_lint(self.cx,
span_lint(
self.cx,
MUT_MUT,
ty.span,
"generally you want to avoid `&mut &mut _` if possible");
"generally you want to avoid `&mut &mut _` if possible",
);
}
}

View File

@ -38,10 +38,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
match e.node {
ExprCall(ref fn_expr, ref arguments) => {
if let ExprPath(ref path) = fn_expr.node {
check_arguments(cx,
check_arguments(
cx,
arguments,
cx.tables.expr_ty(fn_expr),
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
);
}
},
ExprMethodCall(ref path, _, ref arguments) => {
@ -64,10 +66,12 @@ fn check_arguments<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arguments: &[Expr], typ
ty::TyRef(_, ty::TypeAndMut { mutbl: MutImmutable, .. }) |
ty::TyRawPtr(ty::TypeAndMut { mutbl: MutImmutable, .. }) => {
if let ExprAddrOf(MutMutable, _) = argument.node {
span_lint(cx,
span_lint(
cx,
UNNECESSARY_MUT_PASSED,
argument.span,
&format!("The function/method `{}` doesn't need a mutable reference", name));
&format!("The function/method `{}` doesn't need a mutable reference", name),
);
}
},
_ => (),

View File

@ -28,9 +28,11 @@ declare_lint! {
"using a mutex where an atomic value could be used instead"
}
/// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral type.
/// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral
/// type.
///
/// **Why is this bad?** Using a mutex just to make access to a plain integer sequential is
/// **Why is this bad?** Using a mutex just to make access to a plain integer
/// sequential is
/// shooting flies with cannons. `std::atomic::usize` is leaner and faster.
///
/// **Known problems:** This lint cannot detect if the mutex is actually used
@ -61,9 +63,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutexAtomic {
if match_type(cx, ty, &paths::MUTEX) {
let mutex_param = subst.type_at(0);
if let Some(atomic_name) = get_atomic_name(mutex_param) {
let msg = format!("Consider using an {} instead of a Mutex here. If you just want the locking \
let msg = format!(
"Consider using an {} instead of a Mutex here. If you just want the locking \
behaviour and not the internal type, consider using Mutex<()>.",
atomic_name);
atomic_name
);
match mutex_param.sty {
ty::TyUint(t) if t != ast::UintTy::Us => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),
ty::TyInt(t) if t != ast::IntTy::Is => span_lint(cx, MUTEX_INTEGER, expr.span, &msg),

View File

@ -9,7 +9,8 @@ use syntax::codemap::Spanned;
use utils::{span_lint, span_lint_and_sugg, snippet};
use utils::sugg::Sugg;
/// **What it does:** Checks for expressions of the form `if c { true } else { false }`
/// **What it does:** Checks for expressions of the form `if c { true } else {
/// false }`
/// (or vice versa) and suggest using the condition directly.
///
/// **Why is this bad?** Redundant code.
@ -70,28 +71,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
snip.to_string()
};
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression returns a bool literal",
"you can reduce it to",
hint);
hint,
);
};
if let ExprBlock(ref then_block) = then_block.node {
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
(RetBool(true), RetBool(true)) |
(Bool(true), Bool(true)) => {
span_lint(cx,
span_lint(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return true");
"this if-then-else expression will always return true",
);
},
(RetBool(false), RetBool(false)) |
(Bool(false), Bool(false)) => {
span_lint(cx,
span_lint(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return false");
"this if-then-else expression will always return false",
);
},
(RetBool(true), RetBool(false)) => reduce(true, false),
(Bool(true), Bool(false)) => reduce(false, false),
@ -122,39 +129,47 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
(Bool(true), Other) => {
let hint = snippet(cx, right_side.span, "..").into_owned();
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint);
hint,
);
},
(Other, Bool(true)) => {
let hint = snippet(cx, left_side.span, "..").into_owned();
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint);
hint,
);
},
(Bool(false), Other) => {
let hint = Sugg::hir(cx, right_side, "..");
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against false can be replaced by a negation",
"try simplifying it as shown",
(!hint).to_string());
(!hint).to_string(),
);
},
(Other, Bool(false)) => {
let hint = Sugg::hir(cx, left_side, "..");
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against false can be replaced by a negation",
"try simplifying it as shown",
(!hint).to_string());
(!hint).to_string(),
);
},
_ => (),
}

View File

@ -9,7 +9,8 @@ use utils::{span_lint, in_macro};
/// **What it does:** Checks for useless borrowed references.
///
/// **Why is this bad?** It is completely useless and make the code look more complex than it
/// **Why is this bad?** It is completely useless and make the code look more
/// complex than it
/// actually is.
///
/// **Known problems:** None.
@ -19,7 +20,8 @@ use utils::{span_lint, in_macro};
/// let mut v = Vec::<String>::new();
/// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
/// ```
/// This clojure takes a reference on something that has been matched as a reference and
/// This clojure takes a reference on something that has been matched as a
/// reference and
/// de-referenced.
/// As such, it could just be |a| a.is_empty()
declare_lint! {

View File

@ -116,59 +116,60 @@ impl EarlyLintPass for NeedlessContinue {
}
}
/* This lint has to mainly deal with two cases of needless continue statements.
*
* Case 1 [Continue inside else block]:
*
* loop {
* // region A
* if cond {
* // region B
* } else {
* continue;
* }
* // region C
* }
*
* This code can better be written as follows:
*
* loop {
* // region A
* if cond {
* // region B
* // region C
* }
* }
*
* Case 2 [Continue inside then block]:
*
* loop {
* // region A
* if cond {
* continue;
* // potentially more code here.
* } else {
* // region B
* }
* // region C
* }
*
*
* This snippet can be refactored to:
*
* loop {
* // region A
* if !cond {
* // region B
* // region C
* }
* }
* */
/* This lint has to mainly deal with two cases of needless continue
* statements. */
// Case 1 [Continue inside else block]:
//
// loop {
// // region A
// if cond {
// // region B
// } else {
// continue;
// }
// // region C
// }
//
// This code can better be written as follows:
//
// loop {
// // region A
// if cond {
// // region B
// // region C
// }
// }
//
// Case 2 [Continue inside then block]:
//
// loop {
// // region A
// if cond {
// continue;
// // potentially more code here.
// } else {
// // region B
// }
// // region C
// }
//
//
// This snippet can be refactored to:
//
// loop {
// // region A
// if !cond {
// // region B
// // region C
// }
// }
//
/// Given an expression, returns true if either of the following is true
///
/// - The expression is a `continue` node.
/// - The expression node is a block with the first statement being a `continue`.
/// - The expression node is a block with the first statement being a
/// `continue`.
///
fn needless_continue_in_else(else_expr: &ast::Expr) -> bool {
match else_expr.node {
@ -195,7 +196,8 @@ fn is_first_block_stmt_continue(block: &ast::Block) -> bool {
/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
/// the AST object representing the loop block of `expr`.
fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
where F: FnMut(&ast::Block)
where
F: FnMut(&ast::Block),
{
match expr.node {
ast::ExprKind::While(_, ref loop_block, _) |
@ -206,7 +208,8 @@ fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
}
}
/// If `stmt` is an if expression node with an `else` branch, calls func with the
/// If `stmt` is an if expression node with an `else` branch, calls func with
/// the
/// following:
///
/// - The `if` expression itself,
@ -215,7 +218,8 @@ fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
/// - The `else` expression.
///
fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
where F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr)
where
F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
{
match stmt.node {
ast::StmtKind::Semi(ref e) |
@ -271,10 +275,18 @@ fn emit_warning<'a>(ctx: &EarlyContext, data: &'a LintData, header: &str, typ: L
// expr is the expression which the lint warning message refers to.
let (snip, message, expr) = match typ {
LintType::ContinueInsideElseBlock => {
(suggestion_snippet_for_continue_inside_else(ctx, data, header), MSG_REDUNDANT_ELSE_BLOCK, data.else_expr)
(
suggestion_snippet_for_continue_inside_else(ctx, data, header),
MSG_REDUNDANT_ELSE_BLOCK,
data.else_expr,
)
},
LintType::ContinueInsideThenBlock => {
(suggestion_snippet_for_continue_inside_if(ctx, data, header), MSG_ELSE_BLOCK_NOT_NEEDED, data.if_expr)
(
suggestion_snippet_for_continue_inside_if(ctx, data, header),
MSG_ELSE_BLOCK_NOT_NEEDED,
data.if_expr,
)
},
};
span_help_and_lint(ctx, NEEDLESS_CONTINUE, expr.span, message, &snip);
@ -407,7 +419,8 @@ pub fn erode_from_front(s: &str) -> String {
}
/// If `s` contains the code for a block, delimited by braces, this function
/// tries to get the contents of the block. If there is no closing brace present,
/// tries to get the contents of the block. If there is no closing brace
/// present,
/// an empty string is returned.
pub fn erode_block(s: &str) -> String {
erode_from_back(&erode_from_front(s))

View File

@ -13,10 +13,12 @@ use utils::{in_macro, is_self, is_copy, implements_trait, get_trait_def_id, matc
multispan_sugg, paths};
use std::collections::{HashSet, HashMap};
/// **What it does:** Checks for functions taking arguments by value, but not consuming them in its
/// **What it does:** Checks for functions taking arguments by value, but not
/// consuming them in its
/// body.
///
/// **Why is this bad?** Taking arguments by reference is more flexible and can sometimes avoid
/// **Why is this bad?** Taking arguments by reference is more flexible and can
/// sometimes avoid
/// unnecessary allocations.
///
/// **Known problems:** Hopefully none.
@ -53,7 +55,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
decl: &'tcx FnDecl,
body: &'tcx Body,
span: Span,
node_id: NodeId
node_id: NodeId,
) {
if in_macro(span) {
return;
@ -87,8 +89,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
.collect()
};
// Collect moved variables and spans which will need dereferencings from the function body.
let MovedVariablesCtxt { moved_vars, spans_need_deref, .. } = {
// Collect moved variables and spans which will need dereferencings from the
// function body.
let MovedVariablesCtxt {
moved_vars,
spans_need_deref,
..
} = {
let mut ctx = MovedVariablesCtxt::new(cx);
let region_maps = &cx.tcx.region_maps(fn_def_id);
euv::ExprUseVisitor::new(&mut ctx, cx.tcx, cx.param_env, region_maps, cx.tables).consume_body(body);
@ -102,14 +109,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
// Determines whether `ty` implements `Borrow<U>` (U != ty) specifically.
// This is needed due to the `Borrow<T> for T` blanket impl.
let implements_borrow_trait = preds.iter()
let implements_borrow_trait = preds
.iter()
.filter_map(|pred| if let ty::Predicate::Trait(ref poly_trait_ref) = *pred {
Some(poly_trait_ref.skip_binder())
} else {
None
})
.filter(|tpred| tpred.def_id() == borrow_trait && tpred.self_ty() == ty)
.any(|tpred| tpred.input_types().nth(1).expect("Borrow trait must have an parameter") != ty);
.any(|tpred| {
tpred.input_types().nth(1).expect(
"Borrow trait must have an parameter",
) != ty
});
if_let_chain! {[
!is_self(arg),
@ -177,7 +189,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
struct MovedVariablesCtxt<'a, 'tcx: 'a> {
cx: &'a LateContext<'a, 'tcx>,
moved_vars: HashSet<DefId>,
/// Spans which need to be prefixed with `*` for dereferencing the suggested additional
/// Spans which need to be prefixed with `*` for dereferencing the
/// suggested additional
/// reference.
spans_need_deref: HashMap<DefId, HashSet<Span>>,
}

View File

@ -36,10 +36,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
let ty = cx.tables.expr_ty(expr);
if let ty::TyAdt(def, _) = ty.sty {
if fields.len() == def.struct_variant().fields.len() {
span_lint(cx,
span_lint(
cx,
NEEDLESS_UPDATE,
base.span,
"struct update has no effect, all the fields in the struct have already been specified");
"struct update has no effect, all the fields in the struct have already been specified",
);
}
}
}

View File

@ -97,7 +97,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
decl: &'tcx hir::FnDecl,
_: &'tcx hir::Body,
span: Span,
id: ast::NodeId
id: ast::NodeId,
) {
if in_external_macro(cx, span) {
return;
@ -109,13 +109,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
return;
}
if !sig.generics.ty_params.is_empty() {
// when the result of `new()` depends on a type parameter we should not require an
// when the result of `new()` depends on a type parameter we should not require
// an
// impl of `Default`
return;
}
if decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(id) {
let self_ty = cx.tcx
.type_of(cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id)));
let self_ty = cx.tcx.type_of(
cx.tcx.hir.local_def_id(cx.tcx.hir.get_parent(id)),
);
if_let_chain!{[
same_tys(cx, self_ty, return_ty(cx, id)),
let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT),

View File

@ -120,12 +120,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
return;
}
}
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
UNNECESSARY_OPERATION,
stmt.span,
"statement can be reduced",
"replace it with",
snippet);
snippet,
);
}
}
}
@ -152,7 +154,14 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
Expr_::ExprAddrOf(_, ref inner) |
Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
Expr_::ExprStruct(_, ref fields, ref base) => {
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
Some(
fields
.iter()
.map(|f| &f.expr)
.chain(base)
.map(Deref::deref)
.collect(),
)
},
Expr_::ExprCall(ref callee, ref args) => {
if let Expr_::ExprPath(ref qpath) = callee.node {

View File

@ -113,7 +113,13 @@ fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
return true;
}
// *_name
if interned_name.chars().rev().zip(name.chars().rev()).all(|(l, r)| l == r) {
if interned_name.chars().rev().zip(name.chars().rev()).all(
|(l,
r)| {
l == r
},
)
{
return true;
}
}
@ -128,10 +134,12 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
}
self.0.single_char_names.push(c);
if self.0.single_char_names.len() as u64 >= self.0.lint.single_char_binding_names_threshold {
span_lint(self.0.cx,
span_lint(
self.0.cx,
MANY_SINGLE_CHAR_NAMES,
span,
&format!("{}th binding whose name is just one char", self.0.single_char_names.len()));
&format!("{}th binding whose name is just one char", self.0.single_char_names.len()),
);
}
}
fn check_name(&mut self, span: Span, name: Name) {
@ -166,22 +174,41 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
} else {
let mut interned_chars = interned_name.chars();
let mut existing_chars = existing_name.interned.chars();
let first_i = interned_chars.next().expect("we know we have at least one char");
let first_e = existing_chars.next().expect("we know we have at least one char");
let first_i = interned_chars.next().expect(
"we know we have at least one char",
);
let first_e = existing_chars.next().expect(
"we know we have at least one char",
);
let eq_or_numeric = |a: char, b: char| a == b || a.is_numeric() && b.is_numeric();
if eq_or_numeric(first_i, first_e) {
let last_i = interned_chars.next_back().expect("we know we have at least two chars");
let last_e = existing_chars.next_back().expect("we know we have at least two chars");
let last_i = interned_chars.next_back().expect(
"we know we have at least two chars",
);
let last_e = existing_chars.next_back().expect(
"we know we have at least two chars",
);
if eq_or_numeric(last_i, last_e) {
if interned_chars.zip(existing_chars).filter(|&(i, e)| !eq_or_numeric(i, e)).count() != 1 {
if interned_chars
.zip(existing_chars)
.filter(|&(i, e)| !eq_or_numeric(i, e))
.count() != 1
{
continue;
}
} else {
let second_last_i = interned_chars.next_back().expect("we know we have at least three chars");
let second_last_e = existing_chars.next_back().expect("we know we have at least three chars");
let second_last_i = interned_chars.next_back().expect(
"we know we have at least three chars",
);
let second_last_e = existing_chars.next_back().expect(
"we know we have at least three chars",
);
if !eq_or_numeric(second_last_i, second_last_e) || second_last_i == '_' ||
!interned_chars.zip(existing_chars).all(|(i, e)| eq_or_numeric(i, e)) {
!interned_chars.zip(existing_chars).all(|(i, e)| {
eq_or_numeric(i, e)
})
{
// allowed similarity foo_x, foo_y
// or too many chars differ (foo_x, boo_y) or (foox, booy)
continue;
@ -189,10 +216,17 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
}
} else {
let second_i = interned_chars.next().expect("we know we have at least two chars");
let second_e = existing_chars.next().expect("we know we have at least two chars");
let second_i = interned_chars.next().expect(
"we know we have at least two chars",
);
let second_e = existing_chars.next().expect(
"we know we have at least two chars",
);
if !eq_or_numeric(second_i, second_e) || second_i == '_' ||
!interned_chars.zip(existing_chars).all(|(i, e)| eq_or_numeric(i, e)) {
!interned_chars.zip(existing_chars).all(|(i, e)| {
eq_or_numeric(i, e)
})
{
// allowed similarity x_foo, y_foo
// or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
continue;
@ -200,20 +234,26 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
split_at = interned_name.chars().next().map(|c| c.len_utf8());
}
}
span_lint_and_then(self.0.cx,
span_lint_and_then(
self.0.cx,
SIMILAR_NAMES,
span,
"binding's name is too similar to existing binding",
|diag| {
diag.span_note(existing_name.span, "existing binding defined here");
if let Some(split) = split_at {
diag.span_help(span,
&format!("separate the discriminating character by an \
diag.span_help(
span,
&format!(
"separate the discriminating character by an \
underscore like: `{}_{}`",
&interned_name[..split],
&interned_name[split..]));
&interned_name[split..]
),
);
}
});
},
);
return;
}
self.0.names.push(ExistingName {
@ -241,7 +281,8 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
if let Some(ref init) = local.init {
self.apply(|this| walk_expr(this, &**init));
}
// add the pattern after the expression because the bindings aren't available yet in the init
// add the pattern after the expression because the bindings aren't available
// yet in the init
// expression
SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
}

View File

@ -4,7 +4,8 @@ use utils::{paths, method_chain_args, span_help_and_lint, match_type, snippet};
/// **What it does:*** Checks for unnecessary `ok()` in if let.
///
/// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match on `Ok(pat)`
/// **Why is this bad?** Calling `ok()` in if let is unnecessary, instead match
/// on `Ok(pat)`
///
/// **Known problems:** None.
///

View File

@ -109,16 +109,19 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
(false, false, false, false, false);
// This code is almost duplicated (oh, the irony), but I haven't found a way to unify it.
// This code is almost duplicated (oh, the irony), but I haven't found a way to
// unify it.
for option in options {
match *option {
(OpenOption::Create, arg) => {
if create {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"create\" is called more than once");
"the method \"create\" is called more than once",
);
} else {
create = true
}
@ -126,10 +129,12 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
},
(OpenOption::Append, arg) => {
if append {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"append\" is called more than once");
"the method \"append\" is called more than once",
);
} else {
append = true
}
@ -137,10 +142,12 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
},
(OpenOption::Truncate, arg) => {
if truncate {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"truncate\" is called more than once");
"the method \"truncate\" is called more than once",
);
} else {
truncate = true
}
@ -148,10 +155,12 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
},
(OpenOption::Read, arg) => {
if read {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"read\" is called more than once");
"the method \"read\" is called more than once",
);
} else {
read = true
}
@ -159,10 +168,12 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
},
(OpenOption::Write, arg) => {
if write {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"write\" is called more than once");
"the method \"write\" is called more than once",
);
} else {
write = true
}
@ -175,9 +186,11 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
span_lint(cx, NONSENSICAL_OPEN_OPTIONS, span, "file opened with \"truncate\" and \"read\"");
}
if append && truncate && append_arg && truncate_arg {
span_lint(cx,
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"file opened with \"append\" and \"truncate\"");
"file opened with \"append\" and \"truncate\"",
);
}
}

View File

@ -5,8 +5,10 @@ use utils::{span_lint_and_sugg, snippet};
/// **What it does:** Checks for operations where precedence may be unclear
/// and suggests to add parentheses. Currently it catches the following:
/// * mixed usage of arithmetic and bit shifting/combining operators without parentheses
/// * a "negative" numeric literal (which is really a unary `-` followed by a numeric literal)
/// * mixed usage of arithmetic and bit shifting/combining operators without
/// parentheses
/// * a "negative" numeric literal (which is really a unary `-` followed by a
/// numeric literal)
/// followed by a method call
///
/// **Why is this bad?** Not everyone knows the precedence of those operators by
@ -37,12 +39,14 @@ impl EarlyLintPass for Precedence {
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.node {
let span_sugg = |expr: &Expr, sugg| {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
PRECEDENCE,
expr.span,
"operator precedence can trip the unwary",
"consider parenthesizing your expression",
sugg);
sugg,
);
};
if !is_bit_op(op) {
@ -50,24 +54,30 @@ impl EarlyLintPass for Precedence {
}
match (is_arith_expr(left), is_arith_expr(right)) {
(true, true) => {
let sugg = format!("({}) {} ({})",
let sugg = format!(
"({}) {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
snippet(cx, right.span, "..")
);
span_sugg(expr, sugg);
},
(true, false) => {
let sugg = format!("({}) {} {}",
let sugg = format!(
"({}) {} {}",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
snippet(cx, right.span, "..")
);
span_sugg(expr, sugg);
},
(false, true) => {
let sugg = format!("{} {} ({})",
let sugg = format!(
"{} {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
snippet(cx, right.span, "..")
);
span_sugg(expr, sugg);
},
(false, false) => (),
@ -82,12 +92,14 @@ impl EarlyLintPass for Precedence {
LitKind::Int(..) |
LitKind::Float(..) |
LitKind::FloatUnsuffixed(..) => {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
PRECEDENCE,
expr.span,
"unary minus has lower precedence than method call",
"consider adding parentheses to clarify your intent",
format!("-({})", snippet(cx, rhs.span, "..")));
format!("-({})", snippet(cx, rhs.span, "..")),
);
},
_ => (),
}

View File

@ -5,10 +5,12 @@ use utils::paths;
use utils::{is_expn_of, match_def_path, resolve_node, span_lint, match_path_old};
use format::get_argument_fmtstr_parts;
/// **What it does:** This lint warns when you using `print!()` with a format string that
/// **What it does:** This lint warns when you using `print!()` with a format
/// string that
/// ends in a newline.
///
/// **Why is this bad?** You should use `println!()` instead, which appends the newline.
/// **Why is this bad?** You should use `println!()` instead, which appends the
/// newline.
///
/// **Known problems:** None.
///

View File

@ -9,11 +9,14 @@ use syntax::codemap::Span;
use syntax_pos::MultiSpan;
use utils::{match_path, match_type, paths, span_lint, span_lint_and_then};
/// **What it does:** This lint checks for function arguments of type `&String` or `&Vec` unless
/// **What it does:** This lint checks for function arguments of type `&String`
/// or `&Vec` unless
/// the references are mutable.
///
/// **Why is this bad?** Requiring the argument to be of the specific size makes the function less
/// useful for no benefit; slices in the form of `&[T]` or `&str` usually suffice and can be
/// **Why is this bad?** Requiring the argument to be of the specific size
/// makes the function less
/// useful for no benefit; slices in the form of `&[T]` or `&str` usually
/// suffice and can be
/// obtained from other types, too.
///
/// **Known problems:** None.
@ -31,7 +34,8 @@ declare_lint! {
/// **What it does:** This lint checks for equality comparisons with `ptr::null`
///
/// **Why is this bad?** It's easier and more readable to use the inherent `.is_null()`
/// **Why is this bad?** It's easier and more readable to use the inherent
/// `.is_null()`
/// method instead
///
/// **Known problems:** None.
@ -46,15 +50,20 @@ declare_lint! {
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
}
/// **What it does:** This lint checks for functions that take immutable references and return
/// **What it does:** This lint checks for functions that take immutable
/// references and return
/// mutable ones.
///
/// **Why is this bad?** This is trivially unsound, as one can create two mutable references
/// from the same (immutable!) source. This [error](https://github.com/rust-lang/rust/issues/39465)
/// **Why is this bad?** This is trivially unsound, as one can create two
/// mutable references
/// from the same (immutable!) source. This
/// [error](https://github.com/rust-lang/rust/issues/39465)
/// actually lead to an interim Rust release 1.15.1.
///
/// **Known problems:** To be on the conservative side, if there's at least one mutable reference
/// with the output lifetime, this lint will not trigger. In practice, this case is unlikely anyway.
/// **Known problems:** To be on the conservative side, if there's at least one
/// mutable reference
/// with the output lifetime, this lint will not trigger. In practice, this
/// case is unlikely anyway.
///
/// **Example:**
/// ```rust
@ -102,10 +111,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if let ExprBinary(ref op, ref l, ref r) = expr.node {
if (op.node == BiEq || op.node == BiNe) && (is_null_path(l) || is_null_path(r)) {
span_lint(cx,
span_lint(
cx,
CMP_NULL,
expr.span,
"Comparing with null is better expressed by the .is_null() method");
"Comparing with null is better expressed by the .is_null() method",
);
}
}
}
@ -117,19 +128,28 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
let fn_ty = sig.skip_binder();
for (arg, ty) in decl.inputs.iter().zip(fn_ty.inputs()) {
if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {
if let ty::TyRef(_,
ty::TypeAndMut {
ty,
mutbl: MutImmutable,
}) = ty.sty
{
if match_type(cx, ty, &paths::VEC) {
span_lint(cx,
span_lint(
cx,
PTR_ARG,
arg.span,
"writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
with non-Vec-based slices. Consider changing the type to `&[...]`");
with non-Vec-based slices. Consider changing the type to `&[...]`",
);
} else if match_type(cx, ty, &paths::STRING) {
span_lint(cx,
span_lint(
cx,
PTR_ARG,
arg.span,
"writing `&String` instead of `&str` involves a new object where a slice will do. \
Consider changing the type to `&str`");
Consider changing the type to `&str`",
);
}
}
}
@ -138,10 +158,10 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
if let Some((out, MutMutable, _)) = get_rptr_lm(ty) {
let mut immutables = vec![];
for (_, ref mutbl, ref argspan) in
decl.inputs
.iter()
.filter_map(|ty| get_rptr_lm(ty))
.filter(|&(lt, _, _)| lt.name == out.name) {
decl.inputs.iter().filter_map(|ty| get_rptr_lm(ty)).filter(
|&(lt, _, _)| lt.name == out.name,
)
{
if *mutbl == MutMutable {
return;
}

View File

@ -21,7 +21,8 @@ declare_lint! {
"using `Iterator::step_by(0)`, which produces an infinite iterator"
}
/// **What it does:** Checks for zipping a collection with the range of `0.._.len()`.
/// **What it does:** Checks for zipping a collection with the range of
/// `0.._.len()`.
///
/// **Why is this bad?** The code is better expressed with `.enumerate()`.
///
@ -57,10 +58,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
use rustc_const_math::ConstInt::Usize;
if let Some((Constant::Int(Usize(us)), _)) = constant(cx, &args[1]) {
if us.as_u64(cx.sess().target.uint_type) == 0 {
span_lint(cx,
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"Iterator::step_by(0) will panic at runtime");
"Iterator::step_by(0) will panic at runtime",
);
}
}
} else if name == "zip" && args.len() == 2 {

View File

@ -40,12 +40,14 @@ impl EarlyLintPass for Pass {
fn check_expr(&mut self, cx: &EarlyContext, e: &Expr) {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.node {
if let ExprKind::AddrOf(_, ref addrof_target) = without_parens(deref_target).node {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
DEREF_ADDROF,
e.span,
"immediately dereferencing a reference",
"try this",
format!("{}", snippet(cx, addrof_target.span, "_")));
format!("{}", snippet(cx, addrof_target.span, "_")),
);
}
}
}

View File

@ -178,7 +178,8 @@ fn is_trivial_regex(s: &regex_syntax::Expr) -> Option<&'static str> {
},
3 => {
if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) =
(&exprs[0], &exprs[1], &exprs[2]) {
(&exprs[0], &exprs[1], &exprs[2])
{
Some("consider using `==` on `str`s")
} else {
None
@ -211,18 +212,22 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
match builder.parse(r) {
Ok(r) => {
if let Some(repl) = is_trivial_regex(&r) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl));
&format!("consider using {}", repl),
);
}
},
Err(e) => {
span_lint(cx,
span_lint(
cx,
INVALID_REGEX,
str_span(expr.span, r, e.position()),
&format!("regex syntax error: {}", e.description()));
&format!("regex syntax error: {}", e.description()),
);
},
}
}
@ -230,18 +235,22 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
match builder.parse(&r) {
Ok(r) => {
if let Some(repl) = is_trivial_regex(&r) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl));
&format!("consider using {}", repl),
);
}
},
Err(e) => {
span_lint(cx,
span_lint(
cx,
INVALID_REGEX,
expr.span,
&format!("regex syntax error on position {}: {}", e.position(), e.description()));
&format!("regex syntax error on position {}: {}", e.position(), e.description()),
);
},
}
}

View File

@ -23,7 +23,8 @@ declare_lint! {
"using a return statement like `return expr;` where an expression would suffice"
}
/// **What it does:** Checks for `let`-bindings, which are subsequently returned.
/// **What it does:** Checks for `let`-bindings, which are subsequently
/// returned.
///
/// **Why is this bad?** It is just extraneous code. Remove it to make your code
/// more rusty.
@ -93,12 +94,10 @@ impl ReturnPass {
if in_external_macro(cx, inner_span) || in_macro(inner_span) {
return;
}
span_lint_and_then(cx,
NEEDLESS_RETURN,
ret_span,
"unneeded return statement",
|db| if let Some(snippet) = snippet_opt(cx, inner_span) {
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
db.span_suggestion(ret_span, "remove `return` as shown", snippet);
}
});
}

View File

@ -9,7 +9,8 @@ use utils::{span_lint, get_trait_def_id, paths};
///
/// **Known problems:** None.
///
/// **Example:** Implementing `Visitor::visit_string` but not `Visitor::visit_str`.
/// **Example:** Implementing `Visitor::visit_string` but not
/// `Visitor::visit_str`.
declare_lint! {
pub SERDE_API_MISUSE,
Warn,
@ -43,10 +44,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Serde {
}
if let Some(span) = seen_string {
if seen_str.is_none() {
span_lint(cx,
span_lint(
cx,
SERDE_API_MISUSE,
span,
"you should not implement `visit_string` without also implementing `visit_str`");
"you should not implement `visit_string` without also implementing `visit_str`",
);
}
}
}

View File

@ -87,7 +87,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
decl: &'tcx FnDecl,
body: &'tcx Body,
_: Span,
_: NodeId
_: NodeId,
) {
if in_external_macro(cx, body.value.span) {
return;
@ -129,7 +129,13 @@ fn check_decl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl, bindings:
return;
}
if let DeclLocal(ref local) = decl.node {
let Local { ref pat, ref ty, ref init, span, .. } = **local;
let Local {
ref pat,
ref ty,
ref init,
span,
..
} = **local;
if let Some(ref t) = *ty {
check_ty(cx, t, bindings)
}
@ -155,7 +161,7 @@ fn check_pat<'a, 'tcx>(
pat: &'tcx Pat,
init: Option<&'tcx Expr>,
span: Span,
bindings: &mut Vec<(Name, Span)>
bindings: &mut Vec<(Name, Span)>,
) {
// TODO: match more stuff / destructuring
match pat.node {
@ -184,9 +190,9 @@ fn check_pat<'a, 'tcx>(
if let ExprStruct(_, ref efields, _) = init_struct.node {
for field in pfields {
let name = field.node.name;
let efield = efields.iter()
.find(|f| f.name.node == name)
.map(|f| &*f.expr);
let efield = efields.iter().find(|f| f.name.node == name).map(
|f| &*f.expr,
);
check_pat(cx, &field.node.pat, efield, span, bindings);
}
} else {
@ -240,39 +246,51 @@ fn lint_shadow<'a, 'tcx: 'a>(
span: Span,
pattern_span: Span,
init: Option<&'tcx Expr>,
prev_span: Span
prev_span: Span,
) {
if let Some(expr) = init {
if is_self_shadow(name, expr) {
span_lint_and_then(cx,
span_lint_and_then(
cx,
SHADOW_SAME,
span,
&format!("`{}` is shadowed by itself in `{}`",
&format!(
"`{}` is shadowed by itself in `{}`",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
|db| { db.span_note(prev_span, "previous binding is here"); });
snippet(cx, expr.span, "..")
),
|db| { db.span_note(prev_span, "previous binding is here"); },
);
} else if contains_self(name, expr) {
span_lint_and_then(cx,
span_lint_and_then(
cx,
SHADOW_REUSE,
pattern_span,
&format!("`{}` is shadowed by `{}` which reuses the original value",
&format!(
"`{}` is shadowed by `{}` which reuses the original value",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
snippet(cx, expr.span, "..")
),
|db| {
db.span_note(expr.span, "initialization happens here");
db.span_note(prev_span, "previous binding is here");
});
},
);
} else {
span_lint_and_then(cx,
span_lint_and_then(
cx,
SHADOW_UNRELATED,
pattern_span,
&format!("`{}` is shadowed by `{}`",
&format!(
"`{}` is shadowed by `{}`",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
snippet(cx, expr.span, "..")
),
|db| {
db.span_note(expr.span, "initialization happens here");
db.span_note(prev_span, "previous binding is here");
});
},
);
}
} else {
@ -357,7 +375,11 @@ fn is_self_shadow(name: Name, expr: &Expr) -> bool {
ExprBox(ref inner) |
ExprAddrOf(_, ref inner) => is_self_shadow(name, inner),
ExprBlock(ref block) => {
block.stmts.is_empty() && block.expr.as_ref().map_or(false, |e| is_self_shadow(name, e))
block.stmts.is_empty() &&
block.expr.as_ref().map_or(
false,
|e| is_self_shadow(name, e),
)
},
ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
ExprPath(QPath::Resolved(_, ref path)) => path_eq_name(name, path),

View File

@ -2,10 +2,12 @@ use rustc::lint::*;
use rustc::hir::*;
use utils::{is_direct_expn_of, is_expn_of, implements_trait, span_lint};
/// **What it does:** Checks for `assert!(x == y)` or `assert!(x != y)` which can be better written
/// **What it does:** Checks for `assert!(x == y)` or `assert!(x != y)` which
/// can be better written
/// using `assert_eq` or `assert_ne` if `x` and `y` implement `Debug` trait.
///
/// **Why is this bad?** `assert_eq` and `assert_ne` provide better assertion failure reporting.
/// **Why is this bad?** `assert_eq` and `assert_ne` provide better assertion
/// failure reporting.
///
/// **Known problems:** Hopefully none.
///
@ -14,7 +16,8 @@ use utils::{is_direct_expn_of, is_expn_of, implements_trait, span_lint};
/// let (x, y) = (1, 2);
///
/// assert!(x == y); // assertion failed: x == y
/// assert_eq!(x, y); // assertion failed: `(left == right)` (left: `1`, right: `2`)
/// assert_eq!(x, y); // assertion failed: `(left == right)` (left: `1`, right:
/// `2`)
/// ```
declare_lint! {
pub SHOULD_ASSERT_EQ,

View File

@ -96,18 +96,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
}
}
}
span_lint(cx,
span_lint(
cx,
STRING_ADD,
e.span,
"you added something to a string. Consider using `String::push_str()` instead");
"you added something to a string. Consider using `String::push_str()` instead",
);
}
} else if let ExprAssign(ref target, ref src) = e.node {
if is_string(cx, target) && is_add(cx, src, target) {
span_lint(cx,
span_lint(
cx,
STRING_ADD_ASSIGN,
e.span,
"you assigned the result of adding something to this string. Consider using \
`String::push_str()` instead");
`String::push_str()` instead",
);
}
}
}
@ -121,7 +125,11 @@ fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
match src.node {
ExprBinary(Spanned { node: BiAdd, .. }, ref left, _) => SpanlessEq::new(cx).eq_expr(target, left),
ExprBlock(ref block) => {
block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
block.stmts.is_empty() &&
block.expr.as_ref().map_or(
false,
|expr| is_add(cx, expr, target),
)
},
_ => false,
}
@ -147,12 +155,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
if let ExprLit(ref lit) = args[0].node {
if let LitKind::Str(ref lit_content, _) = lit.node {
if lit_content.as_str().chars().all(|c| c.is_ascii()) && !in_macro(args[0].span) {
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
STRING_LIT_AS_BYTES,
e.span,
"calling `as_bytes()` on a string literal",
"consider using a byte string literal instead",
format!("b{}", snippet(cx, args[0].span, r#""foo""#)));
format!("b{}", snippet(cx, args[0].span, r#""foo""#)),
);
}
}
}

View File

@ -49,7 +49,8 @@ declare_lint! {
///
/// **Example:**
/// ```rust
/// core::intrinsics::transmute(t)` // where the result type is the same as `*t` or `&t`'s
/// core::intrinsics::transmute(t)` // where the result type is the same as
/// `*t` or `&t`'s
/// ```
declare_lint! {
pub CROSSPOINTER_TRANSMUTE,
@ -79,7 +80,7 @@ pub struct Transmute;
impl LintPass for Transmute {
fn get_lints(&self) -> LintArray {
lint_array![CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE]
lint_array!(CROSSPOINTER_TRANSMUTE, TRANSMUTE_PTR_TO_REF, USELESS_TRANSMUTE, WRONG_TRANSMUTE)
}
}
@ -95,13 +96,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
match (&from_ty.sty, &to_ty.sty) {
_ if from_ty == to_ty => {
span_lint(cx,
span_lint(
cx,
USELESS_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to itself", from_ty))
&format!("transmute from a type (`{}`) to itself", from_ty),
)
},
(&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => {
span_lint_and_then(cx,
span_lint_and_then(
cx,
USELESS_TRANSMUTE,
e.span,
"transmute from a reference to a pointer",
@ -113,53 +117,63 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
};
db.span_suggestion(e.span, "try", sugg.to_string());
})
},
)
},
(&ty::TyInt(_), &ty::TyRawPtr(_)) |
(&ty::TyUint(_), &ty::TyRawPtr(_)) => {
span_lint_and_then(cx,
span_lint_and_then(
cx,
USELESS_TRANSMUTE,
e.span,
"transmute from an integer to a pointer",
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
db.span_suggestion(e.span,
"try",
arg.as_ty(&to_ty.to_string()).to_string());
})
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
},
)
},
(&ty::TyFloat(_), &ty::TyRef(..)) |
(&ty::TyFloat(_), &ty::TyRawPtr(_)) |
(&ty::TyChar, &ty::TyRef(..)) |
(&ty::TyChar, &ty::TyRawPtr(_)) => {
span_lint(cx,
span_lint(
cx,
WRONG_TRANSMUTE,
e.span,
&format!("transmute from a `{}` to a pointer", from_ty))
&format!("transmute from a `{}` to a pointer", from_ty),
)
},
(&ty::TyRawPtr(from_ptr), _) if from_ptr.ty == to_ty => {
span_lint(cx,
span_lint(
cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to the type that it points to (`{}`)",
&format!(
"transmute from a type (`{}`) to the type that it points to (`{}`)",
from_ty,
to_ty))
to_ty
),
)
},
(_, &ty::TyRawPtr(to_ptr)) if to_ptr.ty == from_ty => {
span_lint(cx,
span_lint(
cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)",
from_ty,
to_ty))
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)", from_ty, to_ty),
)
},
(&ty::TyRawPtr(from_pty), &ty::TyRef(_, to_rty)) => {
span_lint_and_then(cx,
span_lint_and_then(
cx,
TRANSMUTE_PTR_TO_REF,
e.span,
&format!("transmute from a pointer type (`{}`) to a reference type \
&format!(
"transmute from a pointer type (`{}`) to a reference type \
(`{}`)",
from_ty,
to_ty),
to_ty
),
|db| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
@ -175,7 +189,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
};
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
})
},
)
},
_ => return,
};
@ -185,8 +200,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
}
}
/// Get the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is not available , use
/// the type's `ToString` implementation. In weird cases it could lead to types with invalid `'_`
/// Get the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
/// not available , use
/// 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 {
let seg = last_path_segment(path);

View File

@ -42,14 +42,21 @@ declare_lint! {
///
/// **Why is this bad?** Gankro says:
///
/// > The TL;DR of `LinkedList` is that it's built on a massive amount of pointers and indirection.
/// > It wastes memory, it has terrible cache locality, and is all-around slow. `RingBuf`, while
/// > "only" amortized for push/pop, should be faster in the general case for almost every possible
/// > workload, and isn't even amortized at all if you can predict the capacity you need.
/// > The TL;DR of `LinkedList` is that it's built on a massive amount of
/// pointers and indirection.
/// > It wastes memory, it has terrible cache locality, and is all-around slow.
/// `RingBuf`, while
/// > "only" amortized for push/pop, should be faster in the general case for
/// almost every possible
/// > workload, and isn't even amortized at all if you can predict the capacity
/// you need.
/// >
/// > `LinkedList`s are only really good if you're doing a lot of merging or splitting of lists.
/// > This is because they can just mangle some pointers instead of actually copying the data. Even
/// > if you're doing a lot of insertion in the middle of the list, `RingBuf` can still be better
/// > `LinkedList`s are only really good if you're doing a lot of merging or
/// splitting of lists.
/// > This is because they can just mangle some pointers instead of actually
/// copying the data. Even
/// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
/// can still be better
/// > because of how expensive it is to seek to the middle of a `LinkedList`.
///
/// **Known problems:** False positives the instances where using a
@ -68,7 +75,8 @@ declare_lint! {
/// **What it does:** Checks for use of `&Box<T>` anywhere in the code.
///
/// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more general.
/// **Why is this bad?** Any `&Box<T>` can also be a `&T`, which is more
/// general.
///
/// **Known problems:** None.
///
@ -161,11 +169,13 @@ fn check_ty(cx: &LateContext, ast_ty: &hir::Ty, is_local: bool) {
return; // don't recurse into the type
}}
} else if match_def_path(cx.tcx, def_id, &paths::LINKED_LIST) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
LINKEDLIST,
ast_ty.span,
"I see you're using a LinkedList! Perhaps you meant some other data structure?",
"a VecDeque might work");
"a VecDeque might work",
);
return; // don't recurse into the type
}
}
@ -268,11 +278,15 @@ fn check_let_unit(cx: &LateContext, decl: &Decl) {
if higher::is_from_for_desugar(decl) {
return;
}
span_lint(cx,
span_lint(
cx,
LET_UNIT_VALUE,
decl.span,
&format!("this let-binding has unit value. Consider omitting `let {} =`",
snippet(cx, local.pat.span, "..")));
&format!(
"this let-binding has unit value. Consider omitting `let {} =`",
snippet(cx, local.pat.span, "..")
),
);
},
_ => (),
}
@ -336,12 +350,16 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitCmp {
BiEq | BiLe | BiGe => "true",
_ => "false",
};
span_lint(cx,
span_lint(
cx,
UNIT_CMP,
expr.span,
&format!("{}-comparison of unit values detected. This will always be {}",
&format!(
"{}-comparison of unit values detected. This will always be {}",
op.as_str(),
result));
result
),
);
},
_ => (),
}
@ -493,10 +511,12 @@ fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: Ty, cast_t
} else {
int_ty_to_nbits(cast_from).to_string()
};
span_lint(cx,
span_lint(
cx,
CAST_PRECISION_LOSS,
expr.span,
&format!("casting {0} to {1} causes a loss of precision {2}({0} is {3} bits wide, but {1}'s mantissa \
&format!(
"casting {0} to {1} causes a loss of precision {2}({0} is {3} bits wide, but {1}'s mantissa \
is only {4} bits wide)",
cast_from,
if cast_to_f64 { "f64" } else { "f32" },
@ -506,7 +526,9 @@ fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: Ty, cast_t
""
},
from_nbits_str,
mantissa_nbits));
mantissa_nbits
),
);
}
enum ArchSuffix {
@ -520,70 +542,86 @@ fn check_truncation_and_wrapping(cx: &LateContext, expr: &Expr, cast_from: Ty, c
let arch_32_suffix = " on targets with 32-bit wide pointers";
let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed();
let (from_nbits, to_nbits) = (int_ty_to_nbits(cast_from), int_ty_to_nbits(cast_to));
let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) = match (is_isize_or_usize(cast_from),
is_isize_or_usize(cast_to)) {
let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) =
match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
(true, true) | (false, false) => {
(to_nbits < from_nbits,
(
to_nbits < from_nbits,
ArchSuffix::None,
to_nbits == from_nbits && cast_unsigned_to_signed,
ArchSuffix::None)
ArchSuffix::None,
)
},
(true, false) => {
(to_nbits <= 32,
(
to_nbits <= 32,
if to_nbits == 32 {
ArchSuffix::_64
} else {
ArchSuffix::None
},
to_nbits <= 32 && cast_unsigned_to_signed,
ArchSuffix::_32)
ArchSuffix::_32,
)
},
(false, true) => {
(from_nbits == 64,
(
from_nbits == 64,
ArchSuffix::_32,
cast_unsigned_to_signed,
if from_nbits == 64 {
ArchSuffix::_64
} else {
ArchSuffix::_32
})
},
)
},
};
if span_truncation {
span_lint(cx,
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&format!("casting {} to {} may truncate the value{}",
&format!(
"casting {} to {} may truncate the value{}",
cast_from,
cast_to,
match suffix_truncation {
ArchSuffix::_32 => arch_32_suffix,
ArchSuffix::_64 => arch_64_suffix,
ArchSuffix::None => "",
}));
}
),
);
}
if span_wrap {
span_lint(cx,
span_lint(
cx,
CAST_POSSIBLE_WRAP,
expr.span,
&format!("casting {} to {} may wrap around the value{}",
&format!(
"casting {} to {} may wrap around the value{}",
cast_from,
cast_to,
match suffix_wrap {
ArchSuffix::_32 => arch_32_suffix,
ArchSuffix::_64 => arch_64_suffix,
ArchSuffix::None => "",
}));
}
),
);
}
}
impl LintPass for CastPass {
fn get_lints(&self) -> LintArray {
lint_array!(CAST_PRECISION_LOSS,
lint_array!(
CAST_PRECISION_LOSS,
CAST_SIGN_LOSS,
CAST_POSSIBLE_TRUNCATION,
CAST_POSSIBLE_WRAP,
UNNECESSARY_CAST)
UNNECESSARY_CAST
)
}
}
@ -598,12 +636,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
LitKind::FloatUnsuffixed(_) => {},
_ => {
if cast_from.sty == cast_to.sty && !in_external_macro(cx, expr.span) {
span_lint(cx,
span_lint(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting to the same type is unnecessary (`{}` -> `{}`)",
cast_from,
cast_to));
&format!("casting to the same type is unnecessary (`{}` -> `{}`)", cast_from, cast_to),
);
}
},
}
@ -622,33 +660,42 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
}
},
(false, true) => {
span_lint(cx,
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&format!("casting {} to {} may truncate the value", cast_from, cast_to));
&format!("casting {} to {} may truncate the value", cast_from, cast_to),
);
if !cast_to.is_signed() {
span_lint(cx,
span_lint(
cx,
CAST_SIGN_LOSS,
expr.span,
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to),
);
}
},
(true, true) => {
if cast_from.is_signed() && !cast_to.is_signed() {
span_lint(cx,
span_lint(
cx,
CAST_SIGN_LOSS,
expr.span,
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to),
);
}
check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
},
(false, false) => {
if let (&ty::TyFloat(FloatTy::F64), &ty::TyFloat(FloatTy::F32)) =
(&cast_from.sty, &cast_to.sty) {
span_lint(cx,
(&cast_from.sty, &cast_to.sty)
{
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
"casting f64 to f32 may truncate the value");
"casting f64 to f32 may truncate the value",
);
}
},
}
@ -700,7 +747,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexityPass {
decl: &'tcx FnDecl,
_: &'tcx Body,
_: Span,
_: NodeId
_: NodeId,
) {
self.check_fndecl(cx, decl);
}
@ -760,19 +807,18 @@ impl<'a, 'tcx> TypeComplexityPass {
return;
}
let score = {
let mut visitor = TypeComplexityVisitor {
score: 0,
nest: 1,
};
let mut visitor = TypeComplexityVisitor { score: 0, nest: 1 };
visitor.visit_ty(ty);
visitor.score
};
if score > self.threshold {
span_lint(cx,
span_lint(
cx,
TYPE_COMPLEXITY,
ty.span,
"very complex type used. Consider factoring parts into `type` definitions");
"very complex type used. Consider factoring parts into `type` definitions",
);
}
}
}
@ -798,8 +844,9 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
TyBareFn(..) => (50 * self.nest, 1),
TyTraitObject(ref param_bounds, _) => {
let has_lifetime_parameters = param_bounds.iter()
.any(|bound| !bound.bound_lifetimes.is_empty());
let has_lifetime_parameters = param_bounds.iter().any(
|bound| !bound.bound_lifetimes.is_empty(),
);
if has_lifetime_parameters {
// complex trait bounds like A<'a, 'b>
(50 * self.nest, 1)
@ -922,7 +969,7 @@ fn detect_absurd_comparison<'a>(
cx: &LateContext,
op: BinOp_,
lhs: &'a Expr,
rhs: &'a Expr
rhs: &'a Expr,
) -> Option<(ExtremeExpr<'a>, AbsurdComparisonResult)> {
use types::ExtremeType::*;
use types::AbsurdComparisonResult::*;
@ -1042,20 +1089,24 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons {
AlwaysFalse => "this comparison is always false".to_owned(),
AlwaysTrue => "this comparison is always true".to_owned(),
InequalityImpossible => {
format!("the case where the two sides are not equal never occurs, consider using {} == {} \
format!(
"the case where the two sides are not equal never occurs, consider using {} == {} \
instead",
snippet(cx, lhs.span, "lhs"),
snippet(cx, rhs.span, "rhs"))
snippet(cx, rhs.span, "rhs")
)
},
};
let help = format!("because {} is the {} value for this type, {}",
let help = format!(
"because {} is the {} value for this type, {}",
snippet(cx, culprit.expr.span, "x"),
match culprit.which {
Minimum => "minimum",
Maximum => "maximum",
},
conclusion);
conclusion
);
span_help_and_lint(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, &help);
}
@ -1113,7 +1164,9 @@ impl FullInt {
impl PartialEq for FullInt {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other).expect("partial_cmp only returns Some(_)") == Ordering::Equal
self.partial_cmp(other).expect(
"partial_cmp only returns Some(_)",
) == Ordering::Equal
}
}
@ -1129,7 +1182,9 @@ impl PartialOrd for FullInt {
}
impl Ord for FullInt {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).expect("partial_cmp for FullInt can never return None")
self.partial_cmp(other).expect(
"partial_cmp for FullInt can never return None",
)
}
}
@ -1198,14 +1253,16 @@ fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> {
fn err_upcast_comparison(cx: &LateContext, span: &Span, expr: &Expr, always: bool) {
if let ExprCast(ref cast_val, _) = expr.node {
span_lint(cx,
span_lint(
cx,
INVALID_UPCAST_COMPARISONS,
*span,
&format!(
"because of the numeric bounds on `{}` prior to casting, this expression is always {}",
snippet(cx, cast_val.span, "the expression"),
if always { "true" } else { "false" },
));
),
);
}
}
@ -1216,7 +1273,7 @@ fn upcast_comparison_bounds_err(
lhs_bounds: Option<(FullInt, FullInt)>,
lhs: &Expr,
rhs: &Expr,
invert: bool
invert: bool,
) {
use utils::comparisons::*;
@ -1242,7 +1299,8 @@ fn upcast_comparison_bounds_err(
}
},
Rel::Eq | Rel::Ne => unreachable!(),
} {
}
{
err_upcast_comparison(cx, span, lhs, true)
} else if match rel {
Rel::Lt => {
@ -1260,7 +1318,8 @@ fn upcast_comparison_bounds_err(
}
},
Rel::Eq | Rel::Ne => unreachable!(),
} {
}
{
err_upcast_comparison(cx, span, lhs, false)
}
}

View File

@ -12,7 +12,8 @@ use utils::{snippet, span_help_and_lint};
///
/// **Known problems:** None.
///
/// **Example:** You don't see it, but there may be a zero-width space somewhere in this text.
/// **Example:** You don't see it, but there may be a zero-width space
/// somewhere in this text.
declare_lint! {
pub ZERO_WIDTH_SPACE,
Deny,
@ -95,30 +96,40 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
fn check_str(cx: &LateContext, span: Span) {
let string = snippet(cx, span, "");
if string.contains('\u{200B}') {
span_help_and_lint(cx,
span_help_and_lint(
cx,
ZERO_WIDTH_SPACE,
span,
"zero-width space detected",
&format!("Consider replacing the string with:\n\"{}\"",
string.replace("\u{200B}", "\\u{200B}")));
&format!(
"Consider replacing the string with:\n\"{}\"",
string.replace("\u{200B}", "\\u{200B}")
),
);
}
if string.chars().any(|c| c as u32 > 0x7F) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
NON_ASCII_LITERAL,
span,
"literal non-ASCII character detected",
&format!("Consider replacing the string with:\n\"{}\"",
&format!(
"Consider replacing the string with:\n\"{}\"",
if cx.current_level(UNICODE_NOT_NFC) == Level::Allow {
escape(string.chars())
} else {
escape(string.nfc())
}));
}
),
);
}
if cx.current_level(NON_ASCII_LITERAL) == Level::Allow && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
span_help_and_lint(cx,
span_help_and_lint(
cx,
UNICODE_NOT_NFC,
span,
"non-nfc unicode sequence detected",
&format!("Consider replacing the string with:\n\"{}\"", string.nfc().collect::<String>()));
&format!("Consider replacing the string with:\n\"{}\"", string.nfc().collect::<String>()),
);
}
}

View File

@ -38,13 +38,15 @@ impl EarlyLintPass for UnsafeNameRemoval {
if let ItemKind::Use(ref item_use) = item.node {
match item_use.node {
ViewPath_::ViewPathSimple(ref name, ref path) => {
unsafe_to_safe_check(path.segments
unsafe_to_safe_check(
path.segments
.last()
.expect("use paths cannot be empty")
.identifier,
*name,
cx,
&item.span);
&item.span,
);
},
ViewPath_::ViewPathList(_, ref path_list_items) => {
for path_list_item in path_list_items.iter() {
@ -64,10 +66,12 @@ fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext, spa
let old_str = old_name.name.as_str();
let new_str = new_name.name.as_str();
if contains_unsafe(&old_str) && !contains_unsafe(&new_str) {
span_lint(cx,
span_lint(
cx,
UNSAFE_REMOVED_FROM_NAME,
*span,
&format!("removed \"unsafe\" from the name of `{}` in use as `{}`", old_str, new_str));
&format!("removed \"unsafe\" from the name of `{}` in use as `{}`", old_str, new_str),
);
}
}

View File

@ -4,9 +4,12 @@ use utils::{span_lint, match_path, match_trait_method, is_try, paths};
/// **What it does:** Checks for unused written/read amount.
///
/// **Why is this bad?** `io::Write::write` and `io::Read::read` are not guaranteed to
/// process the entire buffer. They return how many bytes were processed, which might be smaller
/// than a given buffer's length. If you don't need to deal with partial-write/read, use
/// **Why is this bad?** `io::Write::write` and `io::Read::read` are not
/// guaranteed to
/// process the entire buffer. They return how many bytes were processed, which
/// might be smaller
/// than a given buffer's length. If you don't need to deal with
/// partial-write/read, use
/// `write_all`/`read_exact` instead.
///
/// **Known problems:** Detects only common patterns.
@ -73,15 +76,19 @@ fn check_method_call(cx: &LateContext, call: &hir::Expr, expr: &hir::Expr) {
if let hir::ExprMethodCall(ref path, _, _) = call.node {
let symbol = &*path.name.as_str();
if match_trait_method(cx, call, &paths::IO_READ) && symbol == "read" {
span_lint(cx,
span_lint(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"handle read amount returned or use `Read::read_exact` instead");
"handle read amount returned or use `Read::read_exact` instead",
);
} else if match_trait_method(cx, call, &paths::IO_WRITE) && symbol == "write" {
span_lint(cx,
span_lint(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"handle written amount returned or use `Write::write_all` instead");
"handle written amount returned or use `Write::write_all` instead",
);
}
}
}

View File

@ -48,7 +48,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedLabel {
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Body,
span: Span,
fn_id: ast::NodeId
fn_id: ast::NodeId,
) {
if in_macro(span) {
return;

View File

@ -7,7 +7,7 @@ use rustc::lint::*;
use rustc::hir;
use rustc::hir::{Expr, QPath, Expr_};
use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use syntax::ast::{Attribute, NodeId, LitKind, DUMMY_NODE_ID, self};
use syntax::ast::{self, Attribute, NodeId, LitKind, DUMMY_NODE_ID};
use syntax::codemap::Span;
use std::collections::HashMap;
@ -166,13 +166,14 @@ impl PrintVisitor {
Vacant(vac) => {
vac.insert(0);
s.to_owned()
}
},
}
}
}
struct PrintVisitor {
/// Fields are the current index that needs to be appended to pattern binding names
/// Fields are the current index that needs to be appended to pattern
/// binding names
ids: HashMap<&'static str, usize>,
/// the name that needs to be destructured
current: String,
@ -254,7 +255,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
println!(" {}.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);
@ -282,7 +283,7 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
self.visit_expr(cond);
self.current = then_pat;
self.visit_expr(then);
}
},
Expr_::ExprWhile(ref _cond, ref _body, ref _opt_label) => {
println!("While(ref cond, ref body, ref opt_label) = {},", current);
println!(" // unimplemented: `ExprWhile` is not further destructured at the moment");
@ -398,7 +399,13 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
let fields_pat = self.next("fields");
if let Some(ref base) = *opt_base {
let base_pat = self.next("base");
println!("Struct(ref {}, ref {}, Some(ref {})) = {},", path_pat, fields_pat, base_pat, current);
println!(
"Struct(ref {}, ref {}, Some(ref {})) = {},",
path_pat,
fields_pat,
base_pat,
current
);
self.current = base_pat;
self.visit_expr(base);
} else {
@ -434,7 +441,8 @@ fn has_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
attr.check_name("clippy") &&
attr.meta_item_list().map_or(false, |list| {
list.len() == 1 && match list[0].node {
list.len() == 1 &&
match list[0].node {
ast::NestedMetaItemKind::MetaItem(ref it) => it.name == "author",
ast::NestedMetaItemKind::Literal(_) => false,
}
@ -444,13 +452,15 @@ fn has_attr(attrs: &[Attribute]) -> bool {
fn print_path(path: &QPath, first: &mut bool) {
match *path {
QPath::Resolved(_, ref path) => for segment in &path.segments {
QPath::Resolved(_, ref path) => {
for segment in &path.segments {
if *first {
*first = false;
} else {
print!(", ");
}
print!("{:?}", segment.name.as_str());
}
},
QPath::TypeRelative(ref ty, ref segment) => {
match ty.node {

View File

@ -17,7 +17,8 @@ pub enum Rel {
Ne,
}
/// Put the expression in the form `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or `lhs != rhs`.
/// Put the expression in the form `lhs < rhs`, `lhs <= rhs`, `lhs == rhs` or
/// `lhs != rhs`.
pub fn normalize_comparison<'a>(op: BinOp_, lhs: &'a Expr, rhs: &'a Expr) -> Option<(Rel, &'a Expr, &'a Expr)> {
match op {
BinOp_::BiLt => Some((Rel::Lt, lhs, rhs)),

View File

@ -9,8 +9,9 @@ use toml;
use std::sync::Mutex;
/// Get the configuration file from arguments.
pub fn file_from_args(args: &[codemap::Spanned<ast::NestedMetaItemKind>])
-> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
pub fn file_from_args(
args: &[codemap::Spanned<ast::NestedMetaItemKind>],
) -> Result<Option<path::PathBuf>, (&'static str, codemap::Span)> {
for arg in args.iter().filter_map(|a| a.meta_item()) {
if arg.name() == "conf_file" {
return match arg.node {
@ -38,12 +39,14 @@ pub enum Error {
/// Not valid toml or doesn't fit the expected conf format
Toml(String),
/// Type error.
Type(/// The name of the key.
Type(
/// The name of the key.
&'static str,
/// The expected type.
&'static str,
/// The type we got instead.
&'static str),
&'static str
),
/// There is an unknown key is the file.
UnknownKey(String),
}
@ -234,11 +237,25 @@ pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
Err(err) => return default(vec![err.into()]),
};
assert!(ERRORS.lock().expect("no threading -> mutex always safe").is_empty());
assert!(
ERRORS
.lock()
.expect("no threading -> mutex always safe")
.is_empty()
);
match toml::from_str(&file) {
Ok(toml) => (toml, ERRORS.lock().expect("no threading -> mutex always safe").split_off(0)),
Ok(toml) => (
toml,
ERRORS
.lock()
.expect("no threading -> mutex always safe")
.split_off(0),
),
Err(e) => {
let mut errors = ERRORS.lock().expect("no threading -> mutex always safe").split_off(0);
let mut errors = ERRORS
.lock()
.expect("no threading -> mutex always safe")
.split_off(0);
errors.push(Error::Toml(e.to_string()));
default(errors)
},

View File

@ -7,5 +7,20 @@
/// See also [the reference][reference-types] for a list of such types.
///
/// [reference-types]: https://doc.rust-lang.org/reference.html#types
pub const BUILTIN_TYPES: &'static [&'static str] = &["i8", "u8", "i16", "u16", "i32", "u32", "i64", "u64", "isize",
"usize", "f32", "f64", "bool", "str", "char"];
pub const BUILTIN_TYPES: &'static [&'static str] = &[
"i8",
"u8",
"i16",
"u16",
"i32",
"u32",
"i64",
"u64",
"isize",
"usize",
"f32",
"f64",
"bool",
"str",
"char",
];

View File

@ -1,4 +1,5 @@
//! This module contains functions for retrieve the original AST from lowered `hir`.
//! This module contains functions for retrieve the original AST from lowered
//! `hir`.
#![deny(missing_docs_in_private_items)]
@ -44,9 +45,11 @@ pub struct Range<'a> {
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
pub fn range(expr: &hir::Expr) -> Option<Range> {
/// Find the field named `name` in the field. Always return `Some` for convenience.
/// Find the field named `name` in the field. Always return `Some` for
/// convenience.
fn get_field<'a>(name: &str, fields: &'a [hir::Field]) -> Option<&'a hir::Expr> {
let expr = &fields.iter()
let expr = &fields
.iter()
.find(|field| field.name.node == name)
.unwrap_or_else(|| panic!("missing {} field for range", name))
.expr;
@ -54,7 +57,8 @@ pub fn range(expr: &hir::Expr) -> Option<Range> {
Some(expr)
}
// The range syntax is expanded to literal paths starting with `core` or `std` depending on
// The range syntax is expanded to literal paths starting with `core` or `std`
// depending on
// `#[no_std]`. Testing both instead of resolving the paths.
match expr.node {
@ -147,7 +151,8 @@ pub enum VecArgs<'a> {
Vec(&'a [hir::Expr]),
}
/// Returns the arguments of the `vec!` macro if this expression was expanded from `vec!`.
/// 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,

View File

@ -7,14 +7,17 @@ use syntax::ast::Name;
use syntax::ptr::P;
use utils::differing_macro_contexts;
/// Type used to check whether two ast are the same. This is different from the operator
/// `==` on ast types as this operator would compare true equality with ID and span.
/// Type used to check whether two ast are the same. This is different from the
/// operator
/// `==` on ast types as this operator would compare true equality with ID and
/// span.
///
/// Note that some expressions kinds are not considered but could be added.
pub struct SpanlessEq<'a, 'tcx: 'a> {
/// Context used to evaluate constant expressions.
cx: &'a LateContext<'a, 'tcx>,
/// If is true, never consider as equal expressions containing function calls.
/// If is true, never consider as equal expressions containing function
/// calls.
ignore_fn: bool,
}
@ -247,22 +250,28 @@ fn swap_binop<'a>(binop: BinOp_, lhs: &'a Expr, rhs: &'a Expr) -> Option<(BinOp_
}
}
/// Check if the two `Option`s are both `None` or some equal values as per `eq_fn`.
/// Check if the two `Option`s are both `None` or some equal values as per
/// `eq_fn`.
fn both<X, F>(l: &Option<X>, r: &Option<X>, mut eq_fn: F) -> bool
where F: FnMut(&X, &X) -> bool
where
F: FnMut(&X, &X) -> bool,
{
l.as_ref().map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
l.as_ref().map_or_else(|| r.is_none(), |x| {
r.as_ref().map_or(false, |y| eq_fn(x, y))
})
}
/// Check if two slices are equal as per `eq_fn`.
fn over<X, F>(left: &[X], right: &[X], mut eq_fn: F) -> bool
where F: FnMut(&X, &X) -> bool
where
F: FnMut(&X, &X) -> bool,
{
left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
}
/// Type used to hash an ast element. This is different from the `Hash` trait on ast types as this
/// Type used to hash an ast element. This is different from the `Hash` trait
/// on ast types as this
/// trait would consider IDs and spans.
///
/// All expressions kind are hashed, but some might have a weaker hash.

View File

@ -8,7 +8,8 @@ use rustc::hir::print;
use syntax::ast::Attribute;
use syntax::attr;
/// **What it does:** Dumps every ast/hir node which has the `#[clippy_dump]` attribute
/// **What it does:** Dumps every ast/hir node which has the `#[clippy_dump]`
/// attribute
///
/// **Example:**
/// ```rust
@ -54,8 +55,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
hir::Visibility::Public => println!("public"),
hir::Visibility::Crate => println!("visible crate wide"),
hir::Visibility::Restricted { ref path, .. } => {
println!("visible in module `{}`",
print::to_string(print::NO_ANN, |s| s.print_path(path, false)))
println!(
"visible in module `{}`",
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
)
},
hir::Visibility::Inherited => println!("visibility inherited from outer item"),
}
@ -71,20 +74,23 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
hir::ImplItemKind::Type(_) => println!("associated type"),
}
}
// fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
// fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
// hir::TraitItem) {
// if !has_attr(&item.attrs) {
// return;
// }
// }
//
// fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx hir::Variant, _:
// fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, var: &'tcx
// hir::Variant, _:
// &hir::Generics) {
// if !has_attr(&var.node.attrs) {
// return;
// }
// }
//
// fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField) {
// fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx
// hir::StructField) {
// if !has_attr(&field.attrs) {
// return;
// }
@ -123,7 +129,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
hir::StmtSemi(ref e, _) => print_expr(cx, e, 0),
}
}
// fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem) {
// fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
// hir::ForeignItem) {
// if !has_attr(&item.attrs) {
// return;
// }
@ -345,8 +352,10 @@ fn print_item(cx: &LateContext, item: &hir::Item) {
hir::Visibility::Public => println!("public"),
hir::Visibility::Crate => println!("visible crate wide"),
hir::Visibility::Restricted { ref path, .. } => {
println!("visible in module `{}`",
print::to_string(print::NO_ANN, |s| s.print_path(path, false)))
println!(
"visible in module `{}`",
print::to_string(print::NO_ANN, |s| s.print_path(path, false))
)
},
hir::Visibility::Inherited => println!("visibility inherited from outer item"),
}
@ -422,9 +431,11 @@ fn print_pat(cx: &LateContext, pat: &hir::Pat, indent: usize) {
},
hir::PatKind::Struct(ref path, ref fields, ignore) => {
println!("{}Struct", ind);
println!("{}name: {}",
println!(
"{}name: {}",
ind,
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false))
);
println!("{}ignore leftover fields: {}", ind, ignore);
println!("{}fields:", ind);
for field in fields {
@ -437,9 +448,11 @@ fn print_pat(cx: &LateContext, pat: &hir::Pat, indent: usize) {
},
hir::PatKind::TupleStruct(ref path, ref fields, opt_dots_position) => {
println!("{}TupleStruct", ind);
println!("{}path: {}",
println!(
"{}path: {}",
ind,
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false))
);
if let Some(dot_position) = opt_dots_position {
println!("{}dot position: {}", ind, dot_position);
}

View File

@ -63,20 +63,28 @@ impl LintPass for Clippy {
impl EarlyLintPass for Clippy {
fn check_crate(&mut self, cx: &EarlyContext, krate: &AstCrate) {
if let Some(utils) = krate.module.items.iter().find(|item| item.ident.name == "utils") {
if let Some(utils) = krate.module.items.iter().find(
|item| item.ident.name == "utils",
)
{
if let ItemKind::Mod(ref utils_mod) = utils.node {
if let Some(paths) = utils_mod.items.iter().find(|item| item.ident.name == "paths") {
if let Some(paths) = utils_mod.items.iter().find(
|item| item.ident.name == "paths",
)
{
if let ItemKind::Mod(ref paths_mod) = paths.node {
let mut last_name: Option<InternedString> = None;
for item in &paths_mod.items {
let name = item.ident.name.as_str();
if let Some(ref last_name) = last_name {
if **last_name > *name {
span_lint(cx,
span_lint(
cx,
CLIPPY_LINTS_INTERNAL,
item.span,
"this constant should be before the previous constant due to lexical \
ordering");
ordering",
);
}
}
last_name = Some(name);
@ -128,13 +136,20 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
// not able to capture the error.
// Therefore, we need to climb the macro expansion tree and find the
// actual span that invoked `declare_lint!`:
let lint_span = lint_span.ctxt.outer().expn_info().map(|ei| ei.call_site).expect("unable to get call_site");
let lint_span = lint_span
.ctxt
.outer()
.expn_info()
.map(|ei| ei.call_site)
.expect("unable to get call_site");
if !self.registered_lints.contains(lint_name) {
span_lint(cx,
span_lint(
cx,
LINT_WITHOUT_LINT_PASS,
lint_span,
&format!("the lint `{}` is not added to any `LintPass`", lint_name));
&format!("the lint `{}` is not added to any `LintPass`", lint_name),
);
}
}
}
@ -142,7 +157,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
fn is_lint_ref_type(ty: &Ty) -> bool {
if let TyRptr(ref lt, MutTy { ty: ref inner, mutbl: MutImmutable }) = ty.node {
if let TyRptr(ref lt,
MutTy {
ty: ref inner,
mutbl: MutImmutable,
}) = ty.node
{
if lt.is_elided() {
return false;
}

View File

@ -93,7 +93,8 @@ macro_rules! if_let_chain {
pub mod higher;
/// Returns true if the two spans come from differing expansions (i.e. one is from a macro and one
/// Returns true if the two spans come from differing expansions (i.e. one is
/// from a macro and one
/// isn't).
pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
rhs.ctxt != lhs.ctxt
@ -119,10 +120,12 @@ pub fn in_macro(span: Span) -> bool {
})
}
/// Returns true if the macro that expanded the crate was outside of the current crate or was a
/// Returns true if the macro that expanded the crate was outside of the
/// current crate or was a
/// compiler plugin.
pub fn in_external_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
/// Invokes `in_macro` with the expansion info of the given span slightly heavy, try to use
/// Invokes `in_macro` with the expansion info of the given span slightly
/// heavy, try to use
/// this after other checks have already happened.
fn in_macro_ext<'a, T: LintContext<'a>>(cx: &T, info: &ExpnInfo) -> bool {
// no ExpnInfo = no macro
@ -133,11 +136,18 @@ pub fn in_external_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
// no span for the callee = external macro
info.callee.span.map_or(true, |span| {
// no snippet = external macro or compiler-builtin expansion
cx.sess().codemap().span_to_snippet(span).ok().map_or(true, |code| !code.starts_with("macro_rules"))
cx.sess().codemap().span_to_snippet(span).ok().map_or(
true,
|code| {
!code.starts_with("macro_rules")
},
)
})
}
span.ctxt.outer().expn_info().map_or(false, |info| in_macro_ext(cx, &info))
span.ctxt.outer().expn_info().map_or(false, |info| {
in_macro_ext(cx, &info)
})
}
/// Check if a `DefId`'s path matches the given absolute type path usage.
@ -170,7 +180,10 @@ pub fn match_def_path(tcx: TyCtxt, def_id: DefId, path: &[&str]) -> bool {
tcx.push_item_path(&mut apb, def_id);
apb.names.len() == path.len() && apb.names.into_iter().zip(path.iter()).all(|(a, &b)| *a == *b)
apb.names.len() == path.len() &&
apb.names.into_iter().zip(path.iter()).all(
|(a, &b)| *a == *b,
)
}
/// Check if type is struct, enum or union type with given def path.
@ -206,9 +219,9 @@ pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool
pub fn last_path_segment(path: &QPath) -> &PathSegment {
match *path {
QPath::Resolved(_, ref path) => {
path.segments
.last()
.expect("A path must have at least one segment")
path.segments.last().expect(
"A path must have at least one segment",
)
},
QPath::TypeRelative(_, ref seg) => seg,
}
@ -244,7 +257,9 @@ pub fn match_path(path: &QPath, segments: &[&str]) -> bool {
}
pub fn match_path_old(path: &Path, segments: &[&str]) -> bool {
path.segments.iter().rev().zip(segments.iter().rev()).all(|(a, b)| a.name == *b)
path.segments.iter().rev().zip(segments.iter().rev()).all(
|(a, b)| a.name == *b,
)
}
/// Match a `Path` against a slice of segment string literals, e.g.
@ -254,7 +269,9 @@ pub fn match_path_old(path: &Path, segments: &[&str]) -> bool {
/// match_path(path, &["std", "rt", "begin_unwind"])
/// ```
pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
path.segments.iter().rev().zip(segments.iter().rev()).all(|(a, b)| a.identifier.name == *b)
path.segments.iter().rev().zip(segments.iter().rev()).all(
|(a, b)| a.identifier.name == *b,
)
}
/// Get the definition associated to a path.
@ -262,7 +279,9 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option<def::Def> {
let cstore = &cx.tcx.sess.cstore;
let crates = cstore.crates();
let krate = crates.iter().find(|&&krate| cstore.crate_name(krate) == path[0]);
let krate = crates.iter().find(
|&&krate| cstore.crate_name(krate) == path[0],
);
if let Some(krate) = krate {
let krate = DefId {
krate: *krate,
@ -312,14 +331,20 @@ pub fn implements_trait<'a, 'tcx>(
cx: &LateContext<'a, 'tcx>,
ty: Ty<'tcx>,
trait_id: DefId,
ty_params: &[Ty<'tcx>]
ty_params: &[Ty<'tcx>],
) -> bool {
let ty = cx.tcx.erase_regions(&ty);
let obligation = cx.tcx
.predicate_for_trait_def(cx.param_env, traits::ObligationCause::dummy(), trait_id, 0, ty, ty_params);
cx.tcx
.infer_ctxt()
.enter(|infcx| traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation))
let obligation = cx.tcx.predicate_for_trait_def(
cx.param_env,
traits::ObligationCause::dummy(),
trait_id,
0,
ty,
ty_params,
);
cx.tcx.infer_ctxt().enter(|infcx| {
traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation)
})
}
/// Resolve the definition of a node from its `NodeId`.
@ -330,7 +355,8 @@ pub fn resolve_node(cx: &LateContext, qpath: &QPath, id: NodeId) -> def::Def {
/// Match an `Expr` against a chain of methods, and return the matched `Expr`s.
///
/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec` containing the `Expr`s for
/// `matched_method_chain(expr, &["bar", "baz"])` will return a `Vec`
/// containing the `Expr`s for
/// `.bar()` and `.baz()`
pub fn method_chain_args<'a>(expr: &'a Expr, methods: &[&str]) -> Option<Vec<&'a [Expr]>> {
let mut current = expr;
@ -382,8 +408,10 @@ pub fn snippet_opt<'a, T: LintContext<'a>>(cx: &T, span: Span) -> Option<String>
cx.sess().codemap().span_to_snippet(span).ok()
}
/// Convert a span (from a block) to a code snippet if available, otherwise use default.
/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
/// Convert a span (from a block) to a code snippet if available, otherwise use
/// default.
/// This trims the code of indentation, except for the first line. Use it for
/// blocks or block-like
/// things which need to be printed as such.
///
/// # Example
@ -401,7 +429,7 @@ pub fn expr_block<'a, 'b, T: LintContext<'b>>(
cx: &T,
expr: &Expr,
option: Option<String>,
default: &'a str
default: &'a str,
) -> Cow<'a, str> {
let code = snippet_block(cx, expr.span, default);
let string = option.unwrap_or_default();
@ -414,7 +442,8 @@ pub fn expr_block<'a, 'b, T: LintContext<'b>>(
}
}
/// Trim indentation from a multiline string with possibility of ignoring the first line.
/// Trim indentation from a multiline string with possibility of ignoring the
/// first line.
pub fn trim_multiline(s: Cow<str>, ignore_first: bool) -> Cow<str> {
let s_space = trim_multiline_inner(s, ignore_first, ' ');
let s_tab = trim_multiline_inner(s_space, ignore_first, '\t');
@ -429,16 +458,19 @@ fn trim_multiline_inner(s: Cow<str>, ignore_first: bool, ch: char) -> Cow<str> {
None
} else {
// ignore empty lines
Some(l.char_indices()
Some(
l.char_indices()
.find(|&(_, x)| x != ch)
.unwrap_or((l.len(), ch))
.0)
.0,
)
}
})
.min()
.unwrap_or(0);
if x > 0 {
Cow::Owned(s.lines()
Cow::Owned(
s.lines()
.enumerate()
.map(|(i, l)| if (ignore_first && i == 0) || l.is_empty() {
l
@ -446,7 +478,8 @@ fn trim_multiline_inner(s: Cow<str>, ignore_first: bool, ch: char) -> Cow<str> {
l.split_at(x).1
})
.collect::<Vec<_>>()
.join("\n"))
.join("\n"),
)
} else {
s
}
@ -460,17 +493,22 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> {
if node_id == parent_id {
return None;
}
map.find(parent_id).and_then(|node| if let Node::NodeExpr(parent) = node {
map.find(parent_id).and_then(
|node| if let Node::NodeExpr(parent) =
node
{
Some(parent)
} else {
None
})
},
)
}
pub fn get_enclosing_block<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, node: NodeId) -> Option<&'tcx Block> {
let map = &cx.tcx.hir;
let enclosing_node = map.get_enclosing_scope(node)
.and_then(|enclosing_id| map.find(enclosing_id));
let enclosing_node = map.get_enclosing_scope(node).and_then(|enclosing_id| {
map.find(enclosing_id)
});
if let Some(node) = enclosing_node {
match node {
Node::NodeBlock(block) => Some(block),
@ -498,8 +536,10 @@ impl<'a> Drop for DiagnosticWrapper<'a> {
impl<'a> DiagnosticWrapper<'a> {
fn wiki_link(&mut self, lint: &'static Lint) {
if env::var("CLIPPY_DISABLE_WIKI_LINKS").is_err() {
self.0.help(&format!("for further information visit https://github.com/Manishearth/rust-clippy/wiki#{}",
lint.name_lower()));
self.0.help(&format!(
"for further information visit https://github.com/Manishearth/rust-clippy/wiki#{}",
lint.name_lower()
));
}
}
}
@ -516,7 +556,7 @@ pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
lint: &'static Lint,
span: Span,
msg: &str,
help: &str
help: &str,
) {
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
if cx.current_level(lint) != Level::Allow {
@ -531,7 +571,7 @@ pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
span: Span,
msg: &str,
note_span: Span,
note: &str
note: &str,
) {
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
if cx.current_level(lint) != Level::Allow {
@ -549,8 +589,9 @@ pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
lint: &'static Lint,
sp: Span,
msg: &str,
f: F
) where F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>)
f: F,
) where
F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
{
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg));
if cx.current_level(lint) != Level::Allow {
@ -565,15 +606,17 @@ pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>(
sp: Span,
msg: &str,
help: &str,
sugg: String
sugg: String,
) {
span_lint_and_then(cx, lint, sp, msg, |db| { db.span_suggestion(sp, help, sugg); });
}
/// Create a suggestion made from several `span → replacement`.
///
/// Note: in the JSON format (used by `compiletest_rs`), the help message will appear once per
/// replacement. In human-readable format though, it only appears once before the whole suggestion.
/// Note: in the JSON format (used by `compiletest_rs`), the help message will
/// appear once per
/// replacement. In human-readable format though, it only appears once before
/// the whole suggestion.
pub fn multispan_sugg(db: &mut DiagnosticBuilder, help_msg: String, sugg: Vec<(Span, String)>) {
let sugg = rustc_errors::CodeSuggestion {
substitution_parts: sugg.into_iter()
@ -598,7 +641,8 @@ pub fn walk_ptrs_ty(ty: Ty) -> Ty {
}
}
/// Return the base type for references and raw pointers, and count reference depth.
/// Return the base type for references and raw pointers, and count reference
/// depth.
pub fn walk_ptrs_ty_depth(ty: Ty) -> (Ty, usize) {
fn inner(ty: Ty, depth: usize) -> (Ty, usize) {
match ty.sty {
@ -639,7 +683,9 @@ impl LimitStack {
LimitStack { stack: vec![limit] }
}
pub fn limit(&self) -> u64 {
*self.stack.last().expect("there should always be a value in the stack")
*self.stack.last().expect(
"there should always be a value in the stack",
)
}
pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
let stack = &mut self.stack;
@ -669,14 +715,14 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
}
}
/// Return the pre-expansion span if is this comes from an expansion of the macro `name`.
/// Return the pre-expansion span if is this comes from an expansion of the
/// macro `name`.
/// See also `is_direct_expn_of`.
pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
loop {
let span_name_span = span.ctxt
.outer()
.expn_info()
.map(|ei| (ei.callee.name(), ei.call_site));
let span_name_span = span.ctxt.outer().expn_info().map(|ei| {
(ei.callee.name(), ei.call_site)
});
match span_name_span {
Some((mac_name, new_span)) if mac_name == name => return Some(new_span),
@ -686,18 +732,19 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
}
}
/// Return the pre-expansion span if is this directly comes from an expansion of the macro `name`.
/// Return the pre-expansion span if is this directly comes from an expansion
/// of the macro `name`.
/// The difference with `is_expn_of` is that in
/// ```rust,ignore
/// foo!(bar!(42));
/// ```
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only `bar!` by
/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
/// `bar!` by
/// `is_direct_expn_of`.
pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
let span_name_span = span.ctxt
.outer()
.expn_info()
.map(|ei| (ei.callee.name(), ei.call_site));
let span_name_span = span.ctxt.outer().expn_info().map(|ei| {
(ei.callee.name(), ei.call_site)
});
match span_name_span {
Some((mac_name, new_span)) if mac_name == name => Some(new_span),
@ -705,7 +752,8 @@ pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
}
}
/// Return the index of the character after the first camel-case component of `s`.
/// Return the index of the character after the first camel-case component of
/// `s`.
pub fn camel_case_until(s: &str) -> usize {
let mut iter = s.char_indices();
if let Some((_, first)) = iter.next() {
@ -771,10 +819,13 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> Ty<'t
}
/// Check if two types are the same.
// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` == `for <'b> Foo<'b>` but
// FIXME: this works correctly for lifetimes bounds (`for <'a> Foo<'a>` == `for
// <'b> Foo<'b>` but
// not for type parameters.
pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
cx.tcx.infer_ctxt().enter(|infcx| infcx.can_eq(cx.param_env, a, b).is_ok())
cx.tcx.infer_ctxt().enter(|infcx| {
infcx.can_eq(cx.param_env, a, b).is_ok()
})
}
/// Return whether the given type is an `unsafe` function.
@ -792,8 +843,10 @@ pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
/// Return whether a pattern is refutable.
pub fn is_refutable(cx: &LateContext, pat: &Pat) -> bool {
fn is_enum_variant(cx: &LateContext, qpath: &QPath, did: NodeId) -> bool {
matches!(cx.tables.qpath_def(qpath, did),
def::Def::Variant(..) | def::Def::VariantCtor(..))
matches!(
cx.tables.qpath_def(qpath, did),
def::Def::Variant(..) | def::Def::VariantCtor(..)
)
}
fn are_refutable<'a, I: Iterator<Item = &'a Pat>>(cx: &LateContext, mut i: I) -> bool {
@ -824,19 +877,26 @@ pub fn is_refutable(cx: &LateContext, pat: &Pat) -> bool {
}
},
PatKind::Slice(ref head, ref middle, ref tail) => {
are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
are_refutable(
cx,
head.iter().chain(middle).chain(tail.iter()).map(
|pat| &**pat,
),
)
},
}
}
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d implementations have.
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
attr::contains_name(attrs, "automatically_derived")
}
/// Remove blocks around an expression.
///
/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return themselves.
/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
/// themselves.
pub fn remove_blocks(expr: &Expr) -> &Expr {
if let ExprBlock(ref block) = expr.node {
if block.stmts.is_empty() {
@ -948,5 +1008,7 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
}
pub fn type_size<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Option<u64> {
ty.layout(cx.tcx, cx.param_env).ok().map(|layout| layout.size(cx.tcx).bytes())
ty.layout(cx.tcx, cx.param_env).ok().map(|layout| {
layout.size(cx.tcx).bytes()
})
}

View File

@ -1,4 +1,5 @@
//! This module contains paths to types and functions Clippy needs to know about.
//! This module contains paths to types and functions Clippy needs to know
//! about.
pub const ASMUT_TRAIT: [&'static str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&'static str; 3] = ["core", "convert", "AsRef"];

View File

@ -20,7 +20,8 @@ pub enum Sugg<'a> {
NonParen(Cow<'a, str>),
/// An expression that does not fit in other variants.
MaybeParen(Cow<'a, str>),
/// A binary operator expression, including `as`-casts and explicit type coercion.
/// A binary operator expression, including `as`-casts and explicit type
/// coercion.
BinOp(AssocOp, Cow<'a, str>),
}
@ -77,7 +78,8 @@ impl<'a> Sugg<'a> {
})
}
/// Convenience function around `hir_opt` for suggestions with a default text.
/// Convenience function around `hir_opt` for suggestions with a default
/// text.
pub fn hir(cx: &LateContext, expr: &hir::Expr, default: &'a str) -> Sugg<'a> {
Self::hir_opt(cx, expr).unwrap_or_else(|| Sugg::NonParen(Cow::Borrowed(default)))
}
@ -156,7 +158,8 @@ impl<'a> Sugg<'a> {
make_unop("*", self)
}
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>` suggestion.
/// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
/// suggestion.
pub fn range(self, end: Self, limit: ast::RangeLimits) -> Sugg<'static> {
match limit {
ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, &end),
@ -164,7 +167,8 @@ impl<'a> Sugg<'a> {
}
}
/// Add parenthesis to any expression that might need them. Suitable to the `self` argument of
/// Add parenthesis to any expression that might need them. Suitable to the
/// `self` argument of
/// a method call (eg. to build `bar.foo()` or `(1 + 2).foo()`).
pub fn maybe_par(self) -> Self {
match self {
@ -233,7 +237,8 @@ impl<T: Display> Display for ParenHelper<T> {
/// Build the string for `<op><expr>` adding parenthesis when necessary.
///
/// For convenience, the operator is taken as a string because all unary operators have the same
/// For convenience, the operator is taken as a string because all unary
/// operators have the same
/// precedence.
pub fn make_unop(op: &str, expr: Sugg) -> Sugg<'static> {
Sugg::MaybeParen(format!("{}{}", op, expr.maybe_par()).into())
@ -241,7 +246,8 @@ pub fn make_unop(op: &str, expr: Sugg) -> Sugg<'static> {
/// Build the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
///
/// Precedence of shift operator relative to other arithmetic operation is often confusing so
/// Precedence of shift operator relative to other arithmetic operation is
/// often confusing so
/// parenthesis will always be added for a mix of these.
pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
/// Whether the operator is a shift operator `<<` or `>>`.
@ -251,18 +257,21 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
/// Whether the operator is a arithmetic operator (`+`, `-`, `*`, `/`, `%`).
fn is_arith(op: &AssocOp) -> bool {
matches!(*op,
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus)
matches!(
*op,
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
)
}
/// Whether the operator `op` needs parenthesis with the operator `other` in the direction
/// Whether the operator `op` needs parenthesis with the operator `other`
/// in the direction
/// `dir`.
fn needs_paren(op: &AssocOp, other: &AssocOp, dir: Associativity) -> bool {
other.precedence() < op.precedence() ||
(other.precedence() == op.precedence() &&
((op != other && associativity(op) != dir) ||
(op == other && associativity(op) != Associativity::Both))) || is_shift(op) && is_arith(other) ||
is_shift(other) && is_arith(op)
(op == other && associativity(op) != Associativity::Both))) ||
is_shift(op) && is_arith(other) || is_shift(other) && is_arith(op)
}
let lhs_paren = if let Sugg::BinOp(ref lop, _) = *lhs {
@ -316,11 +325,13 @@ enum Associativity {
Right,
}
/// Return the associativity/fixity of an operator. The difference with `AssocOp::fixity` is that
/// Return the associativity/fixity of an operator. The difference with
/// `AssocOp::fixity` is that
/// an operator can be both left and right associative (such as `+`:
/// `a + b + c == (a + b) + c == a + (b + c)`.
///
/// Chained `as` and explicit `:` type coercion never need inner parenthesis so they are considered
/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
/// they are considered
/// associative.
fn associativity(op: &AssocOp) -> Associativity {
use syntax::util::parser::AssocOp::*;
@ -374,10 +385,14 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
})
}
/// Return the indentation before `span` if there are nothing but `[ \t]` before it on its line.
/// Return the indentation before `span` if there are nothing but `[ \t]`
/// before it on its line.
fn indentation<'a, T: LintContext<'a>>(cx: &T, span: Span) -> Option<String> {
let lo = cx.sess().codemap().lookup_char_pos(span.lo);
if let Some(line) = lo.file.get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */) {
if let Some(line) = lo.file.get_line(
lo.line - 1, /* line numbers in `Loc` are 1-based */
)
{
if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
// we can mix char and byte positions here because we only consider `[ \t]`
if lo.col == CharPos(pos) {
@ -424,7 +439,10 @@ pub trait DiagnosticBuilderExt<'a, T: LintContext<'a>> {
impl<'a, 'b, 'c, T: LintContext<'c>> DiagnosticBuilderExt<'c, T> for rustc_errors::DiagnosticBuilder<'b> {
fn suggest_item_with_attr<D: Display + ?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D) {
if let Some(indent) = indentation(cx, item) {
let span = Span { hi: item.lo, ..item };
let span = Span {
hi: item.lo,
..item
};
self.span_suggestion(span, msg, format!("{}\n{}", attr, indent));
}
@ -432,10 +450,14 @@ impl<'a, 'b, 'c, T: LintContext<'c>> DiagnosticBuilderExt<'c, T> for rustc_error
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str) {
if let Some(indent) = indentation(cx, item) {
let span = Span { hi: item.lo, ..item };
let span = Span {
hi: item.lo,
..item
};
let mut first = true;
let new_item = new_item.lines()
let new_item = new_item
.lines()
.map(|l| if first {
first = false;
format!("{}\n", l)

View File

@ -63,7 +63,10 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
let parent_item = cx.tcx.hir.get_parent(len.id);
let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
if ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(len).is_ok() {
if ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables)
.eval(len)
.is_ok()
{
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
} else {
return;
@ -84,12 +87,14 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
},
};
span_lint_and_sugg(cx,
span_lint_and_sugg(
cx,
USELESS_VEC,
span,
"useless use of `vec!`",
"you can use a slice directly",
snippet);
snippet,
);
}
/// Return the item type of the vector (ie. the `T` in `Vec<T>`).

View File

@ -5,7 +5,8 @@ use utils::span_help_and_lint;
/// **What it does:** Checks for `0.0 / 0.0`.
///
/// **Why is this bad?** It's less readable than `std::f32::NAN` or `std::f64::NAN`.
/// **Why is this bad?** It's less readable than `std::f32::NAN` or
/// `std::f64::NAN`.
///
/// **Known problems:** None.
///

View File

@ -46,8 +46,13 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
descriptions: &rustc_errors::registry::Registry,
output: ErrorOutputType,
) -> Compilation {
self.default
.early_callback(matches, sopts, cfg, descriptions, output)
self.default.early_callback(
matches,
sopts,
cfg,
descriptions,
output,
)
}
fn no_input(
&mut self,
@ -58,8 +63,14 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
ofile: &Option<PathBuf>,
descriptions: &rustc_errors::registry::Registry,
) -> Option<(Input, Option<PathBuf>)> {
self.default
.no_input(matches, sopts, cfg, odir, ofile, descriptions)
self.default.no_input(
matches,
sopts,
cfg,
odir,
ofile,
descriptions,
)
}
fn late_callback(
&mut self,
@ -69,8 +80,13 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
odir: &Option<PathBuf>,
ofile: &Option<PathBuf>,
) -> Compilation {
self.default
.late_callback(matches, sess, input, odir, ofile)
self.default.late_callback(
matches,
sess,
input,
odir,
ofile,
)
}
fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
let mut control = self.default.build_controller(sess, matches);
@ -79,13 +95,17 @@ impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
let old = std::mem::replace(&mut control.after_parse.callback, box |_| {});
control.after_parse.callback = Box::new(move |state| {
{
let mut registry = rustc_plugin::registry::Registry::new(state.session,
let mut registry = rustc_plugin::registry::Registry::new(
state.session,
state
.krate
.as_ref()
.expect("at this compilation stage \
the krate must be parsed")
.span);
.expect(
"at this compilation stage \
the krate must be parsed",
)
.span,
);
registry.args_hidden = Some(Vec::new());
clippy_lints::register_plugins(&mut registry);
@ -179,9 +199,9 @@ pub fn main() {
if let Some("clippy") = std::env::args().nth(1).as_ref().map(AsRef::as_ref) {
// this arm is executed on the initial call to `cargo clippy`
let manifest_path_arg = std::env::args()
.skip(2)
.find(|val| val.starts_with("--manifest-path="));
let manifest_path_arg = std::env::args().skip(2).find(|val| {
val.starts_with("--manifest-path=")
});
let mut metadata =
if let Ok(metadata) = cargo_metadata::metadata(manifest_path_arg.as_ref().map(AsRef::as_ref)) {
@ -191,19 +211,24 @@ pub fn main() {
process::exit(101);
};
let manifest_path = manifest_path_arg.map(|arg| Path::new(&arg["--manifest-path=".len()..])
.canonicalize().expect("manifest path could not be canonicalized"));
let manifest_path = manifest_path_arg.map(|arg| {
Path::new(&arg["--manifest-path=".len()..])
.canonicalize()
.expect("manifest path could not be canonicalized")
});
let package_index = {
if let Some(manifest_path) = manifest_path {
metadata.packages.iter().position(|package| {
let package_manifest_path = Path::new(&package.manifest_path)
.canonicalize().expect("package manifest path could not be canonicalized");
let package_manifest_path = Path::new(&package.manifest_path).canonicalize().expect(
"package manifest path could not be canonicalized",
);
package_manifest_path == manifest_path
})
} else {
let package_manifest_paths: HashMap<_, _> =
metadata.packages.iter()
let package_manifest_paths: HashMap<_, _> = metadata
.packages
.iter()
.enumerate()
.map(|(i, package)| {
let package_manifest_path = Path::new(&package.manifest_path)
@ -227,19 +252,18 @@ pub fn main() {
loop {
if let Some(&package_index) = package_manifest_paths.get(current_path) {
break Some(package_index);
}
else {
// We'll never reach the filesystem root, because to get to this point in the code
} else {
// We'll never reach the filesystem root, because to get to this point in the
// code
// the call to `cargo_metadata::metadata` must have succeeded. So it's okay to
// unwrap the current path's parent.
current_path = current_path
.parent()
.unwrap_or_else(|| panic!("could not find parent of path {}", current_path.display()));
current_path = current_path.parent().unwrap_or_else(|| {
panic!("could not find parent of path {}", current_path.display())
});
}
}
}
}
.expect("could not find matching package");
}.expect("could not find matching package");
let package = metadata.packages.remove(package_index);
for target in package.targets {
@ -250,9 +274,12 @@ pub fn main() {
std::process::exit(code);
}
} else if ["bin", "example", "test", "bench"].contains(&&**first) {
if let Err(code) = process(vec![format!("--{}", first), target.name]
if let Err(code) = process(
vec![format!("--{}", first), target.name]
.into_iter()
.chain(args)) {
.chain(args),
)
{
std::process::exit(code);
}
}
@ -280,7 +307,9 @@ pub fn main() {
.and_then(|out| String::from_utf8(out.stdout).ok())
.map(|s| s.trim().to_owned())
})
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust")
.expect(
"need to specify SYSROOT env var during clippy compilation, or use rustup or multirust",
)
};
rustc_driver::in_rustc_thread(|| {
@ -310,13 +339,13 @@ pub fn main() {
if let Err(CompileIncomplete::Errored(_)) = result {
std::process::exit(1);
}
})
.expect("rustc_thread failed");
}).expect("rustc_thread failed");
}
}
fn process<I>(old_args: I) -> Result<(), i32>
where I: Iterator<Item = String>
where
I: Iterator<Item = String>,
{
let mut args = vec!["rustc".to_owned()];

View File

@ -29,8 +29,6 @@ fn compile_test() {
prepare_env();
run_mode("run-pass", "run-pass");
run_mode("ui", "ui");
#[cfg(target_os = "windows")]
run_mode("ui-windows", "ui");
#[cfg(not(target_os = "windows"))]
run_mode("ui-posix", "ui");
#[cfg(target_os = "windows")] run_mode("ui-windows", "ui");
#[cfg(not(target_os = "windows"))] run_mode("ui-posix", "ui");
}

View File

@ -24,7 +24,9 @@ fn dogfood() {
let mut s = String::new();
s.push_str(" -L target/debug/");
s.push_str(" -L target/debug/deps");
s.push_str(" -Zextra-plugins=clippy -Ltarget_recur/debug -Dwarnings -Dclippy_pedantic -Dclippy -Dclippy_internal");
s.push_str(
" -Zextra-plugins=clippy -Ltarget_recur/debug -Dwarnings -Dclippy_pedantic -Dclippy -Dclippy_internal",
);
config.target_rustcflags = Some(s);
if let Ok(name) = var("TESTNAME") {
config.filter = Some(name.to_owned())

View File

@ -8,7 +8,8 @@ fn rust_type_id(name: &str) {
if "bool" == &name[..] || "uint" == &name[..] || "u8" == &name[..] || "u16" == &name[..] ||
"u32" == &name[..] || "f32" == &name[..] || "f64" == &name[..] || "i8" == &name[..] ||
"i16" == &name[..] || "i32" == &name[..] ||
"i64" == &name[..] || "Self" == &name[..] || "str" == &name[..] {
"i64" == &name[..] || "Self" == &name[..] || "str" == &name[..]
{
unreachable!();
}
}

View File

@ -19,14 +19,28 @@ fn test_overlapping() {
assert_eq!(None, overlapping::<u8>(&[]));
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
assert_eq!(None, overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))]));
assert_eq!(None,
overlapping(&[sp(1, Bound::Included(4)),
assert_eq!(
None,
overlapping(
&[
sp(1, Bound::Included(4)),
sp(5, Bound::Included(6)),
sp(10, Bound::Included(11))]));
assert_eq!(Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))]));
assert_eq!(Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
overlapping(&[sp(1, Bound::Included(4)),
sp(10, Bound::Included(11)),
],
)
);
assert_eq!(
Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
);
assert_eq!(
Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
overlapping(
&[
sp(1, Bound::Included(4)),
sp(5, Bound::Included(6)),
sp(6, Bound::Included(11))]));
sp(6, Bound::Included(11)),
],
)
);
}

View File

@ -86,4 +86,3 @@ fn test_erode_block() {
println!("input: {}\nexpected:\n{}\ngot:\n{}", input, expected, got);
assert_eq!(expected, got);
}