Merge branch 'master' into allow_deprecated
This commit is contained in:
commit
7a1d85f1de
110 changed files with 2712 additions and 2066 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -24,3 +24,6 @@ Cargo.lock
|
|||
|
||||
# gh pages docs
|
||||
util/gh-pages/lints.json
|
||||
|
||||
# rustfmt backups
|
||||
*.rs.bk
|
||||
|
|
|
|||
|
|
@ -17,11 +17,16 @@ install:
|
|||
- nvm install stable
|
||||
- nvm use stable
|
||||
- npm install remark-cli remark-lint
|
||||
# || true, because we cache rustfmt and don't want to crash on the next travis run
|
||||
# due to rustfmt already being installed
|
||||
- (cargo install rustfmt || true)
|
||||
|
||||
script:
|
||||
- remark -f README.md > /dev/null
|
||||
- python util/update_lints.py -c
|
||||
- set -e
|
||||
- PATH=$PATH:~/.cargo/bin cargo fmt -- --write-mode=diff
|
||||
- cd clippy_lints && PATH=$PATH:~/.cargo/bin cargo fmt -- --write-mode=diff && cd ..
|
||||
- cargo build --features debugging
|
||||
- cargo test --features debugging
|
||||
- mkdir -p ~/rust/cargo/bin
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 0.0.104 — 2016-12-15
|
||||
* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
|
||||
|
||||
## 0.0.103 — 2016-11-25
|
||||
* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
|
||||
|
||||
|
|
@ -260,6 +263,7 @@ All notable changes to this project will be documented in this file.
|
|||
[`crosspointer_transmute`]: https://github.com/Manishearth/rust-clippy/wiki#crosspointer_transmute
|
||||
[`cyclomatic_complexity`]: https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity
|
||||
[`deprecated_semver`]: https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver
|
||||
[`deref_addrof`]: https://github.com/Manishearth/rust-clippy/wiki#deref_addrof
|
||||
[`derive_hash_xor_eq`]: https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq
|
||||
[`diverging_sub_expression`]: https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression
|
||||
[`doc_markdown`]: https://github.com/Manishearth/rust-clippy/wiki#doc_markdown
|
||||
|
|
@ -353,6 +357,7 @@ All notable changes to this project will be documented in this file.
|
|||
[`print_stdout`]: https://github.com/Manishearth/rust-clippy/wiki#print_stdout
|
||||
[`print_with_newline`]: https://github.com/Manishearth/rust-clippy/wiki#print_with_newline
|
||||
[`ptr_arg`]: https://github.com/Manishearth/rust-clippy/wiki#ptr_arg
|
||||
[`pub_enum_variant_names`]: https://github.com/Manishearth/rust-clippy/wiki#pub_enum_variant_names
|
||||
[`range_step_by_zero`]: https://github.com/Manishearth/rust-clippy/wiki#range_step_by_zero
|
||||
[`range_zip_with_len`]: https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len
|
||||
[`redundant_closure`]: https://github.com/Manishearth/rust-clippy/wiki#redundant_closure
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.0.103"
|
||||
version = "0.0.104"
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
"Andre Bogus <bogusandre@gmail.com>",
|
||||
|
|
@ -25,11 +25,11 @@ test = false
|
|||
|
||||
[dependencies]
|
||||
# begin automatic update
|
||||
clippy_lints = { version = "0.0.103", path = "clippy_lints" }
|
||||
clippy_lints = { version = "0.0.104", path = "clippy_lints" }
|
||||
# end automatic update
|
||||
|
||||
[dev-dependencies]
|
||||
compiletest_rs = "0.2.1"
|
||||
compiletest_rs = "0.2.5"
|
||||
lazy_static = "0.1.15"
|
||||
regex = "0.1.71"
|
||||
rustc-serialize = "0.3"
|
||||
|
|
|
|||
135
README.md
135
README.md
|
|
@ -16,10 +16,79 @@ Table of contents:
|
|||
|
||||
## Usage
|
||||
|
||||
Since this is a tool for helping the developer of a library or application
|
||||
write better code, it is recommended not to include clippy as a hard dependency.
|
||||
Options include using it as an optional dependency, as a cargo subcommand, or
|
||||
as an included feature during build. All of these options are detailed below.
|
||||
|
||||
As a general rule clippy will only work with the *latest* Rust nightly for now.
|
||||
|
||||
### Optional dependency
|
||||
|
||||
If you want to make clippy an optional dependency, you can do the following:
|
||||
|
||||
In your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
clippy = {version = "*", optional = true}
|
||||
|
||||
[features]
|
||||
default = []
|
||||
```
|
||||
|
||||
And, in your `main.rs` or `lib.rs`:
|
||||
|
||||
```rust
|
||||
#![cfg_attr(feature="clippy", feature(plugin))]
|
||||
|
||||
#![cfg_attr(feature="clippy", plugin(clippy))]
|
||||
```
|
||||
|
||||
Then build by enabling the feature: `cargo build --features "clippy"`
|
||||
|
||||
Instead of adding the `cfg_attr` attributes you can also run clippy on demand:
|
||||
`cargo rustc --features clippy -- -Z no-trans -Z extra-plugins=clippy`
|
||||
(the `-Z no trans`, while not neccessary, will stop the compilation process after
|
||||
typechecking (and lints) have completed, which can significantly reduce the runtime).
|
||||
|
||||
### As a cargo subcommand (`cargo clippy`)
|
||||
|
||||
An alternate way to use clippy is by installing clippy through cargo as a cargo
|
||||
subcommand.
|
||||
|
||||
```terminal
|
||||
cargo install clippy
|
||||
```
|
||||
|
||||
Now you can run clippy by invoking `cargo clippy`, or
|
||||
`rustup run nightly cargo clippy` directly from a directory that is usually
|
||||
compiled with stable.
|
||||
|
||||
In case you are not using rustup, you need to set the environment flag
|
||||
`SYSROOT` during installation so clippy knows where to find `librustc` and
|
||||
similar crates.
|
||||
|
||||
```terminal
|
||||
SYSROOT=/path/to/rustc/sysroot cargo install clippy
|
||||
```
|
||||
|
||||
### Running clippy from the command line without installing
|
||||
|
||||
To have cargo compile your crate with clippy without needing `#![plugin(clippy)]`
|
||||
in your code, you can use:
|
||||
|
||||
```terminal
|
||||
cargo rustc -- -L /path/to/clippy_so -Z extra-plugins=clippy
|
||||
```
|
||||
|
||||
*[Note](https://github.com/Manishearth/rust-clippy/wiki#a-word-of-warning):*
|
||||
Be sure that clippy was compiled with the same version of rustc that cargo invokes here!
|
||||
|
||||
### As a Compiler Plugin
|
||||
|
||||
*Note:* This is not a recommended installation method.
|
||||
|
||||
Since stable Rust is backwards compatible, you should be able to
|
||||
compile your stable programs with nightly Rust with clippy plugged in to
|
||||
circumvent this.
|
||||
|
|
@ -63,68 +132,6 @@ src/main.rs:8:5: 11:6 help: Try
|
|||
if let Some(y) = x { println!("{:?}", y) }
|
||||
```
|
||||
|
||||
### As a cargo subcommand (`cargo clippy`)
|
||||
|
||||
An alternate way to use clippy is by installing clippy through cargo as a cargo
|
||||
subcommand.
|
||||
|
||||
```terminal
|
||||
cargo install clippy
|
||||
```
|
||||
|
||||
Now you can run clippy by invoking `cargo clippy`, or
|
||||
`rustup run nightly cargo clippy` directly from a directory that is usually
|
||||
compiled with stable.
|
||||
|
||||
In case you are not using rustup, you need to set the environment flag
|
||||
`SYSROOT` during installation so clippy knows where to find `librustc` and
|
||||
similar crates.
|
||||
|
||||
```terminal
|
||||
SYSROOT=/path/to/rustc/sysroot cargo install clippy
|
||||
```
|
||||
|
||||
### Running clippy from the command line without installing
|
||||
|
||||
To have cargo compile your crate with clippy without needing `#![plugin(clippy)]`
|
||||
in your code, you can use:
|
||||
|
||||
```terminal
|
||||
cargo rustc -- -L /path/to/clippy_so -Z extra-plugins=clippy
|
||||
```
|
||||
|
||||
*[Note](https://github.com/Manishearth/rust-clippy/wiki#a-word-of-warning):*
|
||||
Be sure that clippy was compiled with the same version of rustc that cargo invokes here!
|
||||
|
||||
### Optional dependency
|
||||
|
||||
If you want to make clippy an optional dependency, you can do the following:
|
||||
|
||||
In your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
clippy = {version = "*", optional = true}
|
||||
|
||||
[features]
|
||||
default = []
|
||||
```
|
||||
|
||||
And, in your `main.rs` or `lib.rs`:
|
||||
|
||||
```rust
|
||||
#![cfg_attr(feature="clippy", feature(plugin))]
|
||||
|
||||
#![cfg_attr(feature="clippy", plugin(clippy))]
|
||||
```
|
||||
|
||||
Then build by enabling the feature: `cargo build --features "clippy"`
|
||||
|
||||
Instead of adding the `cfg_attr` attributes you can also run clippy on demand:
|
||||
`cargo rustc --features clippy -- -Z no-trans -Z extra-plugins=clippy`
|
||||
(the `-Z no trans`, while not neccessary, will stop the compilation process after
|
||||
typechecking (and lints) have completed, which can significantly reduce the runtime).
|
||||
|
||||
## Configuration
|
||||
|
||||
Some lints can be configured in a `clippy.toml` file. It contains basic `variable = value` mapping eg.
|
||||
|
|
@ -172,7 +179,7 @@ transparently:
|
|||
|
||||
## Lints
|
||||
|
||||
There are 178 lints included in this crate:
|
||||
There are 180 lints included in this crate:
|
||||
|
||||
name | default | triggers on
|
||||
-----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
|
||||
|
|
@ -204,6 +211,7 @@ name
|
|||
[crosspointer_transmute](https://github.com/Manishearth/rust-clippy/wiki#crosspointer_transmute) | warn | transmutes that have to or from types that are a pointer to the other
|
||||
[cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity) | warn | functions that should be split up into multiple functions
|
||||
[deprecated_semver](https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver) | warn | use of `#[deprecated(since = "x")]` where x is not semver
|
||||
[deref_addrof](https://github.com/Manishearth/rust-clippy/wiki#deref_addrof) | warn | use of `*&` or `*&mut` in an expression
|
||||
[derive_hash_xor_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly
|
||||
[diverging_sub_expression](https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression) | warn | whether an expression contains a diverging sub expression
|
||||
[doc_markdown](https://github.com/Manishearth/rust-clippy/wiki#doc_markdown) | warn | presence of `_`, `::` or camel-case outside backticks in documentation
|
||||
|
|
@ -297,6 +305,7 @@ name
|
|||
[print_stdout](https://github.com/Manishearth/rust-clippy/wiki#print_stdout) | allow | printing on stdout
|
||||
[print_with_newline](https://github.com/Manishearth/rust-clippy/wiki#print_with_newline) | warn | using `print!()` with a format string that ends in a newline
|
||||
[ptr_arg](https://github.com/Manishearth/rust-clippy/wiki#ptr_arg) | warn | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively
|
||||
[pub_enum_variant_names](https://github.com/Manishearth/rust-clippy/wiki#pub_enum_variant_names) | allow | enums where all variants share a prefix/postfix
|
||||
[range_step_by_zero](https://github.com/Manishearth/rust-clippy/wiki#range_step_by_zero) | warn | using `Range::step_by(0)`, which produces an infinite iterator
|
||||
[range_zip_with_len](https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len) | warn | zipping iterator with a range when `enumerate()` would do
|
||||
[redundant_closure](https://github.com/Manishearth/rust-clippy/wiki#redundant_closure) | warn | redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
# begin automatic update
|
||||
version = "0.0.103"
|
||||
version = "0.0.104"
|
||||
# end automatic update
|
||||
authors = [
|
||||
"Manish Goregaokar <manishsmail@gmail.com>",
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprLit(ref lit) = e.node {
|
||||
check_lit(cx, lit, e);
|
||||
}
|
||||
|
|
@ -85,7 +85,9 @@ fn check_known_consts(cx: &LateContext, e: &Expr, s: &symbol::Symbol, module: &s
|
|||
APPROX_CONSTANT,
|
||||
e.span,
|
||||
&format!("approximate value of `{}::consts::{}` found. \
|
||||
Consider using it directly", module, &name));
|
||||
Consider using it directly",
|
||||
module,
|
||||
&name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ impl LintPass for Arithmetic {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Arithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
if self.span.is_some() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ impl LateLintPass for Arithmetic {
|
|||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprUnary(hir::UnOp::UnNeg, ref arg) => {
|
||||
let ty = cx.tcx.tables().expr_ty(arg);
|
||||
if ty.is_integral() {
|
||||
|
|
@ -77,12 +77,12 @@ impl LateLintPass for Arithmetic {
|
|||
span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
|
||||
self.span = Some(expr.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext, expr: &hir::Expr) {
|
||||
fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
if Some(expr.span) == self.span {
|
||||
self.span = None;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ impl LintPass for ArrayIndexing {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for ArrayIndexing {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &hir::Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ArrayIndexing {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx hir::Expr) {
|
||||
if let hir::ExprIndex(ref array, ref index) = e.node {
|
||||
// Array with known size can be checked statically
|
||||
let ty = cx.tcx.tables().expr_ty(array);
|
||||
|
|
@ -107,9 +107,12 @@ impl LateLintPass for ArrayIndexing {
|
|||
}
|
||||
|
||||
/// 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)
|
||||
-> Option<(ConstInt, ConstInt)> {
|
||||
fn to_const_range(
|
||||
start: Option<Option<ConstVal>>,
|
||||
end: Option<Option<ConstVal>>,
|
||||
limits: RangeLimits,
|
||||
array_size: ConstInt
|
||||
) -> Option<(ConstInt, ConstInt)> {
|
||||
let start = match start {
|
||||
Some(Some(ConstVal::Integral(x))) => x,
|
||||
Some(_) => return None,
|
||||
|
|
@ -123,7 +126,7 @@ fn to_const_range(start: Option<Option<ConstVal>>, end: Option<Option<ConstVal>>
|
|||
} else {
|
||||
x
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(_) => return None,
|
||||
None => array_size,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use rustc::hir;
|
||||
use rustc::lint::*;
|
||||
use syntax::ast;
|
||||
use utils::{span_lint_and_then, snippet_opt, SpanlessEq, get_trait_def_id, implements_trait};
|
||||
use utils::{higher, sugg};
|
||||
|
||||
|
|
@ -66,8 +67,8 @@ impl LintPass for AssignOps {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for AssignOps {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
match expr.node {
|
||||
hir::ExprAssignOp(op, ref lhs, ref rhs) => {
|
||||
span_lint_and_then(cx, ASSIGN_OPS, expr.span, "assign operation detected", |db| {
|
||||
|
|
@ -94,13 +95,13 @@ impl LateLintPass for AssignOps {
|
|||
expr.span,
|
||||
"variable appears on both sides of an assignment operation",
|
||||
|db| {
|
||||
if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span),
|
||||
snippet_opt(cx, rhs.span)) {
|
||||
db.span_suggestion(expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r));
|
||||
}
|
||||
});
|
||||
if let (Some(snip_a), Some(snip_r)) =
|
||||
(snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span)) {
|
||||
db.span_suggestion(expr.span,
|
||||
"replace it with",
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r));
|
||||
}
|
||||
});
|
||||
};
|
||||
// lhs op= l op r
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
|
||||
|
|
@ -112,7 +113,7 @@ impl LateLintPass for AssignOps {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprAssign(ref assignee, ref e) => {
|
||||
if let hir::ExprBinary(op, ref l, ref r) = e.node {
|
||||
let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
|
||||
|
|
@ -125,7 +126,11 @@ impl LateLintPass for AssignOps {
|
|||
return; // implements_trait does not work with generics
|
||||
}
|
||||
macro_rules! ops {
|
||||
($op:expr, $cx:expr, $ty:expr, $rty:expr, $($trait_name:ident:$full_trait_name:ident),+) => {
|
||||
($op:expr,
|
||||
$cx:expr,
|
||||
$ty:expr,
|
||||
$rty:expr,
|
||||
$($trait_name:ident:$full_trait_name:ident),+) => {
|
||||
match $op {
|
||||
$(hir::$full_trait_name => {
|
||||
let [krate, module] = ::utils::paths::OPS_MODULE;
|
||||
|
|
@ -135,6 +140,16 @@ impl LateLintPass for AssignOps {
|
|||
} else {
|
||||
return; // useless if the trait doesn't exist
|
||||
};
|
||||
// check that we are not inside an `impl AssignOp` of this exact operation
|
||||
let parent_fn = cx.tcx.map.get_parent(e.id);
|
||||
let parent_impl = cx.tcx.map.get_parent(parent_fn);
|
||||
// the crate node is the only one that is not in the map
|
||||
if_let_chain!{[
|
||||
parent_impl != ast::CRATE_NODE_ID,
|
||||
let hir::map::Node::NodeItem(item) = cx.tcx.map.get(parent_impl),
|
||||
let hir::Item_::ItemImpl(_, _, _, Some(ref trait_ref), _, _) = item.node,
|
||||
trait_ref.path.def.def_id() == trait_id
|
||||
], { return; }}
|
||||
implements_trait($cx, $ty, trait_id, vec![$rty])
|
||||
},)*
|
||||
_ => false,
|
||||
|
|
@ -162,13 +177,13 @@ impl LateLintPass for AssignOps {
|
|||
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));
|
||||
}
|
||||
});
|
||||
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
|
||||
|
|
@ -181,13 +196,13 @@ impl LateLintPass for AssignOps {
|
|||
hir::BiAdd | hir::BiMul | hir::BiAnd | hir::BiOr | hir::BiBitXor | hir::BiBitAnd |
|
||||
hir::BiBitOr => {
|
||||
lint(assignee, l);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -195,23 +210,7 @@ impl LateLintPass for AssignOps {
|
|||
fn is_commutative(op: hir::BinOp_) -> bool {
|
||||
use rustc::hir::BinOp_::*;
|
||||
match op {
|
||||
BiAdd |
|
||||
BiMul |
|
||||
BiAnd |
|
||||
BiOr |
|
||||
BiBitXor |
|
||||
BiBitAnd |
|
||||
BiBitOr |
|
||||
BiEq |
|
||||
BiNe => true,
|
||||
BiSub |
|
||||
BiDiv |
|
||||
BiRem |
|
||||
BiShl |
|
||||
BiShr |
|
||||
BiLt |
|
||||
BiLe |
|
||||
BiGe |
|
||||
BiGt => false,
|
||||
BiAdd | BiMul | BiAnd | BiOr | BiBitXor | BiBitAnd | BiBitOr | BiEq | BiNe => true,
|
||||
BiSub | BiDiv | BiRem | BiShl | BiShr | BiLt | BiLe | BiGe | BiGt => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@ declare_lint! {
|
|||
|
||||
/// **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 forgotten
|
||||
/// **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, but it's easier to remove the unused item.
|
||||
/// **Known problems:** Technically one might allow `unused_import` on a `use` item,
|
||||
/// but it's easier to remove the unused item.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
|
|
@ -81,8 +83,8 @@ impl LintPass for AttrPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for AttrPass {
|
||||
fn check_attribute(&mut self, cx: &LateContext, attr: &Attribute) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AttrPass {
|
||||
fn check_attribute(&mut self, cx: &LateContext<'a, 'tcx>, attr: &'tcx Attribute) {
|
||||
if let MetaItemKind::List(ref items) = attr.value.node {
|
||||
if items.is_empty() || attr.name() != "deprecated" {
|
||||
return;
|
||||
|
|
@ -99,13 +101,13 @@ impl LateLintPass for AttrPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if is_relevant_item(cx, item) {
|
||||
check_attrs(cx, item.span, &item.name, &item.attrs)
|
||||
}
|
||||
match item.node {
|
||||
ItemExternCrate(_) |
|
||||
ItemUse(_) => {
|
||||
ItemUse(_, _) => {
|
||||
for attr in &item.attrs {
|
||||
if let MetaItemKind::List(ref lint_list) = attr.value.node {
|
||||
match &*attr.name().as_str() {
|
||||
|
|
@ -113,14 +115,16 @@ impl LateLintPass for AttrPass {
|
|||
// whitelist `unused_imports` and `deprecated`
|
||||
for lint in lint_list {
|
||||
if is_word(lint, "unused_imports") || is_word(lint, "deprecated") {
|
||||
if let ItemUse(_) = item.node {
|
||||
if let ItemUse(_, _) = item.node {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(mut sugg) = snippet_opt(cx, attr.span) {
|
||||
if sugg.len() > 1 {
|
||||
span_lint_and_then(cx, USELESS_ATTRIBUTE, attr.span,
|
||||
span_lint_and_then(cx,
|
||||
USELESS_ATTRIBUTE,
|
||||
attr.span,
|
||||
"useless lint attribute",
|
||||
|db| {
|
||||
sugg.insert(1, '!');
|
||||
|
|
@ -138,13 +142,13 @@ impl LateLintPass for AttrPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
if is_relevant_impl(cx, item) {
|
||||
check_attrs(cx, item.span, &item.name, &item.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
if is_relevant_trait(cx, item) {
|
||||
check_attrs(cx, item.span, &item.name, &item.attrs)
|
||||
}
|
||||
|
|
@ -152,8 +156,8 @@ impl LateLintPass for AttrPass {
|
|||
}
|
||||
|
||||
fn is_relevant_item(cx: &LateContext, item: &Item) -> bool {
|
||||
if let ItemFn(_, _, _, _, _, ref expr) = item.node {
|
||||
is_relevant_expr(cx, expr)
|
||||
if let ItemFn(_, _, _, _, _, eid) = item.node {
|
||||
is_relevant_expr(cx, cx.tcx.map.expr(eid))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -161,7 +165,7 @@ fn is_relevant_item(cx: &LateContext, item: &Item) -> bool {
|
|||
|
||||
fn is_relevant_impl(cx: &LateContext, item: &ImplItem) -> bool {
|
||||
match item.node {
|
||||
ImplItemKind::Method(_, ref expr) => is_relevant_expr(cx, expr),
|
||||
ImplItemKind::Method(_, eid) => is_relevant_expr(cx, cx.tcx.map.expr(eid)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -169,7 +173,7 @@ fn is_relevant_impl(cx: &LateContext, item: &ImplItem) -> bool {
|
|||
fn is_relevant_trait(cx: &LateContext, item: &TraitItem) -> bool {
|
||||
match item.node {
|
||||
MethodTraitItem(_, None) => true,
|
||||
MethodTraitItem(_, Some(ref expr)) => is_relevant_expr(cx, expr),
|
||||
MethodTraitItem(_, Some(eid)) => is_relevant_expr(cx, cx.tcx.map.expr(eid)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -181,7 +185,7 @@ fn is_relevant_block(cx: &LateContext, block: &Block) -> bool {
|
|||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => {
|
||||
return is_relevant_expr(cx, expr);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
block.expr.as_ref().map_or(false, |e| is_relevant_expr(cx, e))
|
||||
|
|
@ -191,15 +195,16 @@ fn is_relevant_expr(cx: &LateContext, expr: &Expr) -> bool {
|
|||
match expr.node {
|
||||
ExprBlock(ref block) => is_relevant_block(cx, block),
|
||||
ExprRet(Some(ref e)) => is_relevant_expr(cx, e),
|
||||
ExprRet(None) | ExprBreak(_, None) => false,
|
||||
ExprRet(None) |
|
||||
ExprBreak(_, None) => false,
|
||||
ExprCall(ref path_expr, _) => {
|
||||
if let ExprPath(..) = path_expr.node {
|
||||
let fun_id = resolve_node(cx, path_expr.id).expect("function should be resolved").def_id();
|
||||
if let ExprPath(ref qpath) = path_expr.node {
|
||||
let fun_id = resolve_node(cx, qpath, path_expr.id).def_id();
|
||||
!match_def_path(cx, fun_id, &paths::BEGIN_PANIC)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::hir::def::{Def, PathResolution};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::lint::*;
|
||||
use rustc_const_eval::lookup_const_by_id;
|
||||
use syntax::ast::LitKind;
|
||||
|
|
@ -79,8 +79,8 @@ impl LintPass for BitMask {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for BitMask {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BitMask {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprBinary(ref cmp, ref left, ref right) = e.node {
|
||||
if cmp.node.is_comparison() {
|
||||
if let Some(cmp_opt) = fetch_int_literal(cx, right) {
|
||||
|
|
@ -134,7 +134,7 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
|
|||
} else if mask_value == 0 {
|
||||
span_lint(cx, BAD_BIT_MASK, *span, "&-masking with zero");
|
||||
}
|
||||
}
|
||||
},
|
||||
BiBitOr => {
|
||||
if mask_value | cmp_value != cmp_value {
|
||||
span_lint(cx,
|
||||
|
|
@ -144,10 +144,10 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
|
|||
mask_value,
|
||||
cmp_value));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
BiLt | BiGe => {
|
||||
match bit_op {
|
||||
BiBitAnd => {
|
||||
|
|
@ -161,7 +161,7 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_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,
|
||||
|
|
@ -173,11 +173,11 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
|
|||
} else {
|
||||
check_ineffective_lt(cx, *span, mask_value, cmp_value, "|");
|
||||
}
|
||||
}
|
||||
},
|
||||
BiBitXor => check_ineffective_lt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
BiLe | BiGt => {
|
||||
match bit_op {
|
||||
BiBitAnd => {
|
||||
|
|
@ -191,7 +191,7 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_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,
|
||||
|
|
@ -203,11 +203,11 @@ fn check_bit_mask(cx: &LateContext, bit_op: BinOp_, cmp_op: BinOp_, mask_value:
|
|||
} else {
|
||||
check_ineffective_gt(cx, *span, mask_value, cmp_value, "|");
|
||||
}
|
||||
}
|
||||
},
|
||||
BiBitXor => check_ineffective_gt(cx, *span, mask_value, cmp_value, "^"),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -244,20 +244,15 @@ fn fetch_int_literal(cx: &LateContext, lit: &Expr) -> Option<u64> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprPath(_, _) => {
|
||||
{
|
||||
// Important to let the borrow expire before the const lookup to avoid double
|
||||
// borrowing.
|
||||
let def_map = cx.tcx.def_map.borrow();
|
||||
match def_map.get(&lit.id) {
|
||||
Some(&PathResolution { base_def: Def::Const(def_id), .. }) => Some(def_id),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
ExprPath(ref qpath) => {
|
||||
let def = cx.tcx.tables().qpath_def(qpath, lit.id);
|
||||
if let Def::Const(def_id) = def {
|
||||
lookup_const_by_id(cx.tcx, def_id, None).and_then(|(l, _ty)| fetch_int_literal(cx, l))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.and_then(|def_id| lookup_const_by_id(cx.tcx, def_id, None))
|
||||
.and_then(|(l, _ty)| fetch_int_literal(cx, l))
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,9 +37,9 @@ impl LintPass for BlackListedName {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for BlackListedName {
|
||||
fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
|
||||
if let PatKind::Binding(_, ref ident, _) = pat.node {
|
||||
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| s == &*ident.node.as_str()) {
|
||||
span_lint(cx,
|
||||
BLACKLISTED_NAME,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc::lint::{LateLintPass, LateContext, LintArray, LintPass};
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use utils::*;
|
||||
|
||||
/// **What it does:** Checks for `if` conditions that use blocks to contain an
|
||||
|
|
@ -49,13 +49,15 @@ impl LintPass for BlockInIfCondition {
|
|||
}
|
||||
}
|
||||
|
||||
struct ExVisitor<'v> {
|
||||
found_block: Option<&'v Expr>,
|
||||
struct ExVisitor<'a, 'tcx: 'a> {
|
||||
found_block: Option<&'tcx Expr>,
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for ExVisitor<'v> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
if let ExprClosure(_, _, ref expr, _) = expr.node {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if let ExprClosure(_, _, eid, _) = expr.node {
|
||||
let expr = self.cx.tcx.map.expr(eid);
|
||||
if matches!(expr.node, ExprBlock(_)) {
|
||||
self.found_block = Some(expr);
|
||||
return;
|
||||
|
|
@ -63,14 +65,17 @@ impl<'v> Visitor<'v> for ExVisitor<'v> {
|
|||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
const BRACED_EXPR_MESSAGE: &'static str = "omit braces around single expression condition";
|
||||
const COMPLEX_BLOCK_MESSAGE: &'static str = "in an 'if' condition, avoid complex blocks or closures with blocks; \
|
||||
instead, move the block or closure higher and bind it with a 'let'";
|
||||
|
||||
impl LateLintPass for BlockInIfCondition {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BlockInIfCondition {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprIf(ref check, ref then, _) = expr.node {
|
||||
if let ExprBlock(ref block) = check.node {
|
||||
if block.rules == DefaultBlock {
|
||||
|
|
@ -105,7 +110,10 @@ impl LateLintPass for BlockInIfCondition {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let mut visitor = ExVisitor { found_block: None };
|
||||
let mut visitor = ExVisitor {
|
||||
found_block: None,
|
||||
cx: cx,
|
||||
};
|
||||
walk_expr(&mut visitor, check);
|
||||
if let Some(block) = visitor.found_block {
|
||||
span_lint(cx, BLOCK_IN_IF_CONDITION_STMT, block.span, COMPLEX_BLOCK_MESSAGE);
|
||||
|
|
|
|||
|
|
@ -53,13 +53,15 @@ impl LintPass for NonminimalBool {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for NonminimalBool {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
NonminimalBoolVisitor(cx).visit_item(item)
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
NonminimalBoolVisitor { cx: cx }.visit_item(item)
|
||||
}
|
||||
}
|
||||
|
||||
struct NonminimalBoolVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);
|
||||
struct NonminimalBoolVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
use quine_mc_cluskey::Bool;
|
||||
struct Hir2Qmm<'a, 'tcx: 'a, 'v> {
|
||||
|
|
@ -92,14 +94,14 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
|||
BiAnd => return Ok(Bool::And(self.extract(BiAnd, &[lhs, rhs], Vec::new())?)),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprLit(ref lit) => {
|
||||
match lit.node {
|
||||
LitKind::Bool(true) => return Ok(Bool::True),
|
||||
LitKind::Bool(false) => return Ok(Bool::False),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +129,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
|||
BiLe => mk_expr(BiGt),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => continue,
|
||||
};
|
||||
if SpanlessEq::new(self.cx).ignore_fn().eq_expr(&negated, expr) {
|
||||
|
|
@ -154,17 +156,17 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
True => {
|
||||
s.push_str("true");
|
||||
s
|
||||
}
|
||||
},
|
||||
False => {
|
||||
s.push_str("false");
|
||||
s
|
||||
}
|
||||
},
|
||||
Not(ref inner) => {
|
||||
match **inner {
|
||||
And(_) | Or(_) => {
|
||||
s.push('!');
|
||||
recurse(true, cx, inner, terminals, s)
|
||||
}
|
||||
},
|
||||
Term(n) => {
|
||||
if let ExprBinary(binop, ref lhs, ref rhs) = terminals[n as usize].node {
|
||||
let op = match binop.node {
|
||||
|
|
@ -177,7 +179,7 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
_ => {
|
||||
s.push('!');
|
||||
return recurse(true, cx, inner, terminals, s);
|
||||
}
|
||||
},
|
||||
};
|
||||
s.push_str(&snip(lhs));
|
||||
s.push_str(op);
|
||||
|
|
@ -187,13 +189,13 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
s.push('!');
|
||||
recurse(false, cx, inner, terminals, s)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
And(ref v) => {
|
||||
if brackets {
|
||||
s.push('(');
|
||||
|
|
@ -215,7 +217,7 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
s.push(')');
|
||||
}
|
||||
s
|
||||
}
|
||||
},
|
||||
Or(ref v) => {
|
||||
if brackets {
|
||||
s.push('(');
|
||||
|
|
@ -229,7 +231,7 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
s.push(')');
|
||||
}
|
||||
s
|
||||
}
|
||||
},
|
||||
Term(n) => {
|
||||
if brackets {
|
||||
if let ExprBinary(..) = terminals[n as usize].node {
|
||||
|
|
@ -243,7 +245,7 @@ fn suggest(cx: &LateContext, suggestion: &Bool, terminals: &[&Expr]) -> String {
|
|||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
recurse(false, cx, suggestion, terminals, String::new())
|
||||
|
|
@ -260,13 +262,13 @@ fn simple_negate(b: Bool) -> Bool {
|
|||
*el = simple_negate(::std::mem::replace(el, True));
|
||||
}
|
||||
Or(v)
|
||||
}
|
||||
},
|
||||
Or(mut v) => {
|
||||
for el in &mut v {
|
||||
*el = simple_negate(::std::mem::replace(el, True));
|
||||
}
|
||||
And(v)
|
||||
}
|
||||
},
|
||||
Not(inner) => *inner,
|
||||
}
|
||||
}
|
||||
|
|
@ -288,13 +290,13 @@ fn terminal_stats(b: &Bool) -> Stats {
|
|||
_ => stats.negations += 1,
|
||||
}
|
||||
recurse(inner, stats);
|
||||
}
|
||||
},
|
||||
And(ref v) | Or(ref v) => {
|
||||
stats.ops += v.len() - 1;
|
||||
for inner in v {
|
||||
recurse(inner, stats);
|
||||
}
|
||||
}
|
||||
},
|
||||
Term(n) => stats.terminals[n as usize] += 1,
|
||||
}
|
||||
}
|
||||
|
|
@ -308,7 +310,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
fn bool_expr(&self, e: &Expr) {
|
||||
let mut h2q = Hir2Qmm {
|
||||
terminals: Vec::new(),
|
||||
cx: self.0,
|
||||
cx: self.cx,
|
||||
};
|
||||
if let Ok(expr) = h2q.run(e) {
|
||||
|
||||
|
|
@ -323,7 +325,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
let mut simplified = expr.simplify();
|
||||
for simple in Bool::Not(Box::new(expr.clone())).simplify() {
|
||||
match simple {
|
||||
Bool::Not(_) | Bool::True | Bool::False => {}
|
||||
Bool::Not(_) | Bool::True | Bool::False => {},
|
||||
_ => simplified.push(Bool::Not(Box::new(simple.clone()))),
|
||||
}
|
||||
let simple_negated = simple_negate(simple);
|
||||
|
|
@ -343,7 +345,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
continue 'simplified;
|
||||
}
|
||||
if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
|
||||
span_lint_and_then(self.0,
|
||||
span_lint_and_then(self.cx,
|
||||
LOGIC_BUG,
|
||||
e.span,
|
||||
"this boolean expression contains a logic bug",
|
||||
|
|
@ -353,7 +355,7 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
outer expression");
|
||||
db.span_suggestion(e.span,
|
||||
"it would look like the following",
|
||||
suggest(self.0, suggestion, &h2q.terminals));
|
||||
suggest(self.cx, suggestion, &h2q.terminals));
|
||||
});
|
||||
// don't also lint `NONMINIMAL_BOOL`
|
||||
return;
|
||||
|
|
@ -370,13 +372,13 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
if !improvements.is_empty() {
|
||||
span_lint_and_then(self.0,
|
||||
span_lint_and_then(self.cx,
|
||||
NONMINIMAL_BOOL,
|
||||
e.span,
|
||||
"this boolean expression can be simplified",
|
||||
|db| {
|
||||
for suggestion in &improvements {
|
||||
db.span_suggestion(e.span, "try", suggest(self.0, suggestion, &h2q.terminals));
|
||||
db.span_suggestion(e.span, "try", suggest(self.cx, suggestion, &h2q.terminals));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -384,21 +386,24 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'v, 'tcx> Visitor<'v> for NonminimalBoolVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'v Expr) {
|
||||
if in_macro(self.0, e.span) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr) {
|
||||
if in_macro(self.cx, e.span) {
|
||||
return;
|
||||
}
|
||||
match e.node {
|
||||
ExprBinary(binop, _, _) if binop.node == BiOr || binop.node == BiAnd => self.bool_expr(e),
|
||||
ExprUnary(UnNot, ref inner) => {
|
||||
if self.0.tcx.tables.borrow().node_types[&inner.id].is_bool() {
|
||||
if self.cx.tcx.tables.borrow().node_types[&inner.id].is_bool() {
|
||||
self.bool_expr(e);
|
||||
} else {
|
||||
walk_expr(self, e);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => walk_expr(self, e),
|
||||
}
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//!
|
||||
//! For example, the lint would catch:
|
||||
//!
|
||||
//! ```rust
|
||||
//! ```rust,ignore
|
||||
//! if x {
|
||||
//! if y {
|
||||
//! println!("Hello world");
|
||||
|
|
@ -28,7 +28,7 @@ use utils::sugg::Sugg;
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// …
|
||||
|
|
@ -48,7 +48,7 @@ use utils::sugg::Sugg;
|
|||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust.ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
|
|
@ -92,10 +92,10 @@ fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
|
|||
} else {
|
||||
check_collapsible_no_if_let(cx, expr, check, then);
|
||||
}
|
||||
}
|
||||
},
|
||||
ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
|
||||
check_collapsible_maybe_if_let(cx, else_);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -120,12 +120,7 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
|
|||
}}
|
||||
}
|
||||
|
||||
fn check_collapsible_no_if_let(
|
||||
cx: &EarlyContext,
|
||||
expr: &ast::Expr,
|
||||
check: &ast::Expr,
|
||||
then: &ast::Block,
|
||||
) {
|
||||
fn check_collapsible_no_if_let(cx: &EarlyContext, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
|
||||
if_let_chain! {[
|
||||
let Some(inner) = expr_block(then),
|
||||
let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
|
||||
|
|
@ -145,13 +140,14 @@ fn check_collapsible_no_if_let(
|
|||
}}
|
||||
}
|
||||
|
||||
/// If the block contains only one expression, returns it.
|
||||
/// If the block contains only one expression, return it.
|
||||
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
|
||||
let mut it = block.stmts.iter();
|
||||
|
||||
if let (Some(stmt), None) = (it.next(), it.next()) {
|
||||
match stmt.node {
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
|
||||
ast::StmtKind::Expr(ref expr) |
|
||||
ast::StmtKind::Semi(ref expr) => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(cast_possible_truncation)]
|
||||
|
||||
use rustc::lint::LateContext;
|
||||
use rustc::hir::def::{Def, PathResolution};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc_const_eval::lookup_const_by_id;
|
||||
use rustc_const_math::{ConstInt, ConstUsize, ConstIsize};
|
||||
use rustc::hir::*;
|
||||
|
|
@ -10,7 +10,7 @@ use std::cmp::PartialOrd;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy, IntTy};
|
||||
use syntax::ast::{FloatTy, LitIntType, LitKind, StrStyle, UintTy, IntTy, NodeId};
|
||||
use syntax::ptr::P;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
@ -75,18 +75,16 @@ impl PartialEq for Constant {
|
|||
(&Constant::Char(l), &Constant::Char(r)) => l == r,
|
||||
(&Constant::Int(l), &Constant::Int(r)) => {
|
||||
l.is_negative() == r.is_negative() && l.to_u64_unchecked() == r.to_u64_unchecked()
|
||||
}
|
||||
},
|
||||
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => {
|
||||
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
|
||||
// `Fw32 == Fw64` so don’t compare them
|
||||
match (ls.parse::<f64>(), rs.parse::<f64>()) {
|
||||
// mem::transmute is required to catch non-matching 0.0, -0.0, and NaNs
|
||||
(Ok(l), Ok(r)) => unsafe {
|
||||
mem::transmute::<f64, u64>(l) == mem::transmute::<f64, u64>(r)
|
||||
},
|
||||
(Ok(l), Ok(r)) => unsafe { mem::transmute::<f64, u64>(l) == mem::transmute::<f64, u64>(r) },
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
},
|
||||
(&Constant::Bool(l), &Constant::Bool(r)) => l == r,
|
||||
(&Constant::Vec(ref l), &Constant::Vec(ref r)) => l == r,
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
|
||||
|
|
@ -104,34 +102,34 @@ impl Hash for Constant {
|
|||
Constant::Str(ref s, ref k) => {
|
||||
s.hash(state);
|
||||
k.hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Binary(ref b) => {
|
||||
b.hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Char(c) => {
|
||||
c.hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Int(i) => {
|
||||
i.to_u64_unchecked().hash(state);
|
||||
i.is_negative().hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Float(ref f, _) => {
|
||||
// don’t use the width here because of PartialEq implementation
|
||||
if let Ok(f) = f.parse::<f64>() {
|
||||
unsafe { mem::transmute::<f64, u64>(f) }.hash(state);
|
||||
}
|
||||
}
|
||||
},
|
||||
Constant::Bool(b) => {
|
||||
b.hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Vec(ref v) |
|
||||
Constant::Tuple(ref v) => {
|
||||
v.hash(state);
|
||||
}
|
||||
},
|
||||
Constant::Repeat(ref c, l) => {
|
||||
c.hash(state);
|
||||
l.hash(state);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -145,19 +143,21 @@ impl PartialOrd for Constant {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Int(l), &Constant::Int(r)) => Some(l.cmp(&r)),
|
||||
(&Constant::Float(ref ls, _), &Constant::Float(ref rs, _)) => {
|
||||
match (ls.parse::<f64>(), rs.parse::<f64>()) {
|
||||
(Ok(ref l), Ok(ref r)) => match (l.partial_cmp(r), l.is_sign_positive() == r.is_sign_positive()) {
|
||||
// Check for comparison of -0.0 and 0.0
|
||||
(Some(Ordering::Equal), false) => None,
|
||||
(x, _) => x
|
||||
(Ok(ref l), Ok(ref r)) => {
|
||||
match (l.partial_cmp(r), l.is_sign_positive() == r.is_sign_positive()) {
|
||||
// Check for comparison of -0.0 and 0.0
|
||||
(Some(Ordering::Equal), false) => None,
|
||||
(x, _) => x,
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) |
|
||||
(&Constant::Vec(ref l), &Constant::Vec(ref r)) => l.partial_cmp(r),
|
||||
|
|
@ -166,7 +166,7 @@ impl PartialOrd for Constant {
|
|||
Some(Equal) => Some(ls.cmp(rs)),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None, //TODO: Are there any useful inter-type orderings?
|
||||
}
|
||||
}
|
||||
|
|
@ -187,14 +187,14 @@ pub fn lit_to_constant(lit: &LitKind) -> Constant {
|
|||
LitKind::Int(value, LitIntType::Unsigned(UintTy::U64)) => Constant::Int(ConstInt::U64(value as u64)),
|
||||
LitKind::Int(value, LitIntType::Unsigned(UintTy::Us)) => {
|
||||
Constant::Int(ConstInt::Usize(ConstUsize::Us32(value as u32)))
|
||||
}
|
||||
},
|
||||
LitKind::Int(value, LitIntType::Signed(IntTy::I8)) => Constant::Int(ConstInt::I8(value as i8)),
|
||||
LitKind::Int(value, LitIntType::Signed(IntTy::I16)) => Constant::Int(ConstInt::I16(value as i16)),
|
||||
LitKind::Int(value, LitIntType::Signed(IntTy::I32)) => Constant::Int(ConstInt::I32(value as i32)),
|
||||
LitKind::Int(value, LitIntType::Signed(IntTy::I64)) => Constant::Int(ConstInt::I64(value as i64)),
|
||||
LitKind::Int(value, LitIntType::Signed(IntTy::Is)) => {
|
||||
Constant::Int(ConstInt::Isize(ConstIsize::Is32(value as i32)))
|
||||
}
|
||||
},
|
||||
LitKind::Float(ref is, ty) => Constant::Float(is.to_string(), ty.into()),
|
||||
LitKind::FloatUnsuffixed(ref is) => Constant::Float(is.to_string(), FloatWidth::Any),
|
||||
LitKind::Bool(b) => Constant::Bool(b),
|
||||
|
|
@ -252,7 +252,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
/// simple constant folding: Insert an expression, get a constant or none.
|
||||
fn expr(&mut self, e: &Expr) -> Option<Constant> {
|
||||
match e.node {
|
||||
ExprPath(_, _) => self.fetch_path(e),
|
||||
ExprPath(ref qpath) => self.fetch_path(qpath, e.id),
|
||||
ExprBlock(ref block) => self.block(block),
|
||||
ExprIf(ref cond, ref then, ref otherwise) => self.ifthenelse(cond, then, otherwise),
|
||||
ExprLit(ref lit) => Some(lit_to_constant(&lit.node)),
|
||||
|
|
@ -260,7 +260,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
ExprTup(ref tup) => self.multi(tup).map(Constant::Tuple),
|
||||
ExprRepeat(ref value, ref number) => {
|
||||
self.binop_apply(value, number, |v, n| Some(Constant::Repeat(Box::new(v), n.as_u64() as usize)))
|
||||
}
|
||||
},
|
||||
ExprUnary(op, ref operand) => {
|
||||
self.expr(operand).and_then(|o| {
|
||||
match op {
|
||||
|
|
@ -269,7 +269,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
UnDeref => Some(o),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
ExprBinary(op, ref left, ref right) => self.binop(op, left, right),
|
||||
// TODO: add other expressions
|
||||
_ => None,
|
||||
|
|
@ -280,26 +280,30 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
/// non-constant part
|
||||
fn multi(&mut self, vec: &[Expr]) -> Option<Vec<Constant>> {
|
||||
vec.iter()
|
||||
.map(|elem| self.expr(elem))
|
||||
.collect::<Option<_>>()
|
||||
.map(|elem| self.expr(elem))
|
||||
.collect::<Option<_>>()
|
||||
}
|
||||
|
||||
/// lookup a possibly constant expression from a ExprPath
|
||||
fn fetch_path(&mut self, e: &Expr) -> Option<Constant> {
|
||||
fn fetch_path(&mut self, qpath: &QPath, id: NodeId) -> Option<Constant> {
|
||||
if let Some(lcx) = self.lcx {
|
||||
let mut maybe_id = None;
|
||||
if let Some(&PathResolution { base_def: Def::Const(id), .. }) = lcx.tcx.def_map.borrow().get(&e.id) {
|
||||
maybe_id = Some(id);
|
||||
}
|
||||
// separate if lets to avoid double borrowing the def_map
|
||||
if let Some(id) = maybe_id {
|
||||
if let Some((const_expr, _ty)) = lookup_const_by_id(lcx.tcx, id, None) {
|
||||
let ret = self.expr(const_expr);
|
||||
if ret.is_some() {
|
||||
self.needed_resolution = true;
|
||||
let def = lcx.tcx.tables().qpath_def(qpath, id);
|
||||
match def {
|
||||
Def::Const(def_id) |
|
||||
Def::AssociatedConst(def_id) => {
|
||||
let substs = Some(lcx.tcx
|
||||
.tables()
|
||||
.node_id_item_substs(id)
|
||||
.unwrap_or_else(|| lcx.tcx.intern_substs(&[])));
|
||||
if let Some((const_expr, _ty)) = lookup_const_by_id(lcx.tcx, def_id, substs) {
|
||||
let ret = self.expr(const_expr);
|
||||
if ret.is_some() {
|
||||
self.needed_resolution = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
None
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ impl LintPass for CopyAndPaste {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if !in_macro(cx, expr.span) {
|
||||
// skip ifs directly in else, it will be checked in the parent if
|
||||
if let Some(&Expr { node: ExprIf(_, _, Some(ref else_expr)), .. }) = get_parent_expr(cx, expr) {
|
||||
|
|
@ -204,8 +204,11 @@ fn lint_match_arms(cx: &LateContext, expr: &Expr) {
|
|||
|
||||
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));
|
||||
// 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));
|
||||
}
|
||||
|
|
@ -245,7 +248,11 @@ fn if_sequence(mut expr: &Expr) -> (SmallVector<&Expr>, SmallVector<&Block>) {
|
|||
|
||||
/// Return the list of bindings in a pattern.
|
||||
fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<InternedString, ty::Ty<'tcx>> {
|
||||
fn bindings_impl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat, map: &mut HashMap<InternedString, ty::Ty<'tcx>>) {
|
||||
fn bindings_impl<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &Pat,
|
||||
map: &mut HashMap<InternedString, ty::Ty<'tcx>>
|
||||
) {
|
||||
match pat.node {
|
||||
PatKind::Box(ref pat) |
|
||||
PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
|
||||
|
|
@ -253,25 +260,25 @@ fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<Interned
|
|||
for pat in pats {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
}
|
||||
PatKind::Binding(_, ref ident, ref as_pat) => {
|
||||
},
|
||||
PatKind::Binding(_, _, ref ident, ref as_pat) => {
|
||||
if let Entry::Vacant(v) = map.entry(ident.node.as_str()) {
|
||||
v.insert(cx.tcx.tables().pat_ty(pat));
|
||||
}
|
||||
if let Some(ref as_pat) = *as_pat {
|
||||
bindings_impl(cx, as_pat, map);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, ref fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, &pat.node.pat, map);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Tuple(ref fields, _) => {
|
||||
for pat in fields {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Slice(ref lhs, ref mid, ref rhs) => {
|
||||
for pat in lhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
|
|
@ -282,7 +289,7 @@ fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> HashMap<Interned
|
|||
for pat in rhs {
|
||||
bindings_impl(cx, pat, map);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Lit(..) |
|
||||
PatKind::Range(..) |
|
||||
PatKind::Wild |
|
||||
|
|
@ -320,10 +327,10 @@ fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Option<(&T, &T)>
|
|||
return Some((o, expr));
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Entry::Vacant(v) => {
|
||||
v.insert(vec![expr]);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use rustc::cfg::CFG;
|
|||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use syntax::ast::Attribute;
|
||||
use syntax::attr;
|
||||
use syntax::codemap::Span;
|
||||
|
|
@ -42,7 +42,7 @@ impl LintPass for CyclomaticComplexity {
|
|||
}
|
||||
|
||||
impl CyclomaticComplexity {
|
||||
fn check<'a, 'tcx>(&mut self, cx: &'a LateContext<'a, 'tcx>, expr: &Expr, span: Span) {
|
||||
fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, expr: &'tcx Expr, span: Span) {
|
||||
if in_macro(cx, span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -60,7 +60,7 @@ impl CyclomaticComplexity {
|
|||
divergence: 0,
|
||||
short_circuits: 0,
|
||||
returns: 0,
|
||||
tcx: &cx.tcx,
|
||||
cx: cx,
|
||||
};
|
||||
helper.visit_expr(expr);
|
||||
let CCHelper { match_arms, divergence, short_circuits, returns, .. } = helper;
|
||||
|
|
@ -90,45 +90,45 @@ impl CyclomaticComplexity {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for CyclomaticComplexity {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
if let ItemFn(_, _, _, _, _, ref expr) = item.node {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemFn(_, _, _, _, _, eid) = item.node {
|
||||
if !attr::contains_name(&item.attrs, "test") {
|
||||
self.check(cx, expr, item.span);
|
||||
self.check(cx, cx.tcx.map.expr(eid), item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
|
||||
if let ImplItemKind::Method(_, ref expr) = item.node {
|
||||
self.check(cx, expr, item.span);
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
if let ImplItemKind::Method(_, eid) = item.node {
|
||||
self.check(cx, cx.tcx.map.expr(eid), item.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
if let MethodTraitItem(_, Some(ref expr)) = item.node {
|
||||
self.check(cx, expr, item.span);
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
if let MethodTraitItem(_, Some(eid)) = item.node {
|
||||
self.check(cx, cx.tcx.map.expr(eid), item.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_lint_attrs(&mut self, cx: &LateContext, attrs: &[Attribute]) {
|
||||
fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.push_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
}
|
||||
fn exit_lint_attrs(&mut self, cx: &LateContext, attrs: &[Attribute]) {
|
||||
fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.pop_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
}
|
||||
}
|
||||
|
||||
struct CCHelper<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
|
||||
struct CCHelper<'a, 'tcx: 'a> {
|
||||
match_arms: u64,
|
||||
divergence: u64,
|
||||
returns: u64,
|
||||
short_circuits: u64, // && and ||
|
||||
tcx: &'a ty::TyCtxt<'a, 'gcx, 'tcx>,
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'tcx, 'gcx> Visitor<'a> for CCHelper<'b, 'gcx, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'a Expr) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for CCHelper<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr) {
|
||||
match e.node {
|
||||
ExprMatch(_, ref arms, _) => {
|
||||
walk_expr(self, e);
|
||||
|
|
@ -136,18 +136,18 @@ impl<'a, 'b, 'tcx, 'gcx> Visitor<'a> for CCHelper<'b, 'gcx, 'tcx> {
|
|||
if arms_n > 1 {
|
||||
self.match_arms += arms_n - 2;
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprCall(ref callee, _) => {
|
||||
walk_expr(self, e);
|
||||
let ty = self.tcx.tables().node_id_to_type(callee.id);
|
||||
let ty = self.cx.tcx.tables().node_id_to_type(callee.id);
|
||||
match ty.sty {
|
||||
ty::TyFnDef(_, _, ty) |
|
||||
ty::TyFnPtr(ty) if ty.sig.skip_binder().output.sty == ty::TyNever => {
|
||||
ty::TyFnPtr(ty) if ty.sig.skip_binder().output().sty == ty::TyNever => {
|
||||
self.divergence += 1;
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprClosure(..) => (),
|
||||
ExprBinary(op, _, _) => {
|
||||
walk_expr(self, e);
|
||||
|
|
@ -155,11 +155,14 @@ impl<'a, 'b, 'tcx, 'gcx> Visitor<'a> for CCHelper<'b, 'gcx, 'tcx> {
|
|||
BiAnd | BiOr => self.short_circuits += 1,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprRet(_) => self.returns += 1,
|
||||
_ => walk_expr(self, e),
|
||||
}
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="debugging")]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use rustc::ty;
|
|||
use rustc::hir::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::paths;
|
||||
use utils::{is_automatically_derived, match_path, span_lint_and_then};
|
||||
use utils::{is_automatically_derived, span_lint_and_then, match_path_old};
|
||||
|
||||
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
|
||||
/// explicitly.
|
||||
|
|
@ -70,8 +70,8 @@ impl LintPass for Derive {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemImpl(_, _, _, Some(ref trait_ref), _, _) = item.node {
|
||||
let ty = cx.tcx.item_type(cx.tcx.map.local_def_id(item.id));
|
||||
let is_automatically_derived = is_automatically_derived(&*item.attrs);
|
||||
|
|
@ -86,10 +86,15 @@ impl LateLintPass for Derive {
|
|||
}
|
||||
|
||||
/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
|
||||
fn check_hash_peq<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, span: Span, trait_ref: &TraitRef, ty: ty::Ty<'tcx>,
|
||||
hash_is_automatically_derived: bool) {
|
||||
fn check_hash_peq<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
span: Span,
|
||||
trait_ref: &TraitRef,
|
||||
ty: ty::Ty<'tcx>,
|
||||
hash_is_automatically_derived: bool
|
||||
) {
|
||||
if_let_chain! {[
|
||||
match_path(&trait_ref.path, &paths::HASH),
|
||||
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.lookup_trait_def(peq_trait_def_id);
|
||||
|
|
@ -131,7 +136,7 @@ fn check_hash_peq<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, span: Span, trait_re
|
|||
|
||||
/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
|
||||
fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref: &TraitRef, ty: ty::Ty<'tcx>) {
|
||||
if match_path(&trait_ref.path, &paths::CLONE_TRAIT) {
|
||||
if match_path_old(&trait_ref.path, &paths::CLONE_TRAIT) {
|
||||
let parameter_environment = ty::ParameterEnvironment::for_item(cx.tcx, item.id);
|
||||
let subst_ty = ty.subst(cx.tcx, parameter_environment.free_substs);
|
||||
|
||||
|
|
@ -149,18 +154,18 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref
|
|||
match field.ty(cx.tcx, substs).sty {
|
||||
TypeVariants::TyArray(_, size) if size > 32 => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
TypeVariants::TyFnPtr(..) => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
TypeVariants::TyTuple(tys) if tys.len() > 12 => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +174,7 @@ fn check_copy_clone<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, item: &Item, trait_ref
|
|||
item.span,
|
||||
"you are implementing `Clone` explicitly on a `Copy` type",
|
||||
|db| {
|
||||
db.span_note(item.span, "consider deriving `Clone` or removing `Copy`");
|
||||
});
|
||||
db.span_note(item.span, "consider deriving `Clone` or removing `Copy`");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,26 +64,21 @@ pub fn strip_doc_comment_decoration((comment, span): (String, Span)) -> Vec<(Str
|
|||
const ONELINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
|
||||
for prefix in ONELINERS {
|
||||
if comment.starts_with(*prefix) {
|
||||
return vec![(
|
||||
comment[prefix.len()..].to_owned(),
|
||||
Span { lo: span.lo + BytePos(prefix.len() as u32), ..span }
|
||||
)];
|
||||
return vec![(comment[prefix.len()..].to_owned(),
|
||||
Span { lo: span.lo + BytePos(prefix.len() as u32), ..span })];
|
||||
}
|
||||
}
|
||||
|
||||
if comment.starts_with("/*") {
|
||||
return comment[3..comment.len() - 2].lines().map(|line| {
|
||||
let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
|
||||
debug_assert_eq!(offset as u32 as usize, offset);
|
||||
return comment[3..comment.len() - 2]
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
|
||||
debug_assert_eq!(offset as u32 as usize, offset);
|
||||
|
||||
(
|
||||
line.to_owned(),
|
||||
Span {
|
||||
lo: span.lo + BytePos(offset as u32),
|
||||
..span
|
||||
}
|
||||
)
|
||||
}).collect();
|
||||
(line.to_owned(), Span { lo: span.lo + BytePos(offset as u32), ..span })
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
|
||||
panic!("not a doc-comment: {}", comment);
|
||||
|
|
@ -273,7 +268,9 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]
|
|||
}
|
||||
|
||||
lookup_parser = parser.clone();
|
||||
if let (Some((false, $c)), Some((false, $c))) = (lookup_parser.next(), lookup_parser.next()) {
|
||||
let a = lookup_parser.next();
|
||||
let b = lookup_parser.next();
|
||||
if let (Some((false, $c)), Some((false, $c))) = (a, b) {
|
||||
let mut close_count = 3;
|
||||
while let Some((false, $c)) = lookup_parser.next() {
|
||||
close_count += 1;
|
||||
|
|
@ -299,16 +296,17 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]
|
|||
match parser.next() {
|
||||
Some((new_line, c)) => {
|
||||
match c {
|
||||
'#' if new_line => { // don’t warn on titles
|
||||
'#' if new_line => {
|
||||
// don’t warn on titles
|
||||
parser.next_line();
|
||||
}
|
||||
},
|
||||
'`' => {
|
||||
if try!(check_block!(parser, '`', new_line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try!(parser.jump_to('`')); // not a code block, just inline code
|
||||
}
|
||||
},
|
||||
'~' => {
|
||||
if try!(check_block!(parser, '~', new_line)) {
|
||||
continue;
|
||||
|
|
@ -317,7 +315,7 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]
|
|||
// ~ does not introduce inline code, but two of them introduce
|
||||
// strikethrough. Too bad for the consistency but we don't care about
|
||||
// strikethrough.
|
||||
}
|
||||
},
|
||||
'[' => {
|
||||
// Check for a reference definition `[foo]:` at the beginning of a line
|
||||
let mut link = true;
|
||||
|
|
@ -335,24 +333,24 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]
|
|||
|
||||
parser.advance_begin();
|
||||
parser.link = link;
|
||||
}
|
||||
},
|
||||
']' if parser.link => {
|
||||
parser.link = false;
|
||||
|
||||
match parser.peek() {
|
||||
Some('(') => {
|
||||
try!(parser.jump_to(')'));
|
||||
}
|
||||
},
|
||||
Some('[') => {
|
||||
try!(parser.jump_to(']'));
|
||||
}
|
||||
},
|
||||
Some(_) => continue,
|
||||
None => return Err(()),
|
||||
}
|
||||
}
|
||||
},
|
||||
c if !is_path_char(c) => {
|
||||
parser.advance_begin();
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
if let Some((_, c)) = parser.find(|&(_, c)| !is_path_char(c)) {
|
||||
parser.put_back(c);
|
||||
|
|
@ -361,10 +359,10 @@ fn check_doc(cx: &EarlyContext, valid_idents: &[String], docs: &[(String, Span)]
|
|||
let (word, span) = parser.word();
|
||||
check_word(cx, valid_idents, word, span);
|
||||
parser.advance_begin();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
|
@ -386,8 +384,7 @@ fn check_word(cx: &EarlyContext, valid_idents: &[String], word: &str, span: Span
|
|||
s
|
||||
};
|
||||
|
||||
s.chars().all(char::is_alphanumeric) &&
|
||||
s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1 &&
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,11 +35,11 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprCall(ref path, ref args) = expr.node {
|
||||
if let ExprPath(None, _) = path.node {
|
||||
let def_id = cx.tcx.expect_def(path.id).def_id();
|
||||
if let ExprPath(ref qpath) = path.node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, path.id).def_id();
|
||||
if match_def_path(cx, def_id, &paths::DROP) {
|
||||
if args.len() != 1 {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, NestedVisitorMap};
|
||||
use rustc::lint::*;
|
||||
use syntax::codemap::Span;
|
||||
use utils::SpanlessEq;
|
||||
|
|
@ -39,8 +39,8 @@ impl LintPass for HashMapLint {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for HashMapLint {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for HashMapLint {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprIf(ref check, ref then_block, ref else_block) = expr.node {
|
||||
if let ExprUnary(UnOp::UnNot, ref check) = check.node {
|
||||
if let Some((ty, map, key)) = check_cond(cx, check) {
|
||||
|
|
@ -78,7 +78,10 @@ impl LateLintPass for HashMapLint {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_cond<'a, 'tcx, 'b>(cx: &'a LateContext<'a, 'tcx>, check: &'b Expr) -> Option<(&'static str, &'b Expr, &'b Expr)> {
|
||||
fn check_cond<'a, 'tcx, 'b>(
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
check: &'b Expr
|
||||
) -> Option<(&'static str, &'b Expr, &'b Expr)> {
|
||||
if_let_chain! {[
|
||||
let ExprMethodCall(ref name, _, ref params) = check.node,
|
||||
params.len() >= 2,
|
||||
|
|
@ -111,8 +114,8 @@ struct InsertVisitor<'a, 'tcx: 'a, 'b> {
|
|||
sole_expr: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v, 'b> Visitor<'v> for InsertVisitor<'a, 'tcx, 'b> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
impl<'a, 'tcx, 'b> Visitor<'tcx> for InsertVisitor<'a, 'tcx, 'b> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if_let_chain! {[
|
||||
let ExprMethodCall(ref name, _, ref params) = expr.node,
|
||||
params.len() == 3,
|
||||
|
|
@ -144,4 +147,7 @@ impl<'a, 'tcx, 'v, 'b> Visitor<'v> for InsertVisitor<'a, 'tcx, 'b> {
|
|||
walk_expr(self, expr);
|
||||
}
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ impl LintPass for UnportableVariant {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for UnportableVariant {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
|
||||
#[allow(cast_possible_truncation, cast_sign_loss)]
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemEnum(ref def, _) = item.node {
|
||||
for var in &def.variants {
|
||||
let variant = &var.node;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
//! lint on `use`ing all variants of an enum
|
||||
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::map::Node::NodeItem;
|
||||
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray, LintContext};
|
||||
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray};
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::codemap::Span;
|
||||
use utils::span_lint;
|
||||
|
|
@ -34,8 +32,8 @@ impl LintPass for EnumGlobUse {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for EnumGlobUse {
|
||||
fn check_mod(&mut self, cx: &LateContext, m: &Mod, _: Span, _: NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EnumGlobUse {
|
||||
fn check_mod(&mut self, cx: &LateContext<'a, 'tcx>, m: &'tcx Mod, _: Span, _: NodeId) {
|
||||
// only check top level `use` statements
|
||||
for item in &m.item_ids {
|
||||
self.lint_item(cx, cx.krate.item(item.id));
|
||||
|
|
@ -48,24 +46,12 @@ impl EnumGlobUse {
|
|||
if item.vis == Visibility::Public {
|
||||
return; // re-exports are fine
|
||||
}
|
||||
if let ItemUse(ref item_use) = item.node {
|
||||
if let ViewPath_::ViewPathGlob(_) = item_use.node {
|
||||
if let Some(def) = cx.tcx.def_map.borrow().get(&item.id) {
|
||||
if let Some(node_id) = cx.tcx.map.as_local_node_id(def.full_def().def_id()) {
|
||||
if let Some(NodeItem(it)) = cx.tcx.map.find(node_id) {
|
||||
if let ItemEnum(..) = it.node {
|
||||
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let child = cx.sess().cstore.item_children(def.full_def().def_id());
|
||||
if let Some(child) = child.first() {
|
||||
if let Def::Variant(..) = child.def {
|
||||
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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`
|
||||
// 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) {
|
||||
span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,27 @@ declare_lint! {
|
|||
"enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
/// **What it does:** Detects enumeration variants that are prefixed or suffixed
|
||||
/// by the same characters.
|
||||
///
|
||||
/// **Why is this bad?** Enumeration variant names should specify their variant,
|
||||
/// not repeat the enumeration name.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Cake {
|
||||
/// BlackForestCake,
|
||||
/// HummingbirdCake,
|
||||
/// }
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub PUB_ENUM_VARIANT_NAMES,
|
||||
Allow,
|
||||
"enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
/// **What it does:** Detects type names that are prefixed or suffixed by the
|
||||
/// containing module's name.
|
||||
///
|
||||
|
|
@ -49,10 +70,13 @@ declare_lint! {
|
|||
|
||||
/// **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 { .. }` in `foo.rs`.
|
||||
/// The expectation is that items inside the inner `mod foo { .. }` are then available
|
||||
/// **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`.
|
||||
/// If this is done on purpose, it would be better to choose a more representative module name.
|
||||
/// If this is done on purpose, it would be better to choose a more
|
||||
/// representative module name.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
|
|
@ -78,13 +102,16 @@ pub struct EnumVariantNames {
|
|||
|
||||
impl EnumVariantNames {
|
||||
pub fn new(threshold: u64) -> EnumVariantNames {
|
||||
EnumVariantNames { modules: Vec::new(), threshold: threshold }
|
||||
EnumVariantNames {
|
||||
modules: Vec::new(),
|
||||
threshold: threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LintPass for EnumVariantNames {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(ENUM_VARIANT_NAMES, STUTTER, MODULE_INCEPTION)
|
||||
lint_array!(ENUM_VARIANT_NAMES, PUB_ENUM_VARIANT_NAMES, STUTTER, MODULE_INCEPTION)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -108,18 +135,25 @@ fn partial_rmatch(post: &str, name: &str) -> usize {
|
|||
|
||||
// FIXME: #600
|
||||
#[allow(while_let_on_iterator)]
|
||||
fn check_variant(cx: &EarlyContext, threshold: u64, def: &EnumDef, item_name: &str,
|
||||
item_name_chars: usize, span: Span) {
|
||||
fn check_variant(
|
||||
cx: &EarlyContext,
|
||||
threshold: u64,
|
||||
def: &EnumDef,
|
||||
item_name: &str,
|
||||
item_name_chars: usize,
|
||||
span: Span,
|
||||
lint: &'static Lint
|
||||
) {
|
||||
if (def.variants.len() as u64) < threshold {
|
||||
return;
|
||||
}
|
||||
for var in &def.variants {
|
||||
let name = var2str(var);
|
||||
if partial_match(item_name, &name) == item_name_chars {
|
||||
span_lint(cx, ENUM_VARIANT_NAMES, var.span, "Variant name starts with the enum's name");
|
||||
span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
|
||||
}
|
||||
if partial_rmatch(item_name, &name) == item_name_chars {
|
||||
span_lint(cx, ENUM_VARIANT_NAMES, var.span, "Variant name ends with the enum's name");
|
||||
span_lint(cx, lint, var.span, "Variant name ends with the enum's name");
|
||||
}
|
||||
}
|
||||
let first = var2str(&def.variants[0]);
|
||||
|
|
@ -154,7 +188,7 @@ fn check_variant(cx: &EarlyContext, threshold: u64, def: &EnumDef, item_name: &s
|
|||
(true, false) => ("post", post),
|
||||
};
|
||||
span_help_and_lint(cx,
|
||||
ENUM_VARIANT_NAMES,
|
||||
lint,
|
||||
span,
|
||||
&format!("All variants have the same {}fix: `{}`", what, value),
|
||||
&format!("remove the {}fixes and use full paths to \
|
||||
|
|
@ -200,7 +234,10 @@ 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 {
|
||||
|
|
@ -218,7 +255,11 @@ impl EarlyLintPass for EnumVariantNames {
|
|||
}
|
||||
}
|
||||
if let ItemKind::Enum(ref def, _) = item.node {
|
||||
check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span);
|
||||
let lint = match item.vis {
|
||||
Visibility::Public => PUB_ENUM_VARIANT_NAMES,
|
||||
_ => ENUM_VARIANT_NAMES,
|
||||
};
|
||||
check_variant(cx, self.threshold, def, &item_name, item_name_chars, item.span, lint);
|
||||
}
|
||||
self.modules.push((item_name, item_camel));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ impl LintPass for EqOp {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for EqOp {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
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,
|
||||
|
|
@ -48,19 +48,7 @@ impl LateLintPass for EqOp {
|
|||
|
||||
fn is_valid_operator(op: &BinOp) -> bool {
|
||||
match op.node {
|
||||
BiSub |
|
||||
BiDiv |
|
||||
BiEq |
|
||||
BiLt |
|
||||
BiLe |
|
||||
BiGt |
|
||||
BiGe |
|
||||
BiNe |
|
||||
BiAnd |
|
||||
BiOr |
|
||||
BiBitXor |
|
||||
BiBitAnd |
|
||||
BiBitOr => true,
|
||||
BiSub | BiDiv | BiEq | BiLt | BiLe | BiGt | BiGe | BiNe | BiAnd | BiOr | BiBitXor | BiBitAnd | BiBitOr => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ fn is_non_trait_box(ty: ty::Ty) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
struct EscapeDelegate<'a, 'tcx: 'a+'gcx, 'gcx: 'a> {
|
||||
struct EscapeDelegate<'a, 'tcx: 'a + 'gcx, 'gcx: 'a> {
|
||||
tcx: ty::TyCtxt<'a, 'tcx, 'tcx>,
|
||||
set: NodeSet,
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'gcx>,
|
||||
|
|
@ -60,8 +60,16 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_fn(&mut self, cx: &LateContext, _: visit::FnKind, decl: &FnDecl, body: &Expr, _: Span, id: NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
_: visit::FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl,
|
||||
body: &'tcx Expr,
|
||||
_: Span,
|
||||
id: NodeId
|
||||
) {
|
||||
let param_env = ty::ParameterEnvironment::for_item(cx.tcx, id);
|
||||
|
||||
let infcx = cx.tcx.borrowck_fake_infer_ctxt(param_env);
|
||||
|
|
@ -90,7 +98,7 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a+'gcx, 'gcx: 'a> Delegate<'tcx> for EscapeDelegate<'a, 'tcx, 'gcx> {
|
||||
impl<'a, 'tcx: 'a + 'gcx, 'gcx: 'a> Delegate<'tcx> for EscapeDelegate<'a, 'tcx, 'gcx> {
|
||||
fn consume(&mut self, _: NodeId, _: Span, cmt: cmt<'tcx>, mode: ConsumeMode) {
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
if self.set.contains(&lid) {
|
||||
|
|
@ -143,18 +151,26 @@ impl<'a, 'tcx: 'a+'gcx, 'gcx: 'a> Delegate<'tcx> for EscapeDelegate<'a, 'tcx, 'g
|
|||
}
|
||||
|
||||
}
|
||||
fn borrow(&mut self, borrow_id: NodeId, _: Span, cmt: cmt<'tcx>, _: &ty::Region, _: ty::BorrowKind,
|
||||
loan_cause: LoanCause) {
|
||||
fn borrow(
|
||||
&mut self,
|
||||
borrow_id: NodeId,
|
||||
_: Span,
|
||||
cmt: cmt<'tcx>,
|
||||
_: &ty::Region,
|
||||
_: ty::BorrowKind,
|
||||
loan_cause: LoanCause
|
||||
) {
|
||||
use rustc::ty::adjustment::Adjust;
|
||||
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
if self.set.contains(&lid) {
|
||||
if let Some(&Adjust::DerefRef { autoderefs, .. }) = self.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.adjustments
|
||||
.get(&borrow_id)
|
||||
.map(|a| &a.kind) {
|
||||
if let Some(&Adjust::DerefRef { autoderefs, .. }) =
|
||||
self.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.adjustments
|
||||
.get(&borrow_id)
|
||||
.map(|a| &a.kind) {
|
||||
if LoanCause::AutoRef == loan_cause {
|
||||
// x.foo()
|
||||
if autoderefs == 0 {
|
||||
|
|
@ -165,14 +181,15 @@ impl<'a, 'tcx: 'a+'gcx, 'gcx: 'a> Delegate<'tcx> for EscapeDelegate<'a, 'tcx, 'g
|
|||
}
|
||||
} else if LoanCause::AddrOf == loan_cause {
|
||||
// &x
|
||||
if let Some(&Adjust::DerefRef { autoderefs, .. }) = self.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.adjustments
|
||||
.get(&self.tcx
|
||||
.map
|
||||
.get_parent_node(borrow_id))
|
||||
.map(|a| &a.kind) {
|
||||
if let Some(&Adjust::DerefRef { autoderefs, .. }) =
|
||||
self.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.adjustments
|
||||
.get(&self.tcx
|
||||
.map
|
||||
.get_parent_node(borrow_id))
|
||||
.map(|a| &a.kind) {
|
||||
if autoderefs <= 1 {
|
||||
// foo(&x) where no extra autoreffing is happening
|
||||
self.set.remove(&lid);
|
||||
|
|
@ -190,7 +207,7 @@ impl<'a, 'tcx: 'a+'gcx, 'gcx: 'a> Delegate<'tcx> for EscapeDelegate<'a, 'tcx, 'g
|
|||
fn mutate(&mut self, _: NodeId, _: Span, _: cmt<'tcx>, _: MutateMode) {}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a+'gcx, 'gcx: 'a> EscapeDelegate<'a, 'tcx, 'gcx> {
|
||||
impl<'a, 'tcx: 'a + 'gcx, 'gcx: 'a> EscapeDelegate<'a, 'tcx, 'gcx> {
|
||||
fn is_large_box(&self, ty: ty::Ty<'gcx>) -> bool {
|
||||
// Large types need to be boxed to avoid stack
|
||||
// overflows.
|
||||
|
|
|
|||
|
|
@ -33,22 +33,23 @@ impl LintPass for EtaPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for EtaPass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
match expr.node {
|
||||
ExprCall(_, ref args) |
|
||||
ExprMethodCall(_, _, ref args) => {
|
||||
for arg in args {
|
||||
check_closure(cx, arg)
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_closure(cx: &LateContext, expr: &Expr) {
|
||||
if let ExprClosure(_, ref decl, ref ex, _) = expr.node {
|
||||
if let ExprClosure(_, ref decl, eid, _) = expr.node {
|
||||
let ex = cx.tcx.map.expr(eid);
|
||||
if let ExprCall(ref caller, ref args) = ex.node {
|
||||
if args.len() != decl.inputs.len() {
|
||||
// Not the same number of arguments, there
|
||||
|
|
@ -64,17 +65,16 @@ fn check_closure(cx: &LateContext, expr: &Expr) {
|
|||
// Is it an unsafe function? They don't implement the closure traits
|
||||
ty::TyFnDef(_, _, fn_ty) |
|
||||
ty::TyFnPtr(fn_ty) => {
|
||||
if fn_ty.unsafety == Unsafety::Unsafe ||
|
||||
fn_ty.sig.skip_binder().output.sty == ty::TyNever {
|
||||
if fn_ty.unsafety == Unsafety::Unsafe || fn_ty.sig.skip_binder().output().sty == ty::TyNever {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
for (a1, a2) in decl.inputs.iter().zip(args) {
|
||||
if let PatKind::Binding(_, ident, _) = a1.pat.node {
|
||||
if let PatKind::Binding(_, _, ident, _) = a1.pat.node {
|
||||
// XXXManishearth Should I be checking the binding mode here?
|
||||
if let ExprPath(None, ref p) = a2.node {
|
||||
if let ExprPath(QPath::Resolved(None, ref p)) = a2.node {
|
||||
if p.segments.len() != 1 {
|
||||
// If it's a proper path, it can't be a local variable
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use rustc::hir::*;
|
||||
use rustc::ty;
|
||||
use rustc::lint::*;
|
||||
|
|
@ -56,34 +56,38 @@ impl LintPass for EvalOrderDependence {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for EvalOrderDependence {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EvalOrderDependence {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
// Find a write to a local variable.
|
||||
match expr.node {
|
||||
ExprAssign(ref lhs, _) | ExprAssignOp(_, ref lhs, _) => {
|
||||
if let ExprPath(None, ref path) = lhs.node {
|
||||
if path.segments.len() == 1 {
|
||||
let var = cx.tcx.expect_def(lhs.id).def_id();
|
||||
let mut visitor = ReadVisitor {
|
||||
cx: cx,
|
||||
var: var,
|
||||
write_expr: expr,
|
||||
last_expr: expr,
|
||||
};
|
||||
check_for_unsequenced_reads(&mut visitor);
|
||||
ExprAssign(ref lhs, _) |
|
||||
ExprAssignOp(_, ref lhs, _) => {
|
||||
if let ExprPath(ref qpath) = lhs.node {
|
||||
if let QPath::Resolved(_, ref path) = *qpath {
|
||||
if path.segments.len() == 1 {
|
||||
let var = cx.tcx.tables().qpath_def(qpath, lhs.id).def_id();
|
||||
let mut visitor = ReadVisitor {
|
||||
cx: cx,
|
||||
var: var,
|
||||
write_expr: expr,
|
||||
last_expr: expr,
|
||||
};
|
||||
check_for_unsequenced_reads(&mut visitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
|
||||
match stmt.node {
|
||||
StmtExpr(ref e, _) | StmtSemi(ref e, _) => DivergenceVisitor(cx).maybe_walk_expr(e),
|
||||
StmtExpr(ref e, _) |
|
||||
StmtSemi(ref e, _) => DivergenceVisitor { cx: cx }.maybe_walk_expr(e),
|
||||
StmtDecl(ref d, _) => {
|
||||
if let DeclLocal(ref local) = d.node {
|
||||
if let Local { init: Some(ref e), .. } = **local {
|
||||
DivergenceVisitor(cx).visit_expr(e);
|
||||
DivergenceVisitor { cx: cx }.visit_expr(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -91,10 +95,12 @@ impl LateLintPass for EvalOrderDependence {
|
|||
}
|
||||
}
|
||||
|
||||
struct DivergenceVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);
|
||||
struct DivergenceVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
||||
fn maybe_walk_expr(&mut self, e: &Expr) {
|
||||
fn maybe_walk_expr(&mut self, e: &'tcx Expr) {
|
||||
match e.node {
|
||||
ExprClosure(..) => {},
|
||||
ExprMatch(ref e, ref arms, _) => {
|
||||
|
|
@ -106,51 +112,52 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
|||
// make sure top level arm expressions aren't linted
|
||||
self.maybe_walk_expr(&*arm.body);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => walk_expr(self, e),
|
||||
}
|
||||
}
|
||||
fn report_diverging_sub_expr(&mut self, e: &Expr) {
|
||||
span_lint(
|
||||
self.0,
|
||||
DIVERGING_SUB_EXPRESSION,
|
||||
e.span,
|
||||
"sub-expression diverges",
|
||||
);
|
||||
span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for DivergenceVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'v Expr) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, e: &'tcx Expr) {
|
||||
match e.node {
|
||||
ExprAgain(_) |
|
||||
ExprBreak(_, _) |
|
||||
ExprRet(_) => self.report_diverging_sub_expr(e),
|
||||
ExprCall(ref func, _) => match self.0.tcx.tables().expr_ty(func).sty {
|
||||
ty::TyFnDef(_, _, fn_ty) |
|
||||
ty::TyFnPtr(fn_ty) => if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&fn_ty.sig).output.sty {
|
||||
self.report_diverging_sub_expr(e);
|
||||
},
|
||||
_ => {},
|
||||
ExprAgain(_) | ExprBreak(_, _) | ExprRet(_) => self.report_diverging_sub_expr(e),
|
||||
ExprCall(ref func, _) => {
|
||||
match self.cx.tcx.tables().expr_ty(func).sty {
|
||||
ty::TyFnDef(_, _, fn_ty) |
|
||||
ty::TyFnPtr(fn_ty) => {
|
||||
if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&fn_ty.sig).output().sty {
|
||||
self.report_diverging_sub_expr(e);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
ExprMethodCall(..) => {
|
||||
let method_call = ty::MethodCall::expr(e.id);
|
||||
let borrowed_table = self.0.tcx.tables.borrow();
|
||||
let borrowed_table = self.cx.tcx.tables.borrow();
|
||||
let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
|
||||
let result_ty = method_type.ty.fn_ret();
|
||||
if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&result_ty).sty {
|
||||
if let ty::TyNever = self.cx.tcx.erase_late_bound_regions(&result_ty).sty {
|
||||
self.report_diverging_sub_expr(e);
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
// do not lint expressions referencing objects of type `!`, as that required a diverging expression to begin with
|
||||
// do not lint expressions referencing objects of type `!`, as that required a diverging expression
|
||||
// to begin with
|
||||
},
|
||||
}
|
||||
self.maybe_walk_expr(e);
|
||||
}
|
||||
fn visit_block(&mut self, _: &'v Block) {
|
||||
fn visit_block(&mut self, _: &'tcx Block) {
|
||||
// don't continue over blocks, LateLintPass already does that
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
/// Walks up the AST from the the given write expression (`vis.write_expr`)
|
||||
|
|
@ -188,7 +195,7 @@ fn check_for_unsequenced_reads(vis: &mut ReadVisitor) {
|
|||
// We reached the top of the function, stop.
|
||||
break;
|
||||
},
|
||||
_ => { StopEarly::KeepGoing }
|
||||
_ => StopEarly::KeepGoing,
|
||||
};
|
||||
match stop_early {
|
||||
StopEarly::Stop => break,
|
||||
|
|
@ -207,7 +214,7 @@ enum StopEarly {
|
|||
Stop,
|
||||
}
|
||||
|
||||
fn check_expr<'v, 't>(vis: & mut ReadVisitor<'v, 't>, expr: &'v Expr) -> StopEarly {
|
||||
fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr) -> StopEarly {
|
||||
if expr.id == vis.last_expr.id {
|
||||
return StopEarly::KeepGoing;
|
||||
}
|
||||
|
|
@ -222,7 +229,7 @@ fn check_expr<'v, 't>(vis: & mut ReadVisitor<'v, 't>, expr: &'v Expr) -> StopEar
|
|||
ExprRepeat(_, _) |
|
||||
ExprStruct(_, _, _) => {
|
||||
walk_expr(vis, expr);
|
||||
}
|
||||
},
|
||||
ExprBinary(op, _, _) |
|
||||
ExprAssignOp(op, _, _) => {
|
||||
if op.node == BiAnd || op.node == BiOr {
|
||||
|
|
@ -231,7 +238,7 @@ fn check_expr<'v, 't>(vis: & mut ReadVisitor<'v, 't>, expr: &'v Expr) -> StopEar
|
|||
} else {
|
||||
walk_expr(vis, expr);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprClosure(_, _, _, _) => {
|
||||
// Either
|
||||
//
|
||||
|
|
@ -245,10 +252,10 @@ fn check_expr<'v, 't>(vis: & mut ReadVisitor<'v, 't>, expr: &'v Expr) -> StopEar
|
|||
//
|
||||
// This is also the only place we need to stop early (grrr).
|
||||
return StopEarly::Stop;
|
||||
}
|
||||
},
|
||||
// All other expressions either have only one child or strictly
|
||||
// sequence the evaluation order of their sub-expressions.
|
||||
_ => {}
|
||||
_ => {},
|
||||
}
|
||||
|
||||
vis.last_expr = expr;
|
||||
|
|
@ -256,7 +263,7 @@ fn check_expr<'v, 't>(vis: & mut ReadVisitor<'v, 't>, expr: &'v Expr) -> StopEar
|
|||
StopEarly::KeepGoing
|
||||
}
|
||||
|
||||
fn check_stmt<'v, 't>(vis: &mut ReadVisitor<'v, 't>, stmt: &'v Stmt) -> StopEarly {
|
||||
fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt) -> StopEarly {
|
||||
match stmt.node {
|
||||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => check_expr(vis, expr),
|
||||
|
|
@ -269,43 +276,45 @@ fn check_stmt<'v, 't>(vis: &mut ReadVisitor<'v, 't>, stmt: &'v Stmt) -> StopEarl
|
|||
};
|
||||
local.and_then(|local| local.init.as_ref())
|
||||
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A visitor that looks for reads from a variable.
|
||||
struct ReadVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>,
|
||||
struct ReadVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
/// The id of the variable we're looking for.
|
||||
var: DefId,
|
||||
/// The expressions where the write to the variable occurred (for reporting
|
||||
/// in the lint).
|
||||
write_expr: &'v Expr,
|
||||
write_expr: &'tcx Expr,
|
||||
/// The last (highest in the AST) expression we've checked, so we know not
|
||||
/// to recheck it.
|
||||
last_expr: &'v Expr,
|
||||
last_expr: &'tcx Expr,
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for ReadVisitor<'v, 't> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if expr.id == self.last_expr.id {
|
||||
return;
|
||||
}
|
||||
|
||||
match expr.node {
|
||||
ExprPath(None, ref path) => {
|
||||
if path.segments.len() == 1 && self.cx.tcx.expect_def(expr.id).def_id() == self.var {
|
||||
if is_in_assignment_position(self.cx, expr) {
|
||||
// This is a write, not a read.
|
||||
} else {
|
||||
span_note_and_lint(
|
||||
self.cx,
|
||||
EVAL_ORDER_DEPENDENCE,
|
||||
expr.span,
|
||||
"unsequenced read of a variable",
|
||||
self.write_expr.span,
|
||||
"whether read occurs before this write depends on evaluation order"
|
||||
);
|
||||
ExprPath(ref qpath) => {
|
||||
if let QPath::Resolved(None, ref path) = *qpath {
|
||||
if path.segments.len() == 1 && self.cx.tcx.tables().qpath_def(qpath, expr.id).def_id() == self.var {
|
||||
if is_in_assignment_position(self.cx, expr) {
|
||||
// This is a write, not a read.
|
||||
} else {
|
||||
span_note_and_lint(
|
||||
self.cx,
|
||||
EVAL_ORDER_DEPENDENCE,
|
||||
expr.span,
|
||||
"unsequenced read of a variable",
|
||||
self.write_expr.span,
|
||||
"whether read occurs before this write depends on evaluation order"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -332,6 +341,9 @@ impl<'v, 't> Visitor<'v> for ReadVisitor<'v, 't> {
|
|||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if `expr` is the LHS of an assignment, like `expr = ...`.
|
||||
|
|
|
|||
|
|
@ -38,17 +38,16 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let Some(span) = is_expn_of(cx, expr.span, "format") {
|
||||
match expr.node {
|
||||
// `format!("{}", foo)` expansion
|
||||
ExprCall(ref fun, ref args) => {
|
||||
if_let_chain!{[
|
||||
let ExprPath(..) = fun.node,
|
||||
let ExprPath(ref qpath) = fun.node,
|
||||
args.len() == 2,
|
||||
let Some(fun) = resolve_node(cx, fun.id),
|
||||
match_def_path(cx, fun.def_id(), &paths::FMT_ARGUMENTS_NEWV1),
|
||||
match_def_path(cx, resolve_node(cx, qpath, fun.id).def_id(), &paths::FMT_ARGUMENTS_NEWV1),
|
||||
// ensure the format string is `"{..}"` with only one argument and no text
|
||||
check_static_str(cx, &args[0]),
|
||||
// ensure the format argument is `{}` ie. Display with no fancy option
|
||||
|
|
@ -56,7 +55,7 @@ impl LateLintPass for Pass {
|
|||
], {
|
||||
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
|
||||
}}
|
||||
}
|
||||
},
|
||||
// `format!("foo")` expansion contains `match () { () => [], }`
|
||||
ExprMatch(ref matchee, _, _) => {
|
||||
if let ExprTup(ref tup) = matchee.node {
|
||||
|
|
@ -64,7 +63,7 @@ impl LateLintPass for Pass {
|
|||
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -73,8 +72,7 @@ impl LateLintPass for Pass {
|
|||
|
||||
/// Returns the slice of format string parts in an `Arguments::new_v1` call.
|
||||
/// Public because it's shared with a lint in print.rs.
|
||||
pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Expr)
|
||||
-> Option<Vec<InternedString>> {
|
||||
pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Expr) -> Option<Vec<InternedString>> {
|
||||
if_let_chain! {[
|
||||
let ExprBlock(ref block) = expr.node,
|
||||
block.stmts.len() == 1,
|
||||
|
|
@ -129,9 +127,8 @@ fn check_arg_is_display(cx: &LateContext, expr: &Expr) -> bool {
|
|||
exprs.len() == 1,
|
||||
let ExprCall(_, ref args) = exprs[0].node,
|
||||
args.len() == 2,
|
||||
let ExprPath(None, _) = args[1].node,
|
||||
let Some(fun) = resolve_node(cx, args[1].id),
|
||||
match_def_path(cx, fun.def_id(), &paths::DISPLAY_FMT_METHOD),
|
||||
let ExprPath(ref qpath) = args[1].node,
|
||||
match_def_path(cx, resolve_node(cx, qpath, args[1].id).def_id(), &paths::DISPLAY_FMT_METHOD),
|
||||
], {
|
||||
let ty = walk_ptrs_ty(cx.tcx.tables().pat_ty(&pat[0]));
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ impl EarlyLintPass for Formatting {
|
|||
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second)) |
|
||||
(&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
|
||||
check_consecutive_ifs(cx, first, second);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,8 +68,16 @@ impl LintPass for Functions {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Functions {
|
||||
fn check_fn(&mut self, cx: &LateContext, kind: intravisit::FnKind, decl: &hir::FnDecl, expr: &hir::Expr, span: Span, nodeid: ast::NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
kind: intravisit::FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
expr: &'tcx hir::Expr,
|
||||
span: Span,
|
||||
nodeid: ast::NodeId
|
||||
) {
|
||||
use rustc::hir::map::Node::*;
|
||||
|
||||
let is_impl = if let Some(NodeItem(item)) = cx.tcx.map.find(cx.tcx.map.get_parent_node(nodeid)) {
|
||||
|
|
@ -97,21 +105,22 @@ impl LateLintPass for Functions {
|
|||
self.check_raw_ptr(cx, unsafety, decl, expr, nodeid);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
|
||||
if let hir::MethodTraitItem(ref sig, ref expr) = item.node {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
|
||||
if let hir::MethodTraitItem(ref sig, eid) = item.node {
|
||||
// don't lint extern functions decls, it's not their fault
|
||||
if sig.abi == Abi::Rust {
|
||||
self.check_arg_number(cx, &sig.decl, item.span);
|
||||
}
|
||||
|
||||
if let Some(ref expr) = *expr {
|
||||
if let Some(eid) = eid {
|
||||
let expr = cx.tcx.map.expr(eid);
|
||||
self.check_raw_ptr(cx, sig.unsafety, &sig.decl, expr, item.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Functions {
|
||||
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 {
|
||||
|
|
@ -122,7 +131,14 @@ impl Functions {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_raw_ptr(&self, cx: &LateContext, unsafety: hir::Unsafety, decl: &hir::FnDecl, expr: &hir::Expr, nodeid: ast::NodeId) {
|
||||
fn check_raw_ptr(
|
||||
&self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
unsafety: hir::Unsafety,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
expr: &'tcx hir::Expr,
|
||||
nodeid: ast::NodeId
|
||||
) {
|
||||
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
|
||||
let raw_ptrs = decl.inputs.iter().filter_map(|arg| raw_ptr_arg(cx, arg)).collect::<HashSet<_>>();
|
||||
|
||||
|
|
@ -138,9 +154,9 @@ impl Functions {
|
|||
}
|
||||
}
|
||||
|
||||
fn raw_ptr_arg(cx: &LateContext, arg: &hir::Arg) -> Option<hir::def_id::DefId> {
|
||||
if let (&hir::PatKind::Binding(_, _, _), &hir::TyPtr(_)) = (&arg.pat.node, &arg.ty.node) {
|
||||
cx.tcx.def_map.borrow().get(&arg.pat.id).map(|pr| pr.full_def().def_id())
|
||||
fn raw_ptr_arg(_cx: &LateContext, arg: &hir::Arg) -> Option<hir::def_id::DefId> {
|
||||
if let (&hir::PatKind::Binding(_, def_id, _, _), &hir::TyPtr(_)) = (&arg.pat.node, &arg.ty.node) {
|
||||
Some(def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -151,8 +167,8 @@ struct DerefVisitor<'a, 'tcx: 'a> {
|
|||
ptrs: HashSet<hir::def_id::DefId>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for DerefVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'v hir::Expr) {
|
||||
impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
|
||||
match expr.node {
|
||||
hir::ExprCall(ref f, ref args) => {
|
||||
let ty = self.cx.tcx.tables().expr_ty(f);
|
||||
|
|
@ -162,7 +178,7 @@ impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for DerefVisitor<'a, 'tcx> {
|
|||
self.check_arg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprMethodCall(_, _, ref args) => {
|
||||
let method_call = ty::MethodCall::expr(expr.id);
|
||||
let base_type = self.cx.tcx.tables.borrow().method_map[&method_call].ty;
|
||||
|
|
@ -172,19 +188,23 @@ impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for DerefVisitor<'a, 'tcx> {
|
|||
self.check_arg(arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprUnary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
hir::intravisit::walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
|
||||
intravisit::NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
|
||||
fn check_arg(&self, ptr: &hir::Expr) {
|
||||
if let Some(def) = self.cx.tcx.def_map.borrow().get(&ptr.id) {
|
||||
if self.ptrs.contains(&def.full_def().def_id()) {
|
||||
if let hir::ExprPath(ref qpath) = ptr.node {
|
||||
let def = self.cx.tcx.tables().qpath_def(qpath, ptr.id);
|
||||
if self.ptrs.contains(&def.def_id()) {
|
||||
span_lint(self.cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
ptr.span,
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ impl LintPass for IdentityOp {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for IdentityOp {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityOp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if in_macro(cx, e.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -41,17 +41,17 @@ impl LateLintPass for IdentityOp {
|
|||
BiAdd | BiBitOr | BiBitXor => {
|
||||
check(cx, left, 0, e.span, right.span);
|
||||
check(cx, right, 0, e.span, left.span);
|
||||
}
|
||||
},
|
||||
BiShl | BiShr | BiSub => check(cx, right, 0, e.span, left.span),
|
||||
BiMul => {
|
||||
check(cx, left, 1, e.span, right.span);
|
||||
check(cx, right, 1, e.span, left.span);
|
||||
}
|
||||
},
|
||||
BiDiv => check(cx, right, 1, e.span, left.span),
|
||||
BiBitAnd => {
|
||||
check(cx, left, -1, e.span, right.span);
|
||||
check(cx, right, -1, e.span, left.span);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,16 +42,15 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
|
||||
if let ExprMatch(ref op, ref arms, MatchSource::IfLetDesugar{..}) = expr.node {
|
||||
if let ExprMatch(ref op, ref arms, MatchSource::IfLetDesugar { .. }) = expr.node {
|
||||
|
||||
if arms[0].pats.len() == 1 {
|
||||
|
||||
let good_method = match arms[0].pats[0].node {
|
||||
PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 && pats[0].node == PatKind::Wild => {
|
||||
|
||||
PatKind::TupleStruct(ref path, ref pats, _) if pats.len() == 1 && pats[0].node == PatKind::Wild => {
|
||||
if match_path(path, &paths::RESULT_OK) {
|
||||
"is_ok()"
|
||||
} else if match_path(path, &paths::RESULT_ERR) {
|
||||
|
|
@ -59,15 +58,13 @@ impl LateLintPass for Pass {
|
|||
} else if match_path(path, &paths::OPTION_SOME) {
|
||||
"is_some()"
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
PatKind::Path(_, ref path) if match_path(path, &paths::OPTION_NONE) => {
|
||||
"is_none()"
|
||||
}
|
||||
PatKind::Path(ref path) if match_path(path, &paths::OPTION_NONE) => "is_none()",
|
||||
|
||||
_ => return
|
||||
_ => return,
|
||||
};
|
||||
|
||||
span_lint_and_then(cx,
|
||||
|
|
@ -75,15 +72,13 @@ impl LateLintPass for Pass {
|
|||
arms[0].pats[0].span,
|
||||
&format!("redundant pattern matching, consider using `{}`", good_method),
|
||||
|db| {
|
||||
let span = Span {
|
||||
lo: expr.span.lo,
|
||||
hi: op.span.hi,
|
||||
expn_id: expr.span.expn_id,
|
||||
};
|
||||
db.span_suggestion(span,
|
||||
"try this",
|
||||
format!("if {}.{}", snippet(cx, op.span, "_"), good_method));
|
||||
});
|
||||
let span = Span {
|
||||
lo: expr.span.lo,
|
||||
hi: op.span.hi,
|
||||
expn_id: expr.span.expn_id,
|
||||
};
|
||||
db.span_suggestion(span, "try this", format!("if {}.{}", snippet(cx, op.span, "_"), good_method));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,14 +55,14 @@ impl EarlyLintPass for IfNotElse {
|
|||
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");
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,9 +47,10 @@ impl EarlyLintPass for ItemsAfterStatements {
|
|||
}
|
||||
|
||||
// skip initial items
|
||||
let stmts = item.stmts.iter()
|
||||
.map(|stmt| &stmt.node)
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
|
||||
let stmts = item.stmts
|
||||
.iter()
|
||||
.map(|stmt| &stmt.node)
|
||||
.skip_while(|s| matches!(**s, StmtKind::Item(..)));
|
||||
|
||||
// lint on all further items
|
||||
for stmt in stmts {
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ impl LintPass for LenZero {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for LenZero {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LenZero {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if in_macro(cx, item.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ impl LateLintPass for LenZero {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if in_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -107,8 +107,7 @@ fn check_trait_items(cx: &LateContext, item: &Item, trait_items: &[TraitItem]) {
|
|||
span_lint(cx,
|
||||
LEN_WITHOUT_IS_EMPTY,
|
||||
i.span,
|
||||
&format!("trait `{}` has a `len` method but no `is_empty` method",
|
||||
item.name));
|
||||
&format!("trait `{}` has a `len` method but no `is_empty` method", item.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +117,8 @@ fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItemRef]) {
|
|||
fn is_named_self(cx: &LateContext, item: &ImplItemRef, name: &str) -> bool {
|
||||
&*item.name.as_str() == name &&
|
||||
if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self && {
|
||||
has_self &&
|
||||
{
|
||||
let did = cx.tcx.map.local_def_id(item.id.node_id);
|
||||
let impl_ty = cx.tcx.item_type(did);
|
||||
impl_ty.fn_args().skip_binder().len() == 1
|
||||
|
|
@ -146,9 +146,7 @@ fn check_impl_items(cx: &LateContext, item: &Item, impl_items: &[ImplItemRef]) {
|
|||
span_lint(cx,
|
||||
LEN_WITHOUT_IS_EMPTY,
|
||||
i.span,
|
||||
&format!("item `{}` has a public `len` method but {} `is_empty` method",
|
||||
ty,
|
||||
is_empty));
|
||||
&format!("item `{}` has a public `len` method but {} `is_empty` method", ty, is_empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +162,7 @@ fn check_cmp(cx: &LateContext, span: Span, left: &Expr, right: &Expr, op: &str)
|
|||
(&ExprLit(ref lit), &ExprMethodCall(ref method, _, ref args)) |
|
||||
(&ExprMethodCall(ref method, _, ref args), &ExprLit(ref lit)) => {
|
||||
check_len_zero(cx, span, &method.node, args, lit, op)
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +186,7 @@ fn has_is_empty(cx: &LateContext, expr: &Expr) -> bool {
|
|||
if let ty::AssociatedKind::Method = item.kind {
|
||||
if &*item.name.as_str() == "is_empty" {
|
||||
let ty = cx.tcx.item_type(item.def_id).fn_sig().skip_binder();
|
||||
ty.inputs.len() == 1
|
||||
ty.inputs().len() == 1
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -199,20 +197,18 @@ 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.borrow().get(&id).map_or(false, |impls| impls.iter().any(|imp| {
|
||||
cx.tcx.associated_items(*imp).any(|item| {
|
||||
is_is_empty(cx, &item)
|
||||
})
|
||||
}))
|
||||
cx.tcx.inherent_impls.borrow().get(&id).map_or(false, |impls| {
|
||||
impls.iter().any(|imp| cx.tcx.associated_items(*imp).any(|item| is_is_empty(cx, &item)))
|
||||
})
|
||||
}
|
||||
|
||||
let ty = &walk_ptrs_ty(cx.tcx.tables().expr_ty(expr));
|
||||
match ty.sty {
|
||||
ty::TyTrait(_) => {
|
||||
ty::TyDynamic(..) => {
|
||||
cx.tcx
|
||||
.associated_items(ty.ty_to_def_id().expect("trait impl not found"))
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
}
|
||||
.associated_items(ty.ty_to_def_id().expect("trait impl not found"))
|
||||
.any(|item| is_is_empty(cx, &item))
|
||||
},
|
||||
ty::TyProjection(_) => ty.ty_to_def_id().map_or(false, |id| has_is_empty_impl(cx, id)),
|
||||
ty::TyAdt(id, _) => has_is_empty_impl(cx, id.did),
|
||||
ty::TyArray(..) | ty::TyStr => true,
|
||||
|
|
|
|||
|
|
@ -57,27 +57,26 @@ impl LintPass for LetIfSeq {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for LetIfSeq {
|
||||
fn check_block(&mut self, cx: &LateContext, block: &hir::Block) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
|
||||
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block) {
|
||||
let mut it = block.stmts.iter().peekable();
|
||||
while let Some(stmt) = it.next() {
|
||||
if_let_chain! {[
|
||||
let Some(expr) = it.peek(),
|
||||
let hir::StmtDecl(ref decl, _) = stmt.node,
|
||||
let hir::DeclLocal(ref decl) = decl.node,
|
||||
let hir::PatKind::Binding(mode, ref name, None) = decl.pat.node,
|
||||
let Some(def) = cx.tcx.def_map.borrow().get(&decl.pat.id),
|
||||
let hir::PatKind::Binding(mode, def_id, ref name, None) = decl.pat.node,
|
||||
let hir::StmtExpr(ref if_, _) = expr.node,
|
||||
let hir::ExprIf(ref cond, ref then, ref else_) = if_.node,
|
||||
!used_in_expr(cx, def.full_def().def_id(), cond),
|
||||
let Some(value) = check_assign(cx, def.full_def().def_id(), then),
|
||||
!used_in_expr(cx, def.full_def().def_id(), value),
|
||||
!used_in_expr(cx, def_id, cond),
|
||||
let Some(value) = check_assign(cx, def_id, then),
|
||||
!used_in_expr(cx, def_id, value),
|
||||
], {
|
||||
let span = codemap::mk_sp(stmt.span.lo, if_.span.hi);
|
||||
|
||||
let (default_multi_stmts, default) = if let Some(ref else_) = *else_ {
|
||||
if let hir::ExprBlock(ref else_) = else_.node {
|
||||
if let Some(default) = check_assign(cx, def.full_def().def_id(), else_) {
|
||||
if let Some(default) = check_assign(cx, def_id, else_) {
|
||||
(else_.stmts.len() > 1, default)
|
||||
} else if let Some(ref default) = decl.init {
|
||||
(true, &**default)
|
||||
|
|
@ -134,29 +133,34 @@ struct UsedVisitor<'a, 'tcx: 'a> {
|
|||
used: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> hir::intravisit::Visitor<'v> for UsedVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'v hir::Expr) {
|
||||
impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
|
||||
if_let_chain! {[
|
||||
let hir::ExprPath(None, _) = expr.node,
|
||||
let Some(def) = self.cx.tcx.def_map.borrow().get(&expr.id),
|
||||
self.id == def.full_def().def_id(),
|
||||
let hir::ExprPath(ref qpath) = expr.node,
|
||||
self.id == self.cx.tcx.tables().qpath_def(qpath, expr.id).def_id(),
|
||||
], {
|
||||
self.used = true;
|
||||
return;
|
||||
}}
|
||||
hir::intravisit::walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> hir::intravisit::NestedVisitorMap<'this, 'tcx> {
|
||||
hir::intravisit::NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_assign<'e>(cx: &LateContext, decl: hir::def_id::DefId, block: &'e hir::Block) -> Option<&'e hir::Expr> {
|
||||
fn check_assign<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
decl: hir::def_id::DefId,
|
||||
block: &'tcx hir::Block
|
||||
) -> Option<&'tcx hir::Expr> {
|
||||
if_let_chain! {[
|
||||
block.expr.is_none(),
|
||||
let Some(expr) = block.stmts.iter().last(),
|
||||
let hir::StmtSemi(ref expr, _) = expr.node,
|
||||
let hir::ExprAssign(ref var, ref value) = expr.node,
|
||||
let hir::ExprPath(None, _) = var.node,
|
||||
let Some(def) = cx.tcx.def_map.borrow().get(&var.id),
|
||||
decl == def.full_def().def_id(),
|
||||
let hir::ExprPath(ref qpath) = var.node,
|
||||
decl == cx.tcx.tables().qpath_def(qpath, var.id).def_id(),
|
||||
], {
|
||||
let mut v = UsedVisitor {
|
||||
cx: cx,
|
||||
|
|
@ -178,7 +182,7 @@ fn check_assign<'e>(cx: &LateContext, decl: hir::def_id::DefId, block: &'e hir::
|
|||
None
|
||||
}
|
||||
|
||||
fn used_in_expr(cx: &LateContext, id: hir::def_id::DefId, expr: &hir::Expr) -> bool {
|
||||
fn used_in_expr<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, id: hir::def_id::DefId, expr: &'tcx hir::Expr) -> bool {
|
||||
let mut v = UsedVisitor {
|
||||
cx: cx,
|
||||
id: id,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
#![feature(repeat_str)]
|
||||
|
||||
#![allow(indexing_slicing, shadow_reuse, unknown_lints, missing_docs_in_private_items)]
|
||||
#![allow(needless_lifetimes)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate syntax;
|
||||
|
|
@ -115,6 +116,7 @@ pub mod precedence;
|
|||
pub mod print;
|
||||
pub mod ptr;
|
||||
pub mod ranges;
|
||||
pub mod reference;
|
||||
pub mod regex;
|
||||
pub mod returns;
|
||||
pub mod serde;
|
||||
|
|
@ -171,10 +173,22 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
};
|
||||
|
||||
let mut store = reg.sess.lint_store.borrow_mut();
|
||||
store.register_removed("unstable_as_slice", "`Vec::as_slice` has been stabilized in 1.7");
|
||||
store.register_removed("unstable_as_mut_slice", "`Vec::as_mut_slice` has been stabilized in 1.7");
|
||||
store.register_removed("str_to_string", "using `str::to_string` is common even today and specialization will likely happen soon");
|
||||
store.register_removed("string_to_string", "using `string::to_string` is common even today and specialization will likely happen soon");
|
||||
store.register_removed(
|
||||
"unstable_as_slice",
|
||||
"`Vec::as_slice` has been stabilized in 1.7",
|
||||
);
|
||||
store.register_removed(
|
||||
"unstable_as_mut_slice",
|
||||
"`Vec::as_mut_slice` has been stabilized in 1.7",
|
||||
);
|
||||
store.register_removed(
|
||||
"str_to_string",
|
||||
"using `str::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
store.register_removed(
|
||||
"string_to_string",
|
||||
"using `string::to_string` is common even today and specialization will likely happen soon",
|
||||
);
|
||||
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
|
||||
|
||||
reg.register_late_lint_pass(box serde::Serde);
|
||||
|
|
@ -227,7 +241,9 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_late_lint_pass(box map_clone::Pass);
|
||||
reg.register_late_lint_pass(box temporary_assignment::Pass);
|
||||
reg.register_late_lint_pass(box transmute::Transmute);
|
||||
reg.register_late_lint_pass(box cyclomatic_complexity::CyclomaticComplexity::new(conf.cyclomatic_complexity_threshold));
|
||||
reg.register_late_lint_pass(
|
||||
box cyclomatic_complexity::CyclomaticComplexity::new(conf.cyclomatic_complexity_threshold)
|
||||
);
|
||||
reg.register_late_lint_pass(box escape::Pass{too_large_for_stack: conf.too_large_for_stack});
|
||||
reg.register_early_lint_pass(box misc_early::MiscEarly);
|
||||
reg.register_late_lint_pass(box array_indexing::ArrayIndexing);
|
||||
|
|
@ -256,7 +272,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_late_lint_pass(box functions::Functions::new(conf.too_many_arguments_threshold));
|
||||
reg.register_early_lint_pass(box doc::Doc::new(conf.doc_valid_idents));
|
||||
reg.register_late_lint_pass(box neg_multiply::NegMultiply);
|
||||
reg.register_late_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval);
|
||||
reg.register_early_lint_pass(box unsafe_removed_from_name::UnsafeNameRemoval);
|
||||
reg.register_late_lint_pass(box mem_forget::MemForget);
|
||||
reg.register_late_lint_pass(box arithmetic::Arithmetic::default());
|
||||
reg.register_late_lint_pass(box assign_ops::AssignOps);
|
||||
|
|
@ -266,6 +282,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_late_lint_pass(box ok_if_let::Pass);
|
||||
reg.register_late_lint_pass(box if_let_redundant_pattern_matching::Pass);
|
||||
reg.register_late_lint_pass(box partialeq_ne_impl::Pass);
|
||||
reg.register_early_lint_pass(box reference::Pass);
|
||||
|
||||
reg.register_lint_group("clippy_restrictions", vec![
|
||||
arithmetic::FLOAT_ARITHMETIC,
|
||||
|
|
@ -277,6 +294,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
reg.register_lint_group("clippy_pedantic", vec![
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
enum_glob_use::ENUM_GLOB_USE,
|
||||
enum_variants::PUB_ENUM_VARIANT_NAMES,
|
||||
enum_variants::STUTTER,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
items_after_statements::ITEMS_AFTER_STATEMENTS,
|
||||
|
|
@ -431,6 +449,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
|
|||
ptr::PTR_ARG,
|
||||
ranges::RANGE_STEP_BY_ZERO,
|
||||
ranges::RANGE_ZIP_WITH_LEN,
|
||||
reference::DEREF_ADDROF,
|
||||
regex::INVALID_REGEX,
|
||||
regex::REGEX_MACRO,
|
||||
regex::TRIVIAL_REGEX,
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ use reexport::*;
|
|||
use rustc::lint::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl, walk_generics};
|
||||
use rustc::hir::intravisit::{Visitor, walk_ty, walk_ty_param_bound, walk_fn_decl, walk_generics, NestedVisitorMap};
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use syntax::codemap::Span;
|
||||
use utils::{in_external_macro, span_lint};
|
||||
use utils::{in_external_macro, span_lint, last_path_segment};
|
||||
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
/// relying on lifetime elision.
|
||||
|
|
@ -56,20 +56,20 @@ impl LintPass for LifetimePass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for LifetimePass {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LifetimePass {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemFn(ref decl, _, _, _, ref generics, _) = item.node {
|
||||
check_fn_inner(cx, decl, generics, item.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
if let ImplItemKind::Method(ref sig, _) = item.node {
|
||||
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
if let MethodTraitItem(ref sig, _) = item.node {
|
||||
check_fn_inner(cx, &sig.decl, &sig.generics, item.span);
|
||||
}
|
||||
|
|
@ -87,25 +87,25 @@ enum RefLt {
|
|||
fn bound_lifetimes(bound: &TyParamBound) -> HirVec<&Lifetime> {
|
||||
if let TraitTyParamBound(ref trait_ref, _) = *bound {
|
||||
trait_ref.trait_ref
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.expect("a path must have at least one segment")
|
||||
.parameters
|
||||
.lifetimes()
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.expect("a path must have at least one segment")
|
||||
.parameters
|
||||
.lifetimes()
|
||||
} else {
|
||||
HirVec::new()
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn_inner(cx: &LateContext, decl: &FnDecl, generics: &Generics, span: Span) {
|
||||
fn check_fn_inner<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, generics: &'tcx Generics, span: Span) {
|
||||
if in_external_macro(cx, span) || has_where_lifetimes(cx, &generics.where_clause) {
|
||||
return;
|
||||
}
|
||||
|
||||
let bounds_lts = generics.ty_params
|
||||
.iter()
|
||||
.flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));
|
||||
.iter()
|
||||
.flat_map(|typ| typ.bounds.iter().flat_map(bound_lifetimes));
|
||||
|
||||
if could_use_elision(cx, decl, &generics.lifetimes, bounds_lts) {
|
||||
span_lint(cx,
|
||||
|
|
@ -116,9 +116,12 @@ fn check_fn_inner(cx: &LateContext, decl: &FnDecl, generics: &Generics, span: Sp
|
|||
report_extra_lifetimes(cx, decl, generics);
|
||||
}
|
||||
|
||||
fn could_use_elision<'a, T: Iterator<Item = &'a Lifetime>>(cx: &LateContext, func: &FnDecl,
|
||||
named_lts: &[LifetimeDef], bounds_lts: T)
|
||||
-> bool {
|
||||
fn could_use_elision<'a, 'tcx: 'a, T: Iterator<Item = &'tcx Lifetime>>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
func: &'tcx FnDecl,
|
||||
named_lts: &'tcx [LifetimeDef],
|
||||
bounds_lts: T
|
||||
) -> bool {
|
||||
// There are two scenarios where elision works:
|
||||
// * no output references, all input references have different LT
|
||||
// * output references, exactly one input reference with same LT
|
||||
|
|
@ -210,8 +213,8 @@ fn unique_lifetimes(lts: &[RefLt]) -> usize {
|
|||
}
|
||||
|
||||
/// A visitor usable for `rustc_front::visit::walk_ty()`.
|
||||
struct RefVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>,
|
||||
struct RefVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
lts: Vec<RefLt>,
|
||||
}
|
||||
|
||||
|
|
@ -239,56 +242,57 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
self.lts
|
||||
}
|
||||
|
||||
fn collect_anonymous_lifetimes(&mut self, path: &Path, ty: &Ty) {
|
||||
let last_path_segment = path.segments.last().map(|s| &s.parameters);
|
||||
if let Some(&AngleBracketedParameters(ref params)) = last_path_segment {
|
||||
fn collect_anonymous_lifetimes(&mut self, qpath: &QPath, ty: &Ty) {
|
||||
let last_path_segment = &last_path_segment(qpath).parameters;
|
||||
if let AngleBracketedParameters(ref params) = *last_path_segment {
|
||||
if params.lifetimes.is_empty() {
|
||||
if let Some(def) = self.cx.tcx.def_map.borrow().get(&ty.id).map(|r| r.full_def()) {
|
||||
match def {
|
||||
Def::TyAlias(def_id) |
|
||||
Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.item_generics(def_id);
|
||||
for _ in generics.regions.as_slice() {
|
||||
self.record(&None);
|
||||
}
|
||||
match self.cx.tcx.tables().qpath_def(qpath, ty.id) {
|
||||
Def::TyAlias(def_id) |
|
||||
Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.item_generics(def_id);
|
||||
for _ in generics.regions.as_slice() {
|
||||
self.record(&None);
|
||||
}
|
||||
Def::Trait(def_id) => {
|
||||
let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
|
||||
for _ in &trait_def.generics.regions {
|
||||
self.record(&None);
|
||||
}
|
||||
},
|
||||
Def::Trait(def_id) => {
|
||||
let trait_def = self.cx.tcx.trait_defs.borrow()[&def_id];
|
||||
for _ in &self.cx.tcx.item_generics(trait_def.def_id).regions {
|
||||
self.record(&None);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for RefVisitor<'v, 't> {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
self.record(&Some(*lifetime));
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'v Ty) {
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty) {
|
||||
match ty.node {
|
||||
TyRptr(None, _) => {
|
||||
self.record(&None);
|
||||
}
|
||||
TyPath(_, ref path) => {
|
||||
},
|
||||
TyPath(ref path) => {
|
||||
self.collect_anonymous_lifetimes(path, ty);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
walk_ty(self, ty);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
|
||||
/// reason about elision.
|
||||
fn has_where_lifetimes(cx: &LateContext, where_clause: &WhereClause) -> bool {
|
||||
fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause) -> bool {
|
||||
for predicate in &where_clause.predicates {
|
||||
match *predicate {
|
||||
WherePredicate::RegionPredicate(..) => return true,
|
||||
|
|
@ -312,47 +316,52 @@ fn has_where_lifetimes(cx: &LateContext, where_clause: &WhereClause) -> bool {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
WherePredicate::EqPredicate(ref pred) => {
|
||||
let mut visitor = RefVisitor::new(cx);
|
||||
walk_ty(&mut visitor, &pred.ty);
|
||||
if !visitor.lts.is_empty() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
struct LifetimeChecker(HashMap<Name, Span>);
|
||||
struct LifetimeChecker {
|
||||
map: HashMap<Name, Span>,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for LifetimeChecker {
|
||||
impl<'tcx> Visitor<'tcx> for LifetimeChecker {
|
||||
// for lifetimes as parameters of generics
|
||||
fn visit_lifetime(&mut self, lifetime: &'v Lifetime) {
|
||||
self.0.remove(&lifetime.name);
|
||||
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
|
||||
self.map.remove(&lifetime.name);
|
||||
}
|
||||
|
||||
fn visit_lifetime_def(&mut self, _: &'v LifetimeDef) {
|
||||
fn visit_lifetime_def(&mut self, _: &'tcx LifetimeDef) {
|
||||
// don't actually visit `<'a>` or `<'a: 'b>`
|
||||
// we've already visited the `'a` declarations and
|
||||
// don't want to spuriously remove them
|
||||
// `'b` in `'a: 'b` is useless unless used elsewhere in
|
||||
// a non-lifetime bound
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::None
|
||||
}
|
||||
}
|
||||
|
||||
fn report_extra_lifetimes(cx: &LateContext, func: &FnDecl, generics: &Generics) {
|
||||
fn report_extra_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, func: &'tcx FnDecl, generics: &'tcx Generics) {
|
||||
let hs = generics.lifetimes
|
||||
.iter()
|
||||
.map(|lt| (lt.lifetime.name, lt.lifetime.span))
|
||||
.collect();
|
||||
let mut checker = LifetimeChecker(hs);
|
||||
.iter()
|
||||
.map(|lt| (lt.lifetime.name, lt.lifetime.span))
|
||||
.collect();
|
||||
let mut checker = LifetimeChecker { map: hs };
|
||||
|
||||
walk_generics(&mut checker, generics);
|
||||
walk_fn_decl(&mut checker, func);
|
||||
|
||||
for &v in checker.0.values() {
|
||||
for &v in checker.map.values() {
|
||||
span_lint(cx, UNUSED_LIFETIMES, v, "this lifetime isn't used in the function definition");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use reexport::*;
|
|||
use rustc::hir::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, walk_block, walk_decl, NestedVisitorMap};
|
||||
use rustc::hir::map::Node::NodeBlock;
|
||||
use rustc::lint::*;
|
||||
use rustc::middle::const_val::ConstVal;
|
||||
|
|
@ -14,9 +14,9 @@ use std::collections::HashMap;
|
|||
use syntax::ast;
|
||||
use utils::sugg;
|
||||
|
||||
use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg,
|
||||
in_external_macro, is_refutable, span_help_and_lint, is_integer_literal,
|
||||
get_enclosing_block, span_lint_and_then, higher, walk_ptrs_ty};
|
||||
use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, multispan_sugg, in_external_macro,
|
||||
is_refutable, span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, higher,
|
||||
walk_ptrs_ty, last_path_segment};
|
||||
use utils::paths;
|
||||
|
||||
/// **What it does:** Checks for looping over the range of `0..len` of some
|
||||
|
|
@ -307,8 +307,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let Some((pat, arg, body)) = higher::for_loop(expr) {
|
||||
check_for_loop(cx, pat, arg, body, expr);
|
||||
}
|
||||
|
|
@ -334,8 +334,7 @@ impl LateLintPass for Pass {
|
|||
match *source {
|
||||
MatchSource::Normal |
|
||||
MatchSource::IfLetDesugar { .. } => {
|
||||
if arms.len() == 2 &&
|
||||
arms[0].pats.len() == 1 && arms[0].guard.is_none() &&
|
||||
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) {
|
||||
if in_external_macro(cx, expr.span) {
|
||||
|
|
@ -352,13 +351,13 @@ impl LateLintPass for Pass {
|
|||
expr.span,
|
||||
"this loop could be written as a `while let` loop",
|
||||
|db| {
|
||||
let sug = format!("while let {} = {} {{ .. }}",
|
||||
snippet(cx, arms[0].pats[0].span, ".."),
|
||||
snippet(cx, matchexpr.span, ".."));
|
||||
db.span_suggestion(expr.span, "try", sug);
|
||||
});
|
||||
let sug = format!("while let {} = {} {{ .. }}",
|
||||
snippet(cx, arms[0].pats[0].span, ".."),
|
||||
snippet(cx, matchexpr.span, ".."));
|
||||
db.span_suggestion(expr.span, "try", sug);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -366,33 +365,28 @@ impl LateLintPass 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 path, ref pat_args, _),
|
||||
if let (&PatKind::TupleStruct(ref qpath, ref pat_args, _),
|
||||
&ExprMethodCall(method_name, _, ref method_args)) = (pat, &match_expr.node) {
|
||||
let iter_expr = &method_args[0];
|
||||
if let Some(lhs_constructor) = path.segments.last() {
|
||||
if &*method_name.node.as_str() == "next" &&
|
||||
match_trait_method(cx, match_expr, &paths::ITERATOR) &&
|
||||
&*lhs_constructor.name.as_str() == "Some" &&
|
||||
!is_refutable(cx, &pat_args[0]) &&
|
||||
!is_iterator_used_after_while_let(cx, iter_expr) {
|
||||
let iterator = snippet(cx, method_args[0].span, "_");
|
||||
let loop_var = snippet(cx, pat_args[0].span, "_");
|
||||
span_lint_and_then(cx,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
expr.span,
|
||||
"this loop could be written as a `for` loop",
|
||||
|db| {
|
||||
db.span_suggestion(expr.span,
|
||||
"try",
|
||||
format!("for {} in {} {{ .. }}", loop_var, iterator));
|
||||
});
|
||||
}
|
||||
let lhs_constructor = last_path_segment(qpath);
|
||||
if &*method_name.node.as_str() == "next" && match_trait_method(cx, match_expr, &paths::ITERATOR) &&
|
||||
&*lhs_constructor.name.as_str() == "Some" && !is_refutable(cx, &pat_args[0]) &&
|
||||
!is_iterator_used_after_while_let(cx, iter_expr) {
|
||||
let iterator = snippet(cx, method_args[0].span, "_");
|
||||
let loop_var = snippet(cx, pat_args[0].span, "_");
|
||||
span_lint_and_then(cx,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
expr.span,
|
||||
"this loop could be written as a `for` loop",
|
||||
|db| {
|
||||
db.span_suggestion(expr.span, "try", format!("for {} in {} {{ .. }}", loop_var, iterator));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
|
||||
if let StmtSemi(ref expr, _) = stmt.node {
|
||||
if let ExprMethodCall(ref method, _, ref args) = expr.node {
|
||||
if args.len() == 1 && &*method.node.as_str() == "collect" &&
|
||||
|
|
@ -408,7 +402,13 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
|
||||
fn check_for_loop<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &'tcx Pat,
|
||||
arg: &'tcx Expr,
|
||||
body: &'tcx Expr,
|
||||
expr: &'tcx Expr
|
||||
) {
|
||||
check_for_loop_range(cx, pat, arg, body, expr);
|
||||
check_for_loop_reverse_range(cx, arg, expr);
|
||||
check_for_loop_arg(cx, pat, arg, expr);
|
||||
|
|
@ -418,13 +418,19 @@ fn check_for_loop(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &E
|
|||
|
||||
/// Check for looping over a range and then indexing a sequence with it.
|
||||
/// The iteratee must be a range literal.
|
||||
fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
|
||||
fn check_for_loop_range<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &'tcx Pat,
|
||||
arg: &'tcx Expr,
|
||||
body: &'tcx Expr,
|
||||
expr: &'tcx Expr
|
||||
) {
|
||||
if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::range(arg) {
|
||||
// the var must be a single name
|
||||
if let PatKind::Binding(_, ref ident, _) = pat.node {
|
||||
if let PatKind::Binding(_, def_id, ref ident, _) = pat.node {
|
||||
let mut visitor = VarVisitor {
|
||||
cx: cx,
|
||||
var: cx.tcx.expect_def(pat.id).def_id(),
|
||||
var: def_id,
|
||||
indexed: HashMap::new(),
|
||||
nonindex: false,
|
||||
};
|
||||
|
|
@ -433,9 +439,9 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
// linting condition: we only indexed one variable
|
||||
if visitor.indexed.len() == 1 {
|
||||
let (indexed, indexed_extent) = visitor.indexed
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| unreachable!() /* len == 1 */);
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| unreachable!() /* len == 1 */);
|
||||
|
||||
// ensure that the indexed variable was declared before the loop, see #601
|
||||
if let Some(indexed_extent) = indexed_extent {
|
||||
|
|
@ -461,10 +467,8 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
ast::RangeLimits::Closed => {
|
||||
let end = sugg::Sugg::hir(cx, end, "<count>");
|
||||
format!(".take({})", end + sugg::ONE)
|
||||
}
|
||||
ast::RangeLimits::HalfOpen => {
|
||||
format!(".take({})", snippet(cx, end.span, ".."))
|
||||
}
|
||||
},
|
||||
ast::RangeLimits::HalfOpen => format!(".take({})", snippet(cx, end.span, "..")),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -477,10 +481,10 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
expr.span,
|
||||
&format!("the loop variable `{}` is used to index `{}`", ident.node, indexed),
|
||||
|db| {
|
||||
multispan_sugg(db, "consider using an iterator".to_string(), &[
|
||||
(pat.span, &format!("({}, <item>)", ident.node)),
|
||||
(arg.span, &format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
|
||||
]);
|
||||
multispan_sugg(db,
|
||||
"consider using an iterator".to_string(),
|
||||
&[(pat.span, &format!("({}, <item>)", ident.node)),
|
||||
(arg.span, &format!("{}.iter().enumerate(){}{}", indexed, take, skip))]);
|
||||
});
|
||||
} else {
|
||||
let repl = if starts_at_zero && take.is_empty() {
|
||||
|
|
@ -492,12 +496,13 @@ fn check_for_loop_range(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, ex
|
|||
span_lint_and_then(cx,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
expr.span,
|
||||
&format!("the loop variable `{}` is only used to index `{}`.", ident.node, indexed),
|
||||
&format!("the loop variable `{}` is only used to index `{}`.",
|
||||
ident.node,
|
||||
indexed),
|
||||
|db| {
|
||||
multispan_sugg(db, "consider using an iterator".to_string(), &[
|
||||
(pat.span, "<item>"),
|
||||
(arg.span, &repl),
|
||||
]);
|
||||
multispan_sugg(db,
|
||||
"consider using an iterator".to_string(),
|
||||
&[(pat.span, "<item>"), (arg.span, &repl)]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -510,7 +515,7 @@ fn is_len_call(expr: &Expr, var: &Name) -> bool {
|
|||
let ExprMethodCall(method, _, ref len_args) = expr.node,
|
||||
len_args.len() == 1,
|
||||
&*method.node.as_str() == "len",
|
||||
let ExprPath(_, ref path) = len_args[0].node,
|
||||
let ExprPath(QPath::Resolved(_, ref path)) = len_args[0].node,
|
||||
path.segments.len() == 1,
|
||||
&path.segments[0].name == var
|
||||
], {
|
||||
|
|
@ -533,7 +538,7 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
|
|||
let (sup, eq) = match (start_idx, end_idx) {
|
||||
(ConstVal::Integral(start_idx), ConstVal::Integral(end_idx)) => {
|
||||
(start_idx > end_idx, start_idx == end_idx)
|
||||
}
|
||||
},
|
||||
_ => (false, false),
|
||||
};
|
||||
|
||||
|
|
@ -551,15 +556,14 @@ fn check_for_loop_reverse_range(cx: &LateContext, arg: &Expr, expr: &Expr) {
|
|||
expr.span,
|
||||
"this range is empty so this for loop will never run",
|
||||
|db| {
|
||||
db.span_suggestion(arg.span,
|
||||
"consider using the following if \
|
||||
you are attempting to iterate \
|
||||
over this range in reverse",
|
||||
format!("({end}{dots}{start}).rev()",
|
||||
end=end_snippet,
|
||||
dots=dots,
|
||||
start=start_snippet));
|
||||
});
|
||||
db.span_suggestion(arg.span,
|
||||
"consider using the following if you are attempting to iterate over this \
|
||||
range in reverse",
|
||||
format!("({end}{dots}{start}).rev()",
|
||||
end = end_snippet,
|
||||
dots = dots,
|
||||
start = start_snippet));
|
||||
});
|
||||
} else if eq && limits != ast::RangeLimits::Closed {
|
||||
// if they are equal, it's also problematic - this loop
|
||||
// will never run.
|
||||
|
|
@ -597,14 +601,14 @@ fn check_for_loop_arg(cx: &LateContext, pat: &Pat, arg: &Expr, expr: &Expr) {
|
|||
method_name));
|
||||
}
|
||||
} else if &*method_name.as_str() == "into_iter" && match_trait_method(cx, arg, &paths::INTO_ITERATOR) {
|
||||
let object = snippet(cx, args[0].span, "_");
|
||||
span_lint(cx,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`",
|
||||
object,
|
||||
object,
|
||||
method_name));
|
||||
let object = snippet(cx, args[0].span, "_");
|
||||
span_lint(cx,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
expr.span,
|
||||
&format!("it is more idiomatic to loop over `{}` instead of `{}.{}()`",
|
||||
object,
|
||||
object,
|
||||
method_name));
|
||||
|
||||
} else if &*method_name.as_str() == "next" && match_trait_method(cx, arg, &paths::ITERATOR) {
|
||||
span_lint(cx,
|
||||
|
|
@ -647,7 +651,12 @@ fn check_arg_type(cx: &LateContext, pat: &Pat, arg: &Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, expr: &Expr) {
|
||||
fn check_for_loop_explicit_counter<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
arg: &'tcx Expr,
|
||||
body: &'tcx Expr,
|
||||
expr: &'tcx Expr
|
||||
) {
|
||||
// Look for variables that are incremented once per loop iteration.
|
||||
let mut visitor = IncrementVisitor {
|
||||
cx: cx,
|
||||
|
|
@ -692,14 +701,20 @@ fn check_for_loop_explicit_counter(cx: &LateContext, arg: &Expr, body: &Expr, ex
|
|||
}
|
||||
|
||||
/// Check for the `FOR_KV_MAP` lint.
|
||||
fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Expr, expr: &Expr) {
|
||||
fn check_for_loop_over_map_kv<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &'tcx Pat,
|
||||
arg: &'tcx Expr,
|
||||
body: &'tcx Expr,
|
||||
expr: &'tcx Expr
|
||||
) {
|
||||
let pat_span = pat.span;
|
||||
|
||||
if let PatKind::Tuple(ref pat, _) = pat.node {
|
||||
if pat.len() == 2 {
|
||||
let (new_pat_span, kind) = match (&pat[0].node, &pat[1].node) {
|
||||
(key, _) if pat_is_wild(key, body) => (pat[1].span, "value"),
|
||||
(_, value) if pat_is_wild(value, body) => (pat[0].span, "key"),
|
||||
(key, _) if pat_is_wild(cx, key, body) => (pat[1].span, "value"),
|
||||
(_, value) if pat_is_wild(cx, value, body) => (pat[0].span, "key"),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
@ -717,10 +732,10 @@ fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Ex
|
|||
&format!("you seem to want to iterate on a map's {}s", kind),
|
||||
|db| {
|
||||
let map = sugg::Sugg::hir(cx, arg, "map");
|
||||
multispan_sugg(db, "use the corresponding method".into(), &[
|
||||
(pat_span, &snippet(cx, new_pat_span, kind)),
|
||||
(arg_span, &format!("{}.{}s()", map.maybe_par(), kind)),
|
||||
]);
|
||||
multispan_sugg(db,
|
||||
"use the corresponding method".into(),
|
||||
&[(pat_span, &snippet(cx, new_pat_span, kind)),
|
||||
(arg_span, &format!("{}.{}s()", map.maybe_par(), kind))]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -729,29 +744,31 @@ fn check_for_loop_over_map_kv(cx: &LateContext, pat: &Pat, arg: &Expr, body: &Ex
|
|||
}
|
||||
|
||||
/// Return true if the pattern is a `PatWild` or an ident prefixed with `'_'`.
|
||||
fn pat_is_wild(pat: &PatKind, body: &Expr) -> bool {
|
||||
fn pat_is_wild<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, pat: &'tcx PatKind, body: &'tcx Expr) -> bool {
|
||||
match *pat {
|
||||
PatKind::Wild => true,
|
||||
PatKind::Binding(_, ident, None) if ident.node.as_str().starts_with('_') => {
|
||||
PatKind::Binding(_, _, ident, None) if ident.node.as_str().starts_with('_') => {
|
||||
let mut visitor = UsedVisitor {
|
||||
var: ident.node,
|
||||
used: false,
|
||||
cx: cx,
|
||||
};
|
||||
walk_expr(&mut visitor, body);
|
||||
!visitor.used
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
struct UsedVisitor {
|
||||
struct UsedVisitor<'a, 'tcx: 'a> {
|
||||
var: ast::Name, // var to look for
|
||||
used: bool, // has the var been used otherwise?
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UsedVisitor {
|
||||
fn visit_expr(&mut self, expr: &Expr) {
|
||||
if let ExprPath(None, ref path) = expr.node {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if let ExprPath(QPath::Resolved(None, ref path)) = expr.node {
|
||||
if path.segments.len() == 1 && path.segments[0].name == self.var {
|
||||
self.used = true;
|
||||
return;
|
||||
|
|
@ -760,31 +777,35 @@ impl<'a> Visitor<'a> for UsedVisitor {
|
|||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
struct VarVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>, // context reference
|
||||
struct VarVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
var: DefId, // var name to look for as index
|
||||
indexed: HashMap<Name, Option<CodeExtent>>, // indexed variables, the extent is None for global
|
||||
nonindex: bool, // has the var been used otherwise?
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
if let ExprPath(None, ref path) = expr.node {
|
||||
if path.segments.len() == 1 && self.cx.tcx.expect_def(expr.id).def_id() == self.var {
|
||||
// we are referencing our variable! now check if it's as an index
|
||||
if_let_chain! {[
|
||||
let Some(parexpr) = get_parent_expr(self.cx, expr),
|
||||
let ExprIndex(ref seqexpr, _) = parexpr.node,
|
||||
let ExprPath(None, ref seqvar) = seqexpr.node,
|
||||
seqvar.segments.len() == 1
|
||||
], {
|
||||
let def_map = self.cx.tcx.def_map.borrow();
|
||||
if let Some(def) = def_map.get(&seqexpr.id) {
|
||||
match def.base_def {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if let ExprPath(ref qpath) = expr.node {
|
||||
if let QPath::Resolved(None, ref path) = *qpath {
|
||||
if path.segments.len() == 1 && self.cx.tcx.tables().qpath_def(qpath, expr.id).def_id() == self.var {
|
||||
// we are referencing our variable! now check if it's as an index
|
||||
if_let_chain! {[
|
||||
let Some(parexpr) = get_parent_expr(self.cx, expr),
|
||||
let ExprIndex(ref seqexpr, _) = parexpr.node,
|
||||
let ExprPath(ref seqpath) = seqexpr.node,
|
||||
let QPath::Resolved(None, ref seqvar) = *seqpath,
|
||||
seqvar.segments.len() == 1
|
||||
], {
|
||||
let def = self.cx.tcx.tables().qpath_def(seqpath, seqexpr.id);
|
||||
match def {
|
||||
Def::Local(..) | Def::Upvar(..) => {
|
||||
let def_id = def.base_def.def_id();
|
||||
let def_id = def.def_id();
|
||||
let node_id = self.cx.tcx.map.as_local_node_id(def_id).unwrap();
|
||||
|
||||
let extent = self.cx.tcx.region_maps.var_scope(node_id);
|
||||
|
|
@ -797,18 +818,21 @@ impl<'v, 't> Visitor<'v> for VarVisitor<'v, 't> {
|
|||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}}
|
||||
// we are not indexing anything, record that
|
||||
self.nonindex = true;
|
||||
return;
|
||||
}}
|
||||
// we are not indexing anything, record that
|
||||
self.nonindex = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_iterator_used_after_while_let(cx: &LateContext, iter_expr: &Expr) -> bool {
|
||||
fn is_iterator_used_after_while_let<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, iter_expr: &'tcx Expr) -> bool {
|
||||
let def_id = match var_def_id(cx, iter_expr) {
|
||||
Some(id) => id,
|
||||
None => return false,
|
||||
|
|
@ -826,16 +850,16 @@ fn is_iterator_used_after_while_let(cx: &LateContext, iter_expr: &Expr) -> bool
|
|||
visitor.var_used_after_while_let
|
||||
}
|
||||
|
||||
struct VarUsedAfterLoopVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>,
|
||||
struct VarUsedAfterLoopVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
def_id: NodeId,
|
||||
iter_expr_id: NodeId,
|
||||
past_while_let: bool,
|
||||
var_used_after_while_let: bool,
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for VarUsedAfterLoopVisitor<'v, 't> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for VarUsedAfterLoopVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if self.past_while_let {
|
||||
if Some(self.def_id) == var_def_id(self.cx, expr) {
|
||||
self.var_used_after_while_let = true;
|
||||
|
|
@ -845,6 +869,9 @@ impl<'v, 't> Visitor<'v> for VarUsedAfterLoopVisitor<'v, 't> {
|
|||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -900,10 +927,11 @@ fn extract_first_expr(block: &Block) -> Option<&Expr> {
|
|||
Some(ref expr) if block.stmts.is_empty() => Some(expr),
|
||||
None if !block.stmts.is_empty() => {
|
||||
match block.stmts[0].node {
|
||||
StmtExpr(ref expr, _) | StmtSemi(ref expr, _) => Some(expr),
|
||||
StmtExpr(ref expr, _) |
|
||||
StmtSemi(ref expr, _) => Some(expr),
|
||||
StmtDecl(..) => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -917,7 +945,7 @@ fn is_break_expr(expr: &Expr) -> bool {
|
|||
Some(subexpr) => is_break_expr(subexpr),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -935,15 +963,15 @@ enum VarState {
|
|||
}
|
||||
|
||||
/// Scan a for loop for variables that are incremented exactly once.
|
||||
struct IncrementVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>, // context reference
|
||||
struct IncrementVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
states: HashMap<NodeId, VarState>, // incremented variables
|
||||
depth: u32, // depth of conditional expressions
|
||||
done: bool,
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for IncrementVisitor<'v, 't> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if self.done {
|
||||
return;
|
||||
}
|
||||
|
|
@ -966,7 +994,7 @@ impl<'v, 't> Visitor<'v> for IncrementVisitor<'v, 't> {
|
|||
*state = VarState::DontWarn;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprAssign(ref lhs, _) if lhs.id == expr.id => *state = VarState::DontWarn,
|
||||
ExprAddrOf(mutability, _) if mutability == MutMutable => *state = VarState::DontWarn,
|
||||
_ => (),
|
||||
|
|
@ -984,12 +1012,15 @@ impl<'v, 't> Visitor<'v> for IncrementVisitor<'v, 't> {
|
|||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether a variable is initialized to zero at the start of a loop.
|
||||
struct InitializeVisitor<'v, 't: 'v> {
|
||||
cx: &'v LateContext<'v, 't>, // context reference
|
||||
end_expr: &'v Expr, // the for loop. Stop scanning here.
|
||||
struct InitializeVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>, // context reference
|
||||
end_expr: &'tcx Expr, // the for loop. Stop scanning here.
|
||||
var_id: NodeId,
|
||||
state: VarState,
|
||||
name: Option<Name>,
|
||||
|
|
@ -997,12 +1028,12 @@ struct InitializeVisitor<'v, 't: 'v> {
|
|||
past_loop: bool,
|
||||
}
|
||||
|
||||
impl<'v, 't> Visitor<'v> for InitializeVisitor<'v, 't> {
|
||||
fn visit_decl(&mut self, decl: &'v Decl) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
||||
fn visit_decl(&mut self, decl: &'tcx Decl) {
|
||||
// Look for declarations of the variable
|
||||
if let DeclLocal(ref local) = decl.node {
|
||||
if local.pat.id == self.var_id {
|
||||
if let PatKind::Binding(_, ref ident, _) = local.pat.node {
|
||||
if let PatKind::Binding(_, _, ref ident, _) = local.pat.node {
|
||||
self.name = Some(ident.node);
|
||||
|
||||
self.state = if let Some(ref init) = local.init {
|
||||
|
|
@ -1020,7 +1051,7 @@ impl<'v, 't> Visitor<'v> for InitializeVisitor<'v, 't> {
|
|||
walk_decl(self, decl);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if self.state == VarState::DontWarn {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1040,14 +1071,14 @@ impl<'v, 't> Visitor<'v> for InitializeVisitor<'v, 't> {
|
|||
match parent.node {
|
||||
ExprAssignOp(_, ref lhs, _) if lhs.id == expr.id => {
|
||||
self.state = VarState::DontWarn;
|
||||
}
|
||||
},
|
||||
ExprAssign(ref lhs, ref rhs) if lhs.id == expr.id => {
|
||||
self.state = if is_integer_literal(rhs, 0) && self.depth == 0 {
|
||||
VarState::Warn
|
||||
} else {
|
||||
VarState::DontWarn
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprAddrOf(mutability, _) if mutability == MutMutable => self.state = VarState::DontWarn,
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -1068,11 +1099,15 @@ impl<'v, 't> Visitor<'v> for InitializeVisitor<'v, 't> {
|
|||
}
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
fn var_def_id(cx: &LateContext, expr: &Expr) -> Option<NodeId> {
|
||||
if let Some(path_res) = cx.tcx.def_map.borrow().get(&expr.id) {
|
||||
if let Def::Local(def_id) = path_res.base_def {
|
||||
if let ExprPath(ref qpath) = expr.node {
|
||||
let path_res = cx.tcx.tables().qpath_def(qpath, expr.id);
|
||||
if let Def::Local(def_id) = path_res {
|
||||
let node_id = cx.tcx.map.as_local_node_id(def_id).expect("That DefId should be valid");
|
||||
return Some(node_id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,14 @@ declare_lint! {
|
|||
#[derive(Copy, Clone)]
|
||||
pub struct Pass;
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
// call to .map()
|
||||
if let ExprMethodCall(name, _, ref args) = expr.node {
|
||||
if &*name.node.as_str() == "map" && args.len() == 2 {
|
||||
match args[1].node {
|
||||
ExprClosure(_, ref decl, ref closure_expr, _) => {
|
||||
let closure_expr = remove_blocks(closure_expr);
|
||||
ExprClosure(_, ref decl, closure_eid, _) => {
|
||||
let closure_expr = remove_blocks(cx.tcx.map.expr(closure_eid));
|
||||
if_let_chain! {[
|
||||
// nothing special in the argument, besides reference bindings
|
||||
// (e.g. .map(|&x| x) )
|
||||
|
|
@ -63,8 +63,8 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
ExprPath(_, ref path) => {
|
||||
},
|
||||
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,
|
||||
|
|
@ -75,7 +75,7 @@ impl LateLintPass for Pass {
|
|||
type_name),
|
||||
&format!("try\n{}.cloned()", snippet(cx, args[0].span, "..")));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -85,13 +85,13 @@ impl LateLintPass for Pass {
|
|||
|
||||
fn expr_eq_name(expr: &Expr, id: ast::Name) -> bool {
|
||||
match expr.node {
|
||||
ExprPath(None, ref path) => {
|
||||
ExprPath(QPath::Resolved(None, ref path)) => {
|
||||
let arg_segment = [PathSegment {
|
||||
name: id,
|
||||
parameters: PathParameters::none(),
|
||||
}];
|
||||
!path.global && path.segments[..] == arg_segment
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ fn get_type_name(cx: &LateContext, expr: &Expr, arg: &Expr) -> Option<&'static s
|
|||
|
||||
fn get_arg_name(pat: &Pat) -> Option<ast::Name> {
|
||||
match pat.node {
|
||||
PatKind::Binding(_, name, None) => Some(name.node),
|
||||
PatKind::Binding(_, _, name, None) => Some(name.node),
|
||||
PatKind::Ref(ref subpat, _) => get_arg_name(subpat),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,12 +125,16 @@ 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)
|
||||
lint_array!(SINGLE_MATCH,
|
||||
MATCH_REF_PATS,
|
||||
MATCH_BOOL,
|
||||
SINGLE_MATCH_ELSE,
|
||||
MATCH_OVERLAPPING_ARM)
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for MatchPass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MatchPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if in_external_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -192,7 +196,14 @@ fn check_single_match_single_pattern(cx: &LateContext, ex: &Expr, arms: &[Arm],
|
|||
}
|
||||
}
|
||||
|
||||
fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr, ty: ty::Ty, els: Option<&Expr>) {
|
||||
fn check_single_match_opt_like(
|
||||
cx: &LateContext,
|
||||
ex: &Expr,
|
||||
arms: &[Arm],
|
||||
expr: &Expr,
|
||||
ty: 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"),
|
||||
|
|
@ -209,9 +220,9 @@ fn check_single_match_opt_like(cx: &LateContext, ex: &Expr, arms: &[Arm], expr:
|
|||
return;
|
||||
}
|
||||
path.to_string()
|
||||
}
|
||||
PatKind::Binding(BindByValue(MutImmutable), ident, None) => ident.node.to_string(),
|
||||
PatKind::Path(None, ref path) => path.to_string(),
|
||||
},
|
||||
PatKind::Binding(BindByValue(MutImmutable), _, ident, None) => ident.node.to_string(),
|
||||
PatKind::Path(ref path) => path.to_string(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
@ -272,16 +283,14 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
|||
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, "..")))
|
||||
}
|
||||
Some(format!("if {} {}", !test, expr_block(cx, false_expr, None, "..")))
|
||||
},
|
||||
(true, true) => None,
|
||||
};
|
||||
|
||||
|
|
@ -291,7 +300,7 @@ fn check_match_bool(cx: &LateContext, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -333,8 +342,7 @@ fn check_match_ref_pats(cx: &LateContext, ex: &Expr, arms: &[Arm], source: Match
|
|||
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",
|
||||
"instead of prefixing all patterns with `&`, you can dereference the expression",
|
||||
template);
|
||||
});
|
||||
}
|
||||
|
|
@ -346,11 +354,12 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
|
|||
arms.iter()
|
||||
.flat_map(|arm| {
|
||||
if let Arm { ref pats, guard: None, .. } = *arm {
|
||||
pats.iter()
|
||||
} else {
|
||||
[].iter()
|
||||
}.filter_map(|pat| {
|
||||
if_let_chain! {[
|
||||
pats.iter()
|
||||
} else {
|
||||
[].iter()
|
||||
}
|
||||
.filter_map(|pat| {
|
||||
if_let_chain! {[
|
||||
let PatKind::Range(ref lhs, ref rhs) = pat.node,
|
||||
let Ok(lhs) = eval_const_expr_partial(cx.tcx, lhs, ExprTypeChecked, None),
|
||||
let Ok(rhs) = eval_const_expr_partial(cx.tcx, rhs, ExprTypeChecked, None)
|
||||
|
|
@ -358,15 +367,15 @@ fn all_ranges(cx: &LateContext, arms: &[Arm]) -> Vec<SpannedRange<ConstVal>> {
|
|||
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) = eval_const_expr_partial(cx.tcx, value, ExprTypeChecked, None)
|
||||
], {
|
||||
return Some(SpannedRange { span: pat.span, node: (value.clone(), value) });
|
||||
}}
|
||||
|
||||
None
|
||||
})
|
||||
None
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
@ -383,17 +392,17 @@ type TypedRanges = Vec<SpannedRange<ConstInt>>;
|
|||
/// `Uint` and `Int` probably don't make sense.
|
||||
fn type_ranges(ranges: &[SpannedRange<ConstVal>]) -> TypedRanges {
|
||||
ranges.iter()
|
||||
.filter_map(|range| {
|
||||
if let (ConstVal::Integral(start), ConstVal::Integral(end)) = range.node {
|
||||
Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, end),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
.filter_map(|range| {
|
||||
if let (ConstVal::Integral(start), ConstVal::Integral(end)) = range.node {
|
||||
Some(SpannedRange {
|
||||
span: range.span,
|
||||
node: (start, end),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn is_unit_expr(expr: &Expr) -> bool {
|
||||
|
|
@ -406,15 +415,15 @@ fn is_unit_expr(expr: &Expr) -> bool {
|
|||
|
||||
fn has_only_ref_pats(arms: &[Arm]) -> bool {
|
||||
let mapped = arms.iter()
|
||||
.flat_map(|a| &a.pats)
|
||||
.map(|p| {
|
||||
match p.node {
|
||||
PatKind::Ref(..) => Some(true), // &-patterns
|
||||
PatKind::Wild => Some(false), // an "anything" wildcard is also fine
|
||||
_ => None, // any other pattern is not fine
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<bool>>>();
|
||||
.flat_map(|a| &a.pats)
|
||||
.map(|p| {
|
||||
match p.node {
|
||||
PatKind::Ref(..) => Some(true), // &-patterns
|
||||
PatKind::Wild => Some(false), // an "anything" wildcard is also fine
|
||||
_ => None, // any other pattern is not fine
|
||||
}
|
||||
})
|
||||
.collect::<Option<Vec<bool>>>();
|
||||
// look for Some(v) where there's at least one true element
|
||||
mapped.map_or(false, |v| v.iter().any(|el| *el))
|
||||
}
|
||||
|
|
@ -481,7 +490,7 @@ pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &
|
|||
if ra.node != rb.node {
|
||||
return Some((ra, rb));
|
||||
}
|
||||
}
|
||||
},
|
||||
(&Kind::End(a, _), &Kind::Start(b, _)) if a != b => (),
|
||||
_ => return Some((a.range(), b.range())),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,11 +27,11 @@ impl LintPass for MemForget {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for MemForget {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprCall(ref path_expr, ref args) = e.node {
|
||||
if let ExprPath(None, _) = path_expr.node {
|
||||
let def_id = cx.tcx.expect_def(path_expr.id).def_id();
|
||||
if let ExprPath(ref qpath) = path_expr.node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, path_expr.id).def_id();
|
||||
if match_def_path(cx, def_id, &paths::MEM_FORGET) {
|
||||
let forgot_ty = cx.tcx.tables().expr_ty(&args[0]);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,16 @@ use rustc::lint::*;
|
|||
use rustc::middle::const_val::ConstVal;
|
||||
use rustc::middle::const_qualif::ConstQualif;
|
||||
use rustc::ty;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc_const_eval::EvalHint::ExprTypeChecked;
|
||||
use rustc_const_eval::eval_const_expr_partial;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, is_copy, match_path,
|
||||
match_trait_method, match_type, method_chain_args, return_ty, same_tys, snippet,
|
||||
span_lint, span_lint_and_then, span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth};
|
||||
use utils::{get_trait_def_id, implements_trait, in_external_macro, in_macro, is_copy, match_path, match_trait_method,
|
||||
match_type, method_chain_args, return_ty, same_tys, snippet, span_lint, span_lint_and_then,
|
||||
span_note_and_lint, walk_ptrs_ty, walk_ptrs_ty_depth, last_path_segment, single_segment_path,
|
||||
match_def_path};
|
||||
use utils::paths;
|
||||
use utils::sugg;
|
||||
|
||||
|
|
@ -547,11 +549,11 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
#[allow(unused_attributes)]
|
||||
// ^ required because `cyclomatic_complexity` attribute shows up as unused
|
||||
#[cyclomatic_complexity = "30"]
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
if in_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -614,20 +616,20 @@ impl LateLintPass for Pass {
|
|||
lint_single_char_pattern(cx, expr, &args[pos]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprBinary(op, ref lhs, ref rhs) if op.node == hir::BiEq || op.node == hir::BiNe => {
|
||||
if !lint_chars_next(cx, expr, lhs, rhs, op.node == hir::BiEq) {
|
||||
lint_chars_next(cx, expr, rhs, lhs, op.node == hir::BiEq);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, implitem: &hir::ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, implitem: &'tcx hir::ImplItem) {
|
||||
if in_external_macro(cx, implitem.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -693,24 +695,27 @@ impl LateLintPass for Pass {
|
|||
/// Checks for the `OR_FUN_CALL` lint.
|
||||
fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir::Expr]) {
|
||||
/// Check for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
|
||||
fn check_unwrap_or_default(cx: &LateContext, name: &str, fun: &hir::Expr, self_expr: &hir::Expr, arg: &hir::Expr,
|
||||
or_has_args: bool, span: Span)
|
||||
-> bool {
|
||||
fn check_unwrap_or_default(
|
||||
cx: &LateContext,
|
||||
name: &str,
|
||||
fun: &hir::Expr,
|
||||
self_expr: &hir::Expr,
|
||||
arg: &hir::Expr,
|
||||
or_has_args: bool,
|
||||
span: Span
|
||||
) -> bool {
|
||||
if or_has_args {
|
||||
return false;
|
||||
}
|
||||
|
||||
if name == "unwrap_or" {
|
||||
if let hir::ExprPath(_, ref path) = fun.node {
|
||||
let path: &str = &path.segments
|
||||
.last()
|
||||
.expect("A path must have at least one segment")
|
||||
.name
|
||||
.as_str();
|
||||
if let hir::ExprPath(ref qpath) = fun.node {
|
||||
let path: &str = &*last_path_segment(qpath).name.as_str();
|
||||
|
||||
if ["default", "new"].contains(&path) {
|
||||
let arg_ty = cx.tcx.tables().expr_ty(arg);
|
||||
let default_trait_id = if let Some(default_trait_id) = get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
|
||||
let default_trait_id = if let Some(default_trait_id) =
|
||||
get_trait_def_id(cx, &paths::DEFAULT_TRAIT) {
|
||||
default_trait_id
|
||||
} else {
|
||||
return false;
|
||||
|
|
@ -718,13 +723,14 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
|||
|
||||
if implements_trait(cx, arg_ty, default_trait_id, Vec::new()) {
|
||||
span_lint_and_then(cx,
|
||||
OR_FUN_CALL,
|
||||
span,
|
||||
&format!("use of `{}` followed by a call to `{}`", name, path),
|
||||
|db| {
|
||||
db.span_suggestion(span, "try this",
|
||||
format!("{}.unwrap_or_default()", snippet(cx, self_expr.span, "_")));
|
||||
});
|
||||
OR_FUN_CALL,
|
||||
span,
|
||||
&format!("use of `{}` followed by a call to `{}`", name, path),
|
||||
|db| {
|
||||
db.span_suggestion(span,
|
||||
"try this",
|
||||
format!("{}.unwrap_or_default()", snippet(cx, self_expr.span, "_")));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -735,8 +741,15 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
|||
}
|
||||
|
||||
/// Check for `*or(foo())`.
|
||||
fn check_general_case(cx: &LateContext, name: &str, fun: &hir::Expr, self_expr: &hir::Expr, arg: &hir::Expr, or_has_args: bool,
|
||||
span: Span) {
|
||||
fn check_general_case(
|
||||
cx: &LateContext,
|
||||
name: &str,
|
||||
fun: &hir::Expr,
|
||||
self_expr: &hir::Expr,
|
||||
arg: &hir::Expr,
|
||||
or_has_args: bool,
|
||||
span: Span
|
||||
) {
|
||||
// don't lint for constant values
|
||||
// FIXME: can we `expect` here instead of match?
|
||||
if let Some(qualif) = cx.tcx.const_qualif_map.borrow().get(&arg.id) {
|
||||
|
|
@ -745,18 +758,16 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
|||
}
|
||||
}
|
||||
// (path, fn_has_argument, methods, suffix)
|
||||
let know_types: &[(&[_], _, &[_], _)] = &[(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION,
|
||||
false,
|
||||
&["map_or", "ok_or", "or", "unwrap_or"],
|
||||
"else"),
|
||||
(&paths::RESULT, true, &["or", "unwrap_or"], "else")];
|
||||
let know_types: &[(&[_], _, &[_], _)] =
|
||||
&[(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
|
||||
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
|
||||
(&paths::RESULT, true, &["or", "unwrap_or"], "else")];
|
||||
|
||||
let self_ty = cx.tcx.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;
|
||||
|
|
@ -772,7 +783,11 @@ fn lint_or_fun_call(cx: &LateContext, expr: &hir::Expr, name: &str, args: &[hir:
|
|||
(false, true) => snippet(cx, fun.span, ".."),
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, OR_FUN_CALL, span, &format!("use of `{}` followed by a function call", name), |db| {
|
||||
span_lint_and_then(cx,
|
||||
OR_FUN_CALL,
|
||||
span,
|
||||
&format!("use of `{}` followed by a function call", name),
|
||||
|db| {
|
||||
db.span_suggestion(span,
|
||||
"try this",
|
||||
format!("{}.{}_{}({})", snippet(cx, self_expr.span, "_"), name, suffix, sugg));
|
||||
|
|
@ -802,7 +817,9 @@ fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, arg_t
|
|||
"using `clone` on a double-reference; \
|
||||
this will copy the reference instead of cloning the inner type",
|
||||
|db| if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
|
||||
db.span_suggestion(expr.span, "try dereferencing it", format!("({}).clone()", snip.deref()));
|
||||
db.span_suggestion(expr.span,
|
||||
"try dereferencing it",
|
||||
format!("({}).clone()", snip.deref()));
|
||||
});
|
||||
return; // don't report clone_on_copy
|
||||
}
|
||||
|
|
@ -826,12 +843,14 @@ fn lint_clone_on_copy(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr, arg_t
|
|||
fn lint_vec_extend(cx: &LateContext, expr: &hir::Expr, args: &[hir::Expr]) {
|
||||
let arg_ty = cx.tcx.tables().expr_ty(&args[1]);
|
||||
if let Some(slice) = derefs_to_slice(cx, &args[1], arg_ty) {
|
||||
span_lint_and_then(cx, EXTEND_FROM_SLICE, expr.span, "use of `extend` to extend a Vec by a slice", |db| {
|
||||
span_lint_and_then(cx,
|
||||
EXTEND_FROM_SLICE,
|
||||
expr.span,
|
||||
"use of `extend` to extend a Vec by a slice",
|
||||
|db| {
|
||||
db.span_suggestion(expr.span,
|
||||
"try this",
|
||||
format!("{}.extend_from_slice({})",
|
||||
snippet(cx, args[0].span, "_"),
|
||||
slice));
|
||||
format!("{}.extend_from_slice({})", snippet(cx, args[0].span, "_"), slice));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -849,18 +868,14 @@ fn lint_string_extend(cx: &LateContext, expr: &hir::Expr, args: &[hir::Expr]) {
|
|||
return;
|
||||
};
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
STRING_EXTEND_CHARS,
|
||||
expr.span,
|
||||
"calling `.extend(_.chars())`",
|
||||
|db| {
|
||||
db.span_suggestion(expr.span, "try this",
|
||||
format!("{}.push_str({}{})",
|
||||
snippet(cx, args[0].span, "_"),
|
||||
ref_str,
|
||||
snippet(cx, target.span, "_")));
|
||||
});
|
||||
span_lint_and_then(cx, STRING_EXTEND_CHARS, expr.span, "calling `.extend(_.chars())`", |db| {
|
||||
db.span_suggestion(expr.span,
|
||||
"try this",
|
||||
format!("{}.push_str({}{})",
|
||||
snippet(cx, args[0].span, "_"),
|
||||
ref_str,
|
||||
snippet(cx, target.span, "_")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -877,8 +892,9 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
|
|||
if_let_chain!{[
|
||||
let hir::ExprCall(ref fun, ref args) = new.node,
|
||||
args.len() == 1,
|
||||
let hir::ExprPath(None, ref path) = fun.node,
|
||||
match_path(path, &paths::CSTRING_NEW),
|
||||
let hir::ExprPath(ref path) = fun.node,
|
||||
let Def::Method(did) = cx.tcx.tables().qpath_def(path, fun.id),
|
||||
match_def_path(cx, did, &paths::CSTRING_NEW)
|
||||
], {
|
||||
span_lint_and_then(cx, TEMPORARY_CSTRING_AS_PTR, expr.span,
|
||||
"you are getting the inner pointer of a temporary `CString`",
|
||||
|
|
@ -889,28 +905,24 @@ fn lint_cstring_as_ptr(cx: &LateContext, expr: &hir::Expr, new: &hir::Expr, unwr
|
|||
}}
|
||||
}
|
||||
|
||||
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr], is_mut: bool){
|
||||
let mut_str = if is_mut { "_mut" } else {""};
|
||||
fn lint_iter_nth(cx: &LateContext, expr: &hir::Expr, iter_args: &[hir::Expr], is_mut: bool) {
|
||||
let mut_str = if is_mut { "_mut" } else { "" };
|
||||
let caller_type = if derefs_to_slice(cx, &iter_args[0], cx.tcx.tables().expr_ty(&iter_args[0])).is_some() {
|
||||
"slice"
|
||||
}
|
||||
else if match_type(cx, cx.tcx.tables().expr_ty(&iter_args[0]), &paths::VEC) {
|
||||
} else if match_type(cx, cx.tcx.tables().expr_ty(&iter_args[0]), &paths::VEC) {
|
||||
"Vec"
|
||||
}
|
||||
else if match_type(cx, cx.tcx.tables().expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
|
||||
} else if match_type(cx, cx.tcx.tables().expr_ty(&iter_args[0]), &paths::VEC_DEQUE) {
|
||||
"VecDeque"
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
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) {
|
||||
|
|
@ -933,32 +945,29 @@ 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_then(
|
||||
cx,
|
||||
GET_UNWRAP,
|
||||
expr.span,
|
||||
&format!("called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
|
||||
mut_str, caller_type),
|
||||
|db| {
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
"try this",
|
||||
format!("{}{}[{}]", borrow_str, snippet(cx, get_args[0].span, "_"),
|
||||
snippet(cx, get_args[1].span, "_"))
|
||||
);
|
||||
}
|
||||
);
|
||||
span_lint_and_then(cx,
|
||||
GET_UNWRAP,
|
||||
expr.span,
|
||||
&format!("called `.get{0}().unwrap()` on a {1}. Using `[]` is more clear and more concise",
|
||||
mut_str,
|
||||
caller_type),
|
||||
|db| {
|
||||
db.span_suggestion(expr.span,
|
||||
"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){
|
||||
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)`");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -976,9 +985,7 @@ fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: ty::Ty) -> Option<sug
|
|||
|
||||
if let hir::ExprMethodCall(name, _, ref args) = expr.node {
|
||||
if &*name.node.as_str() == "iter" && may_slice(cx, cx.tcx.tables().expr_ty(&args[0])) {
|
||||
sugg::Sugg::hir_opt(cx, &args[0]).map(|sugg| {
|
||||
sugg.addr()
|
||||
})
|
||||
sugg::Sugg::hir_opt(cx, &args[0]).map(|sugg| sugg.addr())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -992,7 +999,7 @@ fn derefs_to_slice(cx: &LateContext, expr: &hir::Expr, ty: ty::Ty) -> Option<sug
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1100,8 +1107,8 @@ fn lint_map_unwrap_or_else(cx: &LateContext, expr: &hir::Expr, map_args: &[hir::
|
|||
fn lint_filter_next(cx: &LateContext, expr: &hir::Expr, filter_args: &[hir::Expr]) {
|
||||
// lint if caller of `.filter().next()` is an Iterator
|
||||
if match_trait_method(cx, expr, &paths::ITERATOR) {
|
||||
let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(p)` \
|
||||
instead.";
|
||||
let msg = "called `filter(p).next()` on an `Iterator`. This is more succinctly expressed by calling \
|
||||
`.find(p)` instead.";
|
||||
let filter_snippet = snippet(cx, filter_args[1].span, "..");
|
||||
if filter_snippet.lines().count() <= 1 {
|
||||
// add note if not multi-line
|
||||
|
|
@ -1160,12 +1167,17 @@ fn lint_filter_map_flat_map(cx: &LateContext, expr: &hir::Expr, _filter_args: &[
|
|||
}
|
||||
|
||||
/// lint searching an Iterator followed by `is_some()`
|
||||
fn lint_search_is_some(cx: &LateContext, expr: &hir::Expr, search_method: &str, search_args: &[hir::Expr],
|
||||
is_some_args: &[hir::Expr]) {
|
||||
fn lint_search_is_some(
|
||||
cx: &LateContext,
|
||||
expr: &hir::Expr,
|
||||
search_method: &str,
|
||||
search_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 expressed \
|
||||
by calling `any()`.",
|
||||
let msg = format!("called `is_some()` after searching an `Iterator` with {}. This is more succinctly \
|
||||
expressed by calling `any()`.",
|
||||
search_method);
|
||||
let search_snippet = snippet(cx, search_args[1].span, "..");
|
||||
if search_snippet.lines().count() <= 1 {
|
||||
|
|
@ -1188,8 +1200,9 @@ fn lint_chars_next(cx: &LateContext, expr: &hir::Expr, chain: &hir::Expr, other:
|
|||
let Some(args) = method_chain_args(chain, &["chars", "next"]),
|
||||
let hir::ExprCall(ref fun, ref arg_char) = other.node,
|
||||
arg_char.len() == 1,
|
||||
let hir::ExprPath(None, ref path) = fun.node,
|
||||
path.segments.len() == 1 && &*path.segments[0].name.as_str() == "Some"
|
||||
let hir::ExprPath(ref qpath) = fun.node,
|
||||
let Some(segment) = single_segment_path(qpath),
|
||||
&*segment.name.as_str() == "Some"
|
||||
], {
|
||||
let self_ty = walk_ptrs_ty(cx.tcx.tables().expr_ty_adjusted(&args[0][0]));
|
||||
|
||||
|
|
@ -1227,8 +1240,8 @@ fn lint_single_char_pattern(cx: &LateContext, expr: &hir::Expr, arg: &hir::Expr)
|
|||
arg.span,
|
||||
"single-character string constant used as pattern",
|
||||
|db| {
|
||||
db.span_suggestion(expr.span, "try using a char instead:", hint);
|
||||
});
|
||||
db.span_suggestion(expr.span, "try using a char instead:", hint);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1408,7 +1421,7 @@ impl OutType {
|
|||
}
|
||||
|
||||
fn is_bool(ty: &hir::Ty) -> bool {
|
||||
if let hir::TyPath(None, ref p) = ty.node {
|
||||
if let hir::TyPath(ref p) = ty.node {
|
||||
match_path(p, &["bool"])
|
||||
} else {
|
||||
false
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ impl LintPass for MinMaxPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for MinMaxPass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MinMaxPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
|
||||
if let Some((inner_max, inner_c, _)) = min_max(cx, oe) {
|
||||
if outer_max == inner_max {
|
||||
|
|
@ -46,7 +46,7 @@ impl LateLintPass for MinMaxPass {
|
|||
(MinMax::Min, Some(Ordering::Greater)) => (),
|
||||
_ => {
|
||||
span_lint(cx, MIN_MAX, expr.span, "this min/max combination leads to constant result");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -61,8 +61,8 @@ enum MinMax {
|
|||
|
||||
fn min_max<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(MinMax, Constant, &'a Expr)> {
|
||||
if let ExprCall(ref path, ref args) = expr.node {
|
||||
if let ExprPath(None, _) = path.node {
|
||||
let def_id = cx.tcx.expect_def(path.id).def_id();
|
||||
if let ExprPath(ref qpath) = path.node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, path.id).def_id();
|
||||
|
||||
if match_def_path(cx, def_id, &paths::CMP_MIN) {
|
||||
fetch_const(args, MinMax::Min)
|
||||
|
|
|
|||
|
|
@ -8,10 +8,8 @@ use rustc_const_eval::EvalHint::ExprTypeChecked;
|
|||
use rustc_const_eval::eval_const_expr_partial;
|
||||
use rustc_const_math::ConstFloat;
|
||||
use syntax::codemap::{Span, Spanned, ExpnFormat};
|
||||
use utils::{
|
||||
get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path,
|
||||
snippet, span_lint, span_lint_and_then, walk_ptrs_ty
|
||||
};
|
||||
use utils::{get_item_name, get_parent_expr, implements_trait, in_macro, is_integer_literal, match_path, snippet,
|
||||
span_lint, span_lint_and_then, walk_ptrs_ty, last_path_segment};
|
||||
use utils::sugg::Sugg;
|
||||
|
||||
/// **What it does:** Checks for function arguments and let bindings denoted as `ref`.
|
||||
|
|
@ -161,19 +159,32 @@ 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,
|
||||
lint_array!(TOPLEVEL_REF_ARG,
|
||||
CMP_NAN,
|
||||
FLOAT_CMP,
|
||||
CMP_OWNED,
|
||||
MODULO_ONE,
|
||||
REDUNDANT_PATTERN,
|
||||
USED_UNDERSCORE_BINDING)
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_fn(&mut self, cx: &LateContext, k: FnKind, decl: &FnDecl, _: &Expr, _: Span, _: NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
k: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl,
|
||||
_: &'tcx Expr,
|
||||
_: Span,
|
||||
_: NodeId
|
||||
) {
|
||||
if let FnKind::Closure(_) = k {
|
||||
// Does not apply to closures
|
||||
return;
|
||||
}
|
||||
for arg in &decl.inputs {
|
||||
if let PatKind::Binding(BindByRef(_), _, _) = arg.pat.node {
|
||||
if let PatKind::Binding(BindByRef(_), _, _, _) = arg.pat.node {
|
||||
span_lint(cx,
|
||||
TOPLEVEL_REF_ARG,
|
||||
arg.pat.span,
|
||||
|
|
@ -182,11 +193,11 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext, s: &Stmt) {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, s: &'tcx Stmt) {
|
||||
if_let_chain! {[
|
||||
let StmtDecl(ref d, _) = s.node,
|
||||
let DeclLocal(ref l) = d.node,
|
||||
let PatKind::Binding(BindByRef(mt), i, None) = l.pat.node,
|
||||
let PatKind::Binding(BindByRef(mt), _, i, None) = l.pat.node,
|
||||
let Some(ref init) = l.init
|
||||
], {
|
||||
let init = Sugg::hir(cx, init, "..");
|
||||
|
|
@ -216,14 +227,14 @@ impl LateLintPass for Pass {
|
|||
}}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
|
||||
let op = cmp.node;
|
||||
if op.is_comparison() {
|
||||
if let ExprPath(_, ref path) = left.node {
|
||||
if let ExprPath(QPath::Resolved(_, ref path)) = left.node {
|
||||
check_nan(cx, path, expr.span);
|
||||
}
|
||||
if let ExprPath(_, ref path) = right.node {
|
||||
if let ExprPath(QPath::Resolved(_, ref path)) = right.node {
|
||||
check_nan(cx, path, expr.span);
|
||||
}
|
||||
check_to_owned(cx, left, right, true, cmp.span);
|
||||
|
|
@ -236,23 +247,19 @@ impl LateLintPass 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;
|
||||
}
|
||||
}
|
||||
span_lint_and_then(cx,
|
||||
FLOAT_CMP,
|
||||
expr.span,
|
||||
"strict comparison of f32 or f64",
|
||||
|db| {
|
||||
let lhs = Sugg::hir(cx, left, "..");
|
||||
let rhs = Sugg::hir(cx, right, "..");
|
||||
span_lint_and_then(cx, FLOAT_CMP, expr.span, "strict comparison of f32 or f64", |db| {
|
||||
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_note(expr.span, "std::f32::EPSILON and std::f64::EPSILON are available.");
|
||||
});
|
||||
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) {
|
||||
span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
|
||||
}
|
||||
|
|
@ -262,23 +269,19 @@ impl LateLintPass for Pass {
|
|||
return;
|
||||
}
|
||||
let binding = match expr.node {
|
||||
ExprPath(_, ref path) => {
|
||||
let binding = path.segments
|
||||
.last()
|
||||
.expect("path should always have at least one segment")
|
||||
.name
|
||||
.as_str();
|
||||
ExprPath(ref qpath) => {
|
||||
let binding = last_path_segment(qpath).name.as_str();
|
||||
if binding.starts_with('_') &&
|
||||
!binding.starts_with("__") &&
|
||||
&*binding != "_result" && // FIXME: #944
|
||||
is_used(cx, expr) &&
|
||||
// don't lint if the declaration is in a macro
|
||||
non_macro_local(cx, &cx.tcx.expect_def(expr.id)) {
|
||||
non_macro_local(cx, &cx.tcx.tables().qpath_def(qpath, expr.id)) {
|
||||
Some(binding)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprField(_, spanned) => {
|
||||
let name = spanned.node.as_str();
|
||||
if name.starts_with('_') && !name.starts_with("__") {
|
||||
|
|
@ -286,7 +289,7 @@ impl LateLintPass for Pass {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
if let Some(binding) = binding {
|
||||
|
|
@ -294,19 +297,18 @@ impl LateLintPass for Pass {
|
|||
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));
|
||||
underscore signals that a binding will not be used.",
|
||||
binding));
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
|
||||
if let PatKind::Binding(_, ref ident, Some(ref right)) = pat.node {
|
||||
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));
|
||||
&format!("the `{} @ _` pattern can be written as just `{}`", ident.node, ident.node));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -343,9 +345,8 @@ fn is_allowed(cx: &LateContext, expr: &Expr) -> bool {
|
|||
f64: ::std::f64::NEG_INFINITY,
|
||||
};
|
||||
|
||||
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(zero) == Ok(Ordering::Equal) || val.try_cmp(infinity) == Ok(Ordering::Equal) ||
|
||||
val.try_cmp(neg_infinity) == Ok(Ordering::Equal)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
@ -364,9 +365,9 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: S
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprCall(ref path, ref v) if v.len() == 1 => {
|
||||
if let ExprPath(None, ref path) = path.node {
|
||||
if let ExprPath(ref path) = path.node {
|
||||
if match_path(path, &["String", "from_str"]) || match_path(path, &["String", "from"]) {
|
||||
(cx.tcx.tables().expr_ty(&v[0]), snippet(cx, v[0].span, ".."))
|
||||
} else {
|
||||
|
|
@ -375,7 +376,7 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: S
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
|
@ -412,8 +413,7 @@ fn check_to_owned(cx: &LateContext, expr: &Expr, other: &Expr, left: bool, op: S
|
|||
}
|
||||
|
||||
fn is_str_arg(cx: &LateContext, args: &[Expr]) -> bool {
|
||||
args.len() == 1 &&
|
||||
matches!(walk_ptrs_ty(cx.tcx.tables().expr_ty(&args[0])).sty, ty::TyStr)
|
||||
args.len() == 1 && matches!(walk_ptrs_ty(cx.tcx.tables().expr_ty(&args[0])).sty, ty::TyStr)
|
||||
}
|
||||
|
||||
/// Heuristic to see if an expression is used. Should be compatible with `unused_variables`'s idea
|
||||
|
|
@ -434,24 +434,21 @@ fn is_used(cx: &LateContext, expr: &Expr) -> bool {
|
|||
/// `#[derive(...)`] or the like).
|
||||
fn in_attributes_expansion(cx: &LateContext, expr: &Expr) -> bool {
|
||||
cx.sess().codemap().with_expn_info(expr.span.expn_id, |info_opt| {
|
||||
info_opt.map_or(false, |info| {
|
||||
matches!(info.callee.format, ExpnFormat::MacroAttribute(_))
|
||||
})
|
||||
info_opt.map_or(false, |info| matches!(info.callee.format, ExpnFormat::MacroAttribute(_)))
|
||||
})
|
||||
}
|
||||
|
||||
/// Test whether `def` is a variable defined outside a macro.
|
||||
fn non_macro_local(cx: &LateContext, def: &def::Def) -> bool {
|
||||
match *def {
|
||||
def::Def::Local(id) | def::Def::Upvar(id, _, _) => {
|
||||
let id = cx.tcx.map.as_local_node_id(id).expect("That DefId should be valid");
|
||||
|
||||
if let Some(span) = cx.tcx.map.opt_span(id) {
|
||||
def::Def::Local(id) |
|
||||
def::Def::Upvar(id, _, _) => {
|
||||
if let Some(span) = cx.tcx.map.span_if_local(id) {
|
||||
!in_macro(cx, span)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,9 +168,14 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,14 +277,14 @@ impl EarlyLintPass for MiscEarly {
|
|||
expr.span,
|
||||
"Try not to call a closure in the expression where it is declared.",
|
||||
|db| {
|
||||
if decl.inputs.is_empty() {
|
||||
let hint = snippet(cx, block.span, "..").into_owned();
|
||||
db.span_suggestion(expr.span, "Try doing something like: ", hint);
|
||||
}
|
||||
});
|
||||
if decl.inputs.is_empty() {
|
||||
let hint = snippet(cx, block.span, "..").into_owned();
|
||||
db.span_suggestion(expr.span, "Try doing something like: ", hint);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Unary(UnOp::Neg, ref inner) => {
|
||||
if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
|
||||
span_lint(cx,
|
||||
|
|
@ -287,7 +292,7 @@ impl EarlyLintPass for MiscEarly {
|
|||
expr.span,
|
||||
"`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op");
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Lit(ref lit) => {
|
||||
if_let_chain! {[
|
||||
let LitKind::Int(value, ..) = lit.node,
|
||||
|
|
@ -328,8 +333,16 @@ impl EarlyLintPass for MiscEarly {
|
|||
lit.span,
|
||||
"this is a decimal constant",
|
||||
|db| {
|
||||
db.span_suggestion(lit.span, "if you mean to use a decimal constant, remove the `0` to remove confusion:", src[1..].to_string());
|
||||
db.span_suggestion(lit.span, "if you mean to use an octal constant, use `0o`:", format!("0o{}", &src[1..]));
|
||||
db.span_suggestion(
|
||||
lit.span,
|
||||
"if you mean to use a decimal constant, remove the `0` to remove confusion:",
|
||||
src[1..].to_string(),
|
||||
);
|
||||
db.span_suggestion(
|
||||
lit.span,
|
||||
"if you mean to use an octal constant, use `0o`:",
|
||||
format!("0o{}", &src[1..]),
|
||||
);
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
@ -351,8 +364,8 @@ impl EarlyLintPass for MiscEarly {
|
|||
prev = ch;
|
||||
}
|
||||
}}
|
||||
}
|
||||
_ => ()
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +382,12 @@ impl EarlyLintPass for MiscEarly {
|
|||
let ExprKind::Path(_, ref path) = closure.node
|
||||
], {
|
||||
if sp_ident.node == (&path.segments[0]).identifier {
|
||||
span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
|
||||
span_lint(
|
||||
cx,
|
||||
REDUNDANT_CLOSURE_CALL,
|
||||
second.span,
|
||||
"Closure called just once immediately after it was declared",
|
||||
);
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
/* This file incorporates work covered by the following copyright and
|
||||
* permission notice:
|
||||
* Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
* file at the top-level directory of this distribution and at
|
||||
* http://rust-lang.org/COPYRIGHT.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
* option. This file may not be copied, modified, or distributed
|
||||
* except according to those terms.
|
||||
*/
|
||||
// This file incorporates work covered by the following copyright and
|
||||
// permission notice:
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
|
||||
/* 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
|
||||
*/
|
||||
// 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
|
||||
//
|
||||
|
||||
use rustc::hir;
|
||||
use rustc::lint::*;
|
||||
|
|
@ -51,20 +54,14 @@ impl ::std::default::Default for MissingDoc {
|
|||
|
||||
impl MissingDoc {
|
||||
pub fn new() -> MissingDoc {
|
||||
MissingDoc {
|
||||
doc_hidden_stack: vec![false],
|
||||
}
|
||||
MissingDoc { doc_hidden_stack: vec![false] }
|
||||
}
|
||||
|
||||
fn doc_hidden(&self) -> bool {
|
||||
*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) {
|
||||
fn check_missing_docs_attrs(&self, cx: &LateContext, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
|
||||
// If we're building a test harness, then warning about
|
||||
// documentation is probably not really relevant right now.
|
||||
if cx.sess().opts.test {
|
||||
|
|
@ -82,7 +79,8 @@ impl MissingDoc {
|
|||
|
||||
let has_doc = attrs.iter().any(|a| a.is_value_str() && a.name() == "doc");
|
||||
if !has_doc {
|
||||
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS, sp,
|
||||
cx.span_lint(MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
sp,
|
||||
&format!("missing documentation for {}", desc));
|
||||
}
|
||||
}
|
||||
|
|
@ -94,10 +92,12 @@ impl LintPass for MissingDoc {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for MissingDoc {
|
||||
fn enter_lint_attrs(&mut self, _: &LateContext, attrs: &[ast::Attribute]) {
|
||||
let doc_hidden = self.doc_hidden() || attrs.iter().any(|attr| {
|
||||
attr.check_name("doc") && match attr.meta_item_list() {
|
||||
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"),
|
||||
}
|
||||
|
|
@ -105,15 +105,15 @@ impl LateLintPass for MissingDoc {
|
|||
self.doc_hidden_stack.push(doc_hidden);
|
||||
}
|
||||
|
||||
fn exit_lint_attrs(&mut self, _: &LateContext, _: &[ast::Attribute]) {
|
||||
fn exit_lint_attrs(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx [ast::Attribute]) {
|
||||
self.doc_hidden_stack.pop().expect("empty doc_hidden_stack");
|
||||
}
|
||||
|
||||
fn check_crate(&mut self, cx: &LateContext, krate: &hir::Crate) {
|
||||
fn check_crate(&mut self, cx: &LateContext<'a, 'tcx>, krate: &'tcx hir::Crate) {
|
||||
self.check_missing_docs_attrs(cx, &krate.attrs, krate.span, "crate");
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
|
||||
let desc = match it.node {
|
||||
hir::ItemConst(..) => "a constant",
|
||||
hir::ItemEnum(..) => "an enum",
|
||||
|
|
@ -134,7 +134,7 @@ impl LateLintPass for MissingDoc {
|
|||
self.check_missing_docs_attrs(cx, &it.attrs, it.span, desc);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, trait_item: &'tcx hir::TraitItem) {
|
||||
let desc = match trait_item.node {
|
||||
hir::ConstTraitItem(..) => "an associated constant",
|
||||
hir::MethodTraitItem(..) => "a trait method",
|
||||
|
|
@ -144,16 +144,16 @@ impl LateLintPass for MissingDoc {
|
|||
self.check_missing_docs_attrs(cx, &trait_item.attrs, trait_item.span, desc);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, impl_item: &hir::ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
|
||||
// If the method is an impl for a trait, don't doc.
|
||||
let def_id = cx.tcx.map.local_def_id(impl_item.id);
|
||||
match cx.tcx.associated_item(def_id).container {
|
||||
ty::TraitContainer(_) => return,
|
||||
ty::ImplContainer(cid) => {
|
||||
if cx.tcx.impl_trait_ref(cid).is_some() {
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
let desc = match impl_item.node {
|
||||
|
|
@ -164,13 +164,13 @@ impl LateLintPass for MissingDoc {
|
|||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext, sf: &hir::StructField) {
|
||||
fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, sf: &'tcx hir::StructField) {
|
||||
if !sf.is_positional() {
|
||||
self.check_missing_docs_attrs(cx, &sf.attrs, sf.span, "a struct field");
|
||||
}
|
||||
}
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) {
|
||||
fn check_variant(&mut self, cx: &LateContext<'a, 'tcx>, v: &'tcx hir::Variant, _: &hir::Generics) {
|
||||
self.check_missing_docs_attrs(cx, &v.node.attrs, v.span, "a variant");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,12 +31,12 @@ impl LintPass for MutMut {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for MutMut {
|
||||
fn check_block(&mut self, cx: &LateContext, block: &hir::Block) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutMut {
|
||||
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx hir::Block) {
|
||||
intravisit::walk_block(&mut MutVisitor { cx: cx }, block);
|
||||
}
|
||||
|
||||
fn check_ty(&mut self, cx: &LateContext, ty: &hir::Ty) {
|
||||
fn check_ty(&mut self, cx: &LateContext<'a, 'tcx>, ty: &'tcx hir::Ty) {
|
||||
use rustc::hir::intravisit::Visitor;
|
||||
|
||||
MutVisitor { cx: cx }.visit_ty(ty);
|
||||
|
|
@ -47,8 +47,8 @@ pub struct MutVisitor<'a, 'tcx: 'a> {
|
|||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for MutVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'v hir::Expr) {
|
||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
|
||||
if in_external_macro(self.cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -64,7 +64,10 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> 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 TyRef(_, TypeAndMut { mutbl: hir::MutMutable, .. }) = self.cx.tcx.tables().expr_ty(e).sty {
|
||||
span_lint(self.cx,
|
||||
MUT_MUT,
|
||||
|
|
@ -74,14 +77,20 @@ impl<'a, 'tcx, 'v> intravisit::Visitor<'v> for MutVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &hir::Ty) {
|
||||
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 { 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
intravisit::walk_ty(self, ty);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
|
||||
intravisit::NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,25 +32,24 @@ impl LintPass for UnnecessaryMutPassed {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for UnnecessaryMutPassed {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnnecessaryMutPassed {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
let borrowed_table = cx.tcx.tables.borrow();
|
||||
match e.node {
|
||||
ExprCall(ref fn_expr, ref arguments) => {
|
||||
let function_type = borrowed_table.node_types
|
||||
.get(&fn_expr.id)
|
||||
.expect("A function with an unknown type is called. \
|
||||
If this happened, the compiler would have \
|
||||
aborted the compilation long ago");
|
||||
if let ExprPath(_, ref path) = fn_expr.node {
|
||||
.get(&fn_expr.id)
|
||||
.expect("A function with an unknown type is called. If this happened, the compiler would have \
|
||||
aborted the compilation long ago");
|
||||
if let ExprPath(ref path) = fn_expr.node {
|
||||
check_arguments(cx, arguments, function_type, &path.to_string());
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprMethodCall(ref name, _, ref arguments) => {
|
||||
let method_call = MethodCall::expr(e.id);
|
||||
let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
|
||||
check_arguments(cx, arguments, method_type.ty, &name.node.as_str())
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +59,7 @@ fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS,
|
|||
match type_definition.sty {
|
||||
TypeVariants::TyFnDef(_, _, fn_type) |
|
||||
TypeVariants::TyFnPtr(fn_type) => {
|
||||
let parameters = &fn_type.sig.skip_binder().inputs;
|
||||
let parameters = fn_type.sig.skip_binder().inputs();
|
||||
for (argument, parameter) in arguments.iter().zip(parameters.iter()) {
|
||||
match parameter.sty {
|
||||
TypeVariants::TyRef(_, TypeAndMut { mutbl: MutImmutable, .. }) |
|
||||
|
|
@ -71,11 +70,11 @@ fn check_arguments(cx: &LateContext, arguments: &[Expr], type_definition: &TyS,
|
|||
argument.span,
|
||||
&format!("The function/method \"{}\" doesn't need a mutable reference", name));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ impl LintPass for MutexAtomic {
|
|||
|
||||
pub struct MutexAtomic;
|
||||
|
||||
impl LateLintPass for MutexAtomic {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MutexAtomic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
let ty = cx.tcx.tables().expr_ty(expr);
|
||||
if let ty::TyAdt(_, subst) = ty.sty {
|
||||
if match_type(cx, ty, &paths::MUTEX) {
|
||||
|
|
|
|||
|
|
@ -56,17 +56,13 @@ impl LintPass for NeedlessBool {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for NeedlessBool {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
use self::Expression::*;
|
||||
if let ExprIf(ref pred, ref then_block, Some(ref else_expr)) = e.node {
|
||||
let reduce = |ret, not| {
|
||||
let snip = Sugg::hir(cx, pred, "<predicate>");
|
||||
let snip = if not {
|
||||
!snip
|
||||
} else {
|
||||
snip
|
||||
};
|
||||
let snip = if not { !snip } else { snip };
|
||||
|
||||
let hint = if ret {
|
||||
format!("return {}", snip)
|
||||
|
|
@ -79,8 +75,8 @@ impl LateLintPass for NeedlessBool {
|
|||
e.span,
|
||||
"this if-then-else expression returns a bool literal",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "you can reduce it to", hint);
|
||||
});
|
||||
db.span_suggestion(e.span, "you can reduce it to", hint);
|
||||
});
|
||||
};
|
||||
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
|
||||
(RetBool(true), RetBool(true)) |
|
||||
|
|
@ -89,14 +85,14 @@ impl LateLintPass for NeedlessBool {
|
|||
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");
|
||||
}
|
||||
},
|
||||
(RetBool(true), RetBool(false)) => reduce(true, false),
|
||||
(Bool(true), Bool(false)) => reduce(false, false),
|
||||
(RetBool(false), RetBool(true)) => reduce(true, true),
|
||||
|
|
@ -116,8 +112,8 @@ impl LintPass for BoolComparison {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for BoolComparison {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
use self::Expression::*;
|
||||
if let ExprBinary(Spanned { node: BiEq, .. }, ref left_side, ref right_side) = e.node {
|
||||
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
|
||||
|
|
@ -128,9 +124,9 @@ impl LateLintPass for BoolComparison {
|
|||
e.span,
|
||||
"equality checks against true are unnecessary",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
});
|
||||
}
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
});
|
||||
},
|
||||
(Other, Bool(true)) => {
|
||||
let hint = snippet(cx, left_side.span, "..").into_owned();
|
||||
span_lint_and_then(cx,
|
||||
|
|
@ -138,9 +134,9 @@ impl LateLintPass for BoolComparison {
|
|||
e.span,
|
||||
"equality checks against true are unnecessary",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
});
|
||||
}
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", hint);
|
||||
});
|
||||
},
|
||||
(Bool(false), Other) => {
|
||||
let hint = Sugg::hir(cx, right_side, "..");
|
||||
span_lint_and_then(cx,
|
||||
|
|
@ -148,9 +144,9 @@ impl LateLintPass for BoolComparison {
|
|||
e.span,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
}
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
},
|
||||
(Other, Bool(false)) => {
|
||||
let hint = Sugg::hir(cx, left_side, "..");
|
||||
span_lint_and_then(cx,
|
||||
|
|
@ -158,9 +154,9 @@ impl LateLintPass for BoolComparison {
|
|||
e.span,
|
||||
"equality checks against false can be replaced by a negation",
|
||||
|db| {
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
}
|
||||
db.span_suggestion(e.span, "try simplifying it as shown:", (!hint).to_string());
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -186,7 +182,7 @@ fn fetch_bool_block(block: &Block) -> Expression {
|
|||
} else {
|
||||
Expression::Other
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Expression::Other,
|
||||
}
|
||||
}
|
||||
|
|
@ -200,13 +196,13 @@ fn fetch_bool_expr(expr: &Expr) -> Expression {
|
|||
} else {
|
||||
Expression::Other
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprRet(Some(ref expr)) => {
|
||||
match fetch_bool_expr(expr) {
|
||||
Expression::Bool(value) => Expression::RetBool(value),
|
||||
_ => Expression::Other,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Expression::Other,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,14 +34,15 @@ impl LintPass for NeedlessBorrow {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for NeedlessBorrow {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if in_macro(cx, e.span) {
|
||||
return;
|
||||
}
|
||||
if let ExprAddrOf(MutImmutable, ref inner) = e.node {
|
||||
if let ty::TyRef(..) = cx.tcx.tables().expr_ty(inner).sty {
|
||||
if let Some(&ty::adjustment::Adjust::DerefRef { autoderefs, autoref, .. }) = cx.tcx.tables.borrow().adjustments.get(&e.id).map(|a| &a.kind) {
|
||||
if let Some(&ty::adjustment::Adjust::DerefRef { autoderefs, autoref, .. }) =
|
||||
cx.tcx.tables.borrow().adjustments.get(&e.id).map(|a| &a.kind) {
|
||||
if autoderefs > 1 && autoref.is_some() {
|
||||
span_lint(cx,
|
||||
NEEDLESS_BORROW,
|
||||
|
|
@ -53,18 +54,15 @@ impl LateLintPass for NeedlessBorrow {
|
|||
}
|
||||
}
|
||||
}
|
||||
fn check_pat(&mut self, cx: &LateContext, pat: &Pat) {
|
||||
fn check_pat(&mut self, cx: &LateContext<'a, 'tcx>, pat: &'tcx Pat) {
|
||||
if in_macro(cx, pat.span) {
|
||||
return;
|
||||
}
|
||||
if let PatKind::Binding(BindingMode::BindByRef(MutImmutable), _, _) = pat.node {
|
||||
if let PatKind::Binding(BindingMode::BindByRef(MutImmutable), _, _, _) = pat.node {
|
||||
if let ty::TyRef(_, ref tam) = cx.tcx.tables().pat_ty(pat).sty {
|
||||
if tam.mutbl == MutImmutable {
|
||||
if let ty::TyRef(..) = tam.ty.sty {
|
||||
span_lint(cx,
|
||||
NEEDLESS_BORROW,
|
||||
pat.span,
|
||||
"this pattern creates a reference to a reference")
|
||||
span_lint(cx, NEEDLESS_BORROW, pat.span, "this pattern creates a reference to a reference")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprStruct(_, ref fields, Some(ref base)) = expr.node {
|
||||
let ty = cx.tcx.tables().expr_ty(expr);
|
||||
if let TyAdt(def, _) = ty.sty {
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ impl LintPass for NegMultiply {
|
|||
}
|
||||
|
||||
#[allow(match_same_arms)]
|
||||
impl LateLintPass for NegMultiply {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NegMultiply {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprBinary(Spanned { node: BiMul, .. }, ref l, ref r) = e.node {
|
||||
match (&l.node, &r.node) {
|
||||
(&ExprUnary(..), &ExprUnary(..)) => (),
|
||||
|
|
|
|||
|
|
@ -89,8 +89,16 @@ impl LintPass for NewWithoutDefault {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for NewWithoutDefault {
|
||||
fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, _: &hir::Expr, span: Span, id: ast::NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
_: &'tcx hir::Expr,
|
||||
span: Span,
|
||||
id: ast::NodeId
|
||||
) {
|
||||
if in_external_macro(cx, span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -153,7 +161,7 @@ fn can_derive_default<'t, 'c>(ty: ty::Ty<'t>, cx: &LateContext<'c, 't>, default_
|
|||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::def::{Def, PathResolution};
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::{Expr, Expr_, Stmt, StmtSemi, BlockCheckMode, UnsafeSource};
|
||||
use utils::{in_macro, span_lint, snippet_opt, span_lint_and_then};
|
||||
use std::ops::Deref;
|
||||
|
|
@ -66,17 +66,21 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
|
|||
Some(ref base) => has_no_effect(cx, base),
|
||||
None => true,
|
||||
}
|
||||
}
|
||||
},
|
||||
Expr_::ExprCall(ref callee, ref args) => {
|
||||
let def = cx.tcx.def_map.borrow().get(&callee.id).map(|d| d.full_def());
|
||||
match def {
|
||||
Some(Def::Struct(..)) |
|
||||
Some(Def::Variant(..)) |
|
||||
Some(Def::StructCtor(..)) |
|
||||
Some(Def::VariantCtor(..)) => args.iter().all(|arg| has_no_effect(cx, arg)),
|
||||
_ => false,
|
||||
if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tcx.tables().qpath_def(qpath, callee.id);
|
||||
match def {
|
||||
Def::Struct(..) |
|
||||
Def::Variant(..) |
|
||||
Def::StructCtor(..) |
|
||||
Def::VariantCtor(..) => args.iter().all(|arg| has_no_effect(cx, arg)),
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Expr_::ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() &&
|
||||
if let Some(ref expr) = block.expr {
|
||||
|
|
@ -84,7 +88,7 @@ fn has_no_effect(cx: &LateContext, expr: &Expr) -> bool {
|
|||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -98,8 +102,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx Stmt) {
|
||||
if let StmtSemi(ref expr, _) = stmt.node {
|
||||
if has_no_effect(cx, expr) {
|
||||
span_lint(cx, NO_EFFECT, stmt.span, "statement with no effect");
|
||||
|
|
@ -144,16 +148,21 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
|
|||
Expr_::ExprBox(ref inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
|
||||
Expr_::ExprStruct(_, ref fields, ref base) => {
|
||||
Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
|
||||
}
|
||||
},
|
||||
Expr_::ExprCall(ref callee, ref args) => {
|
||||
match cx.tcx.def_map.borrow().get(&callee.id).map(PathResolution::full_def) {
|
||||
Some(Def::Struct(..)) |
|
||||
Some(Def::Variant(..)) |
|
||||
Some(Def::StructCtor(..)) |
|
||||
Some(Def::VariantCtor(..)) => Some(args.iter().collect()),
|
||||
_ => None,
|
||||
if let Expr_::ExprPath(ref qpath) = callee.node {
|
||||
let def = cx.tcx.tables().qpath_def(qpath, callee.id);
|
||||
match def {
|
||||
Def::Struct(..) |
|
||||
Def::Variant(..) |
|
||||
Def::StructCtor(..) |
|
||||
Def::VariantCtor(..) => Some(args.iter().collect()),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
Expr_::ExprBlock(ref block) => {
|
||||
if block.stmts.is_empty() {
|
||||
block.expr.as_ref().and_then(|e| {
|
||||
|
|
@ -167,7 +176,7 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ struct ExistingName {
|
|||
whitelist: &'static [&'static str],
|
||||
}
|
||||
|
||||
struct SimilarNamesLocalVisitor<'a, 'b: 'a> {
|
||||
struct SimilarNamesLocalVisitor<'a, 'tcx: 'a> {
|
||||
names: Vec<ExistingName>,
|
||||
cx: &'a EarlyContext<'b>,
|
||||
cx: &'a EarlyContext<'tcx>,
|
||||
lint: &'a NonExpressiveNames,
|
||||
single_char_names: Vec<char>,
|
||||
}
|
||||
|
|
@ -76,10 +76,10 @@ const WHITELIST: &'static [&'static [&'static str]] = &[
|
|||
&["set", "get"],
|
||||
];
|
||||
|
||||
struct SimilarNamesNameVisitor<'a, 'b: 'a, 'c: 'b>(&'a mut SimilarNamesLocalVisitor<'b, 'c>);
|
||||
struct SimilarNamesNameVisitor<'a: 'b, 'tcx: 'a, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);
|
||||
|
||||
impl<'a, 'b, 'c> Visitor for SimilarNamesNameVisitor<'a, 'b, 'c> {
|
||||
fn visit_pat(&mut self, pat: &Pat) {
|
||||
impl<'a, 'tcx: 'a, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
fn visit_pat(&mut self, pat: &'tcx Pat) {
|
||||
match pat.node {
|
||||
PatKind::Ident(_, id, _) => self.check_name(id.span, id.node.name),
|
||||
PatKind::Struct(_, ref fields, _) => {
|
||||
|
|
@ -88,7 +88,7 @@ impl<'a, 'b, 'c> Visitor for SimilarNamesNameVisitor<'a, 'b, 'c> {
|
|||
self.visit_pat(&field.node.pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => walk_pat(self, pat),
|
||||
}
|
||||
}
|
||||
|
|
@ -120,7 +120,7 @@ fn whitelisted(interned_name: &str, list: &[&str]) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c> SimilarNamesNameVisitor<'a, 'b, 'c> {
|
||||
impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
|
||||
fn check_short_name(&mut self, c: char, span: Span) {
|
||||
// make sure we ignore shadowing
|
||||
if self.0.single_char_names.contains(&c) {
|
||||
|
|
@ -210,8 +210,8 @@ impl<'a, 'b, 'c> SimilarNamesNameVisitor<'a, 'b, 'c> {
|
|||
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;
|
||||
|
|
@ -236,20 +236,22 @@ impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> Visitor for SimilarNamesLocalVisitor<'a, 'b> {
|
||||
fn visit_local(&mut self, local: &Local) {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: &'tcx Local) {
|
||||
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 expression
|
||||
// add the pattern after the expression because the bindings aren't available yet in the init
|
||||
// expression
|
||||
SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
|
||||
}
|
||||
fn visit_block(&mut self, blk: &Block) {
|
||||
fn visit_block(&mut self, blk: &'tcx Block) {
|
||||
self.apply(|this| walk_block(this, blk));
|
||||
}
|
||||
fn visit_arm(&mut self, arm: &Arm) {
|
||||
fn visit_arm(&mut self, arm: &'tcx Arm) {
|
||||
self.apply(|this| {
|
||||
// just go through the first pattern, as either all patterns bind the same bindings or rustc would have errored much earlier
|
||||
// just go through the first pattern, as either all patterns
|
||||
// bind the same bindings or rustc would have errored much earlier
|
||||
SimilarNamesNameVisitor(this).visit_pat(&arm.pats[0]);
|
||||
this.apply(|this| walk_expr(this, &arm.body));
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use utils::{paths, method_chain_args, span_help_and_lint, match_type, snippet};
|
|||
/// vec.push(bench)
|
||||
/// }
|
||||
/// }
|
||||
///```
|
||||
/// ```
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
|
|
@ -40,13 +40,13 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain! {[ //begin checking variables
|
||||
let ExprMatch(ref op, ref body, ref source) = expr.node, //test if expr is a match
|
||||
let MatchSource::IfLetDesugar { .. } = *source, //test if it is an If Let
|
||||
let ExprMethodCall(_, _, ref result_types) = op.node, //check is expr.ok() has type Result<T,E>.ok()
|
||||
let PatKind::TupleStruct(ref x, ref y, _) = body[0].pats[0].node, //get operation
|
||||
let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = body[0].pats[0].node, //get operation
|
||||
method_chain_args(op, &["ok"]).is_some() //test to see if using ok() methoduse std::marker::Sized;
|
||||
|
||||
], {
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ impl LintPass for NonSensical {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for NonSensical {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSensical {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprMethodCall(ref name, _, ref arguments) = e.node {
|
||||
let (obj_ty, _) = walk_ptrs_ty_depth(cx.tcx.tables().expr_ty(&arguments[0]));
|
||||
if &*name.node.as_str() == "open" && match_type(cx, obj_ty, &paths::OPEN_OPTIONS) {
|
||||
|
|
@ -71,36 +71,32 @@ fn get_open_options(cx: &LateContext, argument: &Expr, options: &mut Vec<(OpenOp
|
|||
let argument_option = match arguments[1].node {
|
||||
ExprLit(ref span) => {
|
||||
if let Spanned { node: LitKind::Bool(lit), .. } = **span {
|
||||
if lit {
|
||||
Argument::True
|
||||
} else {
|
||||
Argument::False
|
||||
}
|
||||
if lit { Argument::True } else { Argument::False }
|
||||
} else {
|
||||
return; // The function is called with a literal
|
||||
// which is not a boolean literal. This is theoretically
|
||||
// possible, but not very likely.
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => Argument::Unknown,
|
||||
};
|
||||
|
||||
match &*name.node.as_str() {
|
||||
"create" => {
|
||||
options.push((OpenOption::Create, argument_option));
|
||||
}
|
||||
},
|
||||
"append" => {
|
||||
options.push((OpenOption::Append, argument_option));
|
||||
}
|
||||
},
|
||||
"truncate" => {
|
||||
options.push((OpenOption::Truncate, argument_option));
|
||||
}
|
||||
},
|
||||
"read" => {
|
||||
options.push((OpenOption::Read, argument_option));
|
||||
}
|
||||
},
|
||||
"write" => {
|
||||
options.push((OpenOption::Write, argument_option));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
|
@ -111,11 +107,8 @@ fn get_open_options(cx: &LateContext, argument: &Expr, options: &mut Vec<(OpenOp
|
|||
|
||||
fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span: 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);
|
||||
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.
|
||||
|
||||
for option in options {
|
||||
|
|
@ -130,7 +123,7 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
|
|||
create = true
|
||||
}
|
||||
create_arg = create_arg || (arg == Argument::True);;
|
||||
}
|
||||
},
|
||||
(OpenOption::Append, arg) => {
|
||||
if append {
|
||||
span_lint(cx,
|
||||
|
|
@ -141,7 +134,7 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
|
|||
append = true
|
||||
}
|
||||
append_arg = append_arg || (arg == Argument::True);;
|
||||
}
|
||||
},
|
||||
(OpenOption::Truncate, arg) => {
|
||||
if truncate {
|
||||
span_lint(cx,
|
||||
|
|
@ -152,7 +145,7 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
|
|||
truncate = true
|
||||
}
|
||||
truncate_arg = truncate_arg || (arg == Argument::True);
|
||||
}
|
||||
},
|
||||
(OpenOption::Read, arg) => {
|
||||
if read {
|
||||
span_lint(cx,
|
||||
|
|
@ -163,7 +156,7 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
|
|||
read = true
|
||||
}
|
||||
read_arg = read_arg || (arg == Argument::True);;
|
||||
}
|
||||
},
|
||||
(OpenOption::Write, arg) => {
|
||||
if write {
|
||||
span_lint(cx,
|
||||
|
|
@ -174,7 +167,7 @@ fn check_open_options(cx: &LateContext, options: &[(OpenOption, Argument)], span
|
|||
write = true
|
||||
}
|
||||
write_arg = write_arg || (arg == Argument::True);;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,27 +29,29 @@ impl LintPass for OverflowCheckConditional {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for OverflowCheckConditional {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OverflowCheckConditional {
|
||||
// a + b < a, a > a + b, a < a - b, a - b > a
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain! {[
|
||||
let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node,
|
||||
let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = first.node,
|
||||
let Expr_::ExprPath(_,ref path1) = ident1.node,
|
||||
let Expr_::ExprPath(_, ref path2) = ident2.node,
|
||||
let Expr_::ExprPath(_, ref path3) = second.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = second.node,
|
||||
&path1.segments[0] == &path3.segments[0] || &path2.segments[0] == &path3.segments[0],
|
||||
cx.tcx.tables().expr_ty(ident1).is_integral(),
|
||||
cx.tcx.tables().expr_ty(ident2).is_integral()
|
||||
], {
|
||||
if let BinOp_::BiLt = op.node {
|
||||
if let BinOp_::BiAdd = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, "You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
}
|
||||
}
|
||||
if let BinOp_::BiGt = op.node {
|
||||
if let BinOp_::BiSub = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, "You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
@ -57,21 +59,23 @@ impl LateLintPass for OverflowCheckConditional {
|
|||
if_let_chain! {[
|
||||
let Expr_::ExprBinary(ref op, ref first, ref second) = expr.node,
|
||||
let Expr_::ExprBinary(ref op2, ref ident1, ref ident2) = second.node,
|
||||
let Expr_::ExprPath(_,ref path1) = ident1.node,
|
||||
let Expr_::ExprPath(_, ref path2) = ident2.node,
|
||||
let Expr_::ExprPath(_, ref path3) = first.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path1)) = ident1.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path2)) = ident2.node,
|
||||
let Expr_::ExprPath(QPath::Resolved(_, ref path3)) = first.node,
|
||||
&path1.segments[0] == &path3.segments[0] || &path2.segments[0] == &path3.segments[0],
|
||||
cx.tcx.tables().expr_ty(ident1).is_integral(),
|
||||
cx.tcx.tables().expr_ty(ident2).is_integral()
|
||||
], {
|
||||
if let BinOp_::BiGt = op.node {
|
||||
if let BinOp_::BiAdd = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, "You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C overflow conditions that will fail in Rust.");
|
||||
}
|
||||
}
|
||||
if let BinOp_::BiLt = op.node {
|
||||
if let BinOp_::BiSub = op2.node {
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, "You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span,
|
||||
"You are trying to use classic C underflow conditions that will fail in Rust.");
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -32,16 +32,15 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain! {[
|
||||
let ExprBlock(ref block) = expr.node,
|
||||
let Some(ref ex) = block.expr,
|
||||
let ExprCall(ref fun, ref params) = ex.node,
|
||||
params.len() == 2,
|
||||
let ExprPath(None, _) = fun.node,
|
||||
let Some(fun) = resolve_node(cx, fun.id),
|
||||
match_def_path(cx, fun.def_id(), &paths::BEGIN_PANIC),
|
||||
let ExprPath(ref qpath) = fun.node,
|
||||
match_def_path(cx, resolve_node(cx, qpath, fun.id).def_id(), &paths::BEGIN_PANIC),
|
||||
let ExprLit(ref lit) = params[0].node,
|
||||
is_direct_expn_of(cx, params[0].span, "panic").is_some(),
|
||||
let LitKind::Str(ref string, _) = lit.node,
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if_let_chain! {[
|
||||
let ItemImpl(_, _, _, Some(ref trait_ref), _, ref impl_items) = item.node,
|
||||
!is_automatically_derived(&*item.attrs),
|
||||
cx.tcx.expect_def(trait_ref.ref_id).def_id() == cx.tcx.lang_items.eq_trait().unwrap(),
|
||||
trait_ref.path.def.def_id() == cx.tcx.lang_items.eq_trait().unwrap(),
|
||||
], {
|
||||
for impl_item in impl_items {
|
||||
if &*impl_item.name.as_str() == "ne" {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl EarlyLintPass for Precedence {
|
|||
snippet(cx, left.span, ".."),
|
||||
op.to_string(),
|
||||
snippet(cx, right.span, "..")));
|
||||
}
|
||||
},
|
||||
(true, false) => {
|
||||
span_lint(cx,
|
||||
PRECEDENCE,
|
||||
|
|
@ -59,7 +59,7 @@ impl EarlyLintPass for Precedence {
|
|||
snippet(cx, left.span, ".."),
|
||||
op.to_string(),
|
||||
snippet(cx, right.span, "..")));
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
span_lint(cx,
|
||||
PRECEDENCE,
|
||||
|
|
@ -69,7 +69,7 @@ impl EarlyLintPass for Precedence {
|
|||
snippet(cx, left.span, ".."),
|
||||
op.to_string(),
|
||||
snippet(cx, right.span, "..")));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ impl EarlyLintPass for Precedence {
|
|||
&format!("unary minus has lower precedence than method call. Consider \
|
||||
adding parentheses to clarify your intent: -({})",
|
||||
snippet(cx, rhs.span, "..")));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc::hir::*;
|
|||
use rustc::hir::map::Node::{NodeItem, NodeImplItem};
|
||||
use rustc::lint::*;
|
||||
use utils::paths;
|
||||
use utils::{is_expn_of, match_path, match_def_path, resolve_node, span_lint};
|
||||
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
|
||||
|
|
@ -65,13 +65,13 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain! {[
|
||||
let ExprCall(ref fun, ref args) = expr.node,
|
||||
let ExprPath(..) = fun.node,
|
||||
let Some(fun) = resolve_node(cx, fun.id),
|
||||
let ExprPath(ref qpath) = fun.node,
|
||||
], {
|
||||
let fun = resolve_node(cx, qpath, fun.id);
|
||||
let fun_id = fun.def_id();
|
||||
|
||||
// Search for `std::io::_print(..)` which is unique in a
|
||||
|
|
@ -93,9 +93,8 @@ impl LateLintPass for Pass {
|
|||
// ensure we're calling Arguments::new_v1
|
||||
args.len() == 1,
|
||||
let ExprCall(ref args_fun, ref args_args) = args[0].node,
|
||||
let ExprPath(..) = args_fun.node,
|
||||
let Some(def) = resolve_node(cx, args_fun.id),
|
||||
match_def_path(cx, def.def_id(), &paths::FMT_ARGUMENTS_NEWV1),
|
||||
let ExprPath(ref qpath) = args_fun.node,
|
||||
match_def_path(cx, resolve_node(cx, qpath, args_fun.id).def_id(), &paths::FMT_ARGUMENTS_NEWV1),
|
||||
args_args.len() == 2,
|
||||
let ExprAddrOf(_, ref match_expr) = args_args[1].node,
|
||||
let ExprMatch(ref args, _, _) = match_expr.node,
|
||||
|
|
@ -121,8 +120,8 @@ impl LateLintPass for Pass {
|
|||
// Search for something like
|
||||
// `::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Debug::fmt)`
|
||||
else if args.len() == 2 && match_def_path(cx, fun_id, &paths::FMT_ARGUMENTV1_NEW) {
|
||||
if let ExprPath(None, _) = args[1].node {
|
||||
let def_id = resolve_node(cx, args[1].id).unwrap().def_id();
|
||||
if let ExprPath(ref qpath) = args[1].node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, args[1].id).def_id();
|
||||
if match_def_path(cx, def_id, &paths::DEBUG_FMT_METHOD) && !is_in_debug_impl(cx, expr) &&
|
||||
is_expn_of(cx, expr.span, "panic").is_none() {
|
||||
span_lint(cx, USE_DEBUG, args[0].span, "use of `Debug`-based formatting");
|
||||
|
|
@ -141,7 +140,7 @@ fn is_in_debug_impl(cx: &LateContext, expr: &Expr) -> bool {
|
|||
// `Debug` impl
|
||||
if let Some(NodeItem(item)) = map.find(map.get_parent(item.id)) {
|
||||
if let ItemImpl(_, _, _, Some(ref tr), _, _) = item.node {
|
||||
return match_path(&tr.path, &["Debug"]);
|
||||
return match_path_old(&tr.path, &["Debug"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,14 +54,14 @@ impl LintPass for PointerPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for PointerPass {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PointerPass {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemFn(ref decl, _, _, _, _, _) = item.node {
|
||||
check_fn(cx, decl, item.id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
if let ImplItemKind::Method(ref sig, _) = item.node {
|
||||
if let Some(NodeItem(it)) = cx.tcx.map.find(cx.tcx.map.get_parent(item.id)) {
|
||||
if let ItemImpl(_, _, _, Some(_), _, _) = it.node {
|
||||
|
|
@ -72,13 +72,13 @@ impl LateLintPass for PointerPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
if let MethodTraitItem(ref sig, _) = item.node {
|
||||
check_fn(cx, &sig.decl, item.id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
|
||||
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,
|
||||
|
|
@ -94,7 +94,7 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
|||
let fn_def_id = cx.tcx.map.local_def_id(fn_id);
|
||||
let fn_ty = cx.tcx.item_type(fn_def_id).fn_sig().skip_binder();
|
||||
|
||||
for (arg, ty) in decl.inputs.iter().zip(&fn_ty.inputs) {
|
||||
for (arg, ty) in decl.inputs.iter().zip(fn_ty.inputs()) {
|
||||
if let ty::TyRef(_, ty::TypeAndMut { ty, mutbl: MutImmutable }) = ty.sty {
|
||||
if match_type(cx, ty, &paths::VEC) {
|
||||
span_lint(cx,
|
||||
|
|
@ -116,8 +116,8 @@ fn check_fn(cx: &LateContext, decl: &FnDecl, fn_id: NodeId) {
|
|||
fn is_null_path(expr: &Expr) -> bool {
|
||||
if let ExprCall(ref pathexp, ref args) = expr.node {
|
||||
if args.is_empty() {
|
||||
if let ExprPath(_, ref path) = pathexp.node {
|
||||
return match_path(path, &paths::PTR_NULL) || match_path(path, &paths::PTR_NULL_MUT)
|
||||
if let ExprPath(ref path) = pathexp.node {
|
||||
return match_path(path, &paths::PTR_NULL) || match_path(path, &paths::PTR_NULL_MUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,14 +46,13 @@ impl LintPass for StepByZero {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for StepByZero {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprMethodCall(Spanned { node: ref name, .. }, _, ref args) = expr.node {
|
||||
let name = &*name.as_str();
|
||||
|
||||
// Range with step_by(0).
|
||||
if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) &&
|
||||
is_integer_literal(&args[1], 0) {
|
||||
if name == "step_by" && args.len() == 2 && has_step_by(cx, &args[0]) && is_integer_literal(&args[1], 0) {
|
||||
span_lint(cx,
|
||||
RANGE_STEP_BY_ZERO,
|
||||
expr.span,
|
||||
|
|
@ -73,15 +72,15 @@ impl LateLintPass for StepByZero {
|
|||
let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = end.node,
|
||||
&*len_name.as_str() == "len" && len_args.len() == 1,
|
||||
// .iter() and .len() called on same Path
|
||||
let ExprPath(_, Path { segments: ref iter_path, .. }) = iter_args[0].node,
|
||||
let ExprPath(_, Path { segments: ref len_path, .. }) = len_args[0].node,
|
||||
iter_path == len_path
|
||||
let ExprPath(QPath::Resolved(_, ref iter_path)) = iter_args[0].node,
|
||||
let ExprPath(QPath::Resolved(_, ref len_path)) = len_args[0].node,
|
||||
iter_path.segments == len_path.segments
|
||||
], {
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("It is more idiomatic to use {}.iter().enumerate()",
|
||||
snippet(cx, iter_args[0].span, "_")));
|
||||
span_lint(cx,
|
||||
RANGE_ZIP_WITH_LEN,
|
||||
expr.span,
|
||||
&format!("It is more idiomatic to use {}.iter().enumerate()",
|
||||
snippet(cx, iter_args[0].span, "_")));
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +93,6 @@ fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
|
|||
let ty = cx.tcx.tables().expr_ty(expr);
|
||||
|
||||
// Note: `RangeTo`, `RangeToInclusive` and `RangeFull` don't have step_by
|
||||
match_type(cx, ty, &paths::RANGE)
|
||||
|| match_type(cx, ty, &paths::RANGE_FROM)
|
||||
|| match_type(cx, ty, &paths::RANGE_INCLUSIVE)
|
||||
match_type(cx, ty, &paths::RANGE) || match_type(cx, ty, &paths::RANGE_FROM) ||
|
||||
match_type(cx, ty, &paths::RANGE_INCLUSIVE)
|
||||
}
|
||||
|
|
|
|||
49
clippy_lints/src/reference.rs
Normal file
49
clippy_lints/src/reference.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use syntax::ast::{Expr, ExprKind, UnOp};
|
||||
use rustc::lint::*;
|
||||
use utils::{span_lint_and_then, snippet};
|
||||
|
||||
/// **What it does:** Checks for usage of `*&` and `*&mut` in expressions.
|
||||
///
|
||||
/// **Why is this bad?** Immediately dereferencing a reference is no-op and
|
||||
/// makes the code less clear.
|
||||
///
|
||||
/// **Known problems:** Multiple dereference/addrof pairs are not handled so
|
||||
/// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let a = f(*&mut b);
|
||||
/// let c = *&d;
|
||||
/// ```
|
||||
declare_lint! {
|
||||
pub DEREF_ADDROF,
|
||||
Warn,
|
||||
"use of `*&` or `*&mut` in an expression"
|
||||
}
|
||||
|
||||
pub struct Pass;
|
||||
|
||||
impl LintPass for Pass {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(DEREF_ADDROF)
|
||||
}
|
||||
}
|
||||
|
||||
fn without_parens(mut e: &Expr) -> &Expr {
|
||||
while let ExprKind::Paren(ref child_e) = e.node {
|
||||
e = child_e;
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
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_then(cx, DEREF_ADDROF, e.span, "immediately dereferencing a reference", |db| {
|
||||
db.span_suggestion(e.span, "try this", format!("{}", snippet(cx, addrof_target.span, "_")));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,12 +82,12 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_crate(&mut self, _: &LateContext, _: &Crate) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
|
||||
self.spans.clear();
|
||||
}
|
||||
|
||||
fn check_block(&mut self, cx: &LateContext, block: &Block) {
|
||||
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
|
||||
if_let_chain!{[
|
||||
self.last.is_none(),
|
||||
let Some(ref expr) = block.expr,
|
||||
|
|
@ -106,19 +106,19 @@ impl LateLintPass for Pass {
|
|||
}}
|
||||
}
|
||||
|
||||
fn check_block_post(&mut self, _: &LateContext, block: &Block) {
|
||||
fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block) {
|
||||
if self.last.map_or(false, |id| block.id == id) {
|
||||
self.last = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain!{[
|
||||
let ExprCall(ref fun, ref args) = expr.node,
|
||||
let ExprPath(ref qpath) = fun.node,
|
||||
args.len() == 1,
|
||||
let Some(def) = cx.tcx.def_map.borrow().get(&fun.id),
|
||||
], {
|
||||
let def_id = def.full_def().def_id();
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, fun.id).def_id();
|
||||
if match_def_path(cx, def_id, &paths::REGEX_NEW) ||
|
||||
match_def_path(cx, def_id, &paths::REGEX_BUILDER_NEW) {
|
||||
check_regex(cx, &args[0], true);
|
||||
|
|
@ -145,7 +145,7 @@ fn str_span(base: Span, s: &str, c: usize) -> Span {
|
|||
hi: base.lo + BytePos(h as u32),
|
||||
..base
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => base,
|
||||
}
|
||||
}
|
||||
|
|
@ -172,17 +172,18 @@ fn is_trivial_regex(s: ®ex_syntax::Expr) -> Option<&'static str> {
|
|||
(&Expr::Literal { .. }, &Expr::EndText) => Some("consider using `str::ends_with`"),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
3 => {
|
||||
if let (&Expr::StartText, &Expr::Literal {..}, &Expr::EndText) = (&exprs[0], &exprs[1], &exprs[2]) {
|
||||
if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) =
|
||||
(&exprs[0], &exprs[1], &exprs[2]) {
|
||||
Some("consider using `==` on `str`s")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -213,14 +214,13 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
|
|||
"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()));
|
||||
}
|
||||
&format!("regex syntax error: {}", e.description()));
|
||||
},
|
||||
}
|
||||
}
|
||||
} else if let Some(r) = const_str(cx, expr) {
|
||||
|
|
@ -233,15 +233,13 @@ fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
|
|||
"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()));
|
||||
}
|
||||
&format!("regex syntax error on position {}: {}", e.position(), e.description()));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,9 +48,10 @@ impl ReturnPass {
|
|||
fn check_block_return(&mut self, cx: &EarlyContext, block: &ast::Block) {
|
||||
if let Some(stmt) = block.stmts.last() {
|
||||
match stmt.node {
|
||||
ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => {
|
||||
ast::StmtKind::Expr(ref expr) |
|
||||
ast::StmtKind::Semi(ref expr) => {
|
||||
self.check_final_expr(cx, expr, Some(stmt.span));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -65,24 +66,24 @@ impl ReturnPass {
|
|||
if !expr.attrs.iter().any(attr_is_cfg) {
|
||||
self.emit_return_lint(cx, span.expect("`else return` is not possible"), inner.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
// a whole block? check it!
|
||||
ast::ExprKind::Block(ref block) => {
|
||||
self.check_block_return(cx, block);
|
||||
}
|
||||
},
|
||||
// an if/if let expr, check both exprs
|
||||
// note, if without else is going to be a type checking error anyways
|
||||
// (except for unit type functions) so we don't match it
|
||||
ast::ExprKind::If(_, ref ifblock, Some(ref elsexpr)) => {
|
||||
self.check_block_return(cx, ifblock);
|
||||
self.check_final_expr(cx, elsexpr, None);
|
||||
}
|
||||
},
|
||||
// a match expr, check all arms
|
||||
ast::ExprKind::Match(_, ref arms) => {
|
||||
for arm in arms {
|
||||
self.check_final_expr(cx, &arm.body, Some(arm.body.span));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +136,8 @@ impl LintPass for ReturnPass {
|
|||
impl EarlyLintPass for ReturnPass {
|
||||
fn check_fn(&mut self, cx: &EarlyContext, kind: FnKind, _: &ast::FnDecl, _: Span, _: ast::NodeId) {
|
||||
match kind {
|
||||
FnKind::ItemFn(.., block) | FnKind::Method(.., block) => self.check_block_return(cx, block),
|
||||
FnKind::ItemFn(.., block) |
|
||||
FnKind::Method(.., block) => self.check_block_return(cx, block),
|
||||
FnKind::Closure(body) => self.check_final_expr(cx, body, None),
|
||||
}
|
||||
}
|
||||
|
|
@ -152,4 +154,3 @@ fn attr_is_cfg(attr: &ast::Attribute) -> bool {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,10 +26,10 @@ impl LintPass for Serde {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Serde {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Serde {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemImpl(_, _, _, Some(ref trait_ref), _, ref items) = item.node {
|
||||
let did = cx.tcx.expect_def(trait_ref.ref_id).def_id();
|
||||
let did = trait_ref.path.def.def_id();
|
||||
if let Some(visit_did) = get_trait_def_id(cx, &paths::SERDE_DE_VISITOR) {
|
||||
if did == visit_did {
|
||||
let mut seen_str = None;
|
||||
|
|
@ -46,8 +46,7 @@ impl LateLintPass for Serde {
|
|||
span_lint(cx,
|
||||
SERDE_API_MISUSE,
|
||||
span,
|
||||
"you should not implement `visit_string` without also implementing `visit_str`",
|
||||
);
|
||||
"you should not implement `visit_string` without also implementing `visit_str`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
use reexport::*;
|
||||
use rustc::lint::*;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, FnKind};
|
||||
use std::ops::Deref;
|
||||
use rustc::hir::intravisit::{Visitor, FnKind, NestedVisitorMap};
|
||||
use rustc::ty;
|
||||
use syntax::codemap::Span;
|
||||
use utils::{higher, in_external_macro, snippet, span_lint_and_then};
|
||||
|
||||
|
|
@ -80,8 +79,16 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, expr: &Expr, _: Span, _: NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
_: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl,
|
||||
expr: &'tcx Expr,
|
||||
_: Span,
|
||||
_: NodeId
|
||||
) {
|
||||
if in_external_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -89,17 +96,17 @@ impl LateLintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_fn(cx: &LateContext, decl: &FnDecl, expr: &Expr) {
|
||||
fn check_fn<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl, expr: &'tcx Expr) {
|
||||
let mut bindings = Vec::new();
|
||||
for arg in &decl.inputs {
|
||||
if let PatKind::Binding(_, ident, _) = arg.pat.node {
|
||||
if let PatKind::Binding(_, _, ident, _) = arg.pat.node {
|
||||
bindings.push((ident.node, ident.span))
|
||||
}
|
||||
}
|
||||
check_expr(cx, expr, &mut bindings);
|
||||
}
|
||||
|
||||
fn check_block(cx: &LateContext, block: &Block, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_block<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, block: &'tcx Block, bindings: &mut Vec<(Name, Span)>) {
|
||||
let len = bindings.len();
|
||||
for stmt in &block.stmts {
|
||||
match stmt.node {
|
||||
|
|
@ -114,7 +121,7 @@ fn check_block(cx: &LateContext, block: &Block, bindings: &mut Vec<(Name, Span)>
|
|||
bindings.truncate(len);
|
||||
}
|
||||
|
||||
fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_decl<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl, bindings: &mut Vec<(Name, Span)>) {
|
||||
if in_external_macro(cx, decl.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -128,27 +135,33 @@ fn check_decl(cx: &LateContext, decl: &Decl, bindings: &mut Vec<(Name, Span)>) {
|
|||
}
|
||||
if let Some(ref o) = *init {
|
||||
check_expr(cx, o, bindings);
|
||||
check_pat(cx, pat, &Some(o), span, bindings);
|
||||
check_pat(cx, pat, Some(o), span, bindings);
|
||||
} else {
|
||||
check_pat(cx, pat, &None, span, bindings);
|
||||
check_pat(cx, pat, None, span, bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_binding(cx: &LateContext, pat: &Pat) -> bool {
|
||||
match cx.tcx.def_map.borrow().get(&pat.id).map(|d| d.full_def()) {
|
||||
Some(Def::Variant(..)) |
|
||||
Some(Def::Struct(..)) => false,
|
||||
fn is_binding(cx: &LateContext, pat_id: NodeId) -> bool {
|
||||
let var_ty = cx.tcx.tables().node_id_to_type(pat_id);
|
||||
match var_ty.sty {
|
||||
ty::TyAdt(..) => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_pat<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
pat: &'tcx Pat,
|
||||
init: Option<&'tcx Expr>,
|
||||
span: Span,
|
||||
bindings: &mut Vec<(Name, Span)>
|
||||
) {
|
||||
// TODO: match more stuff / destructuring
|
||||
match pat.node {
|
||||
PatKind::Binding(_, ref ident, ref inner) => {
|
||||
PatKind::Binding(_, _, ref ident, ref inner) => {
|
||||
let name = ident.node;
|
||||
if is_binding(cx, pat) {
|
||||
if is_binding(cx, pat.id) {
|
||||
let mut new_binding = true;
|
||||
for tup in bindings.iter_mut() {
|
||||
if tup.0 == name {
|
||||
|
|
@ -165,16 +178,16 @@ fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bind
|
|||
if let Some(ref p) = *inner {
|
||||
check_pat(cx, p, init, span, bindings);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Struct(_, ref pfields, _) => {
|
||||
if let Some(init_struct) = *init {
|
||||
if let Some(init_struct) = init {
|
||||
if let ExprStruct(_, ref efields, _) = init_struct.node {
|
||||
for field in pfields {
|
||||
let name = field.node.name;
|
||||
let efield = efields.iter()
|
||||
.find(|f| f.name.node == name)
|
||||
.map(|f| &*f.expr);
|
||||
check_pat(cx, &field.node.pat, &efield, span, bindings);
|
||||
.find(|f| f.name.node == name)
|
||||
.map(|f| &*f.expr);
|
||||
check_pat(cx, &field.node.pat, efield, span, bindings);
|
||||
}
|
||||
} else {
|
||||
for field in pfields {
|
||||
|
|
@ -183,15 +196,15 @@ fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bind
|
|||
}
|
||||
} else {
|
||||
for field in pfields {
|
||||
check_pat(cx, &field.node.pat, &None, span, bindings);
|
||||
check_pat(cx, &field.node.pat, None, span, bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Tuple(ref inner, _) => {
|
||||
if let Some(init_tup) = *init {
|
||||
if let Some(init_tup) = init {
|
||||
if let ExprTup(ref tup) = init_tup.node {
|
||||
for (i, p) in inner.iter().enumerate() {
|
||||
check_pat(cx, p, &Some(&tup[i]), p.span, bindings);
|
||||
check_pat(cx, p, Some(&tup[i]), p.span, bindings);
|
||||
}
|
||||
} else {
|
||||
for p in inner {
|
||||
|
|
@ -200,31 +213,36 @@ fn check_pat(cx: &LateContext, pat: &Pat, init: &Option<&Expr>, span: Span, bind
|
|||
}
|
||||
} else {
|
||||
for p in inner {
|
||||
check_pat(cx, p, &None, span, bindings);
|
||||
check_pat(cx, p, None, span, bindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Box(ref inner) => {
|
||||
if let Some(initp) = *init {
|
||||
if let Some(initp) = init {
|
||||
if let ExprBox(ref inner_init) = initp.node {
|
||||
check_pat(cx, inner, &Some(&**inner_init), span, bindings);
|
||||
check_pat(cx, inner, Some(&**inner_init), span, bindings);
|
||||
} else {
|
||||
check_pat(cx, inner, init, span, bindings);
|
||||
}
|
||||
} else {
|
||||
check_pat(cx, inner, init, span, bindings);
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Ref(ref inner, _) => check_pat(cx, inner, init, span, bindings),
|
||||
// PatVec(Vec<P<Pat>>, Option<P<Pat>>, Vec<P<Pat>>),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: Span, init: &Option<T>, prev_span: Span)
|
||||
where T: Deref<Target = Expr>
|
||||
{
|
||||
if let Some(ref expr) = *init {
|
||||
fn lint_shadow<'a, 'tcx: 'a>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
name: Name,
|
||||
span: Span,
|
||||
pattern_span: Span,
|
||||
init: Option<&'tcx Expr>,
|
||||
prev_span: Span
|
||||
) {
|
||||
if let Some(expr) = init {
|
||||
if is_self_shadow(name, expr) {
|
||||
span_lint_and_then(cx,
|
||||
SHADOW_SAME,
|
||||
|
|
@ -232,9 +250,10 @@ fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: 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) {
|
||||
|db| {
|
||||
db.span_note(prev_span, "previous binding is here");
|
||||
});
|
||||
} else if contains_self(cx, name, expr) {
|
||||
span_lint_and_then(cx,
|
||||
SHADOW_REUSE,
|
||||
pattern_span,
|
||||
|
|
@ -242,9 +261,9 @@ fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: Span,
|
|||
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");
|
||||
});
|
||||
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,
|
||||
|
|
@ -253,9 +272,9 @@ fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: Span,
|
|||
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");
|
||||
});
|
||||
db.span_note(expr.span, "initialization happens here");
|
||||
db.span_note(prev_span, "previous binding is here");
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
@ -263,11 +282,13 @@ fn lint_shadow<T>(cx: &LateContext, name: Name, span: Span, pattern_span: Span,
|
|||
SHADOW_UNRELATED,
|
||||
span,
|
||||
&format!("`{}` shadows a previous declaration", snippet(cx, pattern_span, "_")),
|
||||
|db| { db.span_note(prev_span, "previous binding is here"); });
|
||||
|db| {
|
||||
db.span_note(prev_span, "previous binding is here");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_expr<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr, bindings: &mut Vec<(Name, Span)>) {
|
||||
if in_external_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -285,24 +306,24 @@ fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
|
|||
for e in v {
|
||||
check_expr(cx, e, bindings)
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprIf(ref cond, ref then, ref otherwise) => {
|
||||
check_expr(cx, cond, bindings);
|
||||
check_block(cx, then, bindings);
|
||||
if let Some(ref o) = *otherwise {
|
||||
check_expr(cx, o, bindings);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprWhile(ref cond, ref block, _) => {
|
||||
check_expr(cx, cond, bindings);
|
||||
check_block(cx, block, bindings);
|
||||
}
|
||||
},
|
||||
ExprMatch(ref init, ref arms, _) => {
|
||||
check_expr(cx, init, bindings);
|
||||
let len = bindings.len();
|
||||
for arm in arms {
|
||||
for pat in &arm.pats {
|
||||
check_pat(cx, pat, &Some(&**init), pat.span, bindings);
|
||||
check_pat(cx, pat, Some(&**init), pat.span, bindings);
|
||||
// This is ugly, but needed to get the right type
|
||||
if let Some(ref guard) = arm.guard {
|
||||
check_expr(cx, guard, bindings);
|
||||
|
|
@ -311,26 +332,26 @@ fn check_expr(cx: &LateContext, expr: &Expr, bindings: &mut Vec<(Name, Span)>) {
|
|||
bindings.truncate(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ty(cx: &LateContext, ty: &Ty, bindings: &mut Vec<(Name, Span)>) {
|
||||
fn check_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty, bindings: &mut Vec<(Name, Span)>) {
|
||||
match ty.node {
|
||||
TyObjectSum(ref sty, _) |
|
||||
TySlice(ref sty) => check_ty(cx, sty, bindings),
|
||||
TyArray(ref fty, ref expr) => {
|
||||
check_ty(cx, fty, bindings);
|
||||
check_expr(cx, expr, bindings);
|
||||
}
|
||||
},
|
||||
TyPtr(MutTy { ty: ref mty, .. }) |
|
||||
TyRptr(_, MutTy { ty: ref mty, .. }) => check_ty(cx, mty, bindings),
|
||||
TyTup(ref tup) => {
|
||||
for t in tup {
|
||||
check_ty(cx, t, bindings)
|
||||
}
|
||||
}
|
||||
},
|
||||
TyTypeof(ref expr) => check_expr(cx, expr, bindings),
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -342,9 +363,9 @@ fn is_self_shadow(name: Name, expr: &Expr) -> bool {
|
|||
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))
|
||||
}
|
||||
},
|
||||
ExprUnary(op, ref inner) => (UnDeref == op) && is_self_shadow(name, inner),
|
||||
ExprPath(_, ref path) => path_eq_name(name, path),
|
||||
ExprPath(QPath::Resolved(_, ref path)) => path_eq_name(name, path),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -353,23 +374,28 @@ fn path_eq_name(name: Name, path: &Path) -> bool {
|
|||
!path.global && path.segments.len() == 1 && path.segments[0].name.as_str() == name.as_str()
|
||||
}
|
||||
|
||||
struct ContainsSelf {
|
||||
struct ContainsSelf<'a, 'tcx: 'a> {
|
||||
name: Name,
|
||||
result: bool,
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for ContainsSelf {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for ContainsSelf<'a, 'tcx> {
|
||||
fn visit_name(&mut self, _: Span, name: Name) {
|
||||
if self.name == name {
|
||||
self.result = true;
|
||||
}
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_self(name: Name, expr: &Expr) -> bool {
|
||||
fn contains_self<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, name: Name, expr: &'tcx Expr) -> bool {
|
||||
let mut cs = ContainsSelf {
|
||||
name: name,
|
||||
result: false,
|
||||
cx: cx,
|
||||
};
|
||||
cs.visit_expr(expr);
|
||||
cs.result
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ impl LintPass for StringAdd {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for StringAdd {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
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) {
|
||||
|
|
@ -122,7 +122,7 @@ fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
|
|||
ExprBinary(Spanned { node: BiAdd, .. }, ref left, _) => SpanlessEq::new(cx).eq_expr(target, left),
|
||||
ExprBlock(ref block) => {
|
||||
block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -136,8 +136,8 @@ impl LintPass for StringLitAsBytes {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for StringLitAsBytes {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
use std::ascii::AsciiExt;
|
||||
use syntax::ast::LitKind;
|
||||
use utils::{snippet, in_macro};
|
||||
|
|
@ -152,11 +152,9 @@ impl LateLintPass for StringLitAsBytes {
|
|||
e.span,
|
||||
"calling `as_bytes()` on a string literal",
|
||||
|db| {
|
||||
let sugg = format!("b{}", snippet(cx, args[0].span, r#""foo""#));
|
||||
db.span_suggestion(e.span,
|
||||
"consider using a byte string literal instead",
|
||||
sugg);
|
||||
});
|
||||
let sugg = format!("b{}", snippet(cx, args[0].span, r#""foo""#));
|
||||
db.span_suggestion(e.span, "consider using a byte string literal instead", sugg);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ impl LintPass for Swap {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Swap {
|
||||
fn check_block(&mut self, cx: &LateContext, block: &Block) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
|
||||
fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
|
||||
check_manual_swap(cx, block);
|
||||
check_suspicious_swap(cx, block);
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ fn check_manual_swap(cx: &LateContext, block: &Block) {
|
|||
let StmtDecl(ref tmp, _) = w[0].node,
|
||||
let DeclLocal(ref tmp) = tmp.node,
|
||||
let Some(ref tmp_init) = tmp.init,
|
||||
let PatKind::Binding(_, ref tmp_name, None) = tmp.pat.node,
|
||||
let PatKind::Binding(_, _, ref tmp_name, None) = tmp.pat.node,
|
||||
|
||||
// foo() = bar();
|
||||
let StmtSemi(ref first, _) = w[1].node,
|
||||
|
|
@ -74,14 +74,18 @@ fn check_manual_swap(cx: &LateContext, block: &Block) {
|
|||
// bar() = t;
|
||||
let StmtSemi(ref second, _) = w[2].node,
|
||||
let ExprAssign(ref lhs2, ref rhs2) = second.node,
|
||||
let ExprPath(None, ref rhs2) = rhs2.node,
|
||||
let ExprPath(QPath::Resolved(None, ref rhs2)) = rhs2.node,
|
||||
rhs2.segments.len() == 1,
|
||||
|
||||
tmp_name.node.as_str() == rhs2.segments[0].name.as_str(),
|
||||
SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1),
|
||||
SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2)
|
||||
], {
|
||||
fn check_for_slice<'a>(cx: &LateContext, lhs1: &'a Expr, lhs2: &'a Expr) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
|
||||
fn check_for_slice<'a>(
|
||||
cx: &LateContext,
|
||||
lhs1: &'a Expr,
|
||||
lhs2: &'a Expr,
|
||||
) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
|
||||
if let ExprIndex(ref lhs1, ref idx1) = lhs1.node {
|
||||
if let ExprIndex(ref lhs2, ref idx2) = lhs2.node {
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
|
||||
|
|
@ -104,7 +108,10 @@ fn check_manual_swap(cx: &LateContext, block: &Block) {
|
|||
if let Some(slice) = Sugg::hir_opt(cx, slice) {
|
||||
(false,
|
||||
format!(" elements of `{}`", slice),
|
||||
format!("{}.swap({}, {})", slice.maybe_par(), snippet(cx, idx1.span, ".."), snippet(cx, idx2.span, "..")))
|
||||
format!("{}.swap({}, {})",
|
||||
slice.maybe_par(),
|
||||
snippet(cx, idx1.span, ".."),
|
||||
snippet(cx, idx2.span, "..")))
|
||||
} else {
|
||||
(false, "".to_owned(), "".to_owned())
|
||||
}
|
||||
|
|
@ -148,7 +155,9 @@ fn check_suspicious_swap(cx: &LateContext, block: &Block) {
|
|||
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1),
|
||||
SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0)
|
||||
], {
|
||||
let (what, lhs, rhs) = if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs0), Sugg::hir_opt(cx, rhs0)) {
|
||||
let lhs0 = Sugg::hir_opt(cx, lhs0);
|
||||
let rhs0 = Sugg::hir_opt(cx, rhs0);
|
||||
let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
|
||||
(format!(" `{}` and `{}`", first, second), first.mut_addr().to_string(), second.mut_addr().to_string())
|
||||
} else {
|
||||
("".to_owned(), "".to_owned(), "".to_owned())
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprAssign(ref target, _) = expr.node {
|
||||
match target.node {
|
||||
ExprField(ref base, _) |
|
||||
|
|
@ -46,7 +46,7 @@ impl LateLintPass for Pass {
|
|||
if is_temporary(base) && !is_adjusted(cx, base) {
|
||||
span_lint(cx, TEMPORARY_ASSIGNMENT, expr.span, "assignment to temporary");
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use rustc::lint::*;
|
|||
use rustc::ty::TypeVariants::{TyRawPtr, TyRef};
|
||||
use rustc::ty;
|
||||
use rustc::hir::*;
|
||||
use utils::{match_def_path, paths, span_lint, span_lint_and_then, snippet};
|
||||
use utils::{match_def_path, paths, span_lint, span_lint_and_then, snippet, last_path_segment};
|
||||
use utils::sugg;
|
||||
|
||||
/// **What it does:** Checks for transmutes that can't ever be correct on any
|
||||
|
|
@ -84,29 +84,29 @@ impl LintPass for Transmute {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Transmute {
|
||||
fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Transmute {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprCall(ref path_expr, ref args) = e.node {
|
||||
if let ExprPath(None, ref path) = path_expr.node {
|
||||
let def_id = cx.tcx.expect_def(path_expr.id).def_id();
|
||||
if let ExprPath(ref qpath) = path_expr.node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, path_expr.id).def_id();
|
||||
|
||||
if match_def_path(cx, def_id, &paths::TRANSMUTE) {
|
||||
let from_ty = cx.tcx.tables().expr_ty(&args[0]);
|
||||
let to_ty = cx.tcx.tables().expr_ty(e);
|
||||
|
||||
match (&from_ty.sty, &to_ty.sty) {
|
||||
_ if from_ty == to_ty => span_lint(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to itself", from_ty),
|
||||
),
|
||||
(&TyRef(_, rty), &TyRawPtr(ptr_ty)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from a reference to a pointer",
|
||||
|db| {
|
||||
_ if from_ty == to_ty => {
|
||||
span_lint(cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a type (`{}`) to itself", from_ty))
|
||||
},
|
||||
(&TyRef(_, rty), &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)
|
||||
|
|
@ -116,53 +116,54 @@ impl LateLintPass for Transmute {
|
|||
|
||||
db.span_suggestion(e.span, "try", sugg.to_string());
|
||||
}
|
||||
},
|
||||
),
|
||||
})
|
||||
},
|
||||
(&ty::TyInt(_), &TyRawPtr(_)) |
|
||||
(&ty::TyUint(_), &TyRawPtr(_)) => span_lint_and_then(
|
||||
cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from an integer to a pointer",
|
||||
|db| {
|
||||
(&ty::TyUint(_), &TyRawPtr(_)) => {
|
||||
span_lint_and_then(cx,
|
||||
USELESS_TRANSMUTE,
|
||||
e.span,
|
||||
"transmute from an integer to a pointer",
|
||||
|db| {
|
||||
if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
|
||||
db.span_suggestion(e.span, "try", arg.as_ty(&to_ty.to_string()).to_string());
|
||||
}
|
||||
},
|
||||
),
|
||||
})
|
||||
},
|
||||
(&ty::TyFloat(_), &TyRef(..)) |
|
||||
(&ty::TyFloat(_), &TyRawPtr(_)) |
|
||||
(&ty::TyChar, &TyRef(..)) |
|
||||
(&ty::TyChar, &TyRawPtr(_)) => span_lint(
|
||||
cx,
|
||||
WRONG_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a pointer", from_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),
|
||||
),
|
||||
(_, &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),
|
||||
),
|
||||
(&TyRawPtr(from_pty), &TyRef(_, to_rty)) => span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
e.span,
|
||||
&format!("transmute from a pointer type (`{}`) to a reference type (`{}`)",
|
||||
from_ty,
|
||||
to_ty),
|
||||
|db| {
|
||||
(&ty::TyChar, &TyRawPtr(_)) => {
|
||||
span_lint(cx,
|
||||
WRONG_TRANSMUTE,
|
||||
e.span,
|
||||
&format!("transmute from a `{}` to a pointer", from_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))
|
||||
},
|
||||
(_, &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))
|
||||
},
|
||||
(&TyRawPtr(from_pty), &TyRef(_, to_rty)) => {
|
||||
span_lint_and_then(cx,
|
||||
TRANSMUTE_PTR_TO_REF,
|
||||
e.span,
|
||||
&format!("transmute from a pointer type (`{}`) to a reference type \
|
||||
(`{}`)",
|
||||
from_ty,
|
||||
to_ty),
|
||||
|db| {
|
||||
let arg = sugg::Sugg::hir(cx, &args[0], "..");
|
||||
let (deref, cast) = if to_rty.mutbl == Mutability::MutMutable {
|
||||
("&mut *", "*mut")
|
||||
|
|
@ -173,12 +174,12 @@ impl LateLintPass for Transmute {
|
|||
let arg = if from_pty.ty == to_rty.ty {
|
||||
arg
|
||||
} else {
|
||||
arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, path, to_rty.ty)))
|
||||
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());
|
||||
},
|
||||
),
|
||||
})
|
||||
},
|
||||
_ => return,
|
||||
};
|
||||
}
|
||||
|
|
@ -190,9 +191,9 @@ impl LateLintPass 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 `'_`
|
||||
/// lifetime, but it should be rare.
|
||||
fn get_type_snippet(cx: &LateContext, path: &Path, to_rty: ty::Ty) -> String {
|
||||
fn get_type_snippet(cx: &LateContext, path: &QPath, to_rty: ty::Ty) -> String {
|
||||
let seg = last_path_segment(path);
|
||||
if_let_chain!{[
|
||||
let Some(seg) = path.segments.last(),
|
||||
let PathParameters::AngleBracketedParameters(ref ang) = seg.parameters,
|
||||
let Some(to_ty) = ang.types.get(1),
|
||||
let TyRptr(_, ref to_ty) = to_ty.node,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use reexport::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_ty};
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_ty, NestedVisitorMap};
|
||||
use rustc::lint::*;
|
||||
use rustc::ty;
|
||||
use std::cmp::Ordering;
|
||||
use syntax::ast::{IntTy, UintTy, FloatTy};
|
||||
use syntax::codemap::Span;
|
||||
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet,
|
||||
span_help_and_lint, span_lint};
|
||||
use utils::{comparisons, higher, in_external_macro, in_macro, match_def_path, snippet, span_help_and_lint, span_lint,
|
||||
opt_def_id, last_path_segment};
|
||||
use utils::paths;
|
||||
|
||||
/// Handles all the linting of funky types
|
||||
|
|
@ -69,38 +69,107 @@ impl LintPass for TypePass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for TypePass {
|
||||
fn check_ty(&mut self, cx: &LateContext, ast_ty: &Ty) {
|
||||
if in_macro(cx, ast_ty.span) {
|
||||
return;
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypePass {
|
||||
fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, _: &Expr, _: Span, id: NodeId) {
|
||||
// skip trait implementations, see #605
|
||||
if let Some(map::NodeItem(item)) = cx.tcx.map.find(cx.tcx.map.get_parent(id)) {
|
||||
if let ItemImpl(_, _, _, Some(..), _, _) = item.node {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if let Some(did) = cx.tcx.def_map.borrow().get(&ast_ty.id) {
|
||||
if let def::Def::Struct(..) = did.full_def() {
|
||||
if Some(did.full_def().def_id()) == cx.tcx.lang_items.owned_box() {
|
||||
|
||||
check_fn_decl(cx, decl);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext, field: &StructField) {
|
||||
check_ty(cx, &field.ty);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
match item.node {
|
||||
ConstTraitItem(ref ty, _) |
|
||||
TypeTraitItem(_, Some(ref ty)) => check_ty(cx, ty),
|
||||
MethodTraitItem(ref sig, _) => check_fn_decl(cx, &sig.decl),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_fn_decl(cx: &LateContext, decl: &FnDecl) {
|
||||
for input in &decl.inputs {
|
||||
check_ty(cx, &input.ty);
|
||||
}
|
||||
|
||||
if let FunctionRetTy::Return(ref ty) = decl.output {
|
||||
check_ty(cx, ty);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_ty(cx: &LateContext, ast_ty: &Ty) {
|
||||
if in_macro(cx, ast_ty.span) {
|
||||
return;
|
||||
}
|
||||
match ast_ty.node {
|
||||
TyPath(ref qpath) => {
|
||||
let def = cx.tcx.tables().qpath_def(qpath, ast_ty.id);
|
||||
if let Some(def_id) = opt_def_id(def) {
|
||||
if Some(def_id) == cx.tcx.lang_items.owned_box() {
|
||||
let last = last_path_segment(qpath);
|
||||
if_let_chain! {[
|
||||
let TyPath(_, ref path) = ast_ty.node,
|
||||
let Some(ref last) = path.segments.last(),
|
||||
let PathParameters::AngleBracketedParameters(ref ag) = last.parameters,
|
||||
let Some(ref vec) = ag.types.get(0),
|
||||
let Some(did) = cx.tcx.def_map.borrow().get(&vec.id),
|
||||
let def::Def::Struct(..) = did.full_def(),
|
||||
match_def_path(cx, did.full_def().def_id(), &paths::VEC),
|
||||
let TyPath(ref qpath) = vec.node,
|
||||
let def::Def::Struct(..) = cx.tcx.tables().qpath_def(qpath, vec.id),
|
||||
let Some(did) = opt_def_id(cx.tcx.tables().qpath_def(qpath, vec.id)),
|
||||
match_def_path(cx, did, &paths::VEC),
|
||||
], {
|
||||
span_help_and_lint(cx,
|
||||
BOX_VEC,
|
||||
ast_ty.span,
|
||||
"you seem to be trying to use `Box<Vec<T>>`. Consider using just `Vec<T>`",
|
||||
"`Vec<T>` is already on the heap, `Box<Vec<T>>` makes an extra allocation.");
|
||||
return; // don't recurse into the type
|
||||
}}
|
||||
} else if match_def_path(cx, did.full_def().def_id(), &paths::LINKED_LIST) {
|
||||
} else if match_def_path(cx, 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");
|
||||
return; // don't recurse into the type
|
||||
}
|
||||
}
|
||||
}
|
||||
match *qpath {
|
||||
QPath::Resolved(Some(ref ty), ref p) => {
|
||||
check_ty(cx, ty);
|
||||
for ty in p.segments.iter().flat_map(|seg| seg.parameters.types()) {
|
||||
check_ty(cx, ty);
|
||||
}
|
||||
},
|
||||
QPath::Resolved(None, ref p) => {
|
||||
for ty in p.segments.iter().flat_map(|seg| seg.parameters.types()) {
|
||||
check_ty(cx, ty);
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref seg) => {
|
||||
check_ty(cx, ty);
|
||||
for ty in seg.parameters.types() {
|
||||
check_ty(cx, ty);
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
// recurse
|
||||
TySlice(ref ty) |
|
||||
TyArray(ref ty, _) |
|
||||
TyPtr(MutTy { ref ty, .. }) |
|
||||
TyRptr(_, MutTy { ref ty, .. }) => check_ty(cx, ty),
|
||||
TyTup(ref tys) => {
|
||||
for ty in tys {
|
||||
check_ty(cx, ty);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +209,7 @@ fn check_let_unit(cx: &LateContext, decl: &Decl) {
|
|||
decl.span,
|
||||
&format!("this let-binding has unit value. Consider omitting `let {} =`",
|
||||
snippet(cx, local.pat.span, "..")));
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -152,8 +221,8 @@ impl LintPass for LetPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for LetPass {
|
||||
fn check_decl(&mut self, cx: &LateContext, decl: &Decl) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetPass {
|
||||
fn check_decl(&mut self, cx: &LateContext<'a, 'tcx>, decl: &'tcx Decl) {
|
||||
check_let_unit(cx, decl)
|
||||
}
|
||||
}
|
||||
|
|
@ -189,8 +258,8 @@ impl LintPass for UnitCmp {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for UnitCmp {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitCmp {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if in_macro(cx, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -210,8 +279,8 @@ impl LateLintPass for UnitCmp {
|
|||
&format!("{}-comparison of unit values detected. This will always be {}",
|
||||
op.as_str(),
|
||||
result));
|
||||
}
|
||||
_ => ()
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -335,11 +404,7 @@ fn is_isize_or_usize(typ: &ty::TyS) -> bool {
|
|||
}
|
||||
|
||||
fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: &ty::TyS, cast_to_f64: bool) {
|
||||
let mantissa_nbits = if cast_to_f64 {
|
||||
52
|
||||
} else {
|
||||
23
|
||||
};
|
||||
let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
|
||||
let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
|
||||
let arch_dependent_str = "on targets with 64-bit wide pointers ";
|
||||
let from_nbits_str = if arch_dependent {
|
||||
|
|
@ -355,11 +420,7 @@ fn span_precision_loss_lint(cx: &LateContext, expr: &Expr, cast_from: &ty::TyS,
|
|||
&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 cast_to_f64 { "f64" } else { "f32" },
|
||||
if arch_dependent {
|
||||
arch_dependent_str
|
||||
} else {
|
||||
|
|
@ -387,27 +448,27 @@ fn check_truncation_and_wrapping(cx: &LateContext, expr: &Expr, cast_from: &ty::
|
|||
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
|
||||
},
|
||||
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
|
||||
})
|
||||
}
|
||||
ArchSuffix::_64
|
||||
} else {
|
||||
ArchSuffix::_32
|
||||
})
|
||||
},
|
||||
};
|
||||
if span_truncation {
|
||||
span_lint(cx,
|
||||
|
|
@ -446,8 +507,8 @@ impl LintPass for CastPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for CastPass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CastPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprCast(ref ex, _) = expr.node {
|
||||
let (cast_from, cast_to) = (cx.tcx.tables().expr_ty(ex), cx.tcx.tables().expr_ty(expr));
|
||||
if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx, expr.span) {
|
||||
|
|
@ -462,7 +523,7 @@ impl LateLintPass for CastPass {
|
|||
if is_isize_or_usize(cast_from) || from_nbits >= to_nbits {
|
||||
span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64);
|
||||
}
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
span_lint(cx,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
|
|
@ -474,7 +535,7 @@ impl LateLintPass for CastPass {
|
|||
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,
|
||||
|
|
@ -483,16 +544,16 @@ impl LateLintPass for CastPass {
|
|||
&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) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -534,17 +595,25 @@ impl LintPass for TypeComplexityPass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for TypeComplexityPass {
|
||||
fn check_fn(&mut self, cx: &LateContext, _: FnKind, decl: &FnDecl, _: &Expr, _: Span, _: NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexityPass {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
_: FnKind<'tcx>,
|
||||
decl: &'tcx FnDecl,
|
||||
_: &'tcx Expr,
|
||||
_: Span,
|
||||
_: NodeId
|
||||
) {
|
||||
self.check_fndecl(cx, decl);
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext, field: &StructField) {
|
||||
fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx StructField) {
|
||||
// enum variants are also struct fields now
|
||||
self.check_type(cx, &field.ty);
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
match item.node {
|
||||
ItemStatic(ref ty, _, _) |
|
||||
ItemConst(ref ty, _) => self.check_type(cx, ty),
|
||||
|
|
@ -553,7 +622,7 @@ impl LateLintPass for TypeComplexityPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &TraitItem) {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx TraitItem) {
|
||||
match item.node {
|
||||
ConstTraitItem(ref ty, _) |
|
||||
TypeTraitItem(_, Some(ref ty)) => self.check_type(cx, ty),
|
||||
|
|
@ -563,7 +632,7 @@ impl LateLintPass for TypeComplexityPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
match item.node {
|
||||
ImplItemKind::Const(ref ty, _) |
|
||||
ImplItemKind::Type(ref ty) => self.check_type(cx, ty),
|
||||
|
|
@ -572,15 +641,15 @@ impl LateLintPass for TypeComplexityPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext, local: &Local) {
|
||||
fn check_local(&mut self, cx: &LateContext<'a, 'tcx>, local: &'tcx Local) {
|
||||
if let Some(ref ty) = local.ty {
|
||||
self.check_type(cx, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeComplexityPass {
|
||||
fn check_fndecl(&self, cx: &LateContext, decl: &FnDecl) {
|
||||
impl<'a, 'tcx> TypeComplexityPass {
|
||||
fn check_fndecl(&self, cx: &LateContext<'a, 'tcx>, decl: &'tcx FnDecl) {
|
||||
for arg in &decl.inputs {
|
||||
self.check_type(cx, &arg.ty);
|
||||
}
|
||||
|
|
@ -589,7 +658,7 @@ impl TypeComplexityPass {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_type(&self, cx: &LateContext, ty: &Ty) {
|
||||
fn check_type(&self, cx: &LateContext<'a, 'tcx>, ty: &'tcx Ty) {
|
||||
if in_macro(cx, ty.span) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -597,6 +666,7 @@ impl TypeComplexityPass {
|
|||
let mut visitor = TypeComplexityVisitor {
|
||||
score: 0,
|
||||
nest: 1,
|
||||
cx: cx,
|
||||
};
|
||||
visitor.visit_ty(ty);
|
||||
visitor.score
|
||||
|
|
@ -612,24 +682,22 @@ impl TypeComplexityPass {
|
|||
}
|
||||
|
||||
/// Walks a type and assigns a complexity score to it.
|
||||
struct TypeComplexityVisitor {
|
||||
struct TypeComplexityVisitor<'a, 'tcx: 'a> {
|
||||
/// total complexity score of the type
|
||||
score: u64,
|
||||
/// current nesting level
|
||||
nest: u64,
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for TypeComplexityVisitor {
|
||||
fn visit_ty(&mut self, ty: &'v Ty) {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for TypeComplexityVisitor<'a, 'tcx> {
|
||||
fn visit_ty(&mut self, ty: &'tcx Ty) {
|
||||
let (add_score, sub_nest) = match ty.node {
|
||||
// _, &x and *x have only small overhead; don't mess with nesting level
|
||||
TyInfer | TyPtr(..) | TyRptr(..) => (1, 0),
|
||||
|
||||
// the "normal" components of a type: named types, arrays/tuples
|
||||
TyPath(..) |
|
||||
TySlice(..) |
|
||||
TyTup(..) |
|
||||
TyArray(..) => (10 * self.nest, 1),
|
||||
TyPath(..) | TySlice(..) | TyTup(..) | TyArray(..) => (10 * self.nest, 1),
|
||||
|
||||
// "Sum" of trait bounds
|
||||
TyObjectSum(..) => (20 * self.nest, 0),
|
||||
|
|
@ -645,6 +713,9 @@ impl<'v> Visitor<'v> for TypeComplexityVisitor {
|
|||
walk_ty(self, ty);
|
||||
self.nest -= sub_nest;
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for expressions where a character literal is cast
|
||||
|
|
@ -676,8 +747,8 @@ impl LintPass for CharLitAsU8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for CharLitAsU8 {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CharLitAsU8 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
use syntax::ast::{LitKind, UintTy};
|
||||
|
||||
if let ExprCast(ref e, _) = expr.node {
|
||||
|
|
@ -687,9 +758,7 @@ impl LateLintPass for CharLitAsU8 {
|
|||
let msg = "casting character literal to u8. `char`s \
|
||||
are 4 bytes wide in rust, so casting to u8 \
|
||||
truncates them";
|
||||
let help = format!("Consider using a byte literal \
|
||||
instead:\nb{}",
|
||||
snippet(cx, e.span, "'x'"));
|
||||
let help = format!("Consider using a byte literal instead:\nb{}", snippet(cx, e.span, "'x'"));
|
||||
span_help_and_lint(cx, CHAR_LIT_AS_U8, expr.span, msg, &help);
|
||||
}
|
||||
}
|
||||
|
|
@ -746,12 +815,15 @@ enum AbsurdComparisonResult {
|
|||
|
||||
|
||||
|
||||
fn detect_absurd_comparison<'a>(cx: &LateContext, op: BinOp_, lhs: &'a Expr, rhs: &'a Expr)
|
||||
-> Option<(ExtremeExpr<'a>, AbsurdComparisonResult)> {
|
||||
fn detect_absurd_comparison<'a>(
|
||||
cx: &LateContext,
|
||||
op: BinOp_,
|
||||
lhs: &'a Expr,
|
||||
rhs: &'a Expr
|
||||
) -> Option<(ExtremeExpr<'a>, AbsurdComparisonResult)> {
|
||||
use types::ExtremeType::*;
|
||||
use types::AbsurdComparisonResult::*;
|
||||
use utils::comparisons::*;
|
||||
type Extr<'a> = ExtremeExpr<'a>;
|
||||
|
||||
let normalized = normalize_comparison(op, lhs, rhs);
|
||||
let (rel, normalized_lhs, normalized_rhs) = if let Some(val) = normalized {
|
||||
|
|
@ -766,20 +838,20 @@ fn detect_absurd_comparison<'a>(cx: &LateContext, op: BinOp_, lhs: &'a Expr, rhs
|
|||
Some(match rel {
|
||||
Rel::Lt => {
|
||||
match (lx, rx) {
|
||||
(Some(l @ Extr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
|
||||
(_, Some(r @ Extr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, AlwaysFalse), // max < x
|
||||
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, AlwaysFalse), // x < min
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
Rel::Le => {
|
||||
match (lx, rx) {
|
||||
(Some(l @ Extr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
|
||||
(Some(l @ Extr { which: Maximum, .. }), _) => (l, InequalityImpossible), //max <= x
|
||||
(_, Some(r @ Extr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
|
||||
(_, Some(r @ Extr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
|
||||
(Some(l @ ExtremeExpr { which: Minimum, .. }), _) => (l, AlwaysTrue), // min <= x
|
||||
(Some(l @ ExtremeExpr { which: Maximum, .. }), _) => (l, InequalityImpossible), //max <= x
|
||||
(_, Some(r @ ExtremeExpr { which: Minimum, .. })) => (r, InequalityImpossible), // x <= min
|
||||
(_, Some(r @ ExtremeExpr { which: Maximum, .. })) => (r, AlwaysTrue), // x <= max
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
},
|
||||
Rel::Ne | Rel::Eq => return None,
|
||||
})
|
||||
}
|
||||
|
|
@ -840,8 +912,8 @@ fn detect_extreme_expr<'a>(cx: &LateContext, expr: &'a Expr) -> Option<ExtremeEx
|
|||
})
|
||||
}
|
||||
|
||||
impl LateLintPass for AbsurdExtremeComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AbsurdExtremeComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
use types::ExtremeType::*;
|
||||
use types::AbsurdComparisonResult::*;
|
||||
|
||||
|
|
@ -859,7 +931,7 @@ impl LateLintPass for AbsurdExtremeComparisons {
|
|||
instead",
|
||||
snippet(cx, lhs.span, "lhs"),
|
||||
snippet(cx, rhs.span, "rhs"))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let help = format!("because {} is the {} value for this type, {}",
|
||||
|
|
@ -962,7 +1034,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
|
|||
IntTy::I64 => (FullInt::S(i64::min_value() as i64), FullInt::S(i64::max_value() as i64)),
|
||||
IntTy::Is => (FullInt::S(isize::min_value() as i64), FullInt::S(isize::max_value() as i64)),
|
||||
})
|
||||
}
|
||||
},
|
||||
TyUint(uint_ty) => {
|
||||
Some(match uint_ty {
|
||||
UintTy::U8 => (FullInt::U(u8::min_value() as u64), FullInt::U(u8::max_value() as u64)),
|
||||
|
|
@ -971,7 +1043,7 @@ fn numeric_cast_precast_bounds<'a>(cx: &LateContext, expr: &'a Expr) -> Option<(
|
|||
UintTy::U64 => (FullInt::U(u64::min_value() as u64), FullInt::U(u64::max_value() as u64)),
|
||||
UintTy::Us => (FullInt::U(usize::min_value() as u64), FullInt::U(usize::max_value() as u64)),
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -996,7 +1068,7 @@ fn node_as_const_fullint(cx: &LateContext, expr: &Expr) -> Option<FullInt> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1014,8 +1086,15 @@ fn err_upcast_comparison(cx: &LateContext, span: &Span, expr: &Expr, always: boo
|
|||
}
|
||||
}
|
||||
|
||||
fn upcast_comparison_bounds_err(cx: &LateContext, span: &Span, rel: comparisons::Rel,
|
||||
lhs_bounds: Option<(FullInt, FullInt)>, lhs: &Expr, rhs: &Expr, invert: bool) {
|
||||
fn upcast_comparison_bounds_err(
|
||||
cx: &LateContext,
|
||||
span: &Span,
|
||||
rel: comparisons::Rel,
|
||||
lhs_bounds: Option<(FullInt, FullInt)>,
|
||||
lhs: &Expr,
|
||||
rhs: &Expr,
|
||||
invert: bool
|
||||
) {
|
||||
use utils::comparisons::*;
|
||||
|
||||
if let Some((lb, ub)) = lhs_bounds {
|
||||
|
|
@ -1031,14 +1110,14 @@ fn upcast_comparison_bounds_err(cx: &LateContext, span: &Span, rel: comparisons:
|
|||
} 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)
|
||||
|
|
@ -1049,14 +1128,14 @@ fn upcast_comparison_bounds_err(cx: &LateContext, span: &Span, rel: comparisons:
|
|||
} 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)
|
||||
|
|
@ -1065,8 +1144,8 @@ fn upcast_comparison_bounds_err(cx: &LateContext, span: &Span, rel: comparisons:
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for InvalidUpcastComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidUpcastComparisons {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprBinary(ref cmp, ref lhs, ref rhs) = expr.node {
|
||||
|
||||
let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ impl LintPass for Unicode {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Unicode {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use rustc::hir::*;
|
||||
use rustc::lint::*;
|
||||
use syntax::ast::Name;
|
||||
use syntax::ast::*;
|
||||
use syntax::codemap::Span;
|
||||
use syntax::symbol::InternedString;
|
||||
use utils::span_lint;
|
||||
|
|
@ -34,20 +33,19 @@ impl LintPass for UnsafeNameRemoval {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for UnsafeNameRemoval {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &Item) {
|
||||
if let ItemUse(ref item_use) = item.node {
|
||||
impl EarlyLintPass for UnsafeNameRemoval {
|
||||
fn check_item(&mut self, cx: &EarlyContext, item: &Item) {
|
||||
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")
|
||||
.name,
|
||||
*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() {
|
||||
let plid = path_list_item.node;
|
||||
|
|
@ -55,25 +53,21 @@ impl LateLintPass for UnsafeNameRemoval {
|
|||
unsafe_to_safe_check(plid.name, rename, cx, &item.span);
|
||||
};
|
||||
}
|
||||
}
|
||||
ViewPath_::ViewPathGlob(_) => {}
|
||||
},
|
||||
ViewPath_::ViewPathGlob(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unsafe_to_safe_check(old_name: Name, new_name: Name, cx: &LateContext, span: &Span) {
|
||||
let old_str = old_name.as_str();
|
||||
let new_str = new_name.as_str();
|
||||
fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext, span: &Span) {
|
||||
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
|
||||
));
|
||||
&format!("removed \"unsafe\" from the name of `{}` in use as `{}`", old_str, new_str));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::hir;
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
|
||||
use rustc::hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, NestedVisitorMap};
|
||||
use std::collections::HashMap;
|
||||
use syntax::ast;
|
||||
use syntax::codemap::Span;
|
||||
|
|
@ -29,15 +29,9 @@ declare_lint! {
|
|||
|
||||
pub struct UnusedLabel;
|
||||
|
||||
#[derive(Default)]
|
||||
struct UnusedLabelVisitor {
|
||||
struct UnusedLabelVisitor<'a, 'tcx: 'a> {
|
||||
labels: HashMap<InternedString, Span>,
|
||||
}
|
||||
|
||||
impl UnusedLabelVisitor {
|
||||
pub fn new() -> UnusedLabelVisitor {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl LintPass for UnusedLabel {
|
||||
|
|
@ -46,14 +40,25 @@ impl LintPass for UnusedLabel {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for UnusedLabel {
|
||||
fn check_fn(&mut self, cx: &LateContext, kind: FnKind, decl: &hir::FnDecl, body: &hir::Expr, span: Span, fn_id: ast::NodeId) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedLabel {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
kind: FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Expr,
|
||||
span: Span,
|
||||
fn_id: ast::NodeId
|
||||
) {
|
||||
if in_macro(cx, span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut v = UnusedLabelVisitor::new();
|
||||
walk_fn(&mut v, kind, decl, body, span, fn_id);
|
||||
let mut v = UnusedLabelVisitor {
|
||||
cx: cx,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
walk_fn(&mut v, kind, decl, body.expr_id(), span, fn_id);
|
||||
|
||||
for (label, span) in v.labels {
|
||||
span_lint(cx, UNUSED_LABEL, span, &format!("unused label `{}`", label));
|
||||
|
|
@ -61,20 +66,23 @@ impl LateLintPass for UnusedLabel {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'v> Visitor<'v> for UnusedLabelVisitor {
|
||||
fn visit_expr(&mut self, expr: &hir::Expr) {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for UnusedLabelVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
|
||||
match expr.node {
|
||||
hir::ExprBreak(Some(label), _) |
|
||||
hir::ExprAgain(Some(label)) => {
|
||||
self.labels.remove(&label.node.as_str());
|
||||
}
|
||||
self.labels.remove(&label.name.as_str());
|
||||
},
|
||||
hir::ExprLoop(_, Some(label), _) |
|
||||
hir::ExprWhile(_, _, Some(label)) => {
|
||||
self.labels.insert(label.node.as_str(), expr.span);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ pub enum Rel {
|
|||
}
|
||||
|
||||
/// 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)> {
|
||||
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)),
|
||||
BinOp_::BiLe => Some((Rel::Le, lhs, rhs)),
|
||||
|
|
|
|||
|
|
@ -8,21 +8,20 @@ use syntax::{ast, codemap};
|
|||
use toml;
|
||||
|
||||
/// 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 {
|
||||
ast::MetaItemKind::Word |
|
||||
ast::MetaItemKind::List(_) => {
|
||||
Err(("`conf_file` must be a named value", arg.span))
|
||||
}
|
||||
ast::MetaItemKind::List(_) => Err(("`conf_file` must be a named value", arg.span)),
|
||||
ast::MetaItemKind::NameValue(ref value) => {
|
||||
if let ast::LitKind::Str(ref file, _) = value.node {
|
||||
Ok(Some(file.to_string().into()))
|
||||
} else {
|
||||
Err(("`conf_file` value must be a string", value.span))
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -38,14 +37,12 @@ pub enum Error {
|
|||
/// The file is not valid TOML.
|
||||
Toml(Vec<toml::ParserError>),
|
||||
/// 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),
|
||||
}
|
||||
|
|
@ -66,10 +63,10 @@ impl fmt::Display for Error {
|
|||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
Error::Type(key, expected, got) => {
|
||||
write!(f, "`{}` is expected to be a `{}` but is a `{}`", key, expected, got)
|
||||
}
|
||||
},
|
||||
Error::UnknownKey(ref key) => write!(f, "unknown key `{}`", key),
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +131,12 @@ macro_rules! define_Conf {
|
|||
|
||||
// how to read the value?
|
||||
(CONV i64, $value: expr) => { $value.as_integer() };
|
||||
(CONV u64, $value: expr) => { $value.as_integer().iter().filter_map(|&i| if i >= 0 { Some(i as u64) } else { None }).next() };
|
||||
(CONV u64, $value: expr) => {
|
||||
$value.as_integer()
|
||||
.iter()
|
||||
.filter_map(|&i| if i >= 0 { Some(i as u64) } else { None })
|
||||
.next()
|
||||
};
|
||||
(CONV String, $value: expr) => { $value.as_str().map(Into::into) };
|
||||
(CONV Vec<String>, $value: expr) => {{
|
||||
let slice = $value.as_slice();
|
||||
|
|
@ -142,12 +144,10 @@ macro_rules! define_Conf {
|
|||
if let Some(slice) = slice {
|
||||
if slice.iter().any(|v| v.as_str().is_none()) {
|
||||
None
|
||||
} else {
|
||||
Some(slice.iter().map(|v| v.as_str().expect("already checked").to_owned()).collect())
|
||||
}
|
||||
else {
|
||||
Some(slice.iter().map(|v| v.as_str().unwrap_or_else(|| unreachable!()).to_owned()).collect())
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}};
|
||||
|
|
@ -163,7 +163,19 @@ define_Conf! {
|
|||
/// Lint: CYCLOMATIC_COMPLEXITY. The maximum cyclomatic complexity a function can have
|
||||
("cyclomatic-complexity-threshold", cyclomatic_complexity_threshold, 25 => u64),
|
||||
/// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks
|
||||
("doc-valid-idents", doc_valid_idents, ["MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "GPLv2", "GPLv3", "GitHub", "IPv4", "IPv6", "JavaScript", "NaN", "OAuth", "OpenGL", "TrueType", "iOS", "macOS"] => Vec<String>),
|
||||
("doc-valid-idents", doc_valid_idents, [
|
||||
"MiB", "GiB", "TiB", "PiB", "EiB",
|
||||
"DirectX",
|
||||
"GPLv2", "GPLv3",
|
||||
"GitHub",
|
||||
"IPv4", "IPv6",
|
||||
"JavaScript",
|
||||
"NaN",
|
||||
"OAuth",
|
||||
"OpenGL",
|
||||
"TrueType",
|
||||
"iOS", "macOS",
|
||||
] => Vec<String>),
|
||||
/// Lint: TOO_MANY_ARGUMENTS. The maximum number of argument a function or method can have
|
||||
("too-many-arguments-threshold", too_many_arguments_threshold, 7 => u64),
|
||||
/// Lint: TYPE_COMPLEXITY. The maximum complexity a type can have
|
||||
|
|
@ -196,7 +208,7 @@ pub fn lookup_conf_file() -> io::Result<Option<path::PathBuf>> {
|
|||
if e.kind() != io::ErrorKind::NotFound {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -231,11 +243,11 @@ pub fn read(path: Option<&path::Path>) -> (Conf, Vec<Error>) {
|
|||
}
|
||||
|
||||
buf
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
errors.push(err.into());
|
||||
return (conf, errors);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut parser = toml::Parser::new(&file);
|
||||
|
|
|
|||
|
|
@ -7,15 +7,5 @@
|
|||
/// 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"];
|
||||
|
|
|
|||
|
|
@ -44,34 +44,21 @@ pub struct Range<'a> {
|
|||
|
||||
/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
|
||||
pub fn range(expr: &hir::Expr) -> Option<Range> {
|
||||
/// Skip unstable blocks. To be removed when ranges get stable.
|
||||
fn unwrap_unstable(expr: &hir::Expr) -> &hir::Expr {
|
||||
if let hir::ExprBlock(ref block) = expr.node {
|
||||
if block.rules == hir::BlockCheckMode::PushUnstableBlock || block.rules == hir::BlockCheckMode::PopUnstableBlock {
|
||||
if let Some(ref expr) = block.expr {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr
|
||||
}
|
||||
|
||||
/// 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()
|
||||
.find(|field| field.name.node == name)
|
||||
.unwrap_or_else(|| panic!("missing {} field for range", name))
|
||||
.expr;
|
||||
.find(|field| field.name.node == name)
|
||||
.unwrap_or_else(|| panic!("missing {} field for range", name))
|
||||
.expr;
|
||||
|
||||
Some(unwrap_unstable(expr))
|
||||
Some(expr)
|
||||
}
|
||||
|
||||
// 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 unwrap_unstable(expr).node {
|
||||
hir::ExprPath(None, ref path) => {
|
||||
match expr.node {
|
||||
hir::ExprPath(ref path) => {
|
||||
if match_path(path, &paths::RANGE_FULL_STD) || match_path(path, &paths::RANGE_FULL) {
|
||||
Some(Range {
|
||||
start: None,
|
||||
|
|
@ -81,7 +68,7 @@ pub fn range(expr: &hir::Expr) -> Option<Range> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprStruct(ref path, ref fields, None) => {
|
||||
if match_path(path, &paths::RANGE_FROM_STD) || match_path(path, &paths::RANGE_FROM) {
|
||||
Some(Range {
|
||||
|
|
@ -90,7 +77,7 @@ pub fn range(expr: &hir::Expr) -> Option<Range> {
|
|||
limits: ast::RangeLimits::HalfOpen,
|
||||
})
|
||||
} else if match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY_STD) ||
|
||||
match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
|
||||
match_path(path, &paths::RANGE_INCLUSIVE_NON_EMPTY) {
|
||||
Some(Range {
|
||||
start: get_field("start", fields),
|
||||
end: get_field("end", fields),
|
||||
|
|
@ -117,7 +104,7 @@ pub fn range(expr: &hir::Expr) -> Option<Range> {
|
|||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -168,15 +155,15 @@ pub enum VecArgs<'a> {
|
|||
pub fn vec_macro<'e>(cx: &LateContext, expr: &'e hir::Expr) -> Option<VecArgs<'e>> {
|
||||
if_let_chain!{[
|
||||
let hir::ExprCall(ref fun, ref args) = expr.node,
|
||||
let hir::ExprPath(_, ref path) = fun.node,
|
||||
let Some(fun_def) = resolve_node(cx, fun.id),
|
||||
let hir::ExprPath(ref path) = fun.node,
|
||||
is_expn_of(cx, fun.span, "vec").is_some(),
|
||||
], {
|
||||
let fun_def = resolve_node(cx, path, fun.id);
|
||||
return if match_def_path(cx, fun_def.def_id(), &paths::VEC_FROM_ELEM) && args.len() == 2 {
|
||||
// `vec![elem; size]` case
|
||||
Some(VecArgs::Repeat(&args[0], &args[1]))
|
||||
}
|
||||
else if match_path(path, &["into_vec"]) && args.len() == 1 {
|
||||
else if match_def_path(cx, fun_def.def_id(), &paths::SLICE_INTO_VEC) && args.len() == 1 {
|
||||
// `vec![a, b, c]` case
|
||||
if_let_chain!{[
|
||||
let hir::ExprBox(ref boxed) = args[0].node,
|
||||
|
|
|
|||
|
|
@ -38,12 +38,11 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
match (&left.node, &right.node) {
|
||||
(&StmtDecl(ref l, _), &StmtDecl(ref r, _)) => {
|
||||
if let (&DeclLocal(ref l), &DeclLocal(ref r)) = (&l.node, &r.node) {
|
||||
both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) &&
|
||||
both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
|
||||
both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
(&StmtExpr(ref l, _), &StmtExpr(ref r, _)) |
|
||||
(&StmtSemi(ref l, _), &StmtSemi(ref r, _)) => self.eq_expr(l, r),
|
||||
_ => false,
|
||||
|
|
@ -69,69 +68,64 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
|
||||
match (&left.node, &right.node) {
|
||||
(&ExprAddrOf(l_mut, ref le), &ExprAddrOf(r_mut, ref re)) => l_mut == r_mut && self.eq_expr(le, re),
|
||||
(&ExprAgain(li), &ExprAgain(ri)) => both(&li, &ri, |l, r| l.node.as_str() == r.node.as_str()),
|
||||
(&ExprAgain(li), &ExprAgain(ri)) => both(&li, &ri, |l, r| l.name.as_str() == r.name.as_str()),
|
||||
(&ExprAssign(ref ll, ref lr), &ExprAssign(ref rl, ref rr)) => self.eq_expr(ll, rl) && self.eq_expr(lr, rr),
|
||||
(&ExprAssignOp(ref lo, ref ll, ref lr), &ExprAssignOp(ref ro, ref rl, ref rr)) => {
|
||||
lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
|
||||
}
|
||||
},
|
||||
(&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)
|
||||
})
|
||||
}
|
||||
(&ExprBreak(li, ref le), &ExprBreak(ri, ref re)) =>
|
||||
both(&li, &ri, |l, r| l.node.as_str() == r.node.as_str())
|
||||
&& both(le, re, |l, r| self.eq_expr(l, r)),
|
||||
},
|
||||
(&ExprBreak(li, ref le), &ExprBreak(ri, ref re)) => {
|
||||
both(&li, &ri, |l, r| l.name.as_str() == r.name.as_str()) && 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)) => {
|
||||
!self.ignore_fn && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
|
||||
}
|
||||
},
|
||||
(&ExprCast(ref lx, ref lt), &ExprCast(ref rx, ref rt)) |
|
||||
(&ExprType(ref lx, ref lt), &ExprType(ref rx, ref rt)) => {
|
||||
self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
|
||||
}
|
||||
(&ExprType(ref lx, ref lt), &ExprType(ref rx, ref rt)) => self.eq_expr(lx, rx) && self.eq_ty(lt, rt),
|
||||
(&ExprField(ref l_f_exp, ref l_f_ident), &ExprField(ref r_f_exp, ref r_f_ident)) => {
|
||||
l_f_ident.node == r_f_ident.node && self.eq_expr(l_f_exp, r_f_exp)
|
||||
}
|
||||
},
|
||||
(&ExprIndex(ref la, ref li), &ExprIndex(ref ra, ref ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
|
||||
(&ExprIf(ref lc, ref lt, ref le), &ExprIf(ref rc, ref rt, ref re)) => {
|
||||
self.eq_expr(lc, rc) && self.eq_block(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r))
|
||||
}
|
||||
},
|
||||
(&ExprLit(ref l), &ExprLit(ref r)) => l.node == r.node,
|
||||
(&ExprLoop(ref lb, ref ll, ref lls), &ExprLoop(ref rb, ref rl, ref rls)) => {
|
||||
self.eq_block(lb, rb) && both(ll, rl, |l, r| l.node.as_str() == r.node.as_str()) && lls == rls
|
||||
}
|
||||
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.node.as_str() == r.node.as_str())
|
||||
},
|
||||
(&ExprMatch(ref le, ref la, ref ls), &ExprMatch(ref re, ref ra, ref rs)) => {
|
||||
ls == rs && self.eq_expr(le, re) &&
|
||||
over(la, ra, |l, r| {
|
||||
self.eq_expr(&l.body, &r.body) && both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r)) &&
|
||||
over(&l.pats, &r.pats, |l, r| self.eq_pat(l, r))
|
||||
})
|
||||
}
|
||||
},
|
||||
(&ExprMethodCall(ref l_name, ref l_tys, ref l_args),
|
||||
&ExprMethodCall(ref r_name, ref r_tys, ref r_args)) => {
|
||||
!self.ignore_fn && l_name.node == r_name.node &&
|
||||
over(l_tys, r_tys, |l, r| self.eq_ty(l, r)) &&
|
||||
!self.ignore_fn && l_name.node == r_name.node && over(l_tys, r_tys, |l, r| self.eq_ty(l, r)) &&
|
||||
self.eq_exprs(l_args, r_args)
|
||||
}
|
||||
},
|
||||
(&ExprRepeat(ref le, ref ll), &ExprRepeat(ref re, ref rl)) => self.eq_expr(le, re) && self.eq_expr(ll, rl),
|
||||
(&ExprRet(ref l), &ExprRet(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
|
||||
(&ExprPath(ref l_qself, ref l_subpath), &ExprPath(ref r_qself, ref r_subpath)) => {
|
||||
both(l_qself, r_qself, |l, r| self.eq_qself(l, r)) && self.eq_path(l_subpath, r_subpath)
|
||||
}
|
||||
(&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_path(l_path, r_path) && both(lo, ro, |l, r| self.eq_expr(l, r)) &&
|
||||
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))
|
||||
}
|
||||
},
|
||||
(&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),
|
||||
(&ExprUnary(l_op, ref le), &ExprUnary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
|
||||
(&ExprArray(ref l), &ExprArray(ref r)) => self.eq_exprs(l, r),
|
||||
(&ExprWhile(ref lc, ref lb, ref ll), &ExprWhile(ref rc, ref rb, ref rl)) => {
|
||||
self.eq_expr(lc, rc) && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.node.as_str() == r.node.as_str())
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -153,34 +147,43 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
match (&left.node, &right.node) {
|
||||
(&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
|
||||
(&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
|
||||
self.eq_path(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
|
||||
}
|
||||
(&PatKind::Binding(ref lb, ref li, ref lp), &PatKind::Binding(ref rb, ref ri, ref rp)) => {
|
||||
self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
|
||||
},
|
||||
(&PatKind::Binding(ref lb, _, ref li, ref lp), &PatKind::Binding(ref rb, _, ref ri, ref rp)) => {
|
||||
lb == rb && li.node.as_str() == ri.node.as_str() && both(lp, rp, |l, r| self.eq_pat(l, r))
|
||||
}
|
||||
(&PatKind::Path(ref ql, ref l), &PatKind::Path(ref qr, ref r)) => {
|
||||
both(ql, qr, |ql, qr| self.eq_qself(ql, qr)) && self.eq_path(l, r)
|
||||
}
|
||||
},
|
||||
(&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
|
||||
(&PatKind::Lit(ref l), &PatKind::Lit(ref r)) => self.eq_expr(l, r),
|
||||
(&PatKind::Tuple(ref l, ls), &PatKind::Tuple(ref r, rs)) => {
|
||||
ls == rs && over(l, r, |l, r| self.eq_pat(l, r))
|
||||
}
|
||||
},
|
||||
(&PatKind::Range(ref ls, ref le), &PatKind::Range(ref rs, ref re)) => {
|
||||
self.eq_expr(ls, rs) && self.eq_expr(le, re)
|
||||
}
|
||||
},
|
||||
(&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))
|
||||
}
|
||||
},
|
||||
(&PatKind::Wild, &PatKind::Wild) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_qpath(&self, left: &QPath, right: &QPath) -> bool {
|
||||
match (left, right) {
|
||||
(&QPath::Resolved(ref lty, ref lpath), &QPath::Resolved(ref rty, ref rpath)) => {
|
||||
both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
|
||||
},
|
||||
(&QPath::TypeRelative(ref lty, ref lseg), &QPath::TypeRelative(ref rty, ref rseg)) => {
|
||||
self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_path(&self, left: &Path, right: &Path) -> bool {
|
||||
left.global == right.global &&
|
||||
over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
|
||||
left.global == right.global && over(&left.segments, &right.segments, |l, r| self.eq_path_segment(l, r))
|
||||
}
|
||||
|
||||
fn eq_path_parameters(&self, left: &PathParameters, right: &PathParameters) -> bool {
|
||||
|
|
@ -189,42 +192,31 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
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))
|
||||
}
|
||||
},
|
||||
(&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))
|
||||
}
|
||||
},
|
||||
(&AngleBracketedParameters(_), &ParenthesizedParameters(_)) |
|
||||
(&ParenthesizedParameters(_), &AngleBracketedParameters(_)) => {
|
||||
false
|
||||
}
|
||||
(&ParenthesizedParameters(_), &AngleBracketedParameters(_)) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn eq_path_segment(&self, left: &PathSegment, right: &PathSegment) -> bool {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
left.name.as_str() == right.name.as_str() &&
|
||||
self.eq_path_parameters(&left.parameters, &right.parameters)
|
||||
}
|
||||
|
||||
fn eq_qself(&self, left: &QSelf, right: &QSelf) -> bool {
|
||||
left.ty.node == right.ty.node && left.position == right.position
|
||||
left.name.as_str() == right.name.as_str() && self.eq_path_parameters(&left.parameters, &right.parameters)
|
||||
}
|
||||
|
||||
fn eq_ty(&self, left: &Ty, right: &Ty) -> bool {
|
||||
match (&left.node, &right.node) {
|
||||
(&TySlice(ref l_vec), &TySlice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
|
||||
(&TyArray(ref lt, ref ll), &TyArray(ref rt, ref rl)) => {
|
||||
self.eq_ty(lt, rt) && self.eq_expr(ll, rl)
|
||||
}
|
||||
(&TyArray(ref lt, ref ll), &TyArray(ref rt, ref rl)) => self.eq_ty(lt, rt) && self.eq_expr(ll, rl),
|
||||
(&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)) => {
|
||||
l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
|
||||
}
|
||||
(&TyPath(ref lq, ref l_path), &TyPath(ref rq, ref r_path)) => {
|
||||
both(lq, rq, |l, r| self.eq_qself(l, r)) && self.eq_path(l_path, r_path)
|
||||
}
|
||||
},
|
||||
(&TyPath(ref l), &TyPath(ref r)) => self.eq_qpath(l, r),
|
||||
(&TyTup(ref l), &TyTup(ref r)) => over(l, r, |l, r| self.eq_ty(l, r)),
|
||||
(&TyInfer, &TyInfer) => true,
|
||||
_ => false,
|
||||
|
|
@ -238,13 +230,7 @@ impl<'a, 'tcx: 'a> SpanlessEq<'a, 'tcx> {
|
|||
|
||||
fn swap_binop<'a>(binop: BinOp_, lhs: &'a Expr, rhs: &'a Expr) -> Option<(BinOp_, &'a Expr, &'a Expr)> {
|
||||
match binop {
|
||||
BiAdd |
|
||||
BiMul |
|
||||
BiBitXor |
|
||||
BiBitAnd |
|
||||
BiEq |
|
||||
BiNe |
|
||||
BiBitOr => Some((binop, rhs, lhs)),
|
||||
BiAdd | BiMul | BiBitXor | BiBitAnd | BiEq | BiNe | BiBitOr => Some((binop, rhs, lhs)),
|
||||
BiLt => Some((BiGt, rhs, lhs)),
|
||||
BiLe => Some((BiGe, rhs, lhs)),
|
||||
BiGe => Some((BiLe, rhs, lhs)),
|
||||
|
|
@ -313,88 +299,88 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
c.hash(&mut self.s);
|
||||
m.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
}
|
||||
},
|
||||
ExprAgain(i) => {
|
||||
let c: fn(_) -> _ = ExprAgain;
|
||||
c.hash(&mut self.s);
|
||||
if let Some(i) = i {
|
||||
self.hash_name(&i.node);
|
||||
self.hash_name(&i.name);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprAssign(ref l, ref r) => {
|
||||
let c: fn(_, _) -> _ = ExprAssign;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(l);
|
||||
self.hash_expr(r);
|
||||
}
|
||||
},
|
||||
ExprAssignOp(ref o, ref l, ref r) => {
|
||||
let c: fn(_, _, _) -> _ = ExprAssignOp;
|
||||
c.hash(&mut self.s);
|
||||
o.hash(&mut self.s);
|
||||
self.hash_expr(l);
|
||||
self.hash_expr(r);
|
||||
}
|
||||
},
|
||||
ExprBlock(ref b) => {
|
||||
let c: fn(_) -> _ = ExprBlock;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_block(b);
|
||||
}
|
||||
},
|
||||
ExprBinary(op, ref l, ref r) => {
|
||||
let c: fn(_, _, _) -> _ = ExprBinary;
|
||||
c.hash(&mut self.s);
|
||||
op.node.hash(&mut self.s);
|
||||
self.hash_expr(l);
|
||||
self.hash_expr(r);
|
||||
}
|
||||
},
|
||||
ExprBreak(i, ref j) => {
|
||||
let c: fn(_, _) -> _ = ExprBreak;
|
||||
c.hash(&mut self.s);
|
||||
if let Some(i) = i {
|
||||
self.hash_name(&i.node);
|
||||
self.hash_name(&i.name);
|
||||
}
|
||||
if let Some(ref j) = *j {
|
||||
self.hash_expr(&*j);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprBox(ref e) => {
|
||||
let c: fn(_) -> _ = ExprBox;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
}
|
||||
},
|
||||
ExprCall(ref fun, ref args) => {
|
||||
let c: fn(_, _) -> _ = ExprCall;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(fun);
|
||||
self.hash_exprs(args);
|
||||
}
|
||||
},
|
||||
ExprCast(ref e, ref _ty) => {
|
||||
let c: fn(_, _) -> _ = ExprCast;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
// TODO: _ty
|
||||
}
|
||||
ExprClosure(cap, _, ref e, _) => {
|
||||
},
|
||||
ExprClosure(cap, _, eid, _) => {
|
||||
let c: fn(_, _, _, _) -> _ = ExprClosure;
|
||||
c.hash(&mut self.s);
|
||||
cap.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
}
|
||||
self.hash_expr(self.cx.tcx.map.expr(eid));
|
||||
},
|
||||
ExprField(ref e, ref f) => {
|
||||
let c: fn(_, _) -> _ = ExprField;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
self.hash_name(&f.node);
|
||||
}
|
||||
},
|
||||
ExprIndex(ref a, ref i) => {
|
||||
let c: fn(_, _) -> _ = ExprIndex;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(a);
|
||||
self.hash_expr(i);
|
||||
}
|
||||
},
|
||||
ExprInlineAsm(..) => {
|
||||
let c: fn(_, _, _) -> _ = ExprInlineAsm;
|
||||
c.hash(&mut self.s);
|
||||
}
|
||||
},
|
||||
ExprIf(ref cond, ref t, ref e) => {
|
||||
let c: fn(_, _, _) -> _ = ExprIf;
|
||||
c.hash(&mut self.s);
|
||||
|
|
@ -403,21 +389,20 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
if let Some(ref e) = *e {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprLit(ref l) => {
|
||||
let c: fn(_) -> _ = ExprLit;
|
||||
c.hash(&mut self.s);
|
||||
l.hash(&mut self.s);
|
||||
}
|
||||
ExprLoop(ref b, ref i, ref j) => {
|
||||
},
|
||||
ExprLoop(ref b, ref i, _) => {
|
||||
let c: fn(_, _, _) -> _ = ExprLoop;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_block(b);
|
||||
if let Some(i) = *i {
|
||||
self.hash_name(&i.node);
|
||||
}
|
||||
j.hash(&mut self.s);
|
||||
}
|
||||
},
|
||||
ExprMatch(ref e, ref arms, ref s) => {
|
||||
let c: fn(_, _, _) -> _ = ExprMatch;
|
||||
c.hash(&mut self.s);
|
||||
|
|
@ -432,36 +417,36 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
}
|
||||
|
||||
s.hash(&mut self.s);
|
||||
}
|
||||
},
|
||||
ExprMethodCall(ref name, ref _tys, ref args) => {
|
||||
let c: fn(_, _, _) -> _ = ExprMethodCall;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_name(&name.node);
|
||||
self.hash_exprs(args);
|
||||
}
|
||||
},
|
||||
ExprRepeat(ref e, ref l) => {
|
||||
let c: fn(_, _) -> _ = ExprRepeat;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
self.hash_expr(l);
|
||||
}
|
||||
},
|
||||
ExprRet(ref e) => {
|
||||
let c: fn(_) -> _ = ExprRet;
|
||||
c.hash(&mut self.s);
|
||||
if let Some(ref e) = *e {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
}
|
||||
ExprPath(ref _qself, ref subpath) => {
|
||||
let c: fn(_, _) -> _ = ExprPath;
|
||||
},
|
||||
ExprPath(ref qpath) => {
|
||||
let c: fn(_) -> _ = ExprPath;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_path(subpath);
|
||||
}
|
||||
self.hash_qpath(qpath);
|
||||
},
|
||||
ExprStruct(ref path, ref fields, ref expr) => {
|
||||
let c: fn(_, _, _) -> _ = ExprStruct;
|
||||
c.hash(&mut self.s);
|
||||
|
||||
self.hash_path(path);
|
||||
self.hash_qpath(path);
|
||||
|
||||
for f in fields {
|
||||
self.hash_name(&f.name.node);
|
||||
|
|
@ -471,38 +456,38 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
if let Some(ref e) = *expr {
|
||||
self.hash_expr(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprTup(ref tup) => {
|
||||
let c: fn(_) -> _ = ExprTup;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_exprs(tup);
|
||||
}
|
||||
},
|
||||
ExprTupField(ref le, li) => {
|
||||
let c: fn(_, _) -> _ = ExprTupField;
|
||||
c.hash(&mut self.s);
|
||||
|
||||
self.hash_expr(le);
|
||||
li.node.hash(&mut self.s);
|
||||
}
|
||||
},
|
||||
ExprType(ref e, ref _ty) => {
|
||||
let c: fn(_, _) -> _ = ExprType;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(e);
|
||||
// TODO: _ty
|
||||
}
|
||||
},
|
||||
ExprUnary(lop, ref le) => {
|
||||
let c: fn(_, _) -> _ = ExprUnary;
|
||||
c.hash(&mut self.s);
|
||||
|
||||
lop.hash(&mut self.s);
|
||||
self.hash_expr(le);
|
||||
}
|
||||
},
|
||||
ExprArray(ref v) => {
|
||||
let c: fn(_) -> _ = ExprArray;
|
||||
c.hash(&mut self.s);
|
||||
|
||||
self.hash_exprs(v);
|
||||
}
|
||||
},
|
||||
ExprWhile(ref cond, ref b, l) => {
|
||||
let c: fn(_, _, _) -> _ = ExprWhile;
|
||||
c.hash(&mut self.s);
|
||||
|
|
@ -512,7 +497,7 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
if let Some(l) = l {
|
||||
self.hash_name(&l.node);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,6 +511,18 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
n.as_str().hash(&mut self.s);
|
||||
}
|
||||
|
||||
pub fn hash_qpath(&mut self, p: &QPath) {
|
||||
match *p {
|
||||
QPath::Resolved(_, ref path) => {
|
||||
self.hash_path(path);
|
||||
},
|
||||
QPath::TypeRelative(_, ref path) => {
|
||||
self.hash_name(&path.name);
|
||||
},
|
||||
}
|
||||
// self.cx.tcx.tables().qpath_def(p, id).hash(&mut self.s);
|
||||
}
|
||||
|
||||
pub fn hash_path(&mut self, p: &Path) {
|
||||
p.global.hash(&mut self.s);
|
||||
for p in &p.segments {
|
||||
|
|
@ -544,17 +541,17 @@ impl<'a, 'tcx: 'a> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(init);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
StmtExpr(ref expr, _) => {
|
||||
let c: fn(_, _) -> _ = StmtExpr;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(expr);
|
||||
}
|
||||
},
|
||||
StmtSemi(ref expr, _) => {
|
||||
let c: fn(_, _) -> _ = StmtSemi;
|
||||
c.hash(&mut self.s);
|
||||
self.hash_expr(expr);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,15 +36,15 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_item(&mut self, cx: &LateContext, item: &hir::Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
|
||||
if !has_attr(&item.attrs) {
|
||||
return;
|
||||
}
|
||||
print_item(cx, item);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext, item: &hir::ImplItem) {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ImplItem) {
|
||||
if !has_attr(&item.attrs) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -67,34 +67,34 @@ impl LateLintPass for Pass {
|
|||
hir::ImplItemKind::Type(_) => println!("associated type"),
|
||||
}
|
||||
}
|
||||
/*
|
||||
fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) {
|
||||
if !has_attr(&item.attrs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 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, _:
|
||||
// &hir::Generics) {
|
||||
// if !has_attr(&var.node.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// fn check_struct_field(&mut self, cx: &LateContext<'a, 'tcx>, field: &'tcx hir::StructField) {
|
||||
// if !has_attr(&field.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
fn check_variant(&mut self, cx: &LateContext, var: &hir::Variant, _: &hir::Generics) {
|
||||
if !has_attr(&var.node.attrs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn check_struct_field(&mut self, cx: &LateContext, field: &hir::StructField) {
|
||||
if !has_attr(&field.attrs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &hir::Expr) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
if !has_attr(&expr.attrs) {
|
||||
return;
|
||||
}
|
||||
print_expr(cx, expr, 0);
|
||||
}
|
||||
|
||||
fn check_arm(&mut self, cx: &LateContext, arm: &hir::Arm) {
|
||||
fn check_arm(&mut self, cx: &LateContext<'a, 'tcx>, arm: &'tcx hir::Arm) {
|
||||
if !has_attr(&arm.attrs) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -109,23 +109,22 @@ impl LateLintPass for Pass {
|
|||
print_expr(cx, &arm.body, 1);
|
||||
}
|
||||
|
||||
fn check_stmt(&mut self, cx: &LateContext, stmt: &hir::Stmt) {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, stmt: &'tcx hir::Stmt) {
|
||||
if !has_attr(stmt.node.attrs()) {
|
||||
return;
|
||||
}
|
||||
match stmt.node {
|
||||
hir::StmtDecl(ref decl, _) => print_decl(cx, decl),
|
||||
hir::StmtExpr(ref e, _) | hir::StmtSemi(ref e, _) => print_expr(cx, e, 0),
|
||||
hir::StmtExpr(ref e, _) |
|
||||
hir::StmtSemi(ref e, _) => print_expr(cx, e, 0),
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
fn check_foreign_item(&mut self, cx: &LateContext, item: &hir::ForeignItem) {
|
||||
if !has_attr(&item.attrs) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::ForeignItem) {
|
||||
// if !has_attr(&item.attrs) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
}
|
||||
|
||||
fn has_attr(attrs: &[Attribute]) -> bool {
|
||||
|
|
@ -275,11 +274,14 @@ fn print_expr(cx: &LateContext, expr: &hir::Expr, indent: usize) {
|
|||
println!("{}index expr:", ind);
|
||||
print_expr(cx, idx, indent + 1);
|
||||
},
|
||||
hir::ExprPath(ref sel, ref path) => {
|
||||
println!("{}Path, {}", ind, ty);
|
||||
println!("{}self: {:?}", ind, sel);
|
||||
hir::ExprPath(hir::QPath::Resolved(ref ty, ref path)) => {
|
||||
println!("{}Resolved Path, {:?}", ind, ty);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
},
|
||||
hir::ExprPath(hir::QPath::TypeRelative(ref ty, ref seg)) => {
|
||||
println!("{}Relative Path, {:?}", ind, ty);
|
||||
println!("{}seg: {:?}", ind, seg);
|
||||
},
|
||||
hir::ExprAddrOf(ref muta, ref e) => {
|
||||
println!("{}AddrOf, {}", ind, ty);
|
||||
println!("mutability: {:?}", muta);
|
||||
|
|
@ -353,8 +355,8 @@ fn print_item(cx: &LateContext, item: &hir::Item) {
|
|||
} else {
|
||||
println!("weird extern crate without a crate id");
|
||||
}
|
||||
}
|
||||
hir::ItemUse(ref path) => println!("{:?}", path.node),
|
||||
},
|
||||
hir::ItemUse(ref path, ref kind) => println!("{:?}, {:?}", path, kind),
|
||||
hir::ItemStatic(..) => println!("static item of type {:#?}", cx.tcx.item_type(did)),
|
||||
hir::ItemConst(..) => println!("const item of type {:#?}", cx.tcx.item_type(did)),
|
||||
hir::ItemFn(..) => {
|
||||
|
|
@ -383,13 +385,11 @@ fn print_item(cx: &LateContext, item: &hir::Item) {
|
|||
println!("trait has no default impl");
|
||||
}
|
||||
},
|
||||
hir::ItemDefaultImpl(_, ref trait_ref) => {
|
||||
let trait_did = cx.tcx.expect_def(trait_ref.ref_id).def_id();
|
||||
println!("default impl for `{}`", cx.tcx.item_path_str(trait_did));
|
||||
hir::ItemDefaultImpl(_, ref _trait_ref) => {
|
||||
println!("default impl");
|
||||
},
|
||||
hir::ItemImpl(_, _, _, Some(ref trait_ref), _, _) => {
|
||||
let trait_did = cx.tcx.expect_def(trait_ref.ref_id).def_id();
|
||||
println!("impl of trait `{}`", cx.tcx.item_path_str(trait_did));
|
||||
hir::ItemImpl(_, _, _, Some(ref _trait_ref), _, _) => {
|
||||
println!("trait impl");
|
||||
},
|
||||
hir::ItemImpl(_, _, _, None, _, _) => {
|
||||
println!("impl");
|
||||
|
|
@ -402,7 +402,7 @@ fn print_pat(cx: &LateContext, pat: &hir::Pat, indent: usize) {
|
|||
println!("{}+", ind);
|
||||
match pat.node {
|
||||
hir::PatKind::Wild => println!("{}Wild", ind),
|
||||
hir::PatKind::Binding(ref mode, ref name, ref inner) => {
|
||||
hir::PatKind::Binding(ref mode, _, ref name, ref inner) => {
|
||||
println!("{}Binding", ind);
|
||||
println!("{}mode: {:?}", ind, mode);
|
||||
println!("{}name: {}", ind, name.node);
|
||||
|
|
@ -434,11 +434,14 @@ fn print_pat(cx: &LateContext, pat: &hir::Pat, indent: usize) {
|
|||
print_pat(cx, field, indent + 1);
|
||||
}
|
||||
},
|
||||
hir::PatKind::Path(ref sel, ref path) => {
|
||||
println!("{}Path", ind);
|
||||
println!("{}self: {:?}", ind, sel);
|
||||
hir::PatKind::Path(hir::QPath::Resolved(ref ty, ref path)) => {
|
||||
println!("{}Resolved Path, {:?}", ind, ty);
|
||||
println!("{}path: {:?}", ind, path);
|
||||
},
|
||||
hir::PatKind::Path(hir::QPath::TypeRelative(ref ty, ref seg)) => {
|
||||
println!("{}Relative Path, {:?}", ind, ty);
|
||||
println!("{}seg: {:?}", ind, seg);
|
||||
},
|
||||
hir::PatKind::Tuple(ref pats, opt_dots_position) => {
|
||||
println!("{}Tuple", ind);
|
||||
if let Some(dot_position) = opt_dots_position {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc::lint::*;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc::hir::intravisit::{Visitor, walk_expr, NestedVisitorMap};
|
||||
use utils::{paths, match_path, span_lint};
|
||||
use syntax::symbol::InternedString;
|
||||
use syntax::ast::{Name, NodeId, ItemKind, Crate as AstCrate};
|
||||
|
|
@ -75,8 +75,8 @@ impl EarlyLintPass for Clippy {
|
|||
span_lint(cx,
|
||||
CLIPPY_LINTS_INTERNAL,
|
||||
item.span,
|
||||
"this constant should be before the previous constant due to lexical ordering",
|
||||
);
|
||||
"this constant should be before the previous constant due to lexical \
|
||||
ordering");
|
||||
}
|
||||
}
|
||||
last_name = Some(name);
|
||||
|
|
@ -104,19 +104,22 @@ impl LintPass for LintWithoutLintPass {
|
|||
}
|
||||
|
||||
|
||||
impl LateLintPass for LintWithoutLintPass {
|
||||
fn check_item(&mut self, _: &LateContext, item: &Item) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LintWithoutLintPass {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemStatic(ref ty, MutImmutable, ref expr) = item.node {
|
||||
if is_lint_ref_type(ty) {
|
||||
self.declared_lints.insert(item.name, item.span);
|
||||
} else if is_lint_array_type(ty) && item.vis == Visibility::Inherited && item.name == "ARRAY" {
|
||||
let mut collector = LintCollector { output: &mut self.registered_lints };
|
||||
let mut collector = LintCollector {
|
||||
output: &mut self.registered_lints,
|
||||
cx: cx,
|
||||
};
|
||||
collector.visit_expr(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_crate_post(&mut self, cx: &LateContext, _: &Crate) {
|
||||
fn check_crate_post(&mut self, cx: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
|
||||
for (lint_name, &lint_span) in &self.declared_lints {
|
||||
// When using the `declare_lint!` macro, the original `lint_span`'s
|
||||
// file points to "<rustc macros>".
|
||||
|
|
@ -140,7 +143,7 @@ impl LateLintPass for LintWithoutLintPass {
|
|||
|
||||
fn is_lint_ref_type(ty: &Ty) -> bool {
|
||||
if let TyRptr(Some(_), MutTy { ty: ref inner, mutbl: MutImmutable }) = ty.node {
|
||||
if let TyPath(None, ref path) = inner.node {
|
||||
if let TyPath(ref path) = inner.node {
|
||||
return match_path(path, &paths::LINT);
|
||||
}
|
||||
}
|
||||
|
|
@ -149,25 +152,29 @@ fn is_lint_ref_type(ty: &Ty) -> bool {
|
|||
|
||||
|
||||
fn is_lint_array_type(ty: &Ty) -> bool {
|
||||
if let TyPath(None, ref path) = ty.node {
|
||||
if let TyPath(ref path) = ty.node {
|
||||
match_path(path, &paths::LINT_ARRAY)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
struct LintCollector<'a> {
|
||||
struct LintCollector<'a, 'tcx: 'a> {
|
||||
output: &'a mut HashSet<Name>,
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
}
|
||||
|
||||
impl<'v, 'a: 'v> Visitor<'v> for LintCollector<'a> {
|
||||
fn visit_expr(&mut self, expr: &'v Expr) {
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for LintCollector<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
walk_expr(self, expr);
|
||||
}
|
||||
|
||||
fn visit_path(&mut self, path: &'v Path, _: NodeId) {
|
||||
fn visit_path(&mut self, path: &'tcx Path, _: NodeId) {
|
||||
if path.segments.len() == 1 {
|
||||
self.output.insert(path.segments[0].name);
|
||||
}
|
||||
}
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use reexport::*;
|
||||
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::session::Session;
|
||||
|
|
@ -34,23 +35,27 @@ pub type MethodArgs = HirVec<P<Expr>>;
|
|||
|
||||
/// Produce a nested chain of if-lets and ifs from the patterns:
|
||||
///
|
||||
/// if_let_chain! {[
|
||||
/// let Some(y) = x,
|
||||
/// y.len() == 2,
|
||||
/// let Some(z) = y,
|
||||
/// ], {
|
||||
/// block
|
||||
/// }}
|
||||
/// ```rust,ignore
|
||||
/// if_let_chain! {[
|
||||
/// let Some(y) = x,
|
||||
/// y.len() == 2,
|
||||
/// let Some(z) = y,
|
||||
/// ], {
|
||||
/// block
|
||||
/// }}
|
||||
/// ```
|
||||
///
|
||||
/// becomes
|
||||
///
|
||||
/// if let Some(y) = x {
|
||||
/// if y.len() == 2 {
|
||||
/// if let Some(z) = y {
|
||||
/// block
|
||||
/// }
|
||||
/// ```rust,ignore
|
||||
/// if let Some(y) = x {
|
||||
/// if y.len() == 2 {
|
||||
/// if let Some(z) = y {
|
||||
/// block
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! if_let_chain {
|
||||
([let $pat:pat = $expr:expr, $($tt:tt)+], $block:block) => {
|
||||
|
|
@ -93,16 +98,27 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
|
|||
rhs.expn_id != lhs.expn_id
|
||||
}
|
||||
/// Returns true if this `expn_info` was expanded by any macro.
|
||||
pub fn in_macro<T: LintContext>(cx: &T, span: Span) -> bool {
|
||||
cx.sess().codemap().with_expn_info(span.expn_id, |info| info.is_some())
|
||||
pub fn in_macro<'a, T: LintContext<'a>>(cx: &T, span: Span) -> bool {
|
||||
cx.sess().codemap().with_expn_info(span.expn_id, |info| {
|
||||
match info {
|
||||
Some(info) => {
|
||||
match info.callee.format {
|
||||
// don't treat range expressions desugared to structs as "in_macro"
|
||||
ExpnFormat::CompilerDesugaring(name) => name != "...",
|
||||
_ => true,
|
||||
}
|
||||
},
|
||||
None => false,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<T: LintContext>(cx: &T, span: Span) -> bool {
|
||||
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
|
||||
/// this after other checks have already happened.
|
||||
fn in_macro_ext<T: LintContext>(cx: &T, opt_info: Option<&ExpnInfo>) -> bool {
|
||||
fn in_macro_ext<'a, T: LintContext<'a>>(cx: &T, opt_info: Option<&ExpnInfo>) -> bool {
|
||||
// no ExpnInfo = no macro
|
||||
opt_info.map_or(false, |info| {
|
||||
if let ExpnFormat::MacroAttribute(..) = info.callee.format {
|
||||
|
|
@ -123,7 +139,7 @@ pub fn in_external_macro<T: LintContext>(cx: &T, span: Span) -> bool {
|
|||
/// Check if a `DefId`'s path matches the given absolute type path usage.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// match_def_path(cx, id, &["core", "option", "Option"])
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -150,8 +166,7 @@ pub fn match_def_path(cx: &LateContext, def_id: DefId, path: &[&str]) -> bool {
|
|||
|
||||
cx.tcx.push_item_path(&mut apb, def_id);
|
||||
|
||||
apb.names.len() == path.len() &&
|
||||
apb.names.iter().zip(path.iter()).all(|(a, &b)| &**a == b)
|
||||
apb.names.len() == path.len() && apb.names.iter().zip(path.iter()).all(|(a, &b)| &**a == b)
|
||||
}
|
||||
|
||||
/// Check if type is struct, enum or union type with given def path.
|
||||
|
|
@ -167,11 +182,11 @@ pub fn match_impl_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool {
|
|||
let method_call = ty::MethodCall::expr(expr.id);
|
||||
|
||||
let trt_id = cx.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.method_map
|
||||
.get(&method_call)
|
||||
.and_then(|callee| cx.tcx.impl_of_method(callee.def_id));
|
||||
.tables
|
||||
.borrow()
|
||||
.method_map
|
||||
.get(&method_call)
|
||||
.and_then(|callee| cx.tcx.impl_of_method(callee.def_id));
|
||||
if let Some(trt_id) = trt_id {
|
||||
match_def_path(cx, trt_id, path)
|
||||
} else {
|
||||
|
|
@ -184,11 +199,11 @@ pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool
|
|||
let method_call = ty::MethodCall::expr(expr.id);
|
||||
|
||||
let trt_id = cx.tcx
|
||||
.tables
|
||||
.borrow()
|
||||
.method_map
|
||||
.get(&method_call)
|
||||
.and_then(|callee| cx.tcx.trait_of_item(callee.def_id));
|
||||
.tables
|
||||
.borrow()
|
||||
.method_map
|
||||
.get(&method_call)
|
||||
.and_then(|callee| cx.tcx.trait_of_item(callee.def_id));
|
||||
if let Some(trt_id) = trt_id {
|
||||
match_def_path(cx, trt_id, path)
|
||||
} else {
|
||||
|
|
@ -196,20 +211,54 @@ pub fn match_trait_method(cx: &LateContext, expr: &Expr, path: &[&str]) -> bool
|
|||
}
|
||||
}
|
||||
|
||||
pub fn last_path_segment(path: &QPath) -> &PathSegment {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) => {
|
||||
path.segments
|
||||
.last()
|
||||
.expect("A path must have at least one segment")
|
||||
},
|
||||
QPath::TypeRelative(_, ref seg) => seg,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn single_segment_path(path: &QPath) -> Option<&PathSegment> {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) if path.segments.len() == 1 => Some(&path.segments[0]),
|
||||
QPath::Resolved(..) => None,
|
||||
QPath::TypeRelative(_, ref seg) => Some(seg),
|
||||
}
|
||||
}
|
||||
|
||||
/// Match a `Path` against a slice of segment string literals.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// match_path(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_path(path: &Path, segments: &[&str]) -> bool {
|
||||
pub fn match_path(path: &QPath, segments: &[&str]) -> bool {
|
||||
match *path {
|
||||
QPath::Resolved(_, ref path) => match_path_old(path, segments),
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
match ty.node {
|
||||
TyPath(ref inner_path) => {
|
||||
segments.len() > 0 && match_path(inner_path, &segments[..(segments.len() - 1)]) &&
|
||||
segment.name == segments[segments.len() - 1]
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_path_old(path: &Path, segments: &[&str]) -> bool {
|
||||
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.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// match_path(path, &["std", "rt", "begin_unwind"])
|
||||
/// ```
|
||||
pub fn match_path_ast(path: &ast::Path, segments: &[&str]) -> bool {
|
||||
|
|
@ -224,7 +273,10 @@ pub fn path_to_def(cx: &LateContext, path: &[&str]) -> Option<def::Def> {
|
|||
let crates = cstore.crates();
|
||||
let krate = crates.iter().find(|&&krate| cstore.crate_name(krate) == path[0]);
|
||||
if let Some(krate) = krate {
|
||||
let krate = DefId { krate: *krate, index: CRATE_DEF_INDEX };
|
||||
let krate = DefId {
|
||||
krate: *krate,
|
||||
index: CRATE_DEF_INDEX,
|
||||
};
|
||||
let mut items = cstore.item_children(krate);
|
||||
let mut path_it = path.iter().skip(1).peekable();
|
||||
|
||||
|
|
@ -265,26 +317,25 @@ pub fn get_trait_def_id(cx: &LateContext, path: &[&str]) -> Option<DefId> {
|
|||
|
||||
/// Check whether a type implements a trait.
|
||||
/// See also `get_trait_def_id`.
|
||||
pub fn implements_trait<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, trait_id: DefId,
|
||||
ty_params: Vec<ty::Ty<'tcx>>)
|
||||
-> bool {
|
||||
pub fn implements_trait<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
ty: ty::Ty<'tcx>,
|
||||
trait_id: DefId,
|
||||
ty_params: Vec<ty::Ty<'tcx>>
|
||||
) -> bool {
|
||||
cx.tcx.populate_implementations_for_trait_if_necessary(trait_id);
|
||||
|
||||
let ty = cx.tcx.erase_regions(&ty);
|
||||
cx.tcx.infer_ctxt(None, None, Reveal::All).enter(|infcx| {
|
||||
let obligation = cx.tcx.predicate_for_trait_def(traits::ObligationCause::dummy(),
|
||||
trait_id,
|
||||
0,
|
||||
ty,
|
||||
&ty_params);
|
||||
let obligation = cx.tcx.predicate_for_trait_def(traits::ObligationCause::dummy(), trait_id, 0, ty, &ty_params);
|
||||
|
||||
traits::SelectionContext::new(&infcx).evaluate_obligation_conservatively(&obligation)
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve the definition of a node from its `NodeId`.
|
||||
pub fn resolve_node(cx: &LateContext, id: NodeId) -> Option<def::Def> {
|
||||
cx.tcx.def_map.borrow().get(&id).map(|d| d.full_def())
|
||||
pub fn resolve_node(cx: &LateContext, qpath: &QPath, id: NodeId) -> def::Def {
|
||||
cx.tcx.tables().qpath_def(qpath, id)
|
||||
}
|
||||
|
||||
/// Match an `Expr` against a chain of methods, and return the matched `Expr`s.
|
||||
|
|
@ -327,15 +378,15 @@ pub fn get_item_name(cx: &LateContext, expr: &Expr) -> Option<Name> {
|
|||
/// Convert a span to a code snippet if available, otherwise use default.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// snippet(cx, expr.span, "..")
|
||||
/// ```
|
||||
pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
pub fn snippet<'a, 'b, T: LintContext<'b>>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
cx.sess().codemap().span_to_snippet(span).map(From::from).unwrap_or_else(|_| Cow::Borrowed(default))
|
||||
}
|
||||
|
||||
/// Convert a span to a code snippet. Returns `None` if not available.
|
||||
pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
pub fn snippet_opt<'a, T: LintContext<'a>>(cx: &T, span: Span) -> Option<String> {
|
||||
cx.sess().codemap().span_to_snippet(span).ok()
|
||||
}
|
||||
|
||||
|
|
@ -344,17 +395,22 @@ pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
|||
/// things which need to be printed as such.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// ```rust,ignore
|
||||
/// snippet(cx, expr.span, "..")
|
||||
/// ```
|
||||
pub fn snippet_block<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
pub fn snippet_block<'a, 'b, T: LintContext<'b>>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
|
||||
let snip = snippet(cx, span, default);
|
||||
trim_multiline(snip, true)
|
||||
}
|
||||
|
||||
/// Like `snippet_block`, but add braces if the expr is not an `ExprBlock`.
|
||||
/// Also takes an `Option<String>` which can be put inside the braces.
|
||||
pub fn expr_block<'a, T: LintContext>(cx: &T, expr: &Expr, option: Option<String>, default: &'a str) -> Cow<'a, str> {
|
||||
pub fn expr_block<'a, 'b, T: LintContext<'b>>(
|
||||
cx: &T,
|
||||
expr: &Expr,
|
||||
option: Option<String>,
|
||||
default: &'a str
|
||||
) -> Cow<'a, str> {
|
||||
let code = snippet_block(cx, expr.span, default);
|
||||
let string = option.unwrap_or_default();
|
||||
if let ExprBlock(_) = expr.node {
|
||||
|
|
@ -375,32 +431,32 @@ pub fn trim_multiline(s: Cow<str>, ignore_first: bool) -> Cow<str> {
|
|||
|
||||
fn trim_multiline_inner(s: Cow<str>, ignore_first: bool, ch: char) -> Cow<str> {
|
||||
let x = s.lines()
|
||||
.skip(ignore_first as usize)
|
||||
.filter_map(|l| {
|
||||
if l.is_empty() {
|
||||
None
|
||||
} else {
|
||||
// ignore empty lines
|
||||
Some(l.char_indices()
|
||||
.find(|&(_, x)| x != ch)
|
||||
.unwrap_or((l.len(), ch))
|
||||
.0)
|
||||
}
|
||||
})
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
.skip(ignore_first as usize)
|
||||
.filter_map(|l| {
|
||||
if l.is_empty() {
|
||||
None
|
||||
} else {
|
||||
// ignore empty lines
|
||||
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"))
|
||||
.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
|
||||
}
|
||||
|
|
@ -423,19 +479,19 @@ pub fn get_parent_expr<'c>(cx: &'c LateContext, e: &Expr) -> Option<&'c Expr> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_enclosing_block<'c>(cx: &'c LateContext, node: NodeId) -> Option<&'c Block> {
|
||||
pub fn get_enclosing_block<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, node: NodeId) -> Option<&'tcx Block> {
|
||||
let map = &cx.tcx.map;
|
||||
let enclosing_node = map.get_enclosing_scope(node)
|
||||
.and_then(|enclosing_id| map.find(enclosing_id));
|
||||
.and_then(|enclosing_id| map.find(enclosing_id));
|
||||
if let Some(node) = enclosing_node {
|
||||
match node {
|
||||
Node::NodeBlock(block) => Some(block),
|
||||
Node::NodeItem(&Item { node: ItemFn(_, _, _, _, _, ref expr), .. }) => {
|
||||
match expr.node {
|
||||
Node::NodeItem(&Item { node: ItemFn(_, _, _, _, _, eid), .. }) => {
|
||||
match cx.tcx.map.expr(eid).node {
|
||||
ExprBlock(ref block) => Some(block),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -455,20 +511,25 @@ impl<'a> DiagnosticWrapper<'a> {
|
|||
fn wiki_link(&mut self, lint: &'static Lint) {
|
||||
if env::var("CLIPPY_DISABLE_WIKI_LINKS").is_err() {
|
||||
self.0.help(&format!("for further information visit https://github.com/Manishearth/rust-clippy/wiki#{}",
|
||||
lint.name_lower()));
|
||||
lint.name_lower()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: needless lifetime doesn't trigger here
|
||||
pub fn span_help_and_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, span: Span, msg: &str, help: &str) {
|
||||
pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &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);
|
||||
|
|
@ -476,8 +537,14 @@ pub fn span_help_and_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, sp
|
|||
}
|
||||
}
|
||||
|
||||
pub fn span_note_and_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, span: Span, msg: &str, note_span: Span,
|
||||
note: &str) {
|
||||
pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
span: Span,
|
||||
msg: &str,
|
||||
note_span: Span,
|
||||
note: &str
|
||||
) {
|
||||
let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
|
||||
if cx.current_level(lint) != Level::Allow {
|
||||
if note_span == span {
|
||||
|
|
@ -489,8 +556,13 @@ pub fn span_note_and_lint<'a, T: LintContext>(cx: &'a T, lint: &'static Lint, sp
|
|||
}
|
||||
}
|
||||
|
||||
pub fn span_lint_and_then<'a, T: LintContext, F>(cx: &'a T, lint: &'static Lint, sp: Span, msg: &str, f: F)
|
||||
where F: FnOnce(&mut DiagnosticBuilder<'a>)
|
||||
pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
|
||||
cx: &'a T,
|
||||
lint: &'static Lint,
|
||||
sp: Span,
|
||||
msg: &str,
|
||||
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 {
|
||||
|
|
@ -606,9 +678,9 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
|
|||
pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span> {
|
||||
loop {
|
||||
let span_name_span = cx.tcx
|
||||
.sess
|
||||
.codemap()
|
||||
.with_expn_info(span.expn_id, |expn| expn.map(|ei| (ei.callee.name(), ei.call_site)));
|
||||
.sess
|
||||
.codemap()
|
||||
.with_expn_info(span.expn_id, |expn| expn.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),
|
||||
|
|
@ -627,9 +699,9 @@ pub fn is_expn_of(cx: &LateContext, mut span: Span, name: &str) -> Option<Span>
|
|||
/// `is_direct_expn_of`.
|
||||
pub fn is_direct_expn_of(cx: &LateContext, span: Span, name: &str) -> Option<Span> {
|
||||
let span_name_span = cx.tcx
|
||||
.sess
|
||||
.codemap()
|
||||
.with_expn_info(span.expn_id, |expn| expn.map(|ei| (ei.callee.name(), ei.call_site)));
|
||||
.sess
|
||||
.codemap()
|
||||
.with_expn_info(span.expn_id, |expn| expn.map(|ei| (ei.callee.name(), ei.call_site)));
|
||||
|
||||
match span_name_span {
|
||||
Some((mac_name, new_span)) if mac_name == name => Some(new_span),
|
||||
|
|
@ -663,11 +735,7 @@ pub fn camel_case_until(s: &str) -> usize {
|
|||
return i;
|
||||
}
|
||||
}
|
||||
if up {
|
||||
last_i
|
||||
} else {
|
||||
s.len()
|
||||
}
|
||||
if up { last_i } else { s.len() }
|
||||
}
|
||||
|
||||
/// Return index of the last camel-case component of `s`.
|
||||
|
|
@ -705,13 +773,18 @@ pub fn return_ty<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, fn_item: NodeId) -> ty::T
|
|||
let fn_def_id = cx.tcx.map.local_def_id(fn_item);
|
||||
let fn_sig = cx.tcx.item_type(fn_def_id).fn_sig();
|
||||
let fn_sig = cx.tcx.liberate_late_bound_regions(parameter_env.free_id_outlive, fn_sig);
|
||||
fn_sig.output
|
||||
fn_sig.output()
|
||||
}
|
||||
|
||||
/// Check if two types are the same.
|
||||
// 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::Ty<'tcx>, b: ty::Ty<'tcx>, parameter_item: NodeId) -> bool {
|
||||
pub fn same_tys<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
a: ty::Ty<'tcx>,
|
||||
b: ty::Ty<'tcx>,
|
||||
parameter_item: NodeId
|
||||
) -> bool {
|
||||
let parameter_env = ty::ParameterEnvironment::for_item(cx.tcx, parameter_item);
|
||||
cx.tcx.infer_ctxt(None, Some(parameter_env), Reveal::All).enter(|infcx| {
|
||||
let new_a = a.subst(infcx.tcx, infcx.parameter_environment.free_substs);
|
||||
|
|
@ -736,37 +809,41 @@ pub fn is_copy<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, env: Node
|
|||
|
||||
/// Return whether a pattern is refutable.
|
||||
pub fn is_refutable(cx: &LateContext, pat: &Pat) -> bool {
|
||||
fn is_enum_variant(cx: &LateContext, did: NodeId) -> bool {
|
||||
matches!(cx.tcx.def_map.borrow().get(&did).map(|d| d.full_def()), Some(def::Def::Variant(..)) | Some(def::Def::VariantCtor(..)))
|
||||
fn is_enum_variant(cx: &LateContext, qpath: &QPath, did: NodeId) -> bool {
|
||||
matches!(cx.tcx.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 {
|
||||
fn are_refutable<'a, I: Iterator<Item = &'a Pat>>(cx: &LateContext, mut i: I) -> bool {
|
||||
i.any(|pat| is_refutable(cx, pat))
|
||||
}
|
||||
|
||||
match pat.node {
|
||||
PatKind::Binding(..) | PatKind::Wild => false,
|
||||
PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) | PatKind::Range(..) => true,
|
||||
PatKind::Path(..) => is_enum_variant(cx, pat.id),
|
||||
PatKind::Binding(..) |
|
||||
PatKind::Wild => false,
|
||||
PatKind::Box(ref pat) |
|
||||
PatKind::Ref(ref pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Lit(..) |
|
||||
PatKind::Range(..) => true,
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.id),
|
||||
PatKind::Tuple(ref pats, _) => are_refutable(cx, pats.iter().map(|pat| &**pat)),
|
||||
PatKind::Struct(_, ref fields, _) => {
|
||||
if is_enum_variant(cx, pat.id) {
|
||||
PatKind::Struct(ref qpath, ref fields, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, fields.iter().map(|field| &*field.node.pat))
|
||||
}
|
||||
}
|
||||
PatKind::TupleStruct(_, ref pats, _) => {
|
||||
if is_enum_variant(cx, pat.id) {
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, ref pats, _) => {
|
||||
if is_enum_variant(cx, qpath, pat.id) {
|
||||
true
|
||||
} else {
|
||||
are_refutable(cx, pats.iter().map(|pat| &**pat))
|
||||
}
|
||||
}
|
||||
},
|
||||
PatKind::Slice(ref head, ref middle, ref tail) => {
|
||||
are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -793,3 +870,29 @@ pub fn remove_blocks(expr: &Expr) -> &Expr {
|
|||
expr
|
||||
}
|
||||
}
|
||||
|
||||
pub fn opt_def_id(def: Def) -> Option<DefId> {
|
||||
match def {
|
||||
Def::Fn(id) |
|
||||
Def::Mod(id) |
|
||||
Def::Static(id, _) |
|
||||
Def::Variant(id) |
|
||||
Def::VariantCtor(id, ..) |
|
||||
Def::Enum(id) |
|
||||
Def::TyAlias(id) |
|
||||
Def::AssociatedTy(id) |
|
||||
Def::TyParam(id) |
|
||||
Def::Struct(id) |
|
||||
Def::StructCtor(id, ..) |
|
||||
Def::Union(id) |
|
||||
Def::Trait(id) |
|
||||
Def::Method(id) |
|
||||
Def::Const(id) |
|
||||
Def::AssociatedConst(id) |
|
||||
Def::Local(id) |
|
||||
Def::Upvar(id, ..) |
|
||||
Def::Macro(id) => Some(id),
|
||||
|
||||
Def::Label(..) | Def::PrimTy(..) | Def::SelfTy(..) | Def::Err => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub const CLONE_TRAIT: [&'static str; 3] = ["core", "clone", "Clone"];
|
|||
pub const CMP_MAX: [&'static str; 3] = ["core", "cmp", "max"];
|
||||
pub const CMP_MIN: [&'static str; 3] = ["core", "cmp", "min"];
|
||||
pub const COW: [&'static str; 3] = ["collections", "borrow", "Cow"];
|
||||
pub const CSTRING_NEW: [&'static str; 4] = ["std", "ffi", "CString", "new"];
|
||||
pub const CSTRING_NEW: [&'static str; 5] = ["std", "ffi", "c_str", "CString", "new"];
|
||||
pub const DEBUG_FMT_METHOD: [&'static str; 4] = ["core", "fmt", "Debug", "fmt"];
|
||||
pub const DEFAULT_TRAIT: [&'static str; 3] = ["core", "default", "Default"];
|
||||
pub const DISPLAY_FMT_METHOD: [&'static str; 4] = ["core", "fmt", "Display", "fmt"];
|
||||
|
|
@ -64,6 +64,7 @@ pub const RESULT: [&'static str; 3] = ["core", "result", "Result"];
|
|||
pub const RESULT_ERR: [&'static str; 4] = ["core", "result", "Result", "Err"];
|
||||
pub const RESULT_OK: [&'static str; 4] = ["core", "result", "Result", "Ok"];
|
||||
pub const SERDE_DE_VISITOR: [&'static str; 3] = ["serde", "de", "Visitor"];
|
||||
pub const SLICE_INTO_VEC: [&'static str; 4] = ["collections", "slice", "<impl [T]>", "into_vec"];
|
||||
pub const STRING: [&'static str; 3] = ["collections", "string", "String"];
|
||||
pub const TRANSMUTE: [&'static str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
pub const VEC: [&'static str; 3] = ["collections", "vec", "Vec"];
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
|
|||
impl<'a> Display for Sugg<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
match *self {
|
||||
Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => {
|
||||
s.fmt(f)
|
||||
}
|
||||
Sugg::NonParen(ref s) |
|
||||
Sugg::MaybeParen(ref s) |
|
||||
Sugg::BinOp(_, ref s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -168,10 +168,12 @@ impl<'a> Sugg<'a> {
|
|||
match self {
|
||||
Sugg::NonParen(..) => self,
|
||||
// (x) and (x).y() both don't need additional parens
|
||||
Sugg::MaybeParen(sugg) => if sugg.starts_with('(') && sugg.ends_with(')') {
|
||||
Sugg::MaybeParen(sugg)
|
||||
} else {
|
||||
Sugg::NonParen(format!("({})", sugg).into())
|
||||
Sugg::MaybeParen(sugg) => {
|
||||
if sugg.starts_with('(') && sugg.ends_with(')') {
|
||||
Sugg::MaybeParen(sugg)
|
||||
} else {
|
||||
Sugg::NonParen(format!("({})", sugg).into())
|
||||
}
|
||||
},
|
||||
Sugg::BinOp(_, sugg) => Sugg::NonParen(format!("({})", sugg).into()),
|
||||
}
|
||||
|
|
@ -247,18 +249,17 @@ 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
|
||||
/// `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 {
|
||||
|
|
@ -276,24 +277,12 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg, rhs: &Sugg) -> Sugg<'static> {
|
|||
let lhs = ParenHelper::new(lhs_paren, lhs);
|
||||
let rhs = ParenHelper::new(rhs_paren, rhs);
|
||||
let sugg = match op {
|
||||
AssocOp::Add |
|
||||
AssocOp::BitAnd |
|
||||
AssocOp::BitOr |
|
||||
AssocOp::BitXor |
|
||||
AssocOp::Divide |
|
||||
AssocOp::Equal |
|
||||
AssocOp::Greater |
|
||||
AssocOp::GreaterEqual |
|
||||
AssocOp::LAnd |
|
||||
AssocOp::LOr |
|
||||
AssocOp::Less |
|
||||
AssocOp::LessEqual |
|
||||
AssocOp::Modulus |
|
||||
AssocOp::Multiply |
|
||||
AssocOp::NotEqual |
|
||||
AssocOp::ShiftLeft |
|
||||
AssocOp::ShiftRight |
|
||||
AssocOp::Subtract => format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs),
|
||||
AssocOp::Add | AssocOp::BitAnd | AssocOp::BitOr | AssocOp::BitXor | AssocOp::Divide | AssocOp::Equal |
|
||||
AssocOp::Greater | AssocOp::GreaterEqual | AssocOp::LAnd | AssocOp::LOr | AssocOp::Less |
|
||||
AssocOp::LessEqual | AssocOp::Modulus | AssocOp::Multiply | AssocOp::NotEqual | AssocOp::ShiftLeft |
|
||||
AssocOp::ShiftRight | AssocOp::Subtract => {
|
||||
format!("{} {} {}", lhs, op.to_ast_binop().expect("Those are AST ops").to_string(), rhs)
|
||||
},
|
||||
AssocOp::Inplace => format!("in ({}) {}", lhs, rhs),
|
||||
AssocOp::Assign => format!("{} = {}", lhs, rhs),
|
||||
AssocOp::AssignOp(op) => format!("{} {}= {}", lhs, binop_to_string(op), rhs),
|
||||
|
|
@ -335,11 +324,10 @@ fn associativity(op: &AssocOp) -> Associativity {
|
|||
|
||||
match *op {
|
||||
Inplace | Assign | AssignOp(_) => Associativity::Right,
|
||||
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply |
|
||||
As | Colon => Associativity::Both,
|
||||
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft |
|
||||
ShiftRight | Subtract => Associativity::Left,
|
||||
DotDot | DotDotDot => Associativity::None
|
||||
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
|
||||
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight |
|
||||
Subtract => Associativity::Left,
|
||||
DotDot | DotDotDot => Associativity::None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -384,7 +372,7 @@ fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
|
|||
}
|
||||
|
||||
/// Return the indentation before `span` if there are nothing but `[ \t]` before it on its line.
|
||||
fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
||||
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((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
|
||||
|
|
@ -403,17 +391,17 @@ fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
|
|||
}
|
||||
|
||||
/// Convenience extension trait for `DiagnosticBuilder`.
|
||||
pub trait DiagnosticBuilderExt<T: LintContext> {
|
||||
pub trait DiagnosticBuilderExt<'a, T: LintContext<'a>> {
|
||||
/// Suggests to add an attribute to an item.
|
||||
///
|
||||
/// Correctly handles indentation of the attribute and item.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// db.suggest_item_with_attr(cx, item, "#[derive(Default)]");
|
||||
/// ```
|
||||
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D);
|
||||
fn suggest_item_with_attr<D: Display + ?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D);
|
||||
|
||||
/// Suggest to add an item before another.
|
||||
///
|
||||
|
|
@ -421,7 +409,7 @@ pub trait DiagnosticBuilderExt<T: LintContext> {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// db.suggest_prepend_item(cx, item,
|
||||
/// "fn foo() {
|
||||
/// bar();
|
||||
|
|
@ -430,13 +418,10 @@ pub trait DiagnosticBuilderExt<T: LintContext> {
|
|||
fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str);
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'b> {
|
||||
fn suggest_item_with_attr<D: Display+?Sized>(&mut self, cx: &T, item: Span, msg: &str, attr: &D) {
|
||||
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));
|
||||
}
|
||||
|
|
@ -444,20 +429,19 @@ impl<'a, 'b, T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::Diagnosti
|
|||
|
||||
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().map(|l| {
|
||||
if first {
|
||||
first = false;
|
||||
format!("{}\n", l)
|
||||
} else {
|
||||
format!("{}{}\n", indent, l)
|
||||
}
|
||||
}).collect::<String>();
|
||||
let new_item = new_item.lines()
|
||||
.map(|l| {
|
||||
if first {
|
||||
first = false;
|
||||
format!("{}\n", l)
|
||||
} else {
|
||||
format!("{}{}\n", indent, l)
|
||||
}
|
||||
})
|
||||
.collect::<String>();
|
||||
|
||||
self.span_suggestion(span, msg, format!("{}\n{}", new_item, indent));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
// search for `&vec![_]` expressions where the adjusted type is `&[_]`
|
||||
if_let_chain!{[
|
||||
let ty::TypeVariants::TyRef(_, ref ty) = cx.tcx.tables().expr_ty_adjusted(expr).sty,
|
||||
|
|
@ -65,7 +65,7 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
higher::VecArgs::Vec(args) => {
|
||||
if let Some(last) = args.iter().last() {
|
||||
let span = Span {
|
||||
|
|
@ -78,7 +78,7 @@ fn check_vec_macro(cx: &LateContext, vec_args: &higher::VecArgs, span: Span) {
|
|||
} else {
|
||||
"&[]".into()
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, USELESS_VEC, span, "useless use of `vec!`", |db| {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ impl LintPass for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
impl LateLintPass for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
// check for instances of 0.0/0.0
|
||||
if_let_chain! {[
|
||||
let ExprBinary(ref op, ref left, ref right) = expr.node,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
max_width = 120
|
||||
ideal_width = 100
|
||||
fn_args_density = "Compressed"
|
||||
fn_call_width = 80
|
||||
fn_args_paren_newline = false
|
||||
match_block_trailing_comma = true
|
||||
fn_args_layout = "Block"
|
||||
closure_block_indent_threshold = 0
|
||||
fn_return_indent = "WithWhereClause"
|
||||
wrap_comments = true
|
||||
|
|
|
|||
12
src/lib.rs
12
src/lib.rs
|
|
@ -2,7 +2,6 @@
|
|||
#![feature(plugin_registrar)]
|
||||
#![feature(rustc_private)]
|
||||
#![allow(unknown_lints)]
|
||||
#![feature(borrow_state)]
|
||||
#![allow(missing_docs_in_private_items)]
|
||||
|
||||
extern crate rustc_plugin;
|
||||
|
|
@ -12,11 +11,14 @@ extern crate clippy_lints;
|
|||
|
||||
#[plugin_registrar]
|
||||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
if reg.sess.lint_store.borrow_state() == std::cell::BorrowState::Unused && reg.sess.lint_store.borrow().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();
|
||||
} else {
|
||||
clippy_lints::register_plugins(reg);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
clippy_lints::register_plugins(reg);
|
||||
}
|
||||
|
||||
// only exists to let the dogfood integration test works.
|
||||
|
|
|
|||
71
src/main.rs
71
src/main.rs
|
|
@ -38,32 +38,35 @@ impl ClippyCompilerCalls {
|
|||
}
|
||||
|
||||
impl<'a> CompilerCalls<'a> for ClippyCompilerCalls {
|
||||
fn early_callback(&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sopts: &config::Options,
|
||||
cfg: &ast::CrateConfig,
|
||||
descriptions: &rustc_errors::registry::Registry,
|
||||
output: ErrorOutputType)
|
||||
-> Compilation {
|
||||
fn early_callback(
|
||||
&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sopts: &config::Options,
|
||||
cfg: &ast::CrateConfig,
|
||||
descriptions: &rustc_errors::registry::Registry,
|
||||
output: ErrorOutputType
|
||||
) -> Compilation {
|
||||
self.default.early_callback(matches, sopts, cfg, descriptions, output)
|
||||
}
|
||||
fn no_input(&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sopts: &config::Options,
|
||||
cfg: &ast::CrateConfig,
|
||||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>,
|
||||
descriptions: &rustc_errors::registry::Registry)
|
||||
-> Option<(Input, Option<PathBuf>)> {
|
||||
fn no_input(
|
||||
&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sopts: &config::Options,
|
||||
cfg: &ast::CrateConfig,
|
||||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>,
|
||||
descriptions: &rustc_errors::registry::Registry
|
||||
) -> Option<(Input, Option<PathBuf>)> {
|
||||
self.default.no_input(matches, sopts, cfg, odir, ofile, descriptions)
|
||||
}
|
||||
fn late_callback(&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sess: &Session,
|
||||
input: &Input,
|
||||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>)
|
||||
-> Compilation {
|
||||
fn late_callback(
|
||||
&mut self,
|
||||
matches: &getopts::Matches,
|
||||
sess: &Session,
|
||||
input: &Input,
|
||||
odir: &Option<PathBuf>,
|
||||
ofile: &Option<PathBuf>
|
||||
) -> Compilation {
|
||||
self.default.late_callback(matches, sess, input, odir, ofile)
|
||||
}
|
||||
fn build_controller(&mut self, sess: &Session, matches: &getopts::Matches) -> driver::CompileController<'a> {
|
||||
|
|
@ -73,7 +76,12 @@ 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);
|
||||
|
||||
|
|
@ -153,7 +161,7 @@ pub fn main() {
|
|||
if env::var("CLIPPY_DOGFOOD").map(|_| true).unwrap_or(false) {
|
||||
panic!("yummy");
|
||||
}
|
||||
|
||||
|
||||
// Check for version and help flags even when invoked as 'cargo-clippy'
|
||||
if std::env::args().any(|a| a == "--help" || a == "-h") {
|
||||
show_help();
|
||||
|
|
@ -184,14 +192,16 @@ pub fn main() {
|
|||
|
||||
let current_dir = std::env::current_dir();
|
||||
|
||||
let package_index = metadata.packages.iter()
|
||||
let package_index = metadata.packages
|
||||
.iter()
|
||||
.position(|package| {
|
||||
let package_manifest_path = Path::new(&package.manifest_path);
|
||||
if let Some(ref manifest_path) = manifest_path {
|
||||
package_manifest_path == manifest_path
|
||||
} else {
|
||||
let current_dir = current_dir.as_ref().expect("could not read current directory");
|
||||
let package_manifest_directory = package_manifest_path.parent().expect("could not find parent directory of package manifest");
|
||||
let package_manifest_directory = package_manifest_path.parent()
|
||||
.expect("could not find parent directory of package manifest");
|
||||
package_manifest_directory == current_dir
|
||||
}
|
||||
})
|
||||
|
|
@ -205,7 +215,8 @@ 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), &dep_path) {
|
||||
if let Err(code) = process(vec![format!("--{}", first), target.name].into_iter().chain(args),
|
||||
&dep_path) {
|
||||
std::process::exit(code);
|
||||
}
|
||||
}
|
||||
|
|
@ -285,8 +296,10 @@ fn process<P, I>(old_args: I, dep_path: P) -> Result<(), i32>
|
|||
let exit_status = std::process::Command::new("cargo")
|
||||
.args(&args)
|
||||
.env("RUSTC", path)
|
||||
.spawn().expect("could not run cargo")
|
||||
.wait().expect("failed to wait for cargo?");
|
||||
.spawn()
|
||||
.expect("could not run cargo")
|
||||
.wait()
|
||||
.expect("failed to wait for cargo?");
|
||||
|
||||
if exit_status.success() {
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ fn main() {
|
|||
let u: u32 = 42;
|
||||
|
||||
u <= 0;
|
||||
//~^ ERROR this comparison involving the minimum or maximum element for this type contains a case that is always true or always false
|
||||
//~^ ERROR this comparison involving the minimum or maximum element for this type contains a
|
||||
//~| HELP using u == 0 instead
|
||||
u <= Z;
|
||||
//~^ ERROR this comparison involving
|
||||
|
|
@ -41,10 +41,10 @@ fn main() {
|
|||
//~| HELP because 1-1 is the minimum value for this type, this comparison is always false
|
||||
u >= !0;
|
||||
//~^ ERROR this comparison involving
|
||||
//~| HELP because !0 is the maximum value for this type, the case where the two sides are not equal never occurs, consider using u == !0 instead
|
||||
//~| HELP consider using u == !0 instead
|
||||
u <= 12 - 2*6;
|
||||
//~^ ERROR this comparison involving
|
||||
//~| HELP because 12 - 2*6 is the minimum value for this type, the case where the two sides are not equal never occurs, consider using u == 12 - 2*6 instead
|
||||
//~| HELP consider using u == 12 - 2*6 instead
|
||||
|
||||
let i: i8 = 0;
|
||||
i < -127 - 1;
|
||||
|
|
|
|||
|
|
@ -34,3 +34,24 @@ fn main() {
|
|||
a %= 42 % a;
|
||||
a <<= 6 << a;
|
||||
}
|
||||
|
||||
// check that we don't lint on op assign impls, because that's just the way to impl them
|
||||
|
||||
use std::ops::{Mul, MulAssign};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Wrap(i64);
|
||||
|
||||
impl Mul<i64> for Wrap {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: i64) -> Self {
|
||||
Wrap(self.0 * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<i64> for Wrap {
|
||||
fn mul_assign(&mut self, rhs: i64) {
|
||||
*self = *self * rhs
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@ macro_rules! blocky_too {
|
|||
fn macro_if() {
|
||||
if blocky!() {
|
||||
}
|
||||
|
||||
|
||||
if blocky_too!() {
|
||||
}
|
||||
}
|
||||
|
||||
fn condition_has_block() -> i32 {
|
||||
if { //~ERROR in an 'if' condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a 'let'
|
||||
if { //~ERROR in an 'if' condition, avoid complex blocks or closures with blocks;
|
||||
let x = 3;
|
||||
x == 3
|
||||
} {
|
||||
|
|
@ -55,12 +55,12 @@ fn pred_test() {
|
|||
// this is a sneaky case, where the block isn't directly in the condition, but is actually
|
||||
// inside a closure that the condition is using. same principle applies. add some extra
|
||||
// expressions to make sure linter isn't confused by them.
|
||||
if v == 3 && sky == "blue" && predicate(|x| { let target = 3; x == target }, v) { //~ERROR in an 'if' condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a 'let'
|
||||
|
||||
if v == 3 && sky == "blue" && predicate(|x| { let target = 3; x == target }, v) {
|
||||
//~^ERROR in an 'if' condition, avoid complex blocks or closures with blocks;
|
||||
}
|
||||
|
||||
if predicate(|x| { let target = 3; x == target }, v) { //~ERROR in an 'if' condition, avoid complex blocks or closures with blocks; instead, move the block or closure higher and bind it with a 'let'
|
||||
|
||||
if predicate(|x| { let target = 3; x == target }, v) {
|
||||
//~^ERROR in an 'if' condition, avoid complex blocks or closures with blocks;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue