mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-28 01:34:21 +00:00
Merge branch 'master' into move_links
This commit is contained in:
commit
4470d7a8ca
13
.github/deploy.sh
vendored
13
.github/deploy.sh
vendored
@ -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
|
||||
|
@ -8,11 +8,6 @@ os:
|
||||
|
||||
sudo: false
|
||||
|
||||
cache:
|
||||
cargo: true
|
||||
directories:
|
||||
- target
|
||||
|
||||
env:
|
||||
global:
|
||||
# TRAVIS_TOKEN_CLIPPY_SERVICE
|
||||
|
@ -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`]
|
||||
|
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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>",
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) => {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
// it’s bad when there is a ‘\n’ after the “else”
|
||||
if let Some(else_snippet) = snippet_opt(cx, else_span) {
|
||||
let else_pos = else_snippet.find("else").expect("there must be a `else` here");
|
||||
let else_pos = else_snippet.find("else").expect(
|
||||
"there must be a `else` here",
|
||||
);
|
||||
|
||||
if else_snippet[else_pos..].contains('\n') {
|
||||
span_note_and_lint(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`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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, "..")
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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",
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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();
|
||||
|
@ -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 })
|
||||
|
@ -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 wasn’t 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;
|
||||
|
@ -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,
|
||||
|
@ -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> {
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
|
@ -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),
|
||||
|
@ -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(),
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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! {
|
||||
|
@ -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))
|
||||
|
@ -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>>,
|
||||
}
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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\"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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, "..")),
|
||||
);
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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, "_")),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,8 @@ fn is_trivial_regex(s: ®ex_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()),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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""#)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ declare_lint! {
|
||||
"`foo = bar; bar = foo` sequence"
|
||||
}
|
||||
|
||||
#[derive(Copy,Clone)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Swap;
|
||||
|
||||
impl LintPass for Swap {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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>()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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)),
|
||||
|
@ -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)
|
||||
},
|
||||
|
@ -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",
|
||||
];
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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"];
|
||||
|
@ -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)
|
||||
|
@ -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>`).
|
||||
|
@ -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.
|
||||
///
|
||||
|
15
src/lib.rs
15
src/lib.rs
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
153
src/main.rs
153
src/main.rs
@ -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 = ¤t_dir;
|
||||
let mut current_path: &Path = ¤t_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()];
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user