Merge branch 'master' into move_links

This commit is contained in:
Oliver Schneider 2017-08-14 10:07:26 +02:00 committed by GitHub
commit 4470d7a8ca
107 changed files with 3287 additions and 2125 deletions

13
.github/deploy.sh vendored
View file

@ -33,6 +33,19 @@ if [ -n "$TRAVIS_TAG" ]; then
ln -s "$TRAVIS_TAG" out/current
fi
# Generate version index that is shown as root index page
(
cp util/gh-pages/versions.html out/index.html
cd out
python -c '\
import os, json;\
print json.dumps([\
dir for dir in os.listdir(".")\
if not dir.startswith(".") and os.path.isdir(dir)\
])' > versions.json
)
# Pull requests and commits to other branches shouldn't try to deploy, just build to verify
if [ "$TRAVIS_PULL_REQUEST" != "false" ] || [ "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]; then
# Tags should deploy

View file

@ -8,11 +8,6 @@ os:
sudo: false
cache:
cargo: true
directories:
- target
env:
global:
# TRAVIS_TOKEN_CLIPPY_SERVICE

View file

@ -1,6 +1,12 @@
# Change Log
All notable changes to this project will be documented in this file.
## 0.0.151
* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
## 0.0.150
* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
## 0.0.148
* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
* New lints: [`unreadable_literal`], [`inconsisten_digit_grouping`], [`large_digit_groups`]

View file

@ -4,7 +4,16 @@ Hello fellow Rustacean! Great to see your interest in compiler internals and lin
## Getting started
All issues on Clippy are mentored, if you want help with a bug just ask @Manishearth or @llogiq.
High level approach:
1. Find something to fix/improve
2. Change code (likely some file in `clippy_lints/src/`)
3. Run `cargo test` in the root directory and wiggle code until it passes
4. Open a PR (also can be done between 2. and 3. if you run into problems)
### Finding something to fix/improve
All issues on Clippy are mentored, if you want help with a bug just ask @Manishearth, @llogiq, @mcarton or @oli-obk.
Some issues are easier than others. The [E-easy](https://github.com/rust-lang-nursery/rust-clippy/labels/E-easy)
label can be used to find the easy issues. If you want to work on an issue, please leave a comment
@ -16,7 +25,8 @@ matching of the syntax tree structure, and are generally easier than
and resolved paths.
Issues marked [E-medium](https://github.com/rust-lang-nursery/rust-clippy/labels/E-medium) are generally
pretty easy too, though it's recommended you work on an E-easy issue first.
pretty easy too, though it's recommended you work on an E-easy issue first. They are mostly classified
as `E-medium`, since they might be somewhat involved code wise, but not difficult per-se.
[Llogiq's blog post on lints](https://llogiq.github.io/2015/06/04/workflows.html) is a nice primer
to lint-writing, though it does get into advanced stuff. Most lints consist of an implementation of
@ -35,16 +45,14 @@ T-middle issues can be more involved and require verifying types. The
lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
### Writing code
Compiling clippy can take almost a minute or more depending on your machine.
You can set the environment flag `CARGO_INCREMENTAL=1` to cut down that time to
almost a third on average, depending on the influence your change has.
Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected.
Of course there's little sense in writing the output yourself or copying it around.
Therefore you can simply run `tests/ui/update-all-references.sh` and check whether
the output looks as you expect with `git diff`. Commit all `*.stderr` files, too.
Please document your lint with a doc comment akin to the following:
Also please document your lint with a doc comment akin to the following:
```rust
/// **What it does:** Checks for ... (describe what the lint matches).
///
@ -58,7 +66,12 @@ Also please document your lint with a doc comment akin to the following:
/// ```
```
Our `util/update_wiki.py` script can then add your lint docs to the wiki.
### Running test suite
Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected.
Of course there's little sense in writing the output yourself or copying it around.
Therefore you can simply run `tests/ui/update-all-references.sh` and check whether
the output looks as you expect with `git diff`. Commit all `*.stderr` files, too.
## Contributions

View file

@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.0.149"
version = "0.0.151"
authors = [
"Manish Goregaokar <manishsmail@gmail.com>",
"Andre Bogus <bogusandre@gmail.com>",
@ -31,7 +31,7 @@ path = "src/main.rs"
[dependencies]
# begin automatic update
clippy_lints = { version = "0.0.149", path = "clippy_lints" }
clippy_lints = { version = "0.0.151", path = "clippy_lints" }
# end automatic update
cargo_metadata = "0.2"

View file

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

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,24 +35,26 @@ declare_lint! {
}
// Tuples are of the form (constant, name, min_digits)
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),
(f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
(f64::FRAC_PI_2, "FRAC_PI_2", 5),
(f64::FRAC_PI_3, "FRAC_PI_3", 5),
(f64::FRAC_PI_4, "FRAC_PI_4", 5),
(f64::FRAC_PI_6, "FRAC_PI_6", 5),
(f64::FRAC_PI_8, "FRAC_PI_8", 5),
(f64::LN_10, "LN_10", 5),
(f64::LN_2, "LN_2", 5),
(f64::LOG10_E, "LOG10_E", 5),
(f64::LOG2_E, "LOG2_E", 5),
(f64::PI, "PI", 3),
(f64::SQRT_2, "SQRT_2", 5)];
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),
(f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5),
(f64::FRAC_PI_2, "FRAC_PI_2", 5),
(f64::FRAC_PI_3, "FRAC_PI_3", 5),
(f64::FRAC_PI_4, "FRAC_PI_4", 5),
(f64::FRAC_PI_6, "FRAC_PI_6", 5),
(f64::FRAC_PI_8, "FRAC_PI_8", 5),
(f64::LN_10, "LN_10", 5),
(f64::LN_2, "LN_2", 5),
(f64::LOG10_E, "LOG10_E", 5),
(f64::LOG2_E, "LOG2_E", 5),
(f64::PI, "PI", 3),
(f64::SQRT_2, "SQRT_2", 5),
];
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Pass;
impl LintPass for 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,
APPROX_CONSTANT,
e.span,
&format!("approximate value of `{}::consts::{}` found. \
span_lint(
cx,
APPROX_CONSTANT,
e.span,
&format!(
"approximate value of `{}::consts::{}` found. \
Consider using it directly",
module,
&name));
module,
&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.
///
@ -46,7 +47,7 @@ declare_restriction_lint! {
"indexing/slicing usage"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct ArrayIndexing;
impl LintPass for ArrayIndexing {
@ -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,
@ -128,24 +127,23 @@ fn to_const_range(
Some(Some(ConstVal::Integral(x))) => {
if limits == RangeLimits::Closed {
match x {
ConstInt::U8(_) => (x + ConstInt::U8(1)),
ConstInt::U16(_) => (x + ConstInt::U16(1)),
ConstInt::U32(_) => (x + ConstInt::U32(1)),
ConstInt::U64(_) => (x + ConstInt::U64(1)),
ConstInt::U128(_) => (x + ConstInt::U128(1)),
ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
ConstInt::I8(_) => (x + ConstInt::I8(1)),
ConstInt::I16(_) => (x + ConstInt::I16(1)),
ConstInt::I32(_) => (x + ConstInt::I32(1)),
ConstInt::I64(_) => (x + ConstInt::I64(1)),
ConstInt::I128(_) => (x + ConstInt::I128(1)),
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}
.expect("such a big array is not realistic")
ConstInt::U8(_) => (x + ConstInt::U8(1)),
ConstInt::U16(_) => (x + ConstInt::U16(1)),
ConstInt::U32(_) => (x + ConstInt::U32(1)),
ConstInt::U64(_) => (x + ConstInt::U64(1)),
ConstInt::U128(_) => (x + ConstInt::U128(1)),
ConstInt::Usize(ConstUsize::Us16(_)) => (x + ConstInt::Usize(ConstUsize::Us16(1))),
ConstInt::Usize(ConstUsize::Us32(_)) => (x + ConstInt::Usize(ConstUsize::Us32(1))),
ConstInt::Usize(ConstUsize::Us64(_)) => (x + ConstInt::Usize(ConstUsize::Us64(1))),
ConstInt::I8(_) => (x + ConstInt::I8(1)),
ConstInt::I16(_) => (x + ConstInt::I16(1)),
ConstInt::I32(_) => (x + ConstInt::I32(1)),
ConstInt::I64(_) => (x + ConstInt::I64(1)),
ConstInt::I128(_) => (x + ConstInt::I128(1)),
ConstInt::Isize(ConstIsize::Is16(_)) => (x + ConstInt::Isize(ConstIsize::Is16(1))),
ConstInt::Isize(ConstIsize::Is32(_)) => (x + ConstInt::Isize(ConstIsize::Is32(1))),
ConstInt::Isize(ConstIsize::Is64(_)) => (x + ConstInt::Isize(ConstIsize::Is64(1))),
}.expect("such a big array is not realistic")
} else {
x
}

View file

@ -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,
"replace it with",
format!("{} = {}", lhs, sugg::make_binop(higher::binop(op.node), lhs, rhs)));
db.span_suggestion(
expr.span,
"replace it with",
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,35 +150,40 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
}
}
}
if ops!(op.node,
if ops!(
op.node,
cx,
ty,
rty,
Add: BiAdd,
Sub: BiSub,
Mul: BiMul,
Div: BiDiv,
Rem: BiRem,
And: BiAnd,
Or: BiOr,
BitAnd: BiBitAnd,
BitOr: BiBitOr,
BitXor: BiBitXor,
Shr: BiShr,
Shl: BiShl
)
{
span_lint_and_then(
cx,
ty,
rty,
Add: BiAdd,
Sub: BiSub,
Mul: BiMul,
Div: BiDiv,
Rem: BiRem,
And: BiAnd,
Or: BiOr,
BitAnd: BiBitAnd,
BitOr: BiBitOr,
BitXor: BiBitXor,
Shr: BiShr,
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,
"replace it with",
format!("{} {}= {}",
snip_a,
op.node.as_str(),
snip_r));
});
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,
"replace it with",
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:**
@ -75,7 +78,7 @@ declare_lint! {
"use of `#[deprecated(since = \"x\")]` where x is not semver"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct AttrPass;
impl LintPass for AttrPass {
@ -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,
USELESS_ATTRIBUTE,
attr.span,
"useless lint attribute",
|db| {
sugg.insert(1, '!');
db.span_suggestion(attr.span, "if you just forgot a `!`, use", sugg);
});
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,
);
},
);
}
}
},
@ -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,
INLINE_ALWAYS,
attr.span,
&format!("you have declared `#[inline(always)]` on `{}`. This is usually a bad idea",
name));
span_lint(
cx,
INLINE_ALWAYS,
attr.span,
&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,
DEPRECATED_SEMVER,
span,
"the since field must contain a semver-compliant version");
span_lint(
cx,
DEPRECATED_SEMVER,
span,
"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
///
@ -88,7 +89,7 @@ declare_lint! {
"expressions where a bit mask is less readable than the corresponding method call"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct BitMask;
impl LintPass for BitMask {
@ -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,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` can never be equal to `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ & {}` can never be equal to `{}`",
mask_value,
cmp_value
),
);
}
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
@ -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,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` can never be equal to `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ | {}` can never be equal to `{}`",
mask_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,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` will always be lower than `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ & {}` will always be lower than `{}`",
mask_value,
cmp_value
),
);
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
}
},
BiBitOr => {
if mask_value >= cmp_value {
span_lint(cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` will never be lower than `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ | {}` will never be lower than `{}`",
mask_value,
cmp_value
),
);
} else {
check_ineffective_lt(cx, *span, mask_value, cmp_value, "|");
}
@ -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,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ & {}` will never be higher than `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ & {}` will never be higher than `{}`",
mask_value,
cmp_value
),
);
} else if mask_value == 0 {
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
}
},
BiBitOr => {
if mask_value > cmp_value {
span_lint(cx,
BAD_BIT_MASK,
*span,
&format!("incompatible bit mask: `_ | {}` will always be higher than `{}`",
mask_value,
cmp_value));
span_lint(
cx,
BAD_BIT_MASK,
*span,
&format!(
"incompatible bit mask: `_ | {}` will always be higher than `{}`",
mask_value,
cmp_value
),
);
} else {
check_ineffective_gt(cx, *span, mask_value, cmp_value, "|");
}
@ -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,
INEFFECTIVE_BIT_MASK,
span,
&format!("ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
c));
span_lint(
cx,
INEFFECTIVE_BIT_MASK,
span,
&format!(
"ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
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,
INEFFECTIVE_BIT_MASK,
span,
&format!("ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
c));
span_lint(
cx,
INEFFECTIVE_BIT_MASK,
span,
&format!(
"ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly",
op,
m,
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,
BLACKLISTED_NAME,
ident.span,
&format!("use of a blacklisted/placeholder name `{}`", ident.node));
span_lint(
cx,
BLACKLISTED_NAME,
ident.span,
&format!("use of a blacklisted/placeholder name `{}`", ident.node),
);
}
}
}

View file

@ -40,7 +40,7 @@ declare_lint! {
"complex blocks in conditions, e.g. `if { let x = true; x } ...`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct BlockInIfCondition;
impl LintPass for BlockInIfCondition {
@ -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,
BLOCK_IN_IF_CONDITION_EXPR,
check.span,
BRACED_EXPR_MESSAGE,
&format!("try\nif {} {} ... ",
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,
BLOCK_IN_IF_CONDITION_STMT,
check.span,
COMPLEX_BLOCK_MESSAGE,
&format!("try\nlet res = {};\nif res {} ... ",
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

@ -44,7 +44,7 @@ declare_lint! {
"boolean expressions that contain terminals which can be eliminated"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NonminimalBool;
impl LintPass for NonminimalBool {
@ -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,44 +350,54 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
continue 'simplified;
}
if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
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,
"this expression can be optimized out by applying boolean operations to the \
outer expression");
db.span_suggestion(e.span,
"it would look like the following",
suggest(self.cx, suggestion, &h2q.terminals));
});
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,
"this expression can be optimized out by applying boolean operations to the \
outer expression",
);
db.span_suggestion(
e.span,
"it would look like the following",
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.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
(stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops) ||
(stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
}
if improvement {
improvements.push(suggestion);
}
}
if !improvements.is_empty() {
span_lint_and_then(self.cx,
NONMINIMAL_BOOL,
e.span,
"this boolean expression can be simplified",
|db| {
db.span_suggestions(e.span,
"try",
improvements.into_iter()
.map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
.collect());
});
span_lint_and_then(
self.cx,
NONMINIMAL_BOOL,
e.span,
"this boolean expression can be simplified",
|db| {
db.span_suggestions(
e.span,
"try",
improvements
.into_iter()
.map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
.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
@ -67,7 +68,7 @@ declare_lint! {
"`if`s that can be collapsed (e.g. `if x { if y { ... } }` and `else { if x { ... } }`)"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct CollapsibleIf;
impl LintPass for CollapsibleIf {

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,
IF_SAME_THEN_ELSE,
j.span,
"this `if` has identical blocks",
i.span,
"same as this");
span_note_and_lint(
cx,
IF_SAME_THEN_ELSE,
j.span,
"this `if` has identical blocks",
i.span,
"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,
IFS_SAME_COND,
j.span,
"this `if` has the same condition as a previous if",
i.span,
"same as this");
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",
);
}
}
@ -185,40 +189,48 @@ 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,
MATCH_SAME_ARMS,
j.body.span,
"this `match` has identical arm bodies",
|db| {
db.span_note(i.body.span, "same as this");
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
// user should know about `|` if they are already using it…
// 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 {
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
if i.pats.len() == 1 && j.pats.len() == 1 {
let lhs = snippet(cx, i.pats[0].span, "<pat1>");
let rhs = snippet(cx, j.pats[0].span, "<pat2>");
if let PatKind::Wild = j.pats[0].node {
// 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));
} else {
db.span_note(i.body.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
if let PatKind::Wild = j.pats[0].node {
// 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),
);
} 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

@ -8,14 +8,15 @@ use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
use syntax::ast::{Attribute, NodeId};
use syntax::codemap::Span;
use utils::{in_macro, LimitStack, span_help_and_lint, paths, match_type};
use utils::{in_macro, LimitStack, span_help_and_lint, paths, match_type, is_allowed};
/// **What it does:** Checks for methods with high cyclomatic complexity.
///
/// **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
@ -72,7 +79,16 @@ impl CyclomaticComplexity {
};
if cc + divergence < match_arms + short_circuits {
report_cc_bug(cx, cc, match_arms, divergence, short_circuits, ret_adjust, span);
report_cc_bug(
cx,
cc,
match_arms,
divergence,
short_circuits,
ret_adjust,
span,
body.id().node_id,
);
} else {
let mut rust_cc = cc + divergence - match_arms - short_circuits;
// prevent degenerate cases where unreachable code contains `return` statements
@ -80,11 +96,13 @@ impl CyclomaticComplexity {
rust_cc -= ret_adjust;
}
if rust_cc > self.limit.limit() {
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");
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",
);
}
}
}
@ -98,7 +116,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 +125,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",
);
}
}
@ -162,29 +188,37 @@ 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,
"Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \
#[cfg(feature = "debugging")]
#[allow(too_many_arguments)]
fn report_cc_bug(_: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span, _: NodeId) {
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);
cc,
narms,
div,
shorts,
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 \
#[cfg(not(feature = "debugging"))]
#[allow(too_many_arguments)]
fn report_cc_bug(cx: &LateContext, cc: u64, narms: u64, div: u64, shorts: u64, returns: u64, span: Span, id: NodeId) {
if !is_allowed(cx, CYCLOMATIC_COMPLEXITY, id) {
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.",
cc,
narms,
div,
shorts,
returns));
cc,
narms,
div,
shorts,
returns
),
);
}
}

View file

@ -89,16 +89,14 @@ 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),
let Some(peq_trait_def_id) = cx.tcx.lang_items.eq_trait()
], {
let peq_trait_def = cx.tcx.trait_def(peq_trait_def_id);
// Look for the PartialEq implementations for `ty`
peq_trait_def.for_each_relevant_impl(cx.tcx, ty, |impl_id| {
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = is_automatically_derived(&cx.tcx.get_attrs(impl_id));
if peq_is_automatically_derived == hash_is_automatically_derived {

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;
@ -239,7 +263,7 @@ fn check_word(cx: &EarlyContext, word: &str, span: Span) {
};
s.chars().all(char::is_alphanumeric) && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
}
fn has_underscore(s: &str) -> bool {
@ -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,
DOC_MARKDOWN,
span,
&format!("you should put `{}` between ticks in the documentation", word));
span_lint(
cx,
DOC_MARKDOWN,
span,
&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.
@ -21,7 +22,7 @@ declare_lint! {
"enum with no variants"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct EmptyEnum;
impl LintPass for EmptyEnum {
@ -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

@ -30,7 +30,7 @@ declare_lint! {
"use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct HashMapLint;
impl LintPass for HashMapLint {
@ -48,11 +48,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapLint {
// we can give a better error message
let sole_expr = {
else_block.is_none() &&
if let ExprBlock(ref then_block) = then_block.node {
(then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
} else {
true
}
if let ExprBlock(ref then_block) = then_block.node {
(then_block.expr.is_some() as usize) + then_block.stmts.len() == 1
} else {
true
}
};
let mut visitor = InsertVisitor {
@ -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,
ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span,
"Clike enum variant discriminant is not portable to 32-bit targets");
span_lint(
cx,
ENUM_CLIKE_UNPORTABLE_VARIANT,
var.span,
"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,
lint,
span,
&format!("All variants have the same {}fix: `{}`", what, value),
&format!("remove the {}fixes and use full paths to \
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 \
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,
MODULE_INCEPTION,
item.span,
"module has the same name as its containing module");
span_lint(
cx,
MODULE_INCEPTION,
item.span,
"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.
@ -40,7 +41,7 @@ declare_lint! {
"taking a reference to satisfy the type constraints on `==`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct EqOp;
impl LintPass for EqOp {
@ -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,
EQ_OP,
e.span,
&format!("equal expressions as operands to `{}`", op.node.as_str()));
span_lint(
cx,
EQ_OP,
e.span,
&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,
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,
"use the values directly".to_string(),
vec![(left.span, lsnip), (right.span, rsnip)]);
})
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,
"use the values directly".to_string(),
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,
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);
})
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,
BOXED_LOCAL,
cx.tcx.hir.span(node),
"local variable doesn't need to be boxed here");
span_lint(
cx,
BOXED_LOCAL,
cx.tcx.hir.span(node),
"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,13 +92,11 @@ 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) {
db.span_suggestion(expr.span, "remove closure as shown", snippet);
});
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
@ -47,7 +49,7 @@ declare_lint! {
"whether an expression contains a diverging sub expression"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct EvalOrderDependence;
impl LintPass for EvalOrderDependence {
@ -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.
///
@ -67,12 +69,16 @@ declare_lint! {
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
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,
SUSPICIOUS_ASSIGNMENT_FORMATTING,
eqop_span,
&format!("this looks like you are trying to use `.. {op}= ..`, but you \
span_note_and_lint(
cx,
SUSPICIOUS_ASSIGNMENT_FORMATTING,
eqop_span,
&format!(
"this looks like you are trying to use `.. {op}= ..`, but you \
really are doing `.. = ({op} ..)`",
op = op),
eqop_span,
&format!("to remove this lint, use either `{op}=` or `= {op}`", op = op));
op = op
),
eqop_span,
&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,
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`");
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`",
);
}
}
}
@ -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,
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");
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",
);
}
}
}
@ -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,
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`");
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`",
);
}
}
}

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()`).
@ -50,7 +52,7 @@ declare_lint! {
"public functions dereferencing raw pointer arguments but not marked `unsafe`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Functions {
threshold: u64,
}
@ -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,
TOO_MANY_ARGUMENTS,
span,
&format!("this function has too many arguments ({}/{})", args, self.threshold));
span_lint(
cx,
TOO_MANY_ARGUMENTS,
span,
&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,
NOT_UNSAFE_PTR_ARG_DEREF,
ptr.span,
"this public function dereferences a raw pointer but is not marked `unsafe`");
span_lint(
self.cx,
NOT_UNSAFE_PTR_ARG_DEREF,
ptr.span,
"this public function dereferences a raw pointer but is not marked `unsafe`",
);
}
}
}

View file

@ -22,7 +22,7 @@ declare_lint! {
"using identity operations, e.g. `x + 0` or `y / 1`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct IdentityOp;
impl LintPass for IdentityOp {
@ -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,
IDENTITY_OP,
span,
&format!("the operation is ineffective. Consider reducing it to `{}`",
snippet(cx, arg, "..")));
}
{
span_lint(
cx,
IDENTITY_OP,
span,
&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,
IF_NOT_ELSE,
item.span,
"Unnecessary boolean `not` operation",
"remove the `!` and swap the blocks of the if/else");
span_help_and_lint(
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary boolean `not` operation",
"remove the `!` and swap the blocks of the if/else",
);
},
ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => {
span_help_and_lint(cx,
IF_NOT_ELSE,
item.span,
"Unnecessary `!=` operation",
"change to `==` and swap the blocks of the if/else");
span_help_and_lint(
cx,
IF_NOT_ELSE,
item.span,
"Unnecessary `!=` operation",
"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,
ITEMS_AFTER_STATEMENTS,
it.span,
"adding items after statements is confusing, since items exist from the \
start of the scope");
span_lint(
cx,
ITEMS_AFTER_STATEMENTS,
it.span,
"adding items after statements is confusing, since items exist from the \
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.
@ -25,7 +27,7 @@ declare_lint! {
"large size difference between variants on an enum"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct LargeEnumVariant {
maximum_size_difference_allowed: u64,
}
@ -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,28 +82,34 @@ 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,
LARGE_ENUM_VARIANT,
def.variants[i].span,
"large size difference between variants",
|db| {
if variant.fields.len() == 1 {
let span = match def.variants[i].node.data {
VariantData::Struct(ref fields, _) |
VariantData::Tuple(ref fields, _) => fields[0].ty.span,
VariantData::Unit(_) => unreachable!(),
};
if let Some(snip) = snippet_opt(cx, span) {
db.span_suggestion(span,
"consider boxing the large fields to reduce the total size of the \
span_lint_and_then(
cx,
LARGE_ENUM_VARIANT,
def.variants[i].span,
"large size difference between variants",
|db| {
if variant.fields.len() == 1 {
let span = match def.variants[i].node.data {
VariantData::Struct(ref fields, _) |
VariantData::Tuple(ref fields, _) => fields[0].ty.span,
VariantData::Unit(_) => unreachable!(),
};
if let Some(snip) = snippet_opt(cx, span) {
db.span_suggestion(
span,
"consider boxing the large fields to reduce the total size of the \
enum",
format!("Box<{}>", snip));
return;
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

@ -10,9 +10,10 @@ use utils::{get_item_name, in_macro, snippet, span_lint, span_lint_and_sugg, wal
/// just to compare to zero, and suggests using `.is_empty()` where applicable.
///
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
/// than calculating their length. So it is good to get into the habit of using
/// `.is_empty()`, and having it is cheap. Besides, it makes the intent clearer
/// than a comparison.
/// than calculating their length. Notably, for slices, getting the length
/// requires a subtraction whereas `.is_empty()` is just a comparison. So it is
/// good to get into the habit of using `.is_empty()`, and having it is cheap.
/// Besides, it makes the intent clearer than a manual comparison.
///
/// **Known problems:** None.
///
@ -50,7 +51,7 @@ declare_lint! {
"traits or impls with a public `len` method but no corresponding `is_empty` method"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct LenZero;
impl LintPass for LenZero {
@ -90,24 +91,26 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItemRef]) {
fn is_named_self(cx: &LateContext, item: &TraitItemRef, name: &str) -> bool {
item.name == name &&
if let AssociatedItemKind::Method { has_self } = item.kind {
has_self &&
{
let did = cx.tcx.hir.local_def_id(item.id.node_id);
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
if let AssociatedItemKind::Method { has_self } = item.kind {
has_self &&
{
let did = cx.tcx.hir.local_def_id(item.id.node_id);
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
}
} else {
false
}
} else {
false
}
}
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,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("trait `{}` has a `len` method but no `is_empty` method", item.name));
span_lint(
cx,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("trait `{}` has a `len` method but no `is_empty` method", item.name),
);
}
}
}
@ -116,15 +119,15 @@ fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItemRef]
fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItemRef]) {
fn is_named_self(cx: &LateContext, item: &ImplItemRef, name: &str) -> bool {
item.name == name &&
if let AssociatedItemKind::Method { has_self } = item.kind {
has_self &&
{
let did = cx.tcx.hir.local_def_id(item.id.node_id);
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
if let AssociatedItemKind::Method { has_self } = item.kind {
has_self &&
{
let did = cx.tcx.hir.local_def_id(item.id.node_id);
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
}
} else {
false
}
} else {
false
}
}
let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
@ -142,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,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("item `{}` has a public `len` method but {} `is_empty` method", ty, is_empty));
span_lint(
cx,
LEN_WITHOUT_IS_EMPTY,
item.span,
&format!("item `{}` has a public `len` method but {} `is_empty` method", ty, is_empty),
);
}
}
}
@ -169,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,
LEN_ZERO,
span,
"length comparison to zero",
"using `is_empty` is more concise",
format!("{}{}.is_empty()", op, snippet(cx, args[0].span, "_")));
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, "_")),
);
}
}
}
@ -198,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));
@ -211,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

@ -49,7 +49,7 @@ declare_lint! {
"unidiomatic `let mut` declaration followed by initialization in `if`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct LetIfSeq;
impl LintPass for LetIfSeq {
@ -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

@ -48,7 +48,7 @@ declare_lint! {
"unused lifetimes in function definitions"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct LifetimePass;
impl LintPass for LifetimePass {
@ -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,
NEEDLESS_LIFETIMES,
span,
"explicit lifetimes given in parameter types where they could be elided");
span_lint(
cx,
NEEDLESS_LIFETIMES,
span,
"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) {
let unnamed_and_static = input_lts.iter().all(|lt| {
*lt == RefLt::Unnamed || *lt == RefLt::Static
});
if unnamed_and_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,
UNREADABLE_LITERAL,
*span,
"long literal lacking separators",
&format!("consider: {}", grouping_hint))
span_help_and_lint(
cx,
UNREADABLE_LITERAL,
*span,
"long literal lacking separators",
&format!("consider: {}", grouping_hint),
)
},
WarningType::LargeDigitGroups => {
span_help_and_lint(cx,
LARGE_DIGIT_GROUPS,
*span,
"digit groups should be smaller",
&format!("consider: {}", grouping_hint))
span_help_and_lint(
cx,
LARGE_DIGIT_GROUPS,
*span,
"digit groups should be smaller",
&format!("consider: {}", grouping_hint),
)
},
WarningType::InconsistentDigitGrouping => {
span_help_and_lint(cx,
INCONSISTENT_DIGIT_GROUPING,
*span,
"digits grouped inconsistently by underscores",
&format!("consider: {}", grouping_hint))
span_help_and_lint(
cx,
INCONSISTENT_DIGIT_GROUPING,
*span,
"digits grouped inconsistently by underscores",
&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,20 +313,22 @@ pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(NEEDLESS_RANGE_LOOP,
EXPLICIT_ITER_LOOP,
EXPLICIT_INTO_ITER_LOOP,
ITER_NEXT_LOOP,
FOR_LOOP_OVER_RESULT,
FOR_LOOP_OVER_OPTION,
WHILE_LET_LOOP,
UNUSED_COLLECT,
REVERSE_RANGE_LOOP,
EXPLICIT_COUNTER_LOOP,
EMPTY_LOOP,
WHILE_LET_ON_ITERATOR,
FOR_KV_MAP,
NEVER_LOOP)
lint_array!(
NEEDLESS_RANGE_LOOP,
EXPLICIT_ITER_LOOP,
EXPLICIT_INTO_ITER_LOOP,
ITER_NEXT_LOOP,
FOR_LOOP_OVER_RESULT,
FOR_LOOP_OVER_OPTION,
WHILE_LET_LOOP,
UNUSED_COLLECT,
REVERSE_RANGE_LOOP,
EXPLICIT_COUNTER_LOOP,
EMPTY_LOOP,
WHILE_LET_ON_ITERATOR,
FOR_KV_MAP,
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,
EMPTY_LOOP,
expr.span,
"empty `loop {}` detected. You may want to either use `panic!()` or add \
`std::thread::sleep(..);` to the loop body.");
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.",
);
}
// extract the expression from the first statement (if any) in a block
@ -366,8 +374,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
MatchSource::Normal |
MatchSource::IfLetDesugar { .. } => {
if arms.len() == 2 && arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
is_break_expr(&arms[1].body) {
arms[1].pats.len() == 1 && arms[1].guard.is_none() &&
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,
WHILE_LET_LOOP,
expr.span,
"this loop could be written as a `while let` loop",
"try",
format!("while let {} = {} {{ .. }}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, matchexpr.span, "..")));
span_lint_and_sugg(
cx,
WHILE_LET_LOOP,
expr.span,
"this loop could be written as a `while let` loop",
"try",
format!(
"while let {} = {} {{ .. }}",
snippet(cx, arms[0].pats[0].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]) {
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])
{
let iterator = snippet(cx, method_args[0].span, "_");
let loop_var = snippet(cx, pat_args[0].span, "_");
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));
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),
);
}
}
}
@ -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,
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");
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",
);
}
}
}
@ -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,
REVERSE_RANGE_LOOP,
expr.span,
"this range is empty so this for loop will never run");
span_lint(
cx,
REVERSE_RANGE_LOOP,
expr.span,
"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,
EXPLICIT_ITER_LOOP,
arg.span,
"it is more idiomatic to loop over references to containers instead of using explicit \
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))
"to write this more concisely, try",
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,
EXPLICIT_INTO_ITER_LOOP,
arg.span,
"it is more idiomatic to loop over containers instead of using explicit \
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());
"to write this more concisely, try",
object.to_string(),
);
}
} else if method_name == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
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");
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",
);
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,
FOR_LOOP_OVER_OPTION,
arg.span,
&format!("for loop over `{0}`, which is an `Option`. This is more readably written as an \
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 \
`if let` statement.",
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, "_")
),
&format!(
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
snippet(cx, pat.span, "_"),
snippet(cx, arg.span, "_")
),
);
} else if match_type(cx, ty, &paths::RESULT) {
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 \
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 \
`if let` statement.",
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, "_")
),
&format!(
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
snippet(cx, pat.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,
EXPLICIT_COUNTER_LOOP,
expr.span,
&format!("the variable `{0}` is used as a loop counter. Consider using `for ({0}, \
span_lint(
cx,
EXPLICIT_COUNTER_LOOP,
expr.span,
&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, "_")));
name,
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,17 +1426,18 @@ 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
};
id: id,
iterator: iter_name,
nesting: Unknown,
};
walk_block(&mut block_visitor, block);
if block_visitor.nesting == RuledOut {
return false;
@ -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) {
self.nesting = RuledOut;
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,
MAP_CLONE,
expr.span,
&format!("you seem to be using .map() to clone the contents of an \
span_help_and_lint(
cx,
MAP_CLONE,
expr.span,
&format!(
"you seem to be using .map() to clone the contents of an \
{}, consider using `.cloned()`",
type_name),
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
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 {
name: id,
parameters: PathParameters::none(),
}];
let arg_segment = [
PathSegment {
name: id,
parameters: PathParameters::none(),
},
];
!path.is_global() && path.segments[..] == arg_segment
},
_ => false,

View file

@ -12,7 +12,7 @@ use syntax::ast::NodeId;
use syntax::codemap::Span;
use utils::paths;
use utils::{match_type, snippet, span_note_and_lint, span_lint_and_then, span_lint_and_sugg, in_external_macro,
expr_block, walk_ptrs_ty, is_expn_of, remove_blocks};
expr_block, walk_ptrs_ty, is_expn_of, remove_blocks, is_allowed};
use utils::sugg::Sugg;
/// **What it does:** Checks for matches with a single arm where an `if let`
@ -150,12 +150,14 @@ pub struct MatchPass;
impl LintPass for MatchPass {
fn get_lints(&self) -> LintArray {
lint_array!(SINGLE_MATCH,
MATCH_REF_PATS,
MATCH_BOOL,
SINGLE_MATCH_ELSE,
MATCH_OVERLAPPING_ARM,
MATCH_WILD_ERR_ARM)
lint_array!(
SINGLE_MATCH,
MATCH_REF_PATS,
MATCH_BOOL,
SINGLE_MATCH_ELSE,
MATCH_OVERLAPPING_ARM,
MATCH_WILD_ERR_ARM
)
}
}
@ -192,7 +194,7 @@ fn check_single_match(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
return;
};
let ty = cx.tables.expr_ty(ex);
if ty.sty != ty::TyBool || cx.current_level(MATCH_BOOL) == Allow {
if ty.sty != ty::TyBool || is_allowed(cx, MATCH_BOOL, ex.id) {
check_single_match_single_pattern(cx, ex, arms, expr, els);
check_single_match_opt_like(cx, ex, arms, expr, ty, els);
}
@ -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,
lint,
expr.span,
"you seem to be trying to use match for destructuring a single pattern. Consider using `if \
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 {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str));
"try this",
format!(
"if let {} = {} {}{}",
snippet(cx, arms[0].pats[0].span, ".."),
snippet(cx, ex.span, ".."),
expr_block(cx, &arms[0].body, None, ".."),
els_str
),
);
}
fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, 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"),
(&paths::COW, "Cow::Borrowed"),
(&paths::COW, "Cow::Owned"),
(&paths::COW, "Owned"),
(&paths::OPTION, "None"),
(&paths::RESULT, "Err"),
(&paths::RESULT, "Ok")];
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"),
];
let path = match arms[1].pats[0].node {
PatKind::TupleStruct(ref path, ref inner, _) => {
@ -258,52 +266,60 @@ 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,
MATCH_BOOL,
expr.span,
"you seem to be trying to match on a boolean expression",
move |db| {
if arms.len() == 2 && arms[0].pats.len() == 1 {
// no guards
let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
if let ExprLit(ref lit) = arm_bool.node {
match lit.node {
LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
_ => None,
span_lint_and_then(
cx,
MATCH_BOOL,
expr.span,
"you seem to be trying to match on a boolean expression",
move |db| {
if arms.len() == 2 && arms[0].pats.len() == 1 {
// no guards
let exprs = if let PatKind::Lit(ref arm_bool) = arms[0].pats[0].node {
if let ExprLit(ref lit) = arm_bool.node {
match lit.node {
LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
_ => None,
}
} else {
None
}
} else {
None
}
} else {
None
};
if let Some((true_expr, false_expr)) = exprs {
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => {
Some(format!("if {} {} else {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, ".."),
expr_block(cx, false_expr, None, "..")))
},
(false, true) => {
Some(format!("if {} {}", snippet(cx, ex.span, "b"), expr_block(cx, true_expr, None, "..")))
},
(true, false) => {
let test = Sugg::hir(cx, ex, "..");
Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
},
(true, true) => None,
};
if let Some(sugg) = sugg {
db.span_suggestion(expr.span, "consider using an if/else expression", sugg);
if let Some((true_expr, false_expr)) = exprs {
let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
(false, false) => {
Some(format!(
"if {} {} else {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, ".."),
expr_block(cx, false_expr, None, "..")
))
},
(false, true) => {
Some(format!(
"if {} {}",
snippet(cx, ex.span, "b"),
expr_block(cx, true_expr, None, "..")
))
},
(true, false) => {
let test = Sugg::hir(cx, ex, "..");
Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
},
(true, true) => None,
};
if let Some(sugg) = sugg {
db.span_suggestion(expr.span, "consider using an if/else expression", sugg);
}
}
}
}
});
},
);
}
}
@ -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,
MATCH_OVERLAPPING_ARM,
start.span,
"some ranges overlap",
end.span,
"overlaps with this");
span_note_and_lint(
cx,
MATCH_OVERLAPPING_ARM,
start.span,
"some ranges overlap",
end.span,
"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,
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,
"instead of prefixing all patterns with `&`, you can dereference the expression",
template);
});
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,
"instead of prefixing all patterns with `&`, you can dereference the expression",
template,
);
},
);
}
}
}
@ -399,13 +421,17 @@ 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 {
pats.iter()
} else {
[].iter()
}
.filter_map(|pat| {
if_let_chain! {[
if let Arm {
ref pats,
guard: None,
..
} = *arm
{
pats.iter()
} else {
[].iter()
}.filter_map(|pat| {
if_let_chain! {[
let PatKind::Range(ref lhs, ref rhs, ref range_end) = pat.node,
let Ok(lhs) = constcx.eval(lhs),
let Ok(rhs) = constcx.eval(rhs)
@ -417,15 +443,15 @@ fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &[Arm], id: NodeId) ->
return Some(SpannedRange { span: pat.span, node: (lhs, rhs) });
}}
if_let_chain! {[
if_let_chain! {[
let PatKind::Lit(ref value) = pat.node,
let Ok(value) = constcx.eval(value)
], {
return Some(SpannedRange { span: pat.span, node: (value.clone(), Bound::Included(value)) });
}}
None
})
None
})
})
.collect()
}
@ -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,29 +527,31 @@ declare_lint! {
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED,
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
WRONG_PUB_SELF_CONVENTION,
OK_EXPECT,
OPTION_MAP_UNWRAP_OR,
OPTION_MAP_UNWRAP_OR_ELSE,
OR_FUN_CALL,
CHARS_NEXT_CMP,
CLONE_ON_COPY,
CLONE_DOUBLE_REF,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SEARCH_IS_SOME,
TEMPORARY_CSTRING_AS_PTR,
FILTER_NEXT,
FILTER_MAP,
ITER_NTH,
ITER_SKIP_NEXT,
GET_UNWRAP,
STRING_EXTEND_CHARS,
ITER_CLONED_COLLECT)
lint_array!(
OPTION_UNWRAP_USED,
RESULT_UNWRAP_USED,
SHOULD_IMPLEMENT_TRAIT,
WRONG_SELF_CONVENTION,
WRONG_PUB_SELF_CONVENTION,
OK_EXPECT,
OPTION_MAP_UNWRAP_OR,
OPTION_MAP_UNWRAP_OR_ELSE,
OR_FUN_CALL,
CHARS_NEXT_CMP,
CLONE_ON_COPY,
CLONE_DOUBLE_REF,
NEW_RET_NO_SELF,
SINGLE_CHAR_PATTERN,
SEARCH_IS_SOME,
TEMPORARY_CSTRING_AS_PTR,
FILTER_NEXT,
FILTER_MAP,
ITER_NTH,
ITER_SKIP_NEXT,
GET_UNWRAP,
STRING_EXTEND_CHARS,
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) {
default_trait_id
} else {
return false;
};
let default_trait_id =
if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
default_trait_id
} else {
return false;
};
if implements_trait(cx, arg_ty, default_trait_id, &[]) {
span_lint_and_sugg(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, "_")));
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, "_")),
);
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::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::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
];
let self_ty = cx.tables.expr_ty(self_expr);
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,
OR_FUN_CALL,
span,
&format!("use of `{}` followed by a function call", name),
"try this",
format!("{}.{}_{}({})", snippet(cx, self_expr.span, "_"), name, suffix, sugg));
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),
);
}
if args.len() == 2 {
@ -818,32 +830,30 @@ 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,
CLONE_DOUBLE_REF,
expr.span,
"using `clone` on a double-reference; \
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| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
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) {
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));
}
});
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,
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
"try this",
format!("{}.push_str({}{})",
snippet(cx, args[0].span, "_"),
ref_str,
snippet(cx, target.span, "_")));
span_lint_and_sugg(
cx,
STRING_EXTEND_CHARS,
expr.span,
"calling `.extend(_.chars())`",
"try this",
format!(
"{}.push_str({}{})",
snippet(cx, args[0].span, "_"),
ref_str,
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,
ITER_CLONED_COLLECT,
expr.span,
"called `cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
more readable");
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",
);
}
}
@ -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,
ITER_NTH,
expr.span,
&format!("called `.iter{0}().nth()` on a {1}. Calling `.get{0}()` is both faster and more readable",
mut_str,
caller_type));
span_lint(
cx,
ITER_NTH,
expr.span,
&format!(
"called `.iter{0}().nth()` on a {1}. Calling `.get{0}()` is both faster and more readable",
mut_str,
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,
GET_UNWRAP,
expr.span,
&format!("called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
mut_str,
caller_type),
"try this",
format!("{}{}[{}]",
borrow_str,
snippet(cx, get_args[0].span, "_"),
snippet(cx, get_args[1].span, "_")));
span_lint_and_sugg(
cx,
GET_UNWRAP,
expr.span,
&format!(
"called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
mut_str,
caller_type
),
"try this",
format!(
"{}{}[{}]",
borrow_str,
snippet(cx, get_args[0].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,
ITER_SKIP_NEXT,
expr.span,
"called `skip(x).next()` on an iterator. This is more succinctly expressed by calling `nth(x)`");
span_lint(
cx,
ITER_SKIP_NEXT,
expr.span,
"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,
lint,
expr.span,
&format!("used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
span_lint(
cx,
lint,
expr.span,
&format!(
"used unwrap() on {} value. If you don't want to handle the {} case gracefully, consider \
using expect() to provide a better panic \
message",
kind,
none_value));
kind,
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,
OK_EXPECT,
expr.span,
"called `ok().expect()` on a Result value. You can call `expect` directly on the `Result`");
span_lint(
cx,
OK_EXPECT,
expr.span,
"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,
OPTION_MAP_UNWRAP_OR,
expr.span,
msg,
expr.span,
&format!("replace `map({0}).unwrap_or({1})` with `map_or({1}, {0})`",
map_snippet,
unwrap_snippet));
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})`",
map_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,
OPTION_MAP_UNWRAP_OR_ELSE,
expr.span,
msg,
expr.span,
&format!("replace `map({0}).unwrap_or_else({1})` with `map_or_else({1}, {0})`",
map_snippet,
unwrap_snippet));
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})`",
map_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,
FILTER_NEXT,
expr.span,
msg,
expr.span,
&format!("replace `filter({0}).next()` with `find({0})`", filter_snippet));
span_note_and_lint(
cx,
FILTER_NEXT,
expr.span,
msg,
expr.span,
&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,
SEARCH_IS_SOME,
expr.span,
&msg,
expr.span,
&format!("replace `{0}({1}).is_some()` with `any({1})`", search_method, search_snippet));
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),
);
} 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,
SINGLE_CHAR_PATTERN,
arg.span,
"single-character string constant used as pattern",
|db| { db.span_suggestion(expr.span, "try using a char instead", hint); });
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); },
);
}
}
}
@ -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,18 +1459,22 @@ 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 {
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))
} else {
false
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))
} else {
false
}
})
} 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,15 +205,17 @@ pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array!(TOPLEVEL_REF_ARG,
CMP_NAN,
FLOAT_CMP,
CMP_OWNED,
MODULO_ONE,
REDUNDANT_PATTERN,
USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT,
ZERO_PTR)
lint_array!(
TOPLEVEL_REF_ARG,
CMP_NAN,
FLOAT_CMP,
CMP_OWNED,
MODULO_ONE,
REDUNDANT_PATTERN,
USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT,
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,
TOPLEVEL_REF_ARG,
arg.pat.span,
"`ref` directly on a function argument is ignored. Consider using a reference type \
instead.");
span_lint(
cx,
TOPLEVEL_REF_ARG,
arg.pat.span,
"`ref` directly on a function argument is ignored. Consider using a reference type \
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,
"consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs));
db.span_suggestion(
expr.span,
"consider comparing them within some error",
format!("({}).abs() < error", lhs - rhs),
);
db.span_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
});
} else if op == BiRem && is_integer_literal(right, 1) {
@ -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,
USED_UNDERSCORE_BINDING,
expr.span,
&format!("used binding `{}` which is prefixed with an underscore. A leading \
span_lint(
cx,
USED_UNDERSCORE_BINDING,
expr.span,
&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,
REDUNDANT_PATTERN,
pat.span,
&format!("the `{} @ _` pattern can be written as just `{}`", ident.node, ident.node));
span_lint(
cx,
REDUNDANT_PATTERN,
pat.span,
&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,
CMP_NAN,
expr.span,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead");
span_lint(
cx,
CMP_NAN,
expr.span,
"doomed comparison with NAN, use `std::{f32,f64}::is_nan()` instead",
);
});
}
}
@ -421,7 +444,7 @@ fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
};
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
FloatTy::F64 => {
let zero = ConstFloat {
@ -440,7 +463,7 @@ fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
};
val.try_cmp(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
},
}
} else {
@ -490,37 +513,43 @@ 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,
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
// 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);
let parent_impl = cx.tcx.hir.get_parent(parent_fn);
if parent_impl != CRATE_NODE_ID {
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
// recursion
db.span_label(expr.span, "try calling implementing the comparison without allocating");
return;
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
// 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);
let parent_impl = cx.tcx.hir.get_parent(parent_fn);
if parent_impl != CRATE_NODE_ID {
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
// recursion
db.span_label(expr.span, "try calling implementing the comparison without allocating");
return;
}
}
}
}
}
db.span_suggestion(expr.span, "try", snip.to_string());
});
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,
DUPLICATE_UNDERSCORE_ARGUMENT,
REDUNDANT_CLOSURE_CALL,
DOUBLE_NEG,
MIXED_CASE_HEX_LITERALS,
UNSEPARATED_LITERAL_SUFFIX,
ZERO_PREFIXED_LITERAL,
BUILTIN_TYPE_SHADOW)
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
)
}
}
@ -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,
BUILTIN_TYPE_SHADOW,
ty.span,
&format!("This generic shadows the built-in type `{}`", name));
span_lint(
cx,
BUILTIN_TYPE_SHADOW,
ty.span,
&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,
UNNEEDED_FIELD_PATTERN,
pat.span,
"All the struct fields are matched to a wildcard pattern, consider using `..`.",
&format!("Try with `{} {{ .. }}` instead", type_name));
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),
);
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,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` instead");
span_lint(
cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` instead",
);
} else {
span_help_and_lint(cx,
UNNEEDED_FIELD_PATTERN,
field.span,
"You matched a field with a wildcard pattern. Consider using `..` \
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,
DUPLICATE_UNDERSCORE_ARGUMENT,
*correspondence,
&format!("`{}` already exists, having another argument having almost the same \
span_lint(
cx,
DUPLICATE_UNDERSCORE_ARGUMENT,
*correspondence,
&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,
DOUBLE_NEG,
expr.span,
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
span_lint(
cx,
DOUBLE_NEG,
expr.span,
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
);
}
},
ExprKind::Lit(ref lit) => self.check_lit(cx, lit),

View file

@ -11,12 +11,18 @@
// 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 +34,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 +67,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 +88,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,
sp,
&format!("missing documentation for {}", desc));
cx.span_lint(
MISSING_DOCS_IN_PRIVATE_ITEMS,
sp,
&format!("missing documentation for {}", desc),
);
}
}
}
@ -95,13 +110,13 @@ impl LintPass for MissingDoc {
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
fn enter_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, attrs: &'tcx [ast::Attribute]) {
let doc_hidden = self.doc_hidden() ||
attrs.iter().any(|attr| {
attr.check_name("doc") &&
match attr.meta_item_list() {
None => false,
Some(l) => attr::list_contains_name(&l[..], "hidden"),
}
});
attrs.iter().any(|attr| {
attr.check_name("doc") &&
match attr.meta_item_list() {
None => false,
Some(l) => attr::list_contains_name(&l[..], "hidden"),
}
});
self.doc_hidden_stack.push(doc_hidden);
}

View file

@ -22,7 +22,7 @@ declare_lint! {
"usage of double-mut refs, e.g. `&mut &mut ...`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct MutMut;
impl LintPass for MutMut {
@ -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,
MUT_MUT,
expr.span,
"generally you want to avoid `&mut &mut _` if possible");
span_lint(
self.cx,
MUT_MUT,
expr.span,
"generally you want to avoid `&mut &mut _` if possible",
);
} else if let ty::TyRef(_, ty::TypeAndMut { mutbl: hir::MutMutable, .. }) = self.cx.tables.expr_ty(e).sty {
span_lint(self.cx,
MUT_MUT,
expr.span,
"this expression mutably borrows a mutable reference. Consider reborrowing");
span_lint(
self.cx,
MUT_MUT,
expr.span,
"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,
MUT_MUT,
ty.span,
"generally you want to avoid `&mut &mut _` if possible");
span_lint(
self.cx,
MUT_MUT,
ty.span,
"generally you want to avoid `&mut &mut _` if possible",
);
}
}

View file

@ -24,7 +24,7 @@ declare_lint! {
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct UnnecessaryMutPassed;
impl LintPass for UnnecessaryMutPassed {
@ -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,
arguments,
cx.tables.expr_ty(fn_expr),
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
check_arguments(
cx,
arguments,
cx.tables.expr_ty(fn_expr),
&print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)),
);
}
},
ExprMethodCall(ref path, _, ref arguments) => {
@ -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,
UNNECESSARY_MUT_PASSED,
argument.span,
&format!("The function/method `{}` doesn't need a mutable reference", name));
span_lint(
cx,
UNNECESSARY_MUT_PASSED,
argument.span,
&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.
@ -47,7 +48,7 @@ declare_lint! {
"comparing a variable to a boolean, e.g. `if x == true`"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NeedlessBool;
impl LintPass for NeedlessBool {
@ -70,28 +71,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
snip.to_string()
};
span_lint_and_sugg(cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression returns a bool literal",
"you can reduce it to",
hint);
span_lint_and_sugg(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression returns a bool literal",
"you can reduce it to",
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,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return true");
span_lint(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return true",
);
},
(RetBool(false), RetBool(false)) |
(Bool(false), Bool(false)) => {
span_lint(cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return false");
span_lint(
cx,
NEEDLESS_BOOL,
e.span,
"this if-then-else expression will always return false",
);
},
(RetBool(true), RetBool(false)) => reduce(true, false),
(Bool(true), Bool(false)) => reduce(false, false),
@ -106,7 +113,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
}
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct BoolComparison;
impl LintPass for BoolComparison {
@ -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,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint);
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint,
);
},
(Other, Bool(true)) => {
let hint = snippet(cx, left_side.span, "..").into_owned();
span_lint_and_sugg(cx,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint);
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
e.span,
"equality checks against true are unnecessary",
"try simplifying it as shown",
hint,
);
},
(Bool(false), Other) => {
let hint = Sugg::hir(cx, right_side, "..");
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());
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(),
);
},
(Other, Bool(false)) => {
let hint = Sugg::hir(cx, left_side, "..");
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());
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(),
);
},
_ => (),
}

View file

@ -26,7 +26,7 @@ declare_lint! {
"taking a reference that is going to be automatically dereferenced"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NeedlessBorrow;
impl LintPass for NeedlessBorrow {

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

@ -99,7 +99,7 @@ declare_lint! {
"`continue` statements that can be replaced by a rearrangement of code"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NeedlessContinue;
impl LintPass for NeedlessContinue {
@ -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,
NEEDLESS_UPDATE,
base.span,
"struct update has no effect, all the fields in the struct have already been specified");
span_lint(
cx,
NEEDLESS_UPDATE,
base.span,
"struct update has no effect, all the fields in the struct have already been specified",
);
}
}
}

View file

@ -80,7 +80,7 @@ declare_lint! {
"`fn new() -> Self` without `#[derive]`able `Default` implementation"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NewWithoutDefault;
impl LintPass for NewWithoutDefault {
@ -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

@ -62,10 +62,10 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
Expr_::ExprBox(ref inner) => has_no_effect(cx, inner),
Expr_::ExprStruct(_, ref fields, ref base) => {
fields.iter().all(|field| has_no_effect(cx, &field.expr)) &&
match *base {
Some(ref base) => has_no_effect(cx, base),
None => true,
}
match *base {
Some(ref base) => has_no_effect(cx, base),
None => true,
}
},
Expr_::ExprCall(ref callee, ref args) => {
if let Expr_::ExprPath(ref qpath) = callee.node {
@ -83,11 +83,11 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
},
Expr_::ExprBlock(ref block) => {
block.stmts.is_empty() &&
if let Some(ref expr) = block.expr {
has_no_effect(cx, expr)
} else {
false
}
if let Some(ref expr) = block.expr {
has_no_effect(cx, expr)
} else {
false
}
},
_ => false,
}
@ -120,12 +120,14 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
return;
}
}
span_lint_and_sugg(cx,
UNNECESSARY_OPERATION,
stmt.span,
"statement can be reduced",
"replace it with",
snippet);
span_lint_and_sugg(
cx,
UNNECESSARY_OPERATION,
stmt.span,
"statement can be reduced",
"replace it with",
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

@ -104,20 +104,9 @@ fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
}
fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
if list.iter().any(|&name| interned_name == name) {
return true;
}
for name in list {
// name_*
if interned_name.chars().zip(name.chars()).all(|(l, r)| l == r) {
return true;
}
// *_name
if interned_name.chars().rev().zip(name.chars().rev()).all(|(l, r)| l == r) {
return true;
}
}
false
list.iter().any(|&name| {
interned_name.starts_with(name) || interned_name.ends_with(name)
})
}
impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
@ -128,10 +117,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,
MANY_SINGLE_CHAR_NAMES,
span,
&format!("{}th binding whose name is just one char", self.0.single_char_names.len()));
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()),
);
}
}
fn check_name(&mut self, span: Span, name: Name) {
@ -166,22 +157,39 @@ 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 eq_or_numeric = |a: char, b: char| a == b || a.is_numeric() && b.is_numeric();
let first_i = interned_chars.next().expect(
"we know we have at least one char",
);
let first_e = existing_chars.next().expect(
"we know we have at least one char",
);
let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();
if eq_or_numeric(first_i, first_e) {
let last_i = interned_chars.next_back().expect("we know we have at least two chars");
let last_e = existing_chars.next_back().expect("we know we have at least two chars");
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 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",
);
if eq_or_numeric((last_i, last_e)) {
if interned_chars
.zip(existing_chars)
.filter(|&ie| !eq_or_numeric(ie))
.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");
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)) {
let second_last_i = interned_chars.next_back().expect(
"we know we have at least three chars",
);
let second_last_e = existing_chars.next_back().expect(
"we know we have at least three chars",
);
if !eq_or_numeric((second_last_i, second_last_e)) || second_last_i == '_' ||
!interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity foo_x, foo_y
// or too many chars differ (foo_x, boo_y) or (foox, booy)
continue;
@ -189,10 +197,15 @@ 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");
if !eq_or_numeric(second_i, second_e) || second_i == '_' ||
!interned_chars.zip(existing_chars).all(|(i, e)| eq_or_numeric(i, e)) {
let second_i = interned_chars.next().expect(
"we know we have at least two chars",
);
let second_e = existing_chars.next().expect(
"we know we have at least two chars",
);
if !eq_or_numeric((second_i, second_e)) || second_i == '_' ||
!interned_chars.zip(existing_chars).all(eq_or_numeric)
{
// allowed similarity x_foo, y_foo
// or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
continue;
@ -200,20 +213,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,
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 \
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 \
underscore like: `{}_{}`",
&interned_name[..split],
&interned_name[split..]));
}
});
&interned_name[..split],
&interned_name[split..]
),
);
}
},
);
return;
}
self.0.names.push(ExistingName {
@ -241,7 +260,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

@ -23,7 +23,7 @@ declare_lint! {
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct NonSensical;
impl LintPass for NonSensical {
@ -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,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"create\" is called more than once");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"append\" is called more than once");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"truncate\" is called more than once");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"read\" is called more than once");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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,
NONSENSICAL_OPEN_OPTIONS,
span,
"the method \"write\" is called more than once");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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,
NONSENSICAL_OPEN_OPTIONS,
span,
"file opened with \"append\" and \"truncate\"");
span_lint(
cx,
NONSENSICAL_OPEN_OPTIONS,
span,
"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
@ -24,7 +26,7 @@ declare_lint! {
"operations where precedence may be unclear"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Precedence;
impl LintPass for Precedence {
@ -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,
PRECEDENCE,
expr.span,
"operator precedence can trip the unwary",
"consider parenthesizing your expression",
sugg);
span_lint_and_sugg(
cx,
PRECEDENCE,
expr.span,
"operator precedence can trip the unwary",
"consider parenthesizing your expression",
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!("({}) {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
let sugg = format!(
"({}) {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, "..")
);
span_sugg(expr, sugg);
},
(true, false) => {
let sugg = format!("({}) {} {}",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
let sugg = format!(
"({}) {} {}",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, "..")
);
span_sugg(expr, sugg);
},
(false, true) => {
let sugg = format!("{} {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
snippet(cx, right.span, ".."));
let sugg = format!(
"{} {} ({})",
snippet(cx, left.span, ".."),
op.to_string(),
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,
PRECEDENCE,
expr.span,
"unary minus has lower precedence than method call",
"consider adding parentheses to clarify your intent",
format!("-({})", snippet(cx, rhs.span, "..")));
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, "..")),
);
},
_ => (),
}

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
@ -66,7 +75,7 @@ declare_lint! {
"fns that create mutable refs from immutable ref args"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct PointerPass;
impl LintPass for PointerPass {
@ -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,
CMP_NULL,
expr.span,
"Comparing with null is better expressed by the .is_null() method");
span_lint(
cx,
CMP_NULL,
expr.span,
"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,
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 `&[...]`");
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 `&[...]`",
);
} else if match_type(cx, ty, &paths::STRING) {
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`");
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`",
);
}
}
}
@ -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()`.
///
@ -37,7 +38,7 @@ declare_lint! {
"zipping iterator with a range when `enumerate()` would do"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct StepByZero;
impl LintPass for StepByZero {
@ -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,
ITERATOR_STEP_BY_ZERO,
expr.span,
"Iterator::step_by(0) will panic at runtime");
span_lint(
cx,
ITERATOR_STEP_BY_ZERO,
expr.span,
"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,
DEREF_ADDROF,
e.span,
"immediately dereferencing a reference",
"try this",
format!("{}", snippet(cx, addrof_target.span, "_")));
span_lint_and_sugg(
cx,
DEREF_ADDROF,
e.span,
"immediately dereferencing a reference",
"try this",
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,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl));
span_help_and_lint(
cx,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl),
);
}
},
Err(e) => {
span_lint(cx,
INVALID_REGEX,
str_span(expr.span, r, e.position()),
&format!("regex syntax error: {}", e.description()));
span_lint(
cx,
INVALID_REGEX,
str_span(expr.span, r, e.position()),
&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,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl));
span_help_and_lint(
cx,
TRIVIAL_REGEX,
expr.span,
"trivial regex",
&format!("consider using {}", repl),
);
}
},
Err(e) => {
span_lint(cx,
INVALID_REGEX,
expr.span,
&format!("regex syntax error on position {}: {}", e.position(), e.description()));
span_lint(
cx,
INVALID_REGEX,
expr.span,
&format!("regex syntax error on position {}: {}", e.position(), e.description()),
);
},
}
}

View file

@ -10,7 +10,8 @@ use utils::{span_note_and_lint, span_lint_and_then, snippet_opt, match_path_ast,
/// **Why is this bad?** Removing the `return` and semicolon will make the code
/// more rusty.
///
/// **Known problems:** None.
/// **Known problems:** If the computation returning the value borrows a local
/// variable, removing the `return` may run afoul of the borrow checker.
///
/// **Example:**
/// ```rust
@ -22,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.
@ -92,13 +94,11 @@ 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) {
db.span_suggestion(ret_span, "remove `return` as shown", snippet);
});
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);
}
});
}
// Check for "let x = EXPR; x"

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,
SERDE_API_MISUSE,
span,
"you should not implement `visit_string` without also implementing `visit_str`");
span_lint(
cx,
SERDE_API_MISUSE,
span,
"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,
SHADOW_SAME,
span,
&format!("`{}` is shadowed by itself in `{}`",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
|db| { db.span_note(prev_span, "previous binding is here"); });
span_lint_and_then(
cx,
SHADOW_SAME,
span,
&format!(
"`{}` is shadowed by itself in `{}`",
snippet(cx, pattern_span, "_"),
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,
SHADOW_REUSE,
pattern_span,
&format!("`{}` is shadowed by `{}` which reuses the original value",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
|db| {
db.span_note(expr.span, "initialization happens here");
db.span_note(prev_span, "previous binding is here");
});
span_lint_and_then(
cx,
SHADOW_REUSE,
pattern_span,
&format!(
"`{}` is shadowed by `{}` which reuses the original value",
snippet(cx, pattern_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,
SHADOW_UNRELATED,
pattern_span,
&format!("`{}` is shadowed by `{}`",
snippet(cx, pattern_span, "_"),
snippet(cx, expr.span, "..")),
|db| {
db.span_note(expr.span, "initialization happens here");
db.span_note(prev_span, "previous binding is here");
});
span_lint_and_then(
cx,
SHADOW_UNRELATED,
pattern_span,
&format!(
"`{}` is shadowed by `{}`",
snippet(cx, pattern_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

@ -2,7 +2,7 @@ use rustc::hir::*;
use rustc::lint::*;
use syntax::codemap::Spanned;
use utils::SpanlessEq;
use utils::{match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty, get_parent_expr};
use utils::{match_type, paths, span_lint, span_lint_and_sugg, walk_ptrs_ty, get_parent_expr, is_allowed};
/// **What it does:** Checks for string appends of the form `x = x + y` (without
/// `let`!).
@ -83,9 +83,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
if let ExprBinary(Spanned { node: BiAdd, .. }, ref left, _) = e.node {
if is_string(cx, left) {
if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
// the string_add_assign is allow, so no duplicates
} else {
if !is_allowed(cx, STRING_ADD_ASSIGN, e.id) {
let parent = get_parent_expr(cx, e);
if let Some(p) = parent {
if let ExprAssign(ref target, _) = p.node {
@ -96,18 +94,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringAdd {
}
}
}
span_lint(cx,
STRING_ADD,
e.span,
"you added something to a string. Consider using `String::push_str()` instead");
span_lint(
cx,
STRING_ADD,
e.span,
"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,
STRING_ADD_ASSIGN,
e.span,
"you assigned the result of adding something to this string. Consider using \
`String::push_str()` instead");
span_lint(
cx,
STRING_ADD_ASSIGN,
e.span,
"you assigned the result of adding something to this string. Consider using \
`String::push_str()` instead",
);
}
}
}
@ -121,7 +123,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 +153,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,
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""#)));
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""#)),
);
}
}
}

View file

@ -41,7 +41,7 @@ declare_lint! {
"`foo = bar; bar = foo` sequence"
}
#[derive(Copy,Clone)]
#[derive(Copy, Clone)]
pub struct Swap;
impl LintPass for Swap {

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,87 +96,101 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
match (&from_ty.sty, &to_ty.sty) {
_ if from_ty == to_ty => {
span_lint(cx,
USELESS_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to itself", from_ty))
span_lint(
cx,
USELESS_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to itself", from_ty),
)
},
(&ty::TyRef(_, rty), &ty::TyRawPtr(ptr_ty)) => {
span_lint_and_then(cx,
USELESS_TRANSMUTE,
e.span,
"transmute from a reference to a pointer",
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
let sugg = if ptr_ty == rty {
arg.as_ty(to_ty)
} else {
arg.as_ty(cx.tcx.mk_ptr(rty)).as_ty(to_ty)
};
span_lint_and_then(
cx,
USELESS_TRANSMUTE,
e.span,
"transmute from a reference to a pointer",
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
let sugg = if ptr_ty == rty {
arg.as_ty(to_ty)
} else {
arg.as_ty(cx.tcx.mk_ptr(rty)).as_ty(to_ty)
};
db.span_suggestion(e.span, "try", sugg.to_string());
})
db.span_suggestion(e.span, "try", sugg.to_string());
},
)
},
(&ty::TyInt(_), &ty::TyRawPtr(_)) |
(&ty::TyUint(_), &ty::TyRawPtr(_)) => {
span_lint_and_then(cx,
USELESS_TRANSMUTE,
e.span,
"transmute from an integer to a pointer",
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
db.span_suggestion(e.span,
"try",
arg.as_ty(&to_ty.to_string()).to_string());
})
span_lint_and_then(
cx,
USELESS_TRANSMUTE,
e.span,
"transmute from an integer to a pointer",
|db| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
},
)
},
(&ty::TyFloat(_), &ty::TyRef(..)) |
(&ty::TyFloat(_), &ty::TyRawPtr(_)) |
(&ty::TyChar, &ty::TyRef(..)) |
(&ty::TyChar, &ty::TyRawPtr(_)) => {
span_lint(cx,
WRONG_TRANSMUTE,
e.span,
&format!("transmute from a `{}` to a pointer", from_ty))
span_lint(
cx,
WRONG_TRANSMUTE,
e.span,
&format!("transmute from a `{}` to a pointer", from_ty),
)
},
(&ty::TyRawPtr(from_ptr), _) if from_ptr.ty == to_ty => {
span_lint(cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to the type that it points to (`{}`)",
from_ty,
to_ty))
span_lint(
cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!(
"transmute from a type (`{}`) to the type that it points to (`{}`)",
from_ty,
to_ty
),
)
},
(_, &ty::TyRawPtr(to_ptr)) if to_ptr.ty == from_ty => {
span_lint(cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)",
from_ty,
to_ty))
span_lint(
cx,
CROSSPOINTER_TRANSMUTE,
e.span,
&format!("transmute from a type (`{}`) to a pointer to that type (`{}`)", from_ty, to_ty),
)
},
(&ty::TyRawPtr(from_pty), &ty::TyRef(_, to_rty)) => {
span_lint_and_then(cx,
TRANSMUTE_PTR_TO_REF,
e.span,
&format!("transmute from a pointer type (`{}`) to a reference type \
span_lint_and_then(
cx,
TRANSMUTE_PTR_TO_REF,
e.span,
&format!(
"transmute from a pointer type (`{}`) to a reference type \
(`{}`)",
from_ty,
to_ty),
|db| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
("&mut *", "*mut")
} else {
("&*", "*const")
};
from_ty,
to_ty
),
|db| {
let arg = sugg::Sugg::hir(cx, &args[0], "..");
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
("&mut *", "*mut")
} else {
("&*", "*const")
};
let arg = if from_pty.ty == to_rty.ty {
arg
} else {
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_rty.ty)))
};
let arg = if from_pty.ty == to_rty.ty {
arg
} else {
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_rty.ty)))
};
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
})
db.span_suggestion(e.span, "try", sugg::make_unop(deref, arg).to_string());
},
)
},
_ => return,
};
@ -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,
LINKEDLIST,
ast_ty.span,
"I see you're using a LinkedList! Perhaps you meant some other data structure?",
"a VecDeque might work");
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",
);
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,
LET_UNIT_VALUE,
decl.span,
&format!("this let-binding has unit value. Consider omitting `let {} =`",
snippet(cx, local.pat.span, "..")));
span_lint(
cx,
LET_UNIT_VALUE,
decl.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,
UNIT_CMP,
expr.span,
&format!("{}-comparison of unit values detected. This will always be {}",
op.as_str(),
result));
span_lint(
cx,
UNIT_CMP,
expr.span,
&format!(
"{}-comparison of unit values detected. This will always be {}",
op.as_str(),
result
),
);
},
_ => (),
}
@ -493,20 +511,24 @@ 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,
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 \
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 \
is only {4} bits wide)",
cast_from,
if cast_to_f64 { "f64" } else { "f32" },
if arch_dependent {
arch_dependent_str
} else {
""
},
from_nbits_str,
mantissa_nbits));
cast_from,
if cast_to_f64 { "f64" } else { "f32" },
if arch_dependent {
arch_dependent_str
} else {
""
},
from_nbits_str,
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)) {
(true, true) | (false, false) => {
(to_nbits < from_nbits,
ArchSuffix::None,
to_nbits == from_nbits && cast_unsigned_to_signed,
ArchSuffix::None)
},
(true, false) => {
(to_nbits <= 32,
if to_nbits == 32 {
ArchSuffix::_64
} else {
ArchSuffix::None
},
to_nbits <= 32 && cast_unsigned_to_signed,
ArchSuffix::_32)
},
(false, true) => {
(from_nbits == 64,
ArchSuffix::_32,
cast_unsigned_to_signed,
if from_nbits == 64 {
ArchSuffix::_64
} else {
ArchSuffix::_32
})
},
};
let (span_truncation, suffix_truncation, span_wrap, suffix_wrap) =
match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
(true, true) | (false, false) => {
(
to_nbits < from_nbits,
ArchSuffix::None,
to_nbits == from_nbits && cast_unsigned_to_signed,
ArchSuffix::None,
)
},
(true, false) => {
(
to_nbits <= 32,
if to_nbits == 32 {
ArchSuffix::_64
} else {
ArchSuffix::None
},
to_nbits <= 32 && cast_unsigned_to_signed,
ArchSuffix::_32,
)
},
(false, true) => {
(
from_nbits == 64,
ArchSuffix::_32,
cast_unsigned_to_signed,
if from_nbits == 64 {
ArchSuffix::_64
} else {
ArchSuffix::_32
},
)
},
};
if span_truncation {
span_lint(cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&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 => "",
}));
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&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,
CAST_POSSIBLE_WRAP,
expr.span,
&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 => "",
}));
span_lint(
cx,
CAST_POSSIBLE_WRAP,
expr.span,
&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,
CAST_SIGN_LOSS,
CAST_POSSIBLE_TRUNCATION,
CAST_POSSIBLE_WRAP,
UNNECESSARY_CAST)
lint_array!(
CAST_PRECISION_LOSS,
CAST_SIGN_LOSS,
CAST_POSSIBLE_TRUNCATION,
CAST_POSSIBLE_WRAP,
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,
UNNECESSARY_CAST,
expr.span,
&format!("casting to the same type is unnecessary (`{}` -> `{}`)",
cast_from,
cast_to));
span_lint(
cx,
UNNECESSARY_CAST,
expr.span,
&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,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&format!("casting {} to {} may truncate the value", cast_from, cast_to));
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
&format!("casting {} to {} may truncate the value", cast_from, cast_to),
);
if !cast_to.is_signed() {
span_lint(cx,
CAST_SIGN_LOSS,
expr.span,
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
span_lint(
cx,
CAST_SIGN_LOSS,
expr.span,
&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,
CAST_SIGN_LOSS,
expr.span,
&format!("casting {} to {} may lose the sign of the value", cast_from, cast_to));
span_lint(
cx,
CAST_SIGN_LOSS,
expr.span,
&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_POSSIBLE_TRUNCATION,
expr.span,
"casting f64 to f32 may truncate the value");
(&cast_from.sty, &cast_to.sty)
{
span_lint(
cx,
CAST_POSSIBLE_TRUNCATION,
expr.span,
"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,
TYPE_COMPLEXITY,
ty.span,
"very complex type used. Consider factoring parts into `type` definitions");
span_lint(
cx,
TYPE_COMPLEXITY,
ty.span,
"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, lhs.span, "lhs"),
snippet(cx, rhs.span, "rhs")
)
},
};
let help = format!("because {} is the {} value for this type, {}",
snippet(cx, culprit.expr.span, "x"),
match culprit.which {
Minimum => "minimum",
Maximum => "maximum",
},
conclusion);
let help = format!(
"because {} is the {} value for this type, {}",
snippet(cx, culprit.expr.span, "x"),
match culprit.which {
Minimum => "minimum",
Maximum => "maximum",
},
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,
INVALID_UPCAST_COMPARISONS,
*span,
&format!(
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::*;
@ -1227,40 +1284,42 @@ fn upcast_comparison_bounds_err(
err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
}
} else if match rel {
Rel::Lt => {
if invert {
norm_rhs_val < lb
} else {
ub < norm_rhs_val
}
},
Rel::Le => {
if invert {
norm_rhs_val <= lb
} else {
ub <= norm_rhs_val
}
},
Rel::Eq | Rel::Ne => unreachable!(),
} {
Rel::Lt => {
if invert {
norm_rhs_val < lb
} else {
ub < norm_rhs_val
}
},
Rel::Le => {
if invert {
norm_rhs_val <= lb
} else {
ub <= norm_rhs_val
}
},
Rel::Eq | Rel::Ne => unreachable!(),
}
{
err_upcast_comparison(cx, span, lhs, true)
} else if match rel {
Rel::Lt => {
if invert {
norm_rhs_val >= ub
} else {
lb >= norm_rhs_val
}
},
Rel::Le => {
if invert {
norm_rhs_val > ub
} else {
lb > norm_rhs_val
}
},
Rel::Eq | Rel::Ne => unreachable!(),
} {
Rel::Lt => {
if invert {
norm_rhs_val >= ub
} else {
lb >= norm_rhs_val
}
},
Rel::Le => {
if invert {
norm_rhs_val > ub
} else {
lb > norm_rhs_val
}
},
Rel::Eq | Rel::Ne => unreachable!(),
}
{
err_upcast_comparison(cx, span, lhs, false)
}
}

View file

@ -1,9 +1,9 @@
use rustc::lint::*;
use rustc::hir::*;
use syntax::ast::LitKind;
use syntax::ast::{LitKind, NodeId};
use syntax::codemap::Span;
use unicode_normalization::UnicodeNormalization;
use utils::{snippet, span_help_and_lint};
use utils::{snippet, span_help_and_lint, is_allowed};
/// **What it does:** Checks for the Unicode zero-width space in the code.
///
@ -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,
@ -72,7 +73,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if let ExprLit(ref lit) = expr.node {
if let LitKind::Str(_, _) = lit.node {
check_str(cx, lit.span)
check_str(cx, lit.span, expr.id)
}
}
}
@ -92,33 +93,43 @@ fn escape<T: Iterator<Item = char>>(s: T) -> String {
result
}
fn check_str(cx: &LateContext, span: Span) {
fn check_str(cx: &LateContext, span: Span, id: NodeId) {
let string = snippet(cx, span, "");
if string.contains('\u{200B}') {
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}")));
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}")
),
);
}
if string.chars().any(|c| c as u32 > 0x7F) {
span_help_and_lint(cx,
NON_ASCII_LITERAL,
span,
"literal non-ASCII character detected",
&format!("Consider replacing the string with:\n\"{}\"",
if cx.current_level(UNICODE_NOT_NFC) == Level::Allow {
escape(string.chars())
} else {
escape(string.nfc())
}));
span_help_and_lint(
cx,
NON_ASCII_LITERAL,
span,
"literal non-ASCII character detected",
&format!(
"Consider replacing the string with:\n\"{}\"",
if is_allowed(cx, UNICODE_NOT_NFC, id) {
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,
UNICODE_NOT_NFC,
span,
"non-nfc unicode sequence detected",
&format!("Consider replacing the string with:\n\"{}\"", string.nfc().collect::<String>()));
if is_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
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>()),
);
}
}

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
.last()
.expect("use paths cannot be empty")
.identifier,
*name,
cx,
&item.span);
unsafe_to_safe_check(
path.segments
.last()
.expect("use paths cannot be empty")
.identifier,
*name,
cx,
&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,
UNSAFE_REMOVED_FROM_NAME,
*span,
&format!("removed \"unsafe\" from the name of `{}` in use as `{}`", old_str, new_str));
span_lint(
cx,
UNSAFE_REMOVED_FROM_NAME,
*span,
&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,
UNUSED_IO_AMOUNT,
expr.span,
"handle read amount returned or use `Read::read_exact` instead");
span_lint(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"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,
UNUSED_IO_AMOUNT,
expr.span,
"handle written amount returned or use `Write::write_all` instead");
span_lint(
cx,
UNUSED_IO_AMOUNT,
expr.span,
"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 {
@ -433,24 +440,27 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
fn has_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
attr.check_name("clippy") &&
attr.meta_item_list().map_or(false, |list| {
list.len() == 1 && match list[0].node {
ast::NestedMetaItemKind::MetaItem(ref it) => it.name == "author",
ast::NestedMetaItemKind::Literal(_) => false,
}
})
attr.meta_item_list().map_or(false, |list| {
list.len() == 1 &&
match list[0].node {
ast::NestedMetaItemKind::MetaItem(ref it) => it.name == "author",
ast::NestedMetaItemKind::Literal(_) => false,
}
})
})
}
fn print_path(path: &QPath, first: &mut bool) {
match *path {
QPath::Resolved(_, ref path) => for segment in &path.segments {
if *first {
*first = false;
} else {
print!(", ");
QPath::Resolved(_, ref path) => {
for segment in &path.segments {
if *first {
*first = false;
} else {
print!(", ");
}
print!("{:?}", segment.name.as_str());
}
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.
&'static str,
/// The expected type.
&'static str,
/// The type we got instead.
&'static str),
Type(
/// The name of the key.
&'static str,
/// The expected type.
&'static str,
/// The type we got instead.
&'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,
}
@ -52,7 +55,7 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
/// Check whether two blocks are the same.
pub fn eq_block(&self, left: &Block, right: &Block) -> bool {
over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r)) &&
both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
}
pub fn eq_expr(&self, left: &Expr, right: &Expr) -> bool {
@ -78,13 +81,13 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
(&ExprBlock(ref l), &ExprBlock(ref r)) => self.eq_block(l, r),
(&ExprBinary(l_op, ref ll, ref lr), &ExprBinary(r_op, ref rl, ref rr)) => {
l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr) ||
swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
})
swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
})
},
(&ExprBreak(li, ref le), &ExprBreak(ri, ref re)) => {
both(&li.ident, &ri.ident, |l, r| l.node.name.as_str() == r.node.name.as_str()) &&
both(le, re, |l, r| self.eq_expr(l, r))
both(le, re, |l, r| self.eq_expr(l, r))
},
(&ExprBox(ref l), &ExprBox(ref r)) => self.eq_expr(l, r),
(&ExprCall(ref l_fun, ref l_args), &ExprCall(ref r_fun, ref r_args)) => {
@ -105,23 +108,23 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
},
(&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => {
ls == rs && self.eq_expr(le, re) &&
over(la, ra, |l, r| {
self.eq_expr(&l.body, &r.body) && both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) &&
over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r))
})
over(la, ra, |l, r| {
self.eq_expr(&l.body, &r.body) && both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) &&
over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r))
})
},
(&ExprMethodCall(ref l_path, _, ref l_args), &ExprMethodCall(ref r_path, _, ref r_args)) => {
!self.ignore_fn && l_path == r_path && self.eq_exprs(l_args, r_args)
},
(&ExprRepeat(ref le, ll_id), &ExprRepeat(ref re, rl_id)) => {
self.eq_expr(le, re) &&
self.eq_expr(&self.cx.tcx.hir.body(ll_id).value, &self.cx.tcx.hir.body(rl_id).value)
self.eq_expr(&self.cx.tcx.hir.body(ll_id).value, &self.cx.tcx.hir.body(rl_id).value)
},
(&ExprRet(ref l), &ExprRet(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
(&ExprPath(ref l), &ExprPath(ref r)) => self.eq_qpath(l, r),
(&ExprStruct(ref l_path, ref lf, ref lo), &ExprStruct(ref r_path, ref rf, ref ro)) => {
self.eq_qpath(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) &&
over(lf, rf, |l, r| self.eq_field(l, r))
over(lf, rf, |l, r| self.eq_field(l, r))
},
(&ExprTup(ref l_tup), &ExprTup(ref r_tup)) => self.eq_exprs(l_tup, r_tup),
(&ExprTupField(ref le, li), &ExprTupField(ref re, ri)) => li.node == ri.node && self.eq_expr(le, re),
@ -167,7 +170,7 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
(&PatKind::Ref(ref le, ref lm), &PatKind::Ref(ref re, ref rm)) => lm == rm && self.eq_pat(le, re),
(&PatKind::Slice(ref ls, ref li, ref le), &PatKind::Slice(ref rs, ref ri, ref re)) => {
over(ls, rs, |l, r| self.eq_pat(l, r)) && over(le, re, |l, r| self.eq_pat(l, r)) &&
both(li, ri, |l, r| self.eq_pat(l, r))
both(li, ri, |l, r| self.eq_pat(l, r))
},
(&PatKind::Wild, &PatKind::Wild) => true,
_ => false,
@ -188,19 +191,19 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
fn eq_path(&self, left: &Path, right: &Path) -> bool {
left.is_global() == right.is_global() &&
over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
}
fn eq_path_parameters(&self, left: &PathParameters, right: &PathParameters) -> bool {
match (left, right) {
(&AngleBracketedParameters(ref left), &AngleBracketedParameters(ref right)) => {
over(&left.lifetimes, &right.lifetimes, |l, r| self.eq_lifetime(l, r)) &&
over(&left.types, &right.types, |l, r| self.eq_ty(l, r)) &&
over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
over(&left.types, &right.types, |l, r| self.eq_ty(l, r)) &&
over(&left.bindings, &right.bindings, |l, r| self.eq_type_binding(l, r))
},
(&ParenthesizedParameters(ref left), &ParenthesizedParameters(ref right)) => {
over(&left.inputs, &right.inputs, |l, r| self.eq_ty(l, r)) &&
both(&left.output, &right.output, |l, r| self.eq_ty(l, r))
both(&left.output, &right.output, |l, r| self.eq_ty(l, r))
},
(&AngleBracketedParameters(_), &ParenthesizedParameters(_)) |
(&ParenthesizedParameters(_), &AngleBracketedParameters(_)) => false,
@ -218,7 +221,7 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
(&TySlice(ref l_vec), &TySlice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyArray(ref lt, ll_id), &TyArray(ref rt, rl_id)) => {
self.eq_ty(lt, rt) &&
self.eq_expr(&self.cx.tcx.hir.body(ll_id).value, &self.cx.tcx.hir.body(rl_id).value)
self.eq_expr(&self.cx.tcx.hir.body(ll_id).value, &self.cx.tcx.hir.body(rl_id).value)
},
(&TyPtr(ref l_mut), &TyPtr(ref r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty),
(&TyRptr(_, ref l_rmut), &TyRptr(_, ref r_rmut)) => {
@ -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: {}",
ind,
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
println!(
"{}name: {}",
ind,
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: {}",
ind,
print::to_string(print::NO_ANN, |s| s.print_qpath(path, false)));
println!(
"{}path: {}",
ind,
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,
CLIPPY_LINTS_INTERNAL,
item.span,
"this constant should be before the previous constant due to lexical \
ordering");
span_lint(
cx,
CLIPPY_LINTS_INTERNAL,
item.span,
"this constant should be before the previous constant due to lexical \
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,
LINT_WITHOUT_LINT_PASS,
lint_span,
&format!("the lint `{}` is not added to any `LintPass`", lint_name));
span_lint(
cx,
LINT_WITHOUT_LINT_PASS,
lint_span,
&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

@ -4,7 +4,7 @@ use rustc::hir::*;
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc::hir::def::Def;
use rustc::hir::map::Node;
use rustc::lint::{LintContext, LateContext, Level, Lint};
use rustc::lint::{LintContext, Level, LateContext, Lint};
use rustc::session::Session;
use rustc::traits;
use rustc::ty::{self, TyCtxt, Ty};
@ -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,
}
@ -235,7 +248,7 @@ pub fn match_path(path: &QPath, segments: &[&str]) -> bool {
match ty.node {
TyPath(ref inner_path) => {
!segments.is_empty() && match_path(inner_path, &segments[..(segments.len() - 1)]) &&
segment.name == segments[segments.len() - 1]
segment.name == segments[segments.len() - 1]
},
_ => false,
}
@ -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,24 +458,28 @@ fn trim_multiline_inner(s: Cow<str>, ignore_first: bool, ch: char) -> Cow<str> {
None
} else {
// ignore empty lines
Some(l.char_indices()
.find(|&(_, x)| x != ch)
.unwrap_or((l.len(), ch))
.0)
Some(
l.char_indices()
.find(|&(_, x)| x != ch)
.unwrap_or((l.len(), ch))
.0,
)
}
})
.min()
.unwrap_or(0);
if x > 0 {
Cow::Owned(s.lines()
.enumerate()
.map(|(i, l)| if (ignore_first && i == 0) || l.is_empty() {
l
} else {
l.split_at(x).1
})
.collect::<Vec<_>>()
.join("\n"))
Cow::Owned(
s.lines()
.enumerate()
.map(|(i, l)| if (ignore_first && i == 0) || l.is_empty() {
l
} else {
l.split_at(x).1
})
.collect::<Vec<_>>()
.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 {
Some(parent)
} else {
None
})
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,17 +536,16 @@ 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/rust-lang-nursery/rust-clippy/wiki#{}",
lint.name_lower()));
self.0.help(&format!(
"for further information visit https://github.com/rust-lang-nursery/rust-clippy/wiki#{}",
lint.name_lower()
));
}
}
}
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg));
if cx.current_level(lint) != Level::Allow {
db.wiki_link(lint);
}
DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).wiki_link(lint);
}
pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
@ -516,13 +553,11 @@ 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 {
db.0.help(help);
db.wiki_link(lint);
}
db.0.help(help);
db.wiki_link(lint);
}
pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
@ -531,17 +566,15 @@ 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 {
if note_span == span {
db.0.note(note);
} else {
db.0.span_note(note_span, note);
}
db.wiki_link(lint);
if note_span == span {
db.0.note(note);
} else {
db.0.span_note(note_span, note);
}
db.wiki_link(lint);
}
pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
@ -549,14 +582,13 @@ 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 {
f(&mut db.0);
db.wiki_link(lint);
}
f(&mut db.0);
db.wiki_link(lint);
}
pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>(
@ -565,15 +597,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 +632,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 +674,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 +706,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 +723,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 +743,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 +810,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 +834,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 +868,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 +999,14 @@ pub fn is_try(expr: &Expr) -> Option<&Expr> {
}
pub fn type_size<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: Ty<'tcx>) -> Option<u64> {
ty.layout(cx.tcx, cx.param_env).ok().map(|layout| layout.size(cx.tcx).bytes())
ty.layout(cx.tcx, cx.param_env).ok().map(|layout| {
layout.size(cx.tcx).bytes()
})
}
/// Returns true if the lint is allowed in the current context
///
/// Useful for skipping long running code when it's unnecessary
pub fn is_allowed(cx: &LateContext, lint: &'static Lint, id: NodeId) -> bool {
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
}

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)
(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)
}
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,
USELESS_VEC,
span,
"useless use of `vec!`",
"you can use a slice directly",
snippet);
span_lint_and_sugg(
cx,
USELESS_VEC,
span,
"useless use of `vec!`",
"you can use a slice directly",
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

@ -12,14 +12,13 @@ extern crate clippy_lints;
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
if let Ok(lint_store) = reg.sess.lint_store.try_borrow() {
if lint_store
.get_lint_groups()
.iter()
.any(|&(s, _, _)| s == "clippy") {
reg.sess
.struct_warn("running cargo clippy on a crate that also imports the clippy plugin")
.emit();
return;
for (lint, _, _) in lint_store.get_lint_groups() {
if lint == "clippy" {
reg.sess
.struct_warn("running cargo clippy on a crate that also imports the clippy plugin")
.emit();
return;
}
}
}

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,
state
.krate
.as_ref()
.expect("at this compilation stage \
the krate must be parsed")
.span);
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,
);
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,55 +211,59 @@ 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| {
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",
);
package_manifest_path == manifest_path
})
} else {
let package_manifest_paths: HashMap<_, _> = metadata
.packages
.iter()
.enumerate()
.map(|(i, package)| {
let package_manifest_path = Path::new(&package.manifest_path)
.canonicalize().expect("package manifest path could not be canonicalized");
package_manifest_path == manifest_path
.parent()
.expect("could not find parent directory of package manifest")
.canonicalize()
.expect("package directory cannot be canonicalized");
(package_manifest_path, i)
})
} else {
let package_manifest_paths: HashMap<_, _> =
metadata.packages.iter()
.enumerate()
.map(|(i, package)| {
let package_manifest_path = Path::new(&package.manifest_path)
.parent()
.expect("could not find parent directory of package manifest")
.canonicalize()
.expect("package directory cannot be canonicalized");
(package_manifest_path, i)
})
.collect();
.collect();
let current_dir = std::env::current_dir()
.expect("could not read current directory")
.canonicalize()
.expect("current directory cannot be canonicalized");
let current_dir = std::env::current_dir()
.expect("could not read current directory")
.canonicalize()
.expect("current directory cannot be canonicalized");
let mut current_path: &Path = &current_dir;
let mut current_path: &Path = &current_dir;
// This gets the most-recent parent (the one that takes the fewest `cd ..`s to
// reach).
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
// 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()));
}
// This gets the most-recent parent (the one that takes the fewest `cd ..`s to
// reach).
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
// 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())
});
}
}
}
.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]
.into_iter()
.chain(args)) {
if let Err(code) = process(
vec![format!("--{}", first), target.name]
.into_iter()
.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

@ -6,9 +6,10 @@
// this should compile in a reasonable amount of time
fn rust_type_id(name: &str) {
if "bool" == &name[..] || "uint" == &name[..] || "u8" == &name[..] || "u16" == &name[..] ||
"u32" == &name[..] || "f32" == &name[..] || "f64" == &name[..] || "i8" == &name[..] ||
"i16" == &name[..] || "i32" == &name[..] ||
"i64" == &name[..] || "Self" == &name[..] || "str" == &name[..] {
"u32" == &name[..] || "f32" == &name[..] || "f64" == &name[..] || "i8" == &name[..] ||
"i16" == &name[..] || "i32" == &name[..] ||
"i64" == &name[..] || "Self" == &name[..] || "str" == &name[..]
{
unreachable!();
}
}

Some files were not shown because too many files have changed in this diff Show more