mirror of
https://github.com/rust-lang/rust-clippy
synced 2025-02-17 06:28:42 +00:00
Merge branch 'master' into fix-3739
This commit is contained in:
commit
6937d5581a
233 changed files with 8354 additions and 7185 deletions
35
CHANGELOG.md
35
CHANGELOG.md
|
@ -4,7 +4,38 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## Unreleased / In Rust Beta or Nightly
|
||||
|
||||
[b2601be...master](https://github.com/rust-lang/rust-clippy/compare/b2601be...master)
|
||||
[1b89724...master](https://github.com/rust-lang/rust-clippy/compare/1b89724...master)
|
||||
|
||||
## Rust 1.33 (2019-02-26)
|
||||
|
||||
[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
|
||||
|
||||
* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
|
||||
* The `rust-clippy` repository is now part of the `rust-lang` org.
|
||||
* Rename `stutter` to `module_name_repetitions`
|
||||
* Merge `new_without_default_derive` into `new_without_default` lint
|
||||
* Move `large_digit_groups` from `style` group to `pedantic`
|
||||
* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
|
||||
comparisons against booleans
|
||||
* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
|
||||
* Expand `redundant_clone` to work on struct fields
|
||||
* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
|
||||
* Expand `use_self` to work on tuple structs and also in local macros
|
||||
* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
|
||||
* Fix false positives in `implicit_return`
|
||||
* Fix false positives in `use_self`
|
||||
* Fix false negative in `clone_on_copy`
|
||||
* Fix false positive in `doc_markdown`
|
||||
* Fix false positive in `empty_loop`
|
||||
* Fix false positive in `if_same_then_else`
|
||||
* Fix false positive in `infinite_iter`
|
||||
* Fix false positive in `question_mark`
|
||||
* Fix false positive in `useless_asref`
|
||||
* Fix false positive in `wildcard_dependencies`
|
||||
* Fix false positive in `write_with_newline`
|
||||
* Add suggestion to `explicit_write`
|
||||
* Improve suggestions for `question_mark` lint
|
||||
* Fix incorrect suggestion for `get_unwrap`
|
||||
|
||||
## Rust 1.32 (2019-01-17)
|
||||
|
||||
|
@ -767,11 +798,11 @@ All notable changes to this project will be documented in this file.
|
|||
[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
|
||||
[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
|
||||
[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
|
||||
[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
|
||||
[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
|
||||
[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
|
||||
[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
|
||||
[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
|
||||
[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
|
||||
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
|
||||
|
|
117
CONTRIBUTING.md
117
CONTRIBUTING.md
|
@ -14,11 +14,6 @@ All contributors are expected to follow the [Rust Code of Conduct](http://www.ru
|
|||
* [Getting started](#getting-started)
|
||||
* [Finding something to fix/improve](#finding-something-to-fiximprove)
|
||||
* [Writing code](#writing-code)
|
||||
* [Author lint](#author-lint)
|
||||
* [Documentation](#documentation)
|
||||
* [Running test suite](#running-test-suite)
|
||||
* [Running rustfmt](#running-rustfmt)
|
||||
* [Testing manually](#testing-manually)
|
||||
* [How Clippy works](#how-clippy-works)
|
||||
* [Fixing nightly build failures](#fixing-build-failures-caused-by-rust)
|
||||
* [Issue and PR Triage](#issue-and-pr-triage)
|
||||
|
@ -73,121 +68,15 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us
|
|||
|
||||
## Writing code
|
||||
|
||||
Clippy depends on the current git master version of rustc, which can change rapidly. Make sure you're
|
||||
working near rust-clippy's master, and use the `setup-toolchain.sh` script to configure the appropriate
|
||||
toolchain for this directory.
|
||||
|
||||
[Llogiq's blog post on lints](https://llogiq.github.io/2015/06/04/workflows.html) is a nice primer
|
||||
to lint-writing, though it does get into advanced stuff. Most lints consist of an implementation of
|
||||
`LintPass` with one or more of its default methods overridden. See the existing lints for examples
|
||||
of this.
|
||||
Have a look at the [docs for writing lints](doc/adding_lints.md) for more details. [Llogiq's blog post on lints](https://llogiq.github.io/2015/06/04/workflows.html) is also a nice primer
|
||||
to lint-writing, though it does get into advanced stuff and may be a bit
|
||||
outdated.
|
||||
|
||||
If you want to add a new lint or change existing ones apart from bugfixing, it's
|
||||
also a good idea to give the [stability guarantees][rfc_stability] and
|
||||
[lint categories][rfc_lint_cats] sections of the [Clippy 1.0 RFC][clippy_rfc] a
|
||||
quick read.
|
||||
|
||||
### Author lint
|
||||
|
||||
There is also the internal `author` lint to generate Clippy code that detects the offending pattern. It does not work for all of the Rust syntax, but can give a good starting point.
|
||||
|
||||
First, create a new UI test file in the `tests/ui/` directory with the pattern you want to match:
|
||||
|
||||
```rust
|
||||
// ./tests/ui/my_lint.rs
|
||||
fn main() {
|
||||
#[clippy::author]
|
||||
let arr: [i32; 1] = [7]; // Replace line with the code you want to match
|
||||
}
|
||||
```
|
||||
|
||||
Now you run `TESTNAME=ui/my_lint cargo uitest` to produce
|
||||
a `.stdout` file with the generated code:
|
||||
|
||||
```rust
|
||||
// ./tests/ui/my_lint.stdout
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Array(ref elements) = stmt.node;
|
||||
if elements.len() == 1;
|
||||
if let ExprKind::Lit(ref lit) = elements[0].node;
|
||||
if let LitKind::Int(7, _) = lit.node;
|
||||
then {
|
||||
// report your lint here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the command was executed successfully, you can copy the code over to where you are implementing your lint.
|
||||
|
||||
### Documentation
|
||||
|
||||
Please document your lint with a doc comment akin to the following:
|
||||
|
||||
```rust
|
||||
/// **What it does:** Checks for ... (describe what the lint matches).
|
||||
///
|
||||
/// **Why is this bad?** Supply the reason for linting the code.
|
||||
///
|
||||
/// **Known problems:** None. (Or describe where it could go wrong.)
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// Insert a short example of code that triggers the lint
|
||||
///
|
||||
/// // Good
|
||||
/// Insert a short example of improved code that doesn't trigger the lint
|
||||
/// ```
|
||||
```
|
||||
|
||||
Once your lint is merged it will show up in the [lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
### Running test suite
|
||||
|
||||
Use `cargo test` to run the whole testsuite.
|
||||
|
||||
If you don't want to wait for all tests to finish, you can also execute a single test file by using `TESTNAME` to specify the test to run:
|
||||
|
||||
```bash
|
||||
TESTNAME=ui/empty_line_after_outer_attr cargo uitest
|
||||
```
|
||||
|
||||
Clippy uses UI tests. UI tests check that the output of the compiler is exactly as expected.
|
||||
Of course there's little sense in writing the output yourself or copying it around.
|
||||
Therefore you should use `tests/ui/update-all-references.sh` (after running
|
||||
`cargo test`) and check whether the output looks as you expect with `git diff`. Commit all
|
||||
`*.stderr` files, too.
|
||||
|
||||
If the lint you are working on is making use of structured suggestions, the
|
||||
test file should include a `// run-rustfix` comment at the top. This will
|
||||
additionally run [rustfix](https://github.com/rust-lang-nursery/rustfix) for
|
||||
that test. Rustfix will apply the suggestions from the lint to the code of the
|
||||
test file and compare that to the contents of a `.fixed` file.
|
||||
|
||||
Use `tests/ui/update-all-references.sh` to automatically generate the
|
||||
`.fixed` file after running `cargo test`.
|
||||
|
||||
### Running rustfmt
|
||||
|
||||
[Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust code according
|
||||
to style guidelines. The code has to be formatted by `rustfmt` before a PR will be merged.
|
||||
|
||||
It can be installed via `rustup`:
|
||||
```bash
|
||||
rustup component add rustfmt
|
||||
```
|
||||
|
||||
Use `cargo fmt --all` to format the whole codebase.
|
||||
|
||||
### Testing manually
|
||||
|
||||
Manually testing against an example file is useful if you have added some
|
||||
`println!`s and test suite output becomes unreadable. To try Clippy with your
|
||||
local modifications, run `env CLIPPY_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs`
|
||||
from the working copy root.
|
||||
|
||||
## How Clippy works
|
||||
|
||||
Clippy is a [rustc compiler plugin][compiler_plugin]. The main entry point is at [`src/lib.rs`][main_entry]. In there, the lint registration is delegated to the [`clippy_lints`][lint_crate] crate.
|
||||
|
|
|
@ -47,7 +47,7 @@ rustc_tools_util = { version = "0.1.1", path = "rustc_tools_util"}
|
|||
[dev-dependencies]
|
||||
clippy_dev = { version = "0.0.1", path = "clippy_dev" }
|
||||
cargo_metadata = "0.7.1"
|
||||
compiletest_rs = "0.3.18"
|
||||
compiletest_rs = "0.3.19"
|
||||
lazy_static = "1.0"
|
||||
serde_derive = "1.0"
|
||||
clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
|
||||
|
|
18
README.md
18
README.md
|
@ -11,14 +11,14 @@ A collection of lints to catch common mistakes and improve your [Rust](https://g
|
|||
|
||||
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
|
||||
|
||||
* `clippy::all` (everything that has no false positives)
|
||||
* `clippy::pedantic` (everything)
|
||||
* `clippy::nursery` (new lints that aren't quite ready yet)
|
||||
* `clippy::all` (everything that is on by default: all the categories below except for `nursery`, `pedantic`, and `cargo`)
|
||||
* **`clippy::correctness`** (code that is just outright wrong or very very useless, causes hard errors by default)
|
||||
* `clippy::style` (code that should be written in a more idiomatic way)
|
||||
* `clippy::complexity` (code that does something simple but in a complex way)
|
||||
* `clippy::perf` (code that can be written in a faster way)
|
||||
* `clippy::cargo` (checks against the cargo manifest)
|
||||
* **`clippy::correctness`** (code that is just outright wrong or very very useless)
|
||||
* `clippy::pedantic` (lints which are rather strict, off by default)
|
||||
* `clippy::nursery` (new lints that aren't quite ready yet, off by default)
|
||||
* `clippy::cargo` (checks against the cargo manifest, off by default)
|
||||
|
||||
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
|
||||
|
||||
|
@ -31,6 +31,8 @@ Only the following of those categories are enabled by default:
|
|||
|
||||
Other categories need to be enabled in order for their lints to be executed.
|
||||
|
||||
The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used very selectively, if at all.
|
||||
|
||||
Table of contents:
|
||||
|
||||
* [Usage instructions](#usage)
|
||||
|
@ -105,13 +107,13 @@ script:
|
|||
- cargo clippy
|
||||
# if you want the build job to fail when encountering warnings, use
|
||||
- cargo clippy -- -D warnings
|
||||
# in order to also check tests and none-default crate features, use
|
||||
# in order to also check tests and non-default crate features, use
|
||||
- cargo clippy --all-targets --all-features -- -D warnings
|
||||
- cargo test
|
||||
# etc.
|
||||
```
|
||||
|
||||
It might happen that Clippy is not available for a certain nightly release.
|
||||
If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
|
||||
In this case you can try to conditionally install Clippy from the git repo.
|
||||
|
||||
```yaml
|
||||
|
@ -129,7 +131,7 @@ Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml
|
|||
|
||||
```toml
|
||||
blacklisted-names = ["toto", "tata", "titi"]
|
||||
cyclomatic-complexity-threshold = 30
|
||||
cognitive-complexity-threshold = 30
|
||||
```
|
||||
|
||||
See the [list of lints](https://rust-lang.github.io/rust-clippy/master/index.html) for more information about which lints can be configured and the
|
||||
|
|
|
@ -4,7 +4,7 @@ echo "Running clippy base tests"
|
|||
|
||||
PATH=$PATH:./node_modules/.bin
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
remark -f *.md > /dev/null
|
||||
remark -f *.md -f doc/*.md > /dev/null
|
||||
fi
|
||||
# build clippy in debug mode and run tests
|
||||
cargo build --features debugging
|
||||
|
@ -59,7 +59,7 @@ rustup override set nightly
|
|||
# avoid loop spam and allow cmds with exit status != 0
|
||||
set +ex
|
||||
|
||||
for file in `find tests -not -path "tests/ui/methods.rs" -not -path "tests/ui/format.rs" -not -path "tests/ui/formatting.rs" -not -path "tests/ui/empty_line_after_outer_attribute.rs" -not -path "tests/ui/double_parens.rs" -not -path "tests/ui/doc.rs" -not -path "tests/ui/unused_unit.rs" | grep "\.rs$"` ; do
|
||||
for file in `find tests | grep "\.rs$"` ; do
|
||||
rustfmt ${file} --check
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "${file} needs reformatting!"
|
||||
|
|
|
@ -12,8 +12,9 @@ use walkdir::WalkDir;
|
|||
lazy_static! {
|
||||
static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new(
|
||||
r#"(?x)
|
||||
declare_clippy_lint!\s*[\{(]\s*
|
||||
pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
|
||||
declare_clippy_lint!\s*[\{(]
|
||||
(?:\s+///.*)*
|
||||
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
|
||||
(?P<cat>[a-z_]+)\s*,\s*
|
||||
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
|
||||
"#
|
||||
|
@ -22,7 +23,8 @@ lazy_static! {
|
|||
static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new(
|
||||
r#"(?x)
|
||||
declare_deprecated_lint!\s*[{(]\s*
|
||||
pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
|
||||
(?:\s+///.*)*
|
||||
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
|
||||
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
|
||||
"#
|
||||
)
|
||||
|
@ -189,7 +191,7 @@ pub struct FileChange {
|
|||
pub new_lines: String,
|
||||
}
|
||||
|
||||
/// Replace a region in a file delimited by two lines matching regexes.
|
||||
/// Replaces a region in a file delimited by two lines matching regexes.
|
||||
///
|
||||
/// `path` is the relative path to the file on which you want to perform the replacement.
|
||||
///
|
||||
|
@ -223,7 +225,7 @@ where
|
|||
file_change
|
||||
}
|
||||
|
||||
/// Replace a region in a text delimited by two lines matching regexes.
|
||||
/// Replaces a region in a text delimited by two lines matching regexes.
|
||||
///
|
||||
/// * `text` is the input text on which you want to perform the replacement
|
||||
/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
|
||||
|
|
|
@ -2,7 +2,7 @@ extern crate clap;
|
|||
extern crate clippy_dev;
|
||||
extern crate regex;
|
||||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use clap::{App, AppSettings, Arg, SubCommand};
|
||||
use clippy_dev::*;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
@ -13,9 +13,11 @@ enum UpdateMode {
|
|||
|
||||
fn main() {
|
||||
let matches = App::new("Clippy developer tooling")
|
||||
.setting(AppSettings::SubcommandRequiredElseHelp)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update_lints")
|
||||
.about(
|
||||
.about("Updates lint registration and information from the source code")
|
||||
.long_about(
|
||||
"Makes sure that:\n \
|
||||
* the lint count in README.md is correct\n \
|
||||
* the changelog contains markdown link references at the bottom\n \
|
||||
|
|
|
@ -6,28 +6,28 @@ use std::f64::consts as f64;
|
|||
use syntax::ast::{FloatTy, Lit, LitKind};
|
||||
use syntax::symbol;
|
||||
|
||||
/// **What it does:** Checks for floating point literals that approximate
|
||||
/// constants which are defined in
|
||||
/// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
|
||||
/// or
|
||||
/// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
|
||||
/// respectively, suggesting to use the predefined constant.
|
||||
///
|
||||
/// **Why is this bad?** Usually, the definition in the standard library is more
|
||||
/// precise than what people come up with. If you find that your definition is
|
||||
/// actually more precise, please [file a Rust
|
||||
/// issue](https://github.com/rust-lang/rust/issues).
|
||||
///
|
||||
/// **Known problems:** If you happen to have a value that is within 1/8192 of a
|
||||
/// known constant, but is not *and should not* be the same, this lint will
|
||||
/// report your value anyway. We have not yet noticed any false positives in
|
||||
/// code we tested clippy with (this includes servo), but YMMV.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 3.14;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for floating point literals that approximate
|
||||
/// constants which are defined in
|
||||
/// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
|
||||
/// or
|
||||
/// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
|
||||
/// respectively, suggesting to use the predefined constant.
|
||||
///
|
||||
/// **Why is this bad?** Usually, the definition in the standard library is more
|
||||
/// precise than what people come up with. If you find that your definition is
|
||||
/// actually more precise, please [file a Rust
|
||||
/// issue](https://github.com/rust-lang/rust/issues).
|
||||
///
|
||||
/// **Known problems:** If you happen to have a value that is within 1/8192 of a
|
||||
/// known constant, but is not *and should not* be the same, this lint will
|
||||
/// report your value anyway. We have not yet noticed any false positives in
|
||||
/// code we tested clippy with (this includes servo), but YMMV.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 3.14;
|
||||
/// ```
|
||||
pub APPROX_CONSTANT,
|
||||
correctness,
|
||||
"the approximate of a known float constant (in `std::fXX::consts`)"
|
||||
|
@ -104,7 +104,7 @@ fn check_known_consts(cx: &LateContext<'_, '_>, e: &Expr, s: symbol::Symbol, mod
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns false if the number of significant figures in `value` are
|
||||
/// Returns `false` if the number of significant figures in `value` are
|
||||
/// less than `min_digits`; otherwise, returns true if `value` is equal
|
||||
/// to `constant`, rounded to the number of digits present in `value`.
|
||||
fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
|
||||
|
|
|
@ -5,36 +5,36 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for plain integer arithmetic.
|
||||
///
|
||||
/// **Why is this bad?** This is only checked against overflow in debug builds.
|
||||
/// In some applications one wants explicitly checked, wrapping or saturating
|
||||
/// arithmetic.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for plain integer arithmetic.
|
||||
///
|
||||
/// **Why is this bad?** This is only checked against overflow in debug builds.
|
||||
/// In some applications one wants explicitly checked, wrapping or saturating
|
||||
/// arithmetic.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1
|
||||
/// ```
|
||||
pub INTEGER_ARITHMETIC,
|
||||
restriction,
|
||||
"any integer arithmetic statement"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for float arithmetic.
|
||||
///
|
||||
/// **Why is this bad?** For some embedded systems or kernel development, it
|
||||
/// can be useful to rule out floating-point numbers.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1.0
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for float arithmetic.
|
||||
///
|
||||
/// **Why is this bad?** For some embedded systems or kernel development, it
|
||||
/// can be useful to rule out floating-point numbers.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1.0
|
||||
/// ```
|
||||
pub FLOAT_ARITHMETIC,
|
||||
restriction,
|
||||
"any floating-point arithmetic statement"
|
||||
|
|
|
@ -1,31 +1,32 @@
|
|||
use if_chain::if_chain;
|
||||
use rustc::hir::{Expr, ExprKind};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::rustc::hir::{Expr, ExprKind};
|
||||
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use crate::rustc::{declare_tool_lint, lint_array};
|
||||
use crate::syntax::ast::LitKind;
|
||||
use crate::utils::{in_macro, is_direct_expn_of, span_help_and_lint};
|
||||
use if_chain::if_chain;
|
||||
|
||||
/// **What it does:** Check to call assert!(true/false)
|
||||
///
|
||||
/// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
|
||||
/// panic!() or unreachable!()
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// assert!(false)
|
||||
/// // or
|
||||
/// assert!(true)
|
||||
/// // or
|
||||
/// const B: bool = false;
|
||||
/// assert!(B)
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
|
||||
///
|
||||
/// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
|
||||
/// panic!() or unreachable!()
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// assert!(false)
|
||||
/// // or
|
||||
/// assert!(true)
|
||||
/// // or
|
||||
/// const B: bool = false;
|
||||
/// assert!(B)
|
||||
/// ```
|
||||
pub ASSERTIONS_ON_CONSTANTS,
|
||||
style,
|
||||
"assert!(true/false) will be optimized out by the compiler/should probably be replaced by a panic!() or unreachable!()"
|
||||
"`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
|
||||
}
|
||||
|
||||
pub struct AssertionsOnConstants;
|
||||
|
|
|
@ -1,50 +1,53 @@
|
|||
use crate::utils::{get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, SpanlessEq};
|
||||
use crate::utils::{higher, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir;
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast;
|
||||
|
||||
/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
|
||||
/// patterns.
|
||||
///
|
||||
/// **Why is this bad?** These can be written as the shorter `a op= b`.
|
||||
///
|
||||
/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
|
||||
/// implementations that differ from the regular `Op` impl.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// ...
|
||||
/// a = a + b;
|
||||
/// ```
|
||||
use crate::utils::{
|
||||
get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method, SpanlessEq,
|
||||
};
|
||||
use crate::utils::{higher, sugg};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `a = a op b` or `a = b commutative_op a`
|
||||
/// patterns.
|
||||
///
|
||||
/// **Why is this bad?** These can be written as the shorter `a op= b`.
|
||||
///
|
||||
/// **Known problems:** While forbidden by the spec, `OpAssign` traits may have
|
||||
/// implementations that differ from the regular `Op` impl.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// let b = 0;
|
||||
/// // ...
|
||||
/// a = a + b;
|
||||
/// ```
|
||||
pub ASSIGN_OP_PATTERN,
|
||||
style,
|
||||
"assigning the result of an operation on a variable to that same variable"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
|
||||
///
|
||||
/// **Why is this bad?** Most likely these are bugs where one meant to write `a
|
||||
/// op= b`.
|
||||
///
|
||||
/// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
|
||||
/// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore it suggests both.
|
||||
/// If `a op= a op b` is really the correct behaviour it should be
|
||||
/// written as `a = a op a op b` as it's less confusing.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// ...
|
||||
/// a += a + b;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `a op= a op b` or `a op= b op a` patterns.
|
||||
///
|
||||
/// **Why is this bad?** Most likely these are bugs where one meant to write `a
|
||||
/// op= b`.
|
||||
///
|
||||
/// **Known problems:** Clippy cannot know for sure if `a op= a op b` should have
|
||||
/// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
|
||||
/// If `a op= a op b` is really the correct behaviour it should be
|
||||
/// written as `a = a op a op b` as it's less confusing.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// ...
|
||||
/// a += a + b;
|
||||
/// ```
|
||||
pub MISREFACTORED_ASSIGN_OP,
|
||||
complexity,
|
||||
"having a variable on both sides of an assign op"
|
||||
|
@ -69,58 +72,22 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
|||
match &expr.node {
|
||||
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||
if let hir::ExprKind::Binary(binop, l, r) = &rhs.node {
|
||||
if op.node == binop.node {
|
||||
let lint = |assignee: &hir::Expr, rhs_other: &hir::Expr| {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
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_other.span))
|
||||
{
|
||||
let a = &sugg::Sugg::hir(cx, assignee, "..");
|
||||
let r = &sugg::Sugg::hir(cx, rhs, "..");
|
||||
let long =
|
||||
format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
"Did you mean {} = {} {} {} or {}? Consider replacing it with",
|
||||
snip_a,
|
||||
snip_a,
|
||||
op.node.as_str(),
|
||||
snip_r,
|
||||
long
|
||||
),
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
"or",
|
||||
long,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
// lhs op= l op r
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
|
||||
lint(lhs, r);
|
||||
}
|
||||
// lhs op= l commutative_op r
|
||||
if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
|
||||
lint(lhs, l);
|
||||
}
|
||||
if op.node != binop.node {
|
||||
return;
|
||||
}
|
||||
// lhs op= l op r
|
||||
if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, l) {
|
||||
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
|
||||
}
|
||||
// lhs op= l commutative_op r
|
||||
if is_commutative(op.node) && SpanlessEq::new(cx).ignore_fn().eq_expr(lhs, r) {
|
||||
lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
|
||||
}
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Assign(assignee, e) => {
|
||||
if let hir::ExprKind::Binary(op, l, r) = &e.node {
|
||||
#[allow(clippy::cyclomatic_complexity)]
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
let lint = |assignee: &hir::Expr, rhs: &hir::Expr| {
|
||||
let ty = cx.tables.expr_ty(assignee);
|
||||
let rty = cx.tables.expr_ty(rhs);
|
||||
|
@ -140,14 +107,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
|||
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.hir().get_parent(e.id);
|
||||
let parent_impl = cx.tcx.hir().get_parent(parent_fn);
|
||||
// the crate node is the only one that is not in the map
|
||||
let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
|
||||
if_chain! {
|
||||
if parent_impl != ast::CRATE_NODE_ID;
|
||||
if let hir::Node::Item(item) = cx.tcx.hir().get(parent_impl);
|
||||
if let hir::ItemKind::Impl(_, _, _, _, Some(trait_ref), _, _) =
|
||||
&item.node;
|
||||
if let Some(trait_ref) = trait_ref_of_method(cx, parent_fn);
|
||||
if trait_ref.path.def.def_id() == trait_id;
|
||||
then { return; }
|
||||
}
|
||||
|
@ -235,6 +197,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssignOps {
|
|||
}
|
||||
}
|
||||
|
||||
fn lint_misrefactored_assign_op(
|
||||
cx: &LateContext<'_, '_>,
|
||||
expr: &hir::Expr,
|
||||
op: hir::BinOp,
|
||||
rhs: &hir::Expr,
|
||||
assignee: &hir::Expr,
|
||||
rhs_other: &hir::Expr,
|
||||
) {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
MISREFACTORED_ASSIGN_OP,
|
||||
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_other.span)) {
|
||||
let a = &sugg::Sugg::hir(cx, assignee, "..");
|
||||
let r = &sugg::Sugg::hir(cx, rhs, "..");
|
||||
let long = format!("{} = {}", snip_a, sugg::make_binop(higher::binop(op.node), a, r));
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
&format!(
|
||||
"Did you mean {} = {} {} {} or {}? Consider replacing it with",
|
||||
snip_a,
|
||||
snip_a,
|
||||
op.node.as_str(),
|
||||
snip_r,
|
||||
long
|
||||
),
|
||||
format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
"or",
|
||||
long,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn is_commutative(op: hir::BinOpKind) -> bool {
|
||||
use rustc::hir::BinOpKind::*;
|
||||
match op {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
use crate::reexport::*;
|
||||
use crate::utils::{
|
||||
in_macro, last_line_of_span, match_def_path, opt_def_id, paths, snippet_opt, span_lint, span_lint_and_sugg,
|
||||
span_lint_and_then, without_block_comments,
|
||||
in_macro, last_line_of_span, match_def_path, paths, snippet_opt, span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
without_block_comments,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
|
@ -17,170 +17,170 @@ use semver::Version;
|
|||
use syntax::ast::{AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem, NestedMetaItemKind};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
///
|
||||
/// **Why is this bad?** While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// **Known problems:** False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
///
|
||||
/// **Why is this bad?** While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// **Known problems:** False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```
|
||||
pub INLINE_ALWAYS,
|
||||
pedantic,
|
||||
"use of `#[inline(always)]`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint whitelists `#[allow(unused_imports)]` and `#[allow(deprecated)]` on
|
||||
/// `use` items and `#[allow(unused_imports)]` on `extern crate` items with a
|
||||
/// `#[macro_use]` attribute.
|
||||
///
|
||||
/// **Why is this bad?** Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
///
|
||||
/// // Ok
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint whitelists `#[allow(unused_imports)]` and `#[allow(deprecated)]` on
|
||||
/// `use` items and `#[allow(unused_imports)]` on `extern crate` items with a
|
||||
/// `#[macro_use]` attribute.
|
||||
///
|
||||
/// **Why is this bad?** Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // Bad
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
///
|
||||
/// // Ok
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// ```
|
||||
pub USELESS_ATTRIBUTE,
|
||||
correctness,
|
||||
"use of lint attributes on `extern crate` items"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version.
|
||||
///
|
||||
/// **Why is this bad?** For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else(..) { ... }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version.
|
||||
///
|
||||
/// **Why is this bad?** For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else() { /* ... */ }
|
||||
/// ```
|
||||
pub DEPRECATED_SEMVER,
|
||||
correctness,
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for empty lines after outer attributes
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// Most likely the attribute was meant to be an inner attribute using a '!'.
|
||||
/// If it was meant to be an outer attribute, then the following item
|
||||
/// should not be separated by empty lines.
|
||||
///
|
||||
/// **Known problems:** Can cause false positives.
|
||||
///
|
||||
/// From the clippy side it's difficult to detect empty lines between an attributes and the
|
||||
/// following item because empty lines and comments are not part of the AST. The parsing
|
||||
/// currently works for basic cases but is not perfect.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// #[inline(always)]
|
||||
///
|
||||
/// fn not_quite_good_code(..) { ... }
|
||||
///
|
||||
/// // Good (as inner attribute)
|
||||
/// #![inline(always)]
|
||||
///
|
||||
/// fn this_is_fine(..) { ... }
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[inline(always)]
|
||||
/// fn this_is_fine_too(..) { ... }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for empty lines after outer attributes
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// Most likely the attribute was meant to be an inner attribute using a '!'.
|
||||
/// If it was meant to be an outer attribute, then the following item
|
||||
/// should not be separated by empty lines.
|
||||
///
|
||||
/// **Known problems:** Can cause false positives.
|
||||
///
|
||||
/// From the clippy side it's difficult to detect empty lines between an attributes and the
|
||||
/// following item because empty lines and comments are not part of the AST. The parsing
|
||||
/// currently works for basic cases but is not perfect.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// #[inline(always)]
|
||||
///
|
||||
/// fn not_quite_good_code(..) { ... }
|
||||
///
|
||||
/// // Good (as inner attribute)
|
||||
/// #![inline(always)]
|
||||
///
|
||||
/// fn this_is_fine(..) { ... }
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[inline(always)]
|
||||
/// fn this_is_fine_too(..) { ... }
|
||||
/// ```
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
nursery,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
|
||||
/// lints and if those lints exist in clippy. If there is a uppercase letter in the lint name
|
||||
/// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
|
||||
/// the lint name.
|
||||
///
|
||||
/// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #![warn(if_not_els)]
|
||||
/// #![deny(clippy::All)]
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// #![warn(if_not_else)]
|
||||
/// #![deny(clippy::all)]
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `allow`/`warn`/`deny`/`forbid` attributes with scoped clippy
|
||||
/// lints and if those lints exist in clippy. If there is a uppercase letter in the lint name
|
||||
/// (not the tool name) and a lowercase version of this lint exists, it will suggest to lowercase
|
||||
/// the lint name.
|
||||
///
|
||||
/// **Why is this bad?** A lint attribute with a mistyped lint name won't have an effect.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #![warn(if_not_els)]
|
||||
/// #![deny(clippy::All)]
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// #![warn(if_not_else)]
|
||||
/// #![deny(clippy::all)]
|
||||
/// ```
|
||||
pub UNKNOWN_CLIPPY_LINTS,
|
||||
style,
|
||||
"unknown_lints for scoped Clippy lints"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
|
||||
/// with `#[rustfmt::skip]`.
|
||||
///
|
||||
/// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
|
||||
/// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
|
||||
///
|
||||
/// **Known problems:** This lint doesn't detect crate level inner attributes, because they get
|
||||
/// processed before the PreExpansionPass lints get executed. See
|
||||
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// #[rustfmt::skip]
|
||||
/// fn main() { }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
|
||||
/// with `#[rustfmt::skip]`.
|
||||
///
|
||||
/// **Why is this bad?** Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
|
||||
/// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
|
||||
///
|
||||
/// **Known problems:** This lint doesn't detect crate level inner attributes, because they get
|
||||
/// processed before the PreExpansionPass lints get executed. See
|
||||
/// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// #[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
/// fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// #[rustfmt::skip]
|
||||
/// fn main() { }
|
||||
/// ```
|
||||
pub DEPRECATED_CFG_ATTR,
|
||||
complexity,
|
||||
"usage of `cfg_attr(rustfmt)` instead of `tool_attributes`"
|
||||
|
@ -396,7 +396,7 @@ fn is_relevant_expr(tcx: TyCtxt<'_, '_, '_>, tables: &ty::TypeckTables<'_>, expr
|
|||
ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
|
||||
ExprKind::Call(path_expr, _) => {
|
||||
if let ExprKind::Path(qpath) = &path_expr.node {
|
||||
if let Some(fun_id) = opt_def_id(tables.qpath_def(qpath, path_expr.hir_id)) {
|
||||
if let Some(fun_id) = tables.qpath_def(qpath, path_expr.hir_id).opt_def_id() {
|
||||
!match_def_path(tcx, fun_id, &paths::BEGIN_PANIC)
|
||||
} else {
|
||||
true
|
||||
|
|
|
@ -9,83 +9,83 @@ use rustc_errors::Applicability;
|
|||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for incompatible bit masks in comparisons.
|
||||
///
|
||||
/// The formula for detecting if an expression of the type `_ <bit_op> m
|
||||
/// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
|
||||
/// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
|
||||
/// table:
|
||||
///
|
||||
/// |Comparison |Bit Op|Example |is always|Formula |
|
||||
/// |------------|------|------------|---------|----------------------|
|
||||
/// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
|
||||
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
|
||||
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
|
||||
/// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
|
||||
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
|
||||
/// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
|
||||
///
|
||||
/// **Why is this bad?** If the bits that the comparison cares about are always
|
||||
/// set to zero or one by the bit mask, the comparison is constant `true` or
|
||||
/// `false` (depending on mask, compared value, and operators).
|
||||
///
|
||||
/// So the code is actively misleading, and the only reason someone would write
|
||||
/// this intentionally is to win an underhanded Rust contest or create a
|
||||
/// test-case for this lint.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if (x & 1 == 2) { … }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for incompatible bit masks in comparisons.
|
||||
///
|
||||
/// The formula for detecting if an expression of the type `_ <bit_op> m
|
||||
/// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
|
||||
/// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
|
||||
/// table:
|
||||
///
|
||||
/// |Comparison |Bit Op|Example |is always|Formula |
|
||||
/// |------------|------|------------|---------|----------------------|
|
||||
/// |`==` or `!=`| `&` |`x & 2 == 3`|`false` |`c & m != c` |
|
||||
/// |`<` or `>=`| `&` |`x & 2 < 3` |`true` |`m < c` |
|
||||
/// |`>` or `<=`| `&` |`x & 1 > 1` |`false` |`m <= c` |
|
||||
/// |`==` or `!=`| `|` |`x | 1 == 0`|`false` |`c | m != c` |
|
||||
/// |`<` or `>=`| `|` |`x | 1 < 1` |`false` |`m >= c` |
|
||||
/// |`<=` or `>` | `|` |`x | 1 > 0` |`true` |`m > c` |
|
||||
///
|
||||
/// **Why is this bad?** If the bits that the comparison cares about are always
|
||||
/// set to zero or one by the bit mask, the comparison is constant `true` or
|
||||
/// `false` (depending on mask, compared value, and operators).
|
||||
///
|
||||
/// So the code is actively misleading, and the only reason someone would write
|
||||
/// this intentionally is to win an underhanded Rust contest or create a
|
||||
/// test-case for this lint.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if (x & 1 == 2) { … }
|
||||
/// ```
|
||||
pub BAD_BIT_MASK,
|
||||
correctness,
|
||||
"expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for bit masks in comparisons which can be removed
|
||||
/// without changing the outcome. The basic structure can be seen in the
|
||||
/// following table:
|
||||
///
|
||||
/// |Comparison| Bit Op |Example |equals |
|
||||
/// |----------|---------|-----------|-------|
|
||||
/// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
|
||||
/// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
|
||||
///
|
||||
/// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
|
||||
/// but still a bit misleading, because the bit mask is ineffective.
|
||||
///
|
||||
/// **Known problems:** False negatives: This lint will only match instances
|
||||
/// where we have figured out the math (which is for a power-of-two compared
|
||||
/// value). This means things like `x | 1 >= 7` (which would be better written
|
||||
/// as `x >= 6`) will not be reported (but bit masks like this are fairly
|
||||
/// uncommon).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if (x | 1 > 3) { … }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for bit masks in comparisons which can be removed
|
||||
/// without changing the outcome. The basic structure can be seen in the
|
||||
/// following table:
|
||||
///
|
||||
/// |Comparison| Bit Op |Example |equals |
|
||||
/// |----------|---------|-----------|-------|
|
||||
/// |`>` / `<=`|`|` / `^`|`x | 2 > 3`|`x > 3`|
|
||||
/// |`<` / `>=`|`|` / `^`|`x ^ 1 < 4`|`x < 4`|
|
||||
///
|
||||
/// **Why is this bad?** Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
|
||||
/// but still a bit misleading, because the bit mask is ineffective.
|
||||
///
|
||||
/// **Known problems:** False negatives: This lint will only match instances
|
||||
/// where we have figured out the math (which is for a power-of-two compared
|
||||
/// value). This means things like `x | 1 >= 7` (which would be better written
|
||||
/// as `x >= 6`) will not be reported (but bit masks like this are fairly
|
||||
/// uncommon).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if (x | 1 > 3) { … }
|
||||
/// ```
|
||||
pub INEFFECTIVE_BIT_MASK,
|
||||
correctness,
|
||||
"expressions where a bit mask will be rendered useless by a comparison, e.g. `(x | 1) > 2`"
|
||||
"expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for bit masks that can be replaced by a call
|
||||
/// to `trailing_zeros`
|
||||
///
|
||||
/// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
|
||||
/// == 0`
|
||||
///
|
||||
/// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x & 0x1111 == 0
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for bit masks that can be replaced by a call
|
||||
/// to `trailing_zeros`
|
||||
///
|
||||
/// **Why is this bad?** `x.trailing_zeros() > 4` is much clearer than `x & 15
|
||||
/// == 0`
|
||||
///
|
||||
/// **Known problems:** llvm generates better code for `x & 15 == 0` on x86
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// x & 0x1111 == 0
|
||||
/// ```
|
||||
pub VERBOSE_BIT_MASK,
|
||||
style,
|
||||
"expressions where a bit mask is less readable than the corresponding method call"
|
||||
|
|
|
@ -4,19 +4,19 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
|
||||
/// **What it does:** Checks for usage of blacklisted names for variables, such
|
||||
/// as `foo`.
|
||||
///
|
||||
/// **Why is this bad?** These names are usually placeholder names and should be
|
||||
/// avoided.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let foo = 3.14;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of blacklisted names for variables, such
|
||||
/// as `foo`.
|
||||
///
|
||||
/// **Why is this bad?** These names are usually placeholder names and should be
|
||||
/// avoided.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let foo = 3.14;
|
||||
/// ```
|
||||
pub BLACKLISTED_NAME,
|
||||
style,
|
||||
"usage of a blacklisted/placeholder name"
|
||||
|
|
|
@ -5,41 +5,41 @@ use rustc::hir::*;
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for `if` conditions that use blocks to contain an
|
||||
/// expression.
|
||||
///
|
||||
/// **Why is this bad?** It isn't really Rust style, same as using parentheses
|
||||
/// to contain expressions.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if { true } ..
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `if` conditions that use blocks to contain an
|
||||
/// expression.
|
||||
///
|
||||
/// **Why is this bad?** It isn't really Rust style, same as using parentheses
|
||||
/// to contain expressions.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if { true } { /* ... */ }
|
||||
/// ```
|
||||
pub BLOCK_IN_IF_CONDITION_EXPR,
|
||||
style,
|
||||
"braces that can be eliminated in conditions, e.g. `if { true } ...`"
|
||||
"braces that can be eliminated in conditions, e.g., `if { true } ...`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `if` conditions that use blocks containing
|
||||
/// statements, or conditions that use closures with blocks.
|
||||
///
|
||||
/// **Why is this bad?** Using blocks in the condition makes it hard to read.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if { let x = somefunc(); x } ..
|
||||
/// // or
|
||||
/// if somefunc(|x| { x == 47 }) ..
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `if` conditions that use blocks containing
|
||||
/// statements, or conditions that use closures with blocks.
|
||||
///
|
||||
/// **Why is this bad?** Using blocks in the condition makes it hard to read.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if { let x = somefunc(); x } {}
|
||||
/// // or
|
||||
/// if somefunc(|x| { x == 47 }) {}
|
||||
/// ```
|
||||
pub BLOCK_IN_IF_CONDITION_STMT,
|
||||
style,
|
||||
"complex blocks in conditions, e.g. `if { let x = true; x } ...`"
|
||||
"complex blocks in conditions, e.g., `if { let x = true; x } ...`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -7,42 +7,42 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast::{LitKind, NodeId, DUMMY_NODE_ID};
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::{dummy_spanned, Span, DUMMY_SP};
|
||||
|
||||
/// **What it does:** Checks for boolean expressions that can be written more
|
||||
/// concisely.
|
||||
///
|
||||
/// **Why is this bad?** Readability of boolean expressions suffers from
|
||||
/// unnecessary duplication.
|
||||
///
|
||||
/// **Known problems:** Ignores short circuiting behavior of `||` and
|
||||
/// `&&`. Ignores `|`, `&` and `^`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if a && true // should be: if a
|
||||
/// if !(a == b) // should be: if a != b
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for boolean expressions that can be written more
|
||||
/// concisely.
|
||||
///
|
||||
/// **Why is this bad?** Readability of boolean expressions suffers from
|
||||
/// unnecessary duplication.
|
||||
///
|
||||
/// **Known problems:** Ignores short circuiting behavior of `||` and
|
||||
/// `&&`. Ignores `|`, `&` and `^`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if a && true // should be: if a
|
||||
/// if !(a == b) // should be: if a != b
|
||||
/// ```
|
||||
pub NONMINIMAL_BOOL,
|
||||
complexity,
|
||||
"boolean expressions that can be written more concisely"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for boolean expressions that contain terminals that
|
||||
/// can be eliminated.
|
||||
///
|
||||
/// **Why is this bad?** This is most likely a logic bug.
|
||||
///
|
||||
/// **Known problems:** Ignores short circuiting behavior.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if a && b || a { ... }
|
||||
/// ```
|
||||
/// The `b` is unnecessary, the expression is equivalent to `if a`.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for boolean expressions that contain terminals that
|
||||
/// can be eliminated.
|
||||
///
|
||||
/// **Why is this bad?** This is most likely a logic bug.
|
||||
///
|
||||
/// **Known problems:** Ignores short circuiting behavior.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if a && b || a { ... }
|
||||
/// ```
|
||||
/// The `b` is unnecessary, the expression is equivalent to `if a`.
|
||||
pub LOGIC_BUG,
|
||||
correctness,
|
||||
"boolean expressions that contain terminals which can be eliminated"
|
||||
|
@ -72,7 +72,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonminimalBool {
|
|||
_: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
_: Span,
|
||||
_: NodeId,
|
||||
_: HirId,
|
||||
) {
|
||||
NonminimalBoolVisitor { cx }.visit_body(body)
|
||||
}
|
||||
|
@ -132,7 +132,6 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
|
|||
}
|
||||
|
||||
let mk_expr = |op| Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
hir_id: DUMMY_HIR_ID,
|
||||
span: DUMMY_SP,
|
||||
attrs: ThinVec::new(),
|
||||
|
|
|
@ -10,22 +10,22 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::ast::{Name, UintTy};
|
||||
|
||||
/// **What it does:** Checks for naive byte counts
|
||||
///
|
||||
/// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
|
||||
/// crate has methods to count your bytes faster, especially for large slices.
|
||||
///
|
||||
/// **Known problems:** If you have predominantly small slices, the
|
||||
/// `bytecount::count(..)` method may actually be slower. However, if you can
|
||||
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
|
||||
/// faster in those cases.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for naive byte counts
|
||||
///
|
||||
/// **Why is this bad?** The [`bytecount`](https://crates.io/crates/bytecount)
|
||||
/// crate has methods to count your bytes faster, especially for large slices.
|
||||
///
|
||||
/// **Known problems:** If you have predominantly small slices, the
|
||||
/// `bytecount::count(..)` method may actually be slower. However, if you can
|
||||
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
|
||||
/// faster in those cases.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
|
||||
/// ```
|
||||
pub NAIVE_BYTECOUNT,
|
||||
perf,
|
||||
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
|
||||
|
|
|
@ -7,28 +7,28 @@ use syntax::{ast::*, source_map::DUMMY_SP};
|
|||
|
||||
use cargo_metadata;
|
||||
|
||||
/// **What it does:** Checks to see if all common metadata is defined in
|
||||
/// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
|
||||
///
|
||||
/// **Why is this bad?** It will be more difficult for users to discover the
|
||||
/// purpose of the crate, and key information related to it.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```toml
|
||||
/// # This `Cargo.toml` is missing an authors field:
|
||||
/// [package]
|
||||
/// name = "clippy"
|
||||
/// version = "0.0.212"
|
||||
/// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
/// repository = "https://github.com/rust-lang/rust-clippy"
|
||||
/// readme = "README.md"
|
||||
/// license = "MIT/Apache-2.0"
|
||||
/// keywords = ["clippy", "lint", "plugin"]
|
||||
/// categories = ["development-tools", "development-tools::cargo-plugins"]
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks to see if all common metadata is defined in
|
||||
/// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
|
||||
///
|
||||
/// **Why is this bad?** It will be more difficult for users to discover the
|
||||
/// purpose of the crate, and key information related to it.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```toml
|
||||
/// # This `Cargo.toml` is missing an authors field:
|
||||
/// [package]
|
||||
/// name = "clippy"
|
||||
/// version = "0.0.212"
|
||||
/// description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
/// repository = "https://github.com/rust-lang/rust-clippy"
|
||||
/// readme = "README.md"
|
||||
/// license = "MIT/Apache-2.0"
|
||||
/// keywords = ["clippy", "lint", "plugin"]
|
||||
/// categories = ["development-tools", "development-tools::cargo-plugins"]
|
||||
/// ```
|
||||
pub CARGO_COMMON_METADATA,
|
||||
cargo,
|
||||
"common metadata is defined in `Cargo.toml`"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! calculate cyclomatic complexity and warn about overly complex functions
|
||||
//! calculate cognitive complexity and warn about overly complex functions
|
||||
|
||||
use rustc::cfg::CFG;
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
|
@ -6,31 +6,31 @@ use rustc::hir::*;
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::{Attribute, NodeId};
|
||||
use syntax::ast::Attribute;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
use crate::utils::{in_macro, is_allowed, match_type, paths, span_help_and_lint, LimitStack};
|
||||
|
||||
/// **What it does:** Checks for methods with high cyclomatic complexity.
|
||||
///
|
||||
/// **Why is this bad?** Methods of high cyclomatic complexity tend to be badly
|
||||
/// readable. Also LLVM will usually optimize small methods better.
|
||||
///
|
||||
/// **Known problems:** Sometimes it's hard to find a way to reduce the
|
||||
/// complexity.
|
||||
///
|
||||
/// **Example:** No. You'll see it when you get the warning.
|
||||
declare_clippy_lint! {
|
||||
pub CYCLOMATIC_COMPLEXITY,
|
||||
/// **What it does:** Checks for methods with high cognitive complexity.
|
||||
///
|
||||
/// **Why is this bad?** Methods of high cognitive complexity tend to be hard to
|
||||
/// both read and maintain. Also LLVM will tend to optimize small methods better.
|
||||
///
|
||||
/// **Known problems:** Sometimes it's hard to find a way to reduce the
|
||||
/// complexity.
|
||||
///
|
||||
/// **Example:** No. You'll see it when you get the warning.
|
||||
pub COGNITIVE_COMPLEXITY,
|
||||
complexity,
|
||||
"functions that should be split up into multiple functions"
|
||||
}
|
||||
|
||||
pub struct CyclomaticComplexity {
|
||||
pub struct CognitiveComplexity {
|
||||
limit: LimitStack,
|
||||
}
|
||||
|
||||
impl CyclomaticComplexity {
|
||||
impl CognitiveComplexity {
|
||||
pub fn new(limit: u64) -> Self {
|
||||
Self {
|
||||
limit: LimitStack::new(limit),
|
||||
|
@ -38,17 +38,17 @@ impl CyclomaticComplexity {
|
|||
}
|
||||
}
|
||||
|
||||
impl LintPass for CyclomaticComplexity {
|
||||
impl LintPass for CognitiveComplexity {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array!(CYCLOMATIC_COMPLEXITY)
|
||||
lint_array!(COGNITIVE_COMPLEXITY)
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"CyclomaticComplexity"
|
||||
"CognitiveComplexity"
|
||||
}
|
||||
}
|
||||
|
||||
impl CyclomaticComplexity {
|
||||
impl CognitiveComplexity {
|
||||
fn check<'a, 'tcx: 'a>(&mut self, cx: &'a LateContext<'a, 'tcx>, body: &'tcx Body, span: Span) {
|
||||
if in_macro(span) {
|
||||
return;
|
||||
|
@ -105,9 +105,9 @@ impl CyclomaticComplexity {
|
|||
if rust_cc > self.limit.limit() {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
CYCLOMATIC_COMPLEXITY,
|
||||
COGNITIVE_COMPLEXITY,
|
||||
span,
|
||||
&format!("the function has a cyclomatic complexity of {}", rust_cc),
|
||||
&format!("the function has a cognitive complexity of {}", rust_cc),
|
||||
"you could split it up into multiple smaller functions",
|
||||
);
|
||||
}
|
||||
|
@ -115,7 +115,7 @@ impl CyclomaticComplexity {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CognitiveComplexity {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
|
@ -123,19 +123,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CyclomaticComplexity {
|
|||
_: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
let def_id = cx.tcx.hir().local_def_id(node_id);
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(hir_id);
|
||||
if !cx.tcx.has_attr(def_id, "test") {
|
||||
self.check(cx, body, span);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.push_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity");
|
||||
}
|
||||
fn exit_lint_attrs(&mut self, cx: &LateContext<'a, 'tcx>, attrs: &'tcx [Attribute]) {
|
||||
self.limit.pop_attrs(cx.sess(), attrs, "cyclomatic_complexity");
|
||||
self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,7 @@ fn report_cc_bug(
|
|||
) {
|
||||
span_bug!(
|
||||
span,
|
||||
"Clippy encountered a bug calculating cyclomatic complexity: cc = {}, arms = {}, \
|
||||
"Clippy encountered a bug calculating cognitive complexity: cc = {}, arms = {}, \
|
||||
div = {}, shorts = {}, returns = {}. Please file a bug report.",
|
||||
cc,
|
||||
narms,
|
||||
|
@ -222,13 +222,12 @@ fn report_cc_bug(
|
|||
span: Span,
|
||||
id: HirId,
|
||||
) {
|
||||
let node_id = cx.tcx.hir().hir_to_node_id(id);
|
||||
if !is_allowed(cx, CYCLOMATIC_COMPLEXITY, node_id) {
|
||||
if !is_allowed(cx, COGNITIVE_COMPLEXITY, id) {
|
||||
cx.sess().span_note_without_error(
|
||||
span,
|
||||
&format!(
|
||||
"Clippy encountered a bug calculating cyclomatic complexity \
|
||||
(hide this message with `#[allow(cyclomatic_complexity)]`): \
|
||||
"Clippy encountered a bug calculating cognitive complexity \
|
||||
(hide this message with `#[allow(cognitive_complexity)]`): \
|
||||
cc = {}, arms = {}, div = {}, shorts = {}, returns = {}. \
|
||||
Please file a bug report.",
|
||||
cc, narms, div, shorts, returns
|
|
@ -21,54 +21,54 @@ use crate::utils::sugg::Sugg;
|
|||
use crate::utils::{in_macro, snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions and for `else { if ... }` expressions
|
||||
/// that
|
||||
/// can be collapsed to `else if ...`.
|
||||
///
|
||||
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// …
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else {
|
||||
/// if y {
|
||||
/// …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else if y {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions and for `else { if ... }` expressions
|
||||
/// that
|
||||
/// can be collapsed to `else if ...`.
|
||||
///
|
||||
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// …
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else {
|
||||
/// if y {
|
||||
/// …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Should be written:
|
||||
///
|
||||
/// ```rust.ignore
|
||||
/// if x && y {
|
||||
/// …
|
||||
/// }
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// if x {
|
||||
/// …
|
||||
/// } else if y {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"`if`s that can be collapsed (e.g. `if x { if y { ... } }` and `else { if x { ... } }`)"
|
||||
"`if`s that can be collapsed (e.g., `if x { if y { ... } }` and `else { if x { ... } }`)"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -4,23 +4,23 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::ast::*;
|
||||
|
||||
/// **What it does:** Checks for constants with an explicit `'static` lifetime.
|
||||
///
|
||||
/// **Why is this bad?** Adding `'static` to every reference can create very
|
||||
/// complicated types.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
|
||||
/// &[...]
|
||||
/// ```
|
||||
/// This code can be rewritten as
|
||||
/// ```rust
|
||||
/// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for constants with an explicit `'static` lifetime.
|
||||
///
|
||||
/// **Why is this bad?** Adding `'static` to every reference can create very
|
||||
/// complicated types.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
|
||||
/// &[...]
|
||||
/// ```
|
||||
/// This code can be rewritten as
|
||||
/// ```ignore
|
||||
/// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
|
||||
/// ```
|
||||
pub CONST_STATIC_LIFETIME,
|
||||
style,
|
||||
"Using explicit `'static` lifetime for constants when elision rules would allow omitting them."
|
||||
|
|
|
@ -5,7 +5,7 @@ use if_chain::if_chain;
|
|||
use rustc::hir::def::Def;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::LateContext;
|
||||
use rustc::ty::subst::{Subst, Substs};
|
||||
use rustc::ty::subst::{Subst, SubstsRef};
|
||||
use rustc::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc::{bug, span_bug};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -21,27 +21,27 @@ use syntax_pos::symbol::Symbol;
|
|||
/// A `LitKind`-like enum to fold constant `Expr`s into.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Constant {
|
||||
/// a String "abc"
|
||||
/// A `String` (e.g., "abc").
|
||||
Str(String),
|
||||
/// a Binary String b"abc"
|
||||
/// A binary string (e.g., `b"abc"`).
|
||||
Binary(Lrc<Vec<u8>>),
|
||||
/// a single char 'a'
|
||||
/// A single `char` (e.g., `'a'`).
|
||||
Char(char),
|
||||
/// an integer's bit representation
|
||||
/// An integer's bit representation.
|
||||
Int(u128),
|
||||
/// an f32
|
||||
/// An `f32`.
|
||||
F32(f32),
|
||||
/// an f64
|
||||
/// An `f64`.
|
||||
F64(f64),
|
||||
/// true or false
|
||||
/// `true` or `false`.
|
||||
Bool(bool),
|
||||
/// an array of constants
|
||||
/// An array of constants.
|
||||
Vec(Vec<Constant>),
|
||||
/// also an array, but with only one constant, repeated N times
|
||||
/// Also an array, but with only one constant, repeated N times.
|
||||
Repeat(Box<Constant>, u64),
|
||||
/// a tuple of constants
|
||||
/// A tuple of constants.
|
||||
Tuple(Vec<Constant>),
|
||||
/// a literal with syntax error
|
||||
/// A literal with syntax error.
|
||||
Err(Symbol),
|
||||
}
|
||||
|
||||
|
@ -53,15 +53,15 @@ impl PartialEq for Constant {
|
|||
(&Constant::Char(l), &Constant::Char(r)) => l == r,
|
||||
(&Constant::Int(l), &Constant::Int(r)) => l == r,
|
||||
(&Constant::F64(l), &Constant::F64(r)) => {
|
||||
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
|
||||
// `Fw32 == Fw64` so don’t compare them
|
||||
// to_bits is required to catch non-matching 0.0, -0.0, and NaNs
|
||||
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
|
||||
// `Fw32 == Fw64`, so don’t compare them.
|
||||
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
|
||||
l.to_bits() == r.to_bits()
|
||||
},
|
||||
(&Constant::F32(l), &Constant::F32(r)) => {
|
||||
// we want `Fw32 == FwAny` and `FwAny == Fw64`, by transitivity we must have
|
||||
// `Fw32 == Fw64` so don’t compare them
|
||||
// to_bits is required to catch non-matching 0.0, -0.0, and NaNs
|
||||
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
|
||||
// `Fw32 == Fw64`, so don’t compare them.
|
||||
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
|
||||
f64::from(l).to_bits() == f64::from(r).to_bits()
|
||||
},
|
||||
(&Constant::Bool(l), &Constant::Bool(r)) => l == r,
|
||||
|
@ -69,7 +69,8 @@ impl PartialEq for Constant {
|
|||
l == r
|
||||
},
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
|
||||
_ => false, // TODO: Are there inter-type equalities?
|
||||
// TODO: are there inter-type equalities?
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,12 +143,13 @@ impl Constant {
|
|||
x => x,
|
||||
}
|
||||
},
|
||||
_ => None, // TODO: Are there any useful inter-type orderings?
|
||||
// TODO: are there any useful inter-type orderings?
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// parse a `LitKind` to a `Constant`
|
||||
/// Parses a `LitKind` to a `Constant`.
|
||||
pub fn lit_to_constant<'tcx>(lit: &LitKind, ty: Ty<'tcx>) -> Constant {
|
||||
use syntax::ast::*;
|
||||
|
||||
|
@ -209,7 +211,7 @@ pub struct ConstEvalLateContext<'a, 'tcx: 'a> {
|
|||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
needed_resolution: bool,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
substs: SubstsRef<'tcx>,
|
||||
}
|
||||
|
||||
impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
||||
|
|
|
@ -10,94 +10,94 @@ use std::collections::hash_map::Entry;
|
|||
use std::hash::BuildHasherDefault;
|
||||
use syntax::symbol::LocalInternedString;
|
||||
|
||||
/// **What it does:** Checks for consecutive `if`s with the same condition.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if a == b {
|
||||
/// …
|
||||
/// } else if a == b {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that this lint ignores all conditions with a function call as it could
|
||||
/// have side effects:
|
||||
///
|
||||
/// ```rust
|
||||
/// if foo() {
|
||||
/// …
|
||||
/// } else if foo() { // not linted
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for consecutive `if`s with the same condition.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if a == b {
|
||||
/// …
|
||||
/// } else if a == b {
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Note that this lint ignores all conditions with a function call as it could
|
||||
/// have side effects:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if foo() {
|
||||
/// …
|
||||
/// } else if foo() { // not linted
|
||||
/// …
|
||||
/// }
|
||||
/// ```
|
||||
pub IFS_SAME_COND,
|
||||
correctness,
|
||||
"consecutive `ifs` with the same condition"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
correctness,
|
||||
"if with the same *then* and *else* blocks"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for `match` with identical arm bodies.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies
|
||||
/// are the same on purpose, you can factor them
|
||||
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
|
||||
///
|
||||
/// **Known problems:** False positive possible with order dependent `match`
|
||||
/// (see issue
|
||||
/// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => bar(), // <= oops
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should probably be
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => baz(), // <= fixed
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or if the original code was not a typo:
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar | Baz => bar(), // <= shows the intent better
|
||||
/// Quz => quz(),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `match` with identical arm bodies.
|
||||
///
|
||||
/// **Why is this bad?** This is probably a copy & paste error. If arm bodies
|
||||
/// are the same on purpose, you can factor them
|
||||
/// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
|
||||
///
|
||||
/// **Known problems:** False positive possible with order dependent `match`
|
||||
/// (see issue
|
||||
/// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => bar(), // <= oops
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This should probably be
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar => bar(),
|
||||
/// Quz => quz(),
|
||||
/// Baz => baz(), // <= fixed
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or if the original code was not a typo:
|
||||
/// ```rust,ignore
|
||||
/// match foo {
|
||||
/// Bar | Baz => bar(), // <= shows the intent better
|
||||
/// Quz => quz(),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_SAME_ARMS,
|
||||
pedantic,
|
||||
"`match` with identical arm bodies"
|
||||
|
@ -125,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyAndPaste {
|
|||
..
|
||||
}) = get_parent_expr(cx, expr)
|
||||
{
|
||||
if else_expr.id == expr.id {
|
||||
if else_expr.hir_id == expr.hir_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -239,9 +239,9 @@ fn lint_match_arms(cx: &LateContext<'_, '_>, expr: &Expr) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the list of condition expressions and the list of blocks in a
|
||||
/// Returns the list of condition expressions and the list of blocks in a
|
||||
/// sequence of `if/else`.
|
||||
/// Eg. would return `([a, b], [c, d, e])` for the expression
|
||||
/// E.g., this returns `([a, b], [c, d, e])` for the expression
|
||||
/// `if a { c } else if b { d } else { e }`.
|
||||
fn if_sequence(mut expr: &Expr) -> (SmallVec<[&Expr; 1]>, SmallVec<[&Block; 1]>) {
|
||||
let mut conds = SmallVec::new();
|
||||
|
@ -272,7 +272,7 @@ fn if_sequence(mut expr: &Expr) -> (SmallVec<[&Expr; 1]>, SmallVec<[&Block; 1]>)
|
|||
(conds, blocks)
|
||||
}
|
||||
|
||||
/// Return the list of bindings in a pattern.
|
||||
/// Returns the list of bindings in a pattern.
|
||||
fn bindings<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, pat: &Pat) -> FxHashMap<LocalInternedString, Ty<'tcx>> {
|
||||
fn bindings_impl<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
|
|
|
@ -3,27 +3,27 @@ use rustc::hir::{Item, ItemKind};
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for types that implement `Copy` as well as
|
||||
/// `Iterator`.
|
||||
///
|
||||
/// **Why is this bad?** Implicit copies can be confusing when working with
|
||||
/// iterator combinators.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct Countdown(u8);
|
||||
///
|
||||
/// impl Iterator for Countdown {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// let a: Vec<_> = my_iterator.take(1).collect();
|
||||
/// let b: Vec<_> = my_iterator.collect();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for types that implement `Copy` as well as
|
||||
/// `Iterator`.
|
||||
///
|
||||
/// **Why is this bad?** Implicit copies can be confusing when working with
|
||||
/// iterator combinators.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct Countdown(u8);
|
||||
///
|
||||
/// impl Iterator for Countdown {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// let a: Vec<_> = my_iterator.take(1).collect();
|
||||
/// let b: Vec<_> = my_iterator.collect();
|
||||
/// ```
|
||||
pub COPY_ITERATOR,
|
||||
pedantic,
|
||||
"implementing `Iterator` on a `Copy` type"
|
||||
|
@ -44,7 +44,7 @@ impl LintPass for CopyIterator {
|
|||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CopyIterator {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) = item.node {
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.id));
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id_from_hir_id(item.hir_id));
|
||||
|
||||
if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
|
||||
span_note_and_lint(
|
||||
|
|
|
@ -6,22 +6,22 @@ use syntax::ast;
|
|||
use syntax::source_map::Span;
|
||||
use syntax::tokenstream::TokenStream;
|
||||
|
||||
/// **What it does:** Checks for usage of dbg!() macro.
|
||||
///
|
||||
/// **Why is this bad?** `dbg!` macro is intended as a debugging tool. It
|
||||
/// should not be in version control.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// dbg!(true)
|
||||
///
|
||||
/// // Good
|
||||
/// true
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of dbg!() macro.
|
||||
///
|
||||
/// **Why is this bad?** `dbg!` macro is intended as a debugging tool. It
|
||||
/// should not be in version control.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// // Bad
|
||||
/// dbg!(true)
|
||||
///
|
||||
/// // Good
|
||||
/// true
|
||||
/// ```
|
||||
pub DBG_MACRO,
|
||||
restriction,
|
||||
"`dbg!` macro is intended as a debugging tool"
|
||||
|
|
|
@ -5,24 +5,24 @@ use rustc::ty;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
use crate::utils::{any_parent_is_automatically_derived, match_def_path, opt_def_id, paths, span_lint_and_sugg};
|
||||
use crate::utils::{any_parent_is_automatically_derived, match_def_path, paths, span_lint_and_sugg};
|
||||
|
||||
/// **What it does:** Checks for literal calls to `Default::default()`.
|
||||
///
|
||||
/// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
|
||||
/// being gotten than the generic `Default`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let s: String = Default::default();
|
||||
///
|
||||
/// // Good
|
||||
/// let s = String::default();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for literal calls to `Default::default()`.
|
||||
///
|
||||
/// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
|
||||
/// being gotten than the generic `Default`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let s: String = Default::default();
|
||||
///
|
||||
/// // Good
|
||||
/// let s = String::default();
|
||||
/// ```
|
||||
pub DEFAULT_TRAIT_ACCESS,
|
||||
pedantic,
|
||||
"checks for literal calls to Default::default()"
|
||||
|
@ -45,9 +45,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DefaultTraitAccess {
|
|||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref path, ..) = expr.node;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.id);
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if let ExprKind::Path(ref qpath) = path.node;
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
|
||||
if let Some(def_id) = cx.tables.qpath_def(qpath, path.hir_id).opt_def_id();
|
||||
if match_def_path(cx.tcx, def_id, &paths::DEFAULT_TRAIT_METHOD);
|
||||
then {
|
||||
match qpath {
|
||||
|
|
|
@ -90,7 +90,7 @@ declare_deprecated_lint! {
|
|||
/// counterparts, so this lint may suggest a change in behavior or the code may not compile.
|
||||
declare_deprecated_lint! {
|
||||
pub ASSIGN_OPS,
|
||||
"using compound assignment operators (e.g. `+=`) is harmless"
|
||||
"using compound assignment operators (e.g., `+=`) is harmless"
|
||||
}
|
||||
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
|
|
|
@ -7,56 +7,56 @@ use rustc::ty::{self, Ty};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
|
||||
/// explicitly or vice versa.
|
||||
///
|
||||
/// **Why is this bad?** The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```rust
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for deriving `Hash` but implementing `PartialEq`
|
||||
/// explicitly or vice versa.
|
||||
///
|
||||
/// **Why is this bad?** The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```text
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
pub DERIVE_HASH_XOR_EQ,
|
||||
correctness,
|
||||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for explicit `Clone` implementations for `Copy`
|
||||
/// types.
|
||||
///
|
||||
/// **Why is this bad?** To avoid surprising behaviour, these traits should
|
||||
/// agree and the behaviour of `Copy` cannot be overridden. In almost all
|
||||
/// situations a `Copy` type should have a `Clone` implementation that does
|
||||
/// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
|
||||
/// gets you.
|
||||
///
|
||||
/// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[derive(Copy)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Clone for Foo {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for explicit `Clone` implementations for `Copy`
|
||||
/// types.
|
||||
///
|
||||
/// **Why is this bad?** To avoid surprising behaviour, these traits should
|
||||
/// agree and the behaviour of `Copy` cannot be overridden. In almost all
|
||||
/// situations a `Copy` type should have a `Clone` implementation that does
|
||||
/// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
|
||||
/// gets you.
|
||||
///
|
||||
/// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[derive(Copy)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Clone for Foo {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXPL_IMPL_CLONE_ON_COPY,
|
||||
pedantic,
|
||||
"implementing `Clone` explicitly on `Copy` types"
|
||||
|
@ -77,7 +77,7 @@ impl LintPass for Derive {
|
|||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Derive {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let ItemKind::Impl(_, _, _, _, Some(ref trait_ref), _, _) = item.node {
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id(item.id));
|
||||
let ty = cx.tcx.type_of(cx.tcx.hir().local_def_id_from_hir_id(item.hir_id));
|
||||
let is_automatically_derived = is_automatically_derived(&*item.attrs);
|
||||
|
||||
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||
|
|
|
@ -9,25 +9,25 @@ use syntax::source_map::{BytePos, Span};
|
|||
use syntax_pos::Pos;
|
||||
use url::Url;
|
||||
|
||||
/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
|
||||
/// outside ticks in documentation.
|
||||
///
|
||||
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
|
||||
/// camel-case probably indicates some code which should be included between
|
||||
/// ticks. `_` can also be used for emphasis in markdown, this lint tries to
|
||||
/// consider that.
|
||||
///
|
||||
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
|
||||
/// for is limited, and there are still false positives.
|
||||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
/// /// Do something with the foo_bar parameter. See also
|
||||
/// /// that::other::module::foo.
|
||||
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
|
||||
/// fn doit(foo_bar) { .. }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the presence of `_`, `::` or camel-case words
|
||||
/// outside ticks in documentation.
|
||||
///
|
||||
/// **Why is this bad?** *Rustdoc* supports markdown formatting, `_`, `::` and
|
||||
/// camel-case probably indicates some code which should be included between
|
||||
/// ticks. `_` can also be used for emphasis in markdown, this lint tries to
|
||||
/// consider that.
|
||||
///
|
||||
/// **Known problems:** Lots of bad docs won’t be fixed, what the lint checks
|
||||
/// for is limited, and there are still false positives.
|
||||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
/// /// Do something with the foo_bar parameter. See also
|
||||
/// /// that::other::module::foo.
|
||||
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
|
||||
/// fn doit(foo_bar) { .. }
|
||||
/// ```
|
||||
pub DOC_MARKDOWN,
|
||||
pedantic,
|
||||
"presence of `_`, `::` or camel-case outside backticks in documentation"
|
||||
|
@ -257,10 +257,9 @@ fn check_text(cx: &EarlyContext<'_>, valid_idents: &FxHashSet<String>, text: &st
|
|||
}
|
||||
|
||||
fn check_word(cx: &EarlyContext<'_>, word: &str, span: Span) {
|
||||
/// Checks if a string is camel-case, ie. contains at least two uppercase
|
||||
/// letter (`Clippy` is
|
||||
/// ok) and one lower-case letter (`NASA` is ok). Plural are also excluded
|
||||
/// (`IDs` is ok).
|
||||
/// Checks if a string is camel-case, i.e., contains at least two uppercase
|
||||
/// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
|
||||
/// Plurals are also excluded (`IDs` is ok).
|
||||
fn is_camel_case(s: &str) -> bool {
|
||||
if s.starts_with(|c: char| c.is_digit(10)) {
|
||||
return false;
|
||||
|
|
|
@ -8,24 +8,24 @@ use syntax::source_map::Span;
|
|||
|
||||
use crate::utils::{snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
|
||||
|
||||
/// **What it does:** Checks for double comparions that could be simplified to a single expression.
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x == y || x < y
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// x <= y
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for double comparions that could be simplified to a single expression.
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x == y || x < y
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// x <= y
|
||||
/// ```
|
||||
pub DOUBLE_COMPARISONS,
|
||||
complexity,
|
||||
"unnecessary double comparisons that can be simplified"
|
||||
|
|
|
@ -3,20 +3,20 @@ use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::*;
|
||||
|
||||
/// **What it does:** Checks for unnecessary double parentheses.
|
||||
///
|
||||
/// **Why is this bad?** This makes code harder to read and might indicate a
|
||||
/// mistake.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ((0))
|
||||
/// foo((0))
|
||||
/// ((1, 2))
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for unnecessary double parentheses.
|
||||
///
|
||||
/// **Why is this bad?** This makes code harder to read and might indicate a
|
||||
/// mistake.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ((0))
|
||||
/// foo((0))
|
||||
/// ((1, 2))
|
||||
/// ```
|
||||
pub DOUBLE_PARENS,
|
||||
complexity,
|
||||
"Warn on unnecessary double parentheses"
|
||||
|
|
|
@ -4,29 +4,29 @@ use rustc::hir::*;
|
|||
use rustc::lint::{LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for generics with `std::ops::Drop` as bounds.
|
||||
///
|
||||
/// **Why is this bad?** `Drop` bounds do not really accomplish anything.
|
||||
/// A type may have compiler-generated drop glue without implementing the
|
||||
/// `Drop` trait itself. The `Drop` trait also only has one method,
|
||||
/// `Drop::drop`, and that function is by fiat not callable in user code.
|
||||
/// So there is really no use case for using `Drop` in trait bounds.
|
||||
///
|
||||
/// The most likely use case of a drop bound is to distinguish between types
|
||||
/// that have destructors and types that don't. Combined with specialization,
|
||||
/// a naive coder would write an implementation that assumed a type could be
|
||||
/// trivially dropped, then write a specialization for `T: Drop` that actually
|
||||
/// calls the destructor. Except that doing so is not correct; String, for
|
||||
/// example, doesn't actually implement Drop, but because String contains a
|
||||
/// Vec, assuming it can be trivially dropped will leak memory.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo<T: Drop>() {}
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for generics with `std::ops::Drop` as bounds.
|
||||
///
|
||||
/// **Why is this bad?** `Drop` bounds do not really accomplish anything.
|
||||
/// A type may have compiler-generated drop glue without implementing the
|
||||
/// `Drop` trait itself. The `Drop` trait also only has one method,
|
||||
/// `Drop::drop`, and that function is by fiat not callable in user code.
|
||||
/// So there is really no use case for using `Drop` in trait bounds.
|
||||
///
|
||||
/// The most likely use case of a drop bound is to distinguish between types
|
||||
/// that have destructors and types that don't. Combined with specialization,
|
||||
/// a naive coder would write an implementation that assumed a type could be
|
||||
/// trivially dropped, then write a specialization for `T: Drop` that actually
|
||||
/// calls the destructor. Except that doing so is not correct; String, for
|
||||
/// example, doesn't actually implement Drop, but because String contains a
|
||||
/// Vec, assuming it can be trivially dropped will leak memory.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo<T: Drop>() {}
|
||||
/// ```
|
||||
pub DROP_BOUNDS,
|
||||
correctness,
|
||||
"Bounds of the form `T: Drop` are useless"
|
||||
|
|
|
@ -1,97 +1,97 @@
|
|||
use crate::utils::{is_copy, match_def_path, opt_def_id, paths, span_note_and_lint};
|
||||
use crate::utils::{is_copy, match_def_path, paths, span_note_and_lint};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for calls to `std::mem::drop` with a reference
|
||||
/// instead of an owned value.
|
||||
///
|
||||
/// **Why is this bad?** Calling `drop` on a reference will only drop the
|
||||
/// reference itself, which is a no-op. It will not call the `drop` method (from
|
||||
/// the `Drop` trait implementation) on the underlying referenced value, which
|
||||
/// is likely what was intended.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut lock_guard = mutex.lock();
|
||||
/// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
|
||||
/// // still locked
|
||||
/// operation_that_requires_mutex_to_be_unlocked();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls to `std::mem::drop` with a reference
|
||||
/// instead of an owned value.
|
||||
///
|
||||
/// **Why is this bad?** Calling `drop` on a reference will only drop the
|
||||
/// reference itself, which is a no-op. It will not call the `drop` method (from
|
||||
/// the `Drop` trait implementation) on the underlying referenced value, which
|
||||
/// is likely what was intended.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let mut lock_guard = mutex.lock();
|
||||
/// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
|
||||
/// // still locked
|
||||
/// operation_that_requires_mutex_to_be_unlocked();
|
||||
/// ```
|
||||
pub DROP_REF,
|
||||
correctness,
|
||||
"calls to `std::mem::drop` with a reference instead of an owned value"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for calls to `std::mem::forget` with a reference
|
||||
/// instead of an owned value.
|
||||
///
|
||||
/// **Why is this bad?** Calling `forget` on a reference will only forget the
|
||||
/// reference itself, which is a no-op. It will not forget the underlying
|
||||
/// referenced
|
||||
/// value, which is likely what was intended.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Box::new(1);
|
||||
/// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls to `std::mem::forget` with a reference
|
||||
/// instead of an owned value.
|
||||
///
|
||||
/// **Why is this bad?** Calling `forget` on a reference will only forget the
|
||||
/// reference itself, which is a no-op. It will not forget the underlying
|
||||
/// referenced
|
||||
/// value, which is likely what was intended.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Box::new(1);
|
||||
/// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
|
||||
/// ```
|
||||
pub FORGET_REF,
|
||||
correctness,
|
||||
"calls to `std::mem::forget` with a reference instead of an owned value"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for calls to `std::mem::drop` with a value
|
||||
/// that derives the Copy trait
|
||||
///
|
||||
/// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
|
||||
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
|
||||
/// value will be copied and moved into the function on invocation.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: i32 = 42; // i32 implements Copy
|
||||
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the
|
||||
/// // original unaffected
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls to `std::mem::drop` with a value
|
||||
/// that derives the Copy trait
|
||||
///
|
||||
/// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
|
||||
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
|
||||
/// value will be copied and moved into the function on invocation.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: i32 = 42; // i32 implements Copy
|
||||
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the
|
||||
/// // original unaffected
|
||||
/// ```
|
||||
pub DROP_COPY,
|
||||
correctness,
|
||||
"calls to `std::mem::drop` with a value that implements Copy"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for calls to `std::mem::forget` with a value that
|
||||
/// derives the Copy trait
|
||||
///
|
||||
/// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
|
||||
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
|
||||
/// value will be copied and moved into the function on invocation.
|
||||
///
|
||||
/// An alternative, but also valid, explanation is that Copy types do not
|
||||
/// implement
|
||||
/// the Drop trait, which means they have no destructors. Without a destructor,
|
||||
/// there
|
||||
/// is nothing for `std::mem::forget` to ignore.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: i32 = 42; // i32 implements Copy
|
||||
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the
|
||||
/// // original unaffected
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls to `std::mem::forget` with a value that
|
||||
/// derives the Copy trait
|
||||
///
|
||||
/// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
|
||||
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
|
||||
/// value will be copied and moved into the function on invocation.
|
||||
///
|
||||
/// An alternative, but also valid, explanation is that Copy types do not
|
||||
/// implement
|
||||
/// the Drop trait, which means they have no destructors. Without a destructor,
|
||||
/// there
|
||||
/// is nothing for `std::mem::forget` to ignore.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: i32 = 42; // i32 implements Copy
|
||||
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the
|
||||
/// // original unaffected
|
||||
/// ```
|
||||
pub FORGET_COPY,
|
||||
correctness,
|
||||
"calls to `std::mem::forget` with a value that implements Copy"
|
||||
|
@ -124,7 +124,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
if let ExprKind::Call(ref path, ref args) = expr.node;
|
||||
if let ExprKind::Path(ref qpath) = path.node;
|
||||
if args.len() == 1;
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
|
||||
if let Some(def_id) = cx.tables.qpath_def(qpath, path.hir_id).opt_def_id();
|
||||
then {
|
||||
let lint;
|
||||
let msg;
|
||||
|
|
|
@ -9,21 +9,21 @@ use crate::consts::{constant, Constant};
|
|||
use crate::utils::paths;
|
||||
use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty};
|
||||
|
||||
/// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
|
||||
/// from other `Duration` methods.
|
||||
///
|
||||
/// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or
|
||||
/// `Duration::subsec_millis()` than to calculate them.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let dur = Duration::new(5, 0);
|
||||
/// let _micros = dur.subsec_nanos() / 1_000;
|
||||
/// let _millis = dur.subsec_nanos() / 1_000_000;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
|
||||
/// from other `Duration` methods.
|
||||
///
|
||||
/// **Why is this bad?** It's more concise to call `Duration::subsec_micros()` or
|
||||
/// `Duration::subsec_millis()` than to calculate them.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let dur = Duration::new(5, 0);
|
||||
/// let _micros = dur.subsec_nanos() / 1_000;
|
||||
/// let _millis = dur.subsec_nanos() / 1_000_000;
|
||||
/// ```
|
||||
pub DURATION_SUBSEC,
|
||||
complexity,
|
||||
"checks for calculation of subsecond microseconds or milliseconds"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! lint on if expressions with an else if, but without a final else branch
|
||||
//! Lint on if expressions with an else if, but without a final else branch.
|
||||
|
||||
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
@ -6,34 +6,34 @@ use syntax::ast::*;
|
|||
|
||||
use crate::utils::span_help_and_lint;
|
||||
|
||||
/// **What it does:** Checks for usage of if expressions with an `else if` branch,
|
||||
/// but without a final `else` branch.
|
||||
///
|
||||
/// **Why is this bad?** Some coding guidelines require this (e.g. MISRA-C:2004 Rule 14.10).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
/// b();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
/// b();
|
||||
/// } else {
|
||||
/// // we don't care about zero
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of if expressions with an `else if` branch,
|
||||
/// but without a final `else` branch.
|
||||
///
|
||||
/// **Why is this bad?** Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
/// b();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
/// b();
|
||||
/// } else {
|
||||
/// // We don't care about zero.
|
||||
/// }
|
||||
/// ```
|
||||
pub ELSE_IF_WITHOUT_ELSE,
|
||||
restriction,
|
||||
"if expression with an `else if`, but without a final `else` branch"
|
||||
|
|
|
@ -5,19 +5,19 @@ use rustc::hir::*;
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for `enum`s with no variants.
|
||||
///
|
||||
/// **Why is this bad?** Enum's with no variants should be replaced with `!`,
|
||||
/// the uninhabited type,
|
||||
/// or a wrapper around it.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Test {}
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `enum`s with no variants.
|
||||
///
|
||||
/// **Why is this bad?** Enum's with no variants should be replaced with `!`,
|
||||
/// the uninhabited type,
|
||||
/// or a wrapper around it.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Test {}
|
||||
/// ```
|
||||
pub EMPTY_ENUM,
|
||||
pedantic,
|
||||
"enum with no variants"
|
||||
|
@ -38,7 +38,7 @@ impl LintPass for EmptyEnum {
|
|||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EmptyEnum {
|
||||
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item) {
|
||||
let did = cx.tcx.hir().local_def_id(item.id);
|
||||
let did = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
|
||||
if let ItemKind::Enum(..) = item.node {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
|
||||
|
|
|
@ -8,30 +8,30 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
|
||||
/// or `BTreeMap`.
|
||||
///
|
||||
/// **Why is this bad?** Using `entry` is more efficient.
|
||||
///
|
||||
/// **Known problems:** Some false negatives, eg.:
|
||||
/// ```rust
|
||||
/// let k = &key;
|
||||
/// if !m.contains_key(k) {
|
||||
/// m.insert(k.clone(), v);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if !m.contains_key(&k) {
|
||||
/// m.insert(k, v)
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten as:
|
||||
/// ```rust
|
||||
/// m.entry(k).or_insert(v);
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for uses of `contains_key` + `insert` on `HashMap`
|
||||
/// or `BTreeMap`.
|
||||
///
|
||||
/// **Why is this bad?** Using `entry` is more efficient.
|
||||
///
|
||||
/// **Known problems:** Some false negatives, eg.:
|
||||
/// ```rust
|
||||
/// let k = &key;
|
||||
/// if !m.contains_key(k) {
|
||||
/// m.insert(k.clone(), v);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if !m.contains_key(&k) {
|
||||
/// m.insert(k, v)
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten as:
|
||||
/// ```rust
|
||||
/// m.entry(k).or_insert(v);
|
||||
/// ```
|
||||
pub MAP_ENTRY,
|
||||
perf,
|
||||
"use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
|
||||
|
|
|
@ -7,28 +7,28 @@ use rustc::hir::*;
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::mir::interpret::GlobalId;
|
||||
use rustc::ty;
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::ty::subst::InternalSubsts;
|
||||
use rustc::ty::util::IntTypeExt;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::{IntTy, UintTy};
|
||||
|
||||
/// **What it does:** Checks for C-like enumerations that are
|
||||
/// `repr(isize/usize)` and have values that don't fit into an `i32`.
|
||||
///
|
||||
/// **Why is this bad?** This will truncate the variant value on 32 bit
|
||||
/// architectures, but works fine on 64 bit.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[repr(usize)]
|
||||
/// enum NonPortable {
|
||||
/// X = 0x1_0000_0000,
|
||||
/// Y = 0,
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for C-like enumerations that are
|
||||
/// `repr(isize/usize)` and have values that don't fit into an `i32`.
|
||||
///
|
||||
/// **Why is this bad?** This will truncate the variant value on 32 bit
|
||||
/// architectures, but works fine on 64 bit.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// #[repr(usize)]
|
||||
/// enum NonPortable {
|
||||
/// X = 0x1_0000_0000,
|
||||
/// Y = 0,
|
||||
/// }
|
||||
/// ```
|
||||
pub ENUM_CLIKE_UNPORTABLE_VARIANT,
|
||||
correctness,
|
||||
"C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
|
||||
|
@ -58,7 +58,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnportableVariant {
|
|||
if let Some(ref anon_const) = variant.disr_expr {
|
||||
let param_env = ty::ParamEnv::empty();
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(anon_const.body);
|
||||
let substs = Substs::identity_for_item(cx.tcx.global_tcx(), def_id);
|
||||
let substs = InternalSubsts::identity_for_item(cx.tcx.global_tcx(), def_id);
|
||||
let instance = ty::Instance::new(def_id, substs);
|
||||
let c_id = GlobalId {
|
||||
instance,
|
||||
|
|
|
@ -5,22 +5,21 @@ use rustc::hir::def::Def;
|
|||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for `use Enum::*`.
|
||||
///
|
||||
/// **Why is this bad?** It is usually better style to use the prefixed name of
|
||||
/// an enumeration variant, rather than importing variants.
|
||||
///
|
||||
/// **Known problems:** Old-style enumerations that prefix the variants are
|
||||
/// still around.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::cmp::Ordering::*;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `use Enum::*`.
|
||||
///
|
||||
/// **Why is this bad?** It is usually better style to use the prefixed name of
|
||||
/// an enumeration variant, rather than importing variants.
|
||||
///
|
||||
/// **Known problems:** Old-style enumerations that prefix the variants are
|
||||
/// still around.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::cmp::Ordering::*;
|
||||
/// ```
|
||||
pub ENUM_GLOB_USE,
|
||||
pedantic,
|
||||
"use items that import all variants of an enum"
|
||||
|
@ -39,7 +38,7 @@ impl LintPass for EnumGlobUse {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EnumGlobUse {
|
||||
fn check_mod(&mut self, cx: &LateContext<'a, 'tcx>, m: &'tcx Mod, _: Span, _: NodeId) {
|
||||
fn check_mod(&mut self, cx: &LateContext<'a, 'tcx>, m: &'tcx Mod, _: Span, _: HirId) {
|
||||
// only check top level `use` statements
|
||||
for item in &m.item_ids {
|
||||
self.lint_item(cx, cx.tcx.hir().expect_item(item.id));
|
||||
|
|
|
@ -8,94 +8,94 @@ use syntax::ast::*;
|
|||
use syntax::source_map::Span;
|
||||
use syntax::symbol::{InternedString, LocalInternedString};
|
||||
|
||||
/// **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,
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **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,
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
pub ENUM_VARIANT_NAMES,
|
||||
style,
|
||||
"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,
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **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,
|
||||
/// BattenbergCake,
|
||||
/// }
|
||||
/// ```
|
||||
pub PUB_ENUM_VARIANT_NAMES,
|
||||
pedantic,
|
||||
"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.
|
||||
///
|
||||
/// **Why is this bad?** It requires the user to type the module name twice.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// mod cake {
|
||||
/// struct BlackForestCake;
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects type names that are prefixed or suffixed by the
|
||||
/// containing module's name.
|
||||
///
|
||||
/// **Why is this bad?** It requires the user to type the module name twice.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// mod cake {
|
||||
/// struct BlackForestCake;
|
||||
/// }
|
||||
/// ```
|
||||
pub MODULE_NAME_REPETITIONS,
|
||||
pedantic,
|
||||
"type names prefixed/postfixed with their containing module's name"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for modules that have the same name as their
|
||||
/// parent module
|
||||
///
|
||||
/// **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.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_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
|
||||
/// 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.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
pub MODULE_INCEPTION,
|
||||
style,
|
||||
"modules that have the same name as their parent module"
|
||||
|
|
|
@ -6,40 +6,40 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for equal operands to comparison, logical and
|
||||
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
|
||||
/// `||`, `&`, `|`, `^`, `-` and `/`).
|
||||
///
|
||||
/// **Why is this bad?** This is usually just a typo or a copy and paste error.
|
||||
///
|
||||
/// **Known problems:** False negatives: We had some false positives regarding
|
||||
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
|
||||
/// of `x.pop() && x.pop()`), so we removed matching any function or method
|
||||
/// calls. We may introduce a whitelist of known pure functions in the future.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x + 1 == x + 1
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for equal operands to comparison, logical and
|
||||
/// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
|
||||
/// `||`, `&`, `|`, `^`, `-` and `/`).
|
||||
///
|
||||
/// **Why is this bad?** This is usually just a typo or a copy and paste error.
|
||||
///
|
||||
/// **Known problems:** False negatives: We had some false positives regarding
|
||||
/// calls (notably [racer](https://github.com/phildawes/racer) had one instance
|
||||
/// of `x.pop() && x.pop()`), so we removed matching any function or method
|
||||
/// calls. We may introduce a whitelist of known pure functions in the future.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// x + 1 == x + 1
|
||||
/// ```
|
||||
pub EQ_OP,
|
||||
correctness,
|
||||
"equal operands on both sides of a comparison or bitwise combination (e.g. `x == x`)"
|
||||
"equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for arguments to `==` which have their address
|
||||
/// taken to satisfy a bound
|
||||
/// and suggests to dereference the other argument instead
|
||||
///
|
||||
/// **Why is this bad?** It is more idiomatic to dereference the other argument.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// &x == y
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for arguments to `==` which have their address
|
||||
/// taken to satisfy a bound
|
||||
/// and suggests to dereference the other argument instead
|
||||
///
|
||||
/// **Why is this bad?** It is more idiomatic to dereference the other argument.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// &x == y
|
||||
/// ```
|
||||
pub OP_REF,
|
||||
style,
|
||||
"taking a reference to satisfy the type constraints on `==`"
|
||||
|
|
|
@ -1,28 +1,30 @@
|
|||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{in_macro, span_lint};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for erasing operations, e.g. `x * 0`.
|
||||
///
|
||||
/// **Why is this bad?** The whole expression can be replaced by zero.
|
||||
/// This is most likely not the intended outcome and should probably be
|
||||
/// corrected
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 0 / x;
|
||||
/// 0 * x;
|
||||
/// x & 0
|
||||
/// ```
|
||||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{in_macro, span_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for erasing operations, e.g., `x * 0`.
|
||||
///
|
||||
/// **Why is this bad?** The whole expression can be replaced by zero.
|
||||
/// This is most likely not the intended outcome and should probably be
|
||||
/// corrected
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 1;
|
||||
/// 0 / x;
|
||||
/// 0 * x;
|
||||
/// x & 0;
|
||||
/// ```
|
||||
pub ERASING_OP,
|
||||
correctness,
|
||||
"using erasing operations, e.g. `x * 0` or `y & 0`"
|
||||
"using erasing operations, e.g., `x * 0` or `y & 0`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use crate::utils::span_lint;
|
||||
use rustc::hir::intravisit as visit;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
|
@ -6,33 +5,34 @@ use rustc::middle::expr_use_visitor::*;
|
|||
use rustc::middle::mem_categorization::{cmt_, Categorization};
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::util::nodemap::NodeSet;
|
||||
use rustc::util::nodemap::HirIdSet;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
use crate::utils::span_lint;
|
||||
|
||||
pub struct Pass {
|
||||
pub too_large_for_stack: u64,
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
|
||||
/// work fine.
|
||||
///
|
||||
/// **Why is this bad?** This is an unnecessary allocation, and bad for
|
||||
/// performance. It is only necessary to allocate if you wish to move the box
|
||||
/// into something.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `Box<T>` where an unboxed `T` would
|
||||
/// work fine.
|
||||
///
|
||||
/// **Why is this bad?** This is an unnecessary allocation, and bad for
|
||||
/// performance. It is only necessary to allocate if you wish to move the box
|
||||
/// into something.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// }
|
||||
/// ```
|
||||
pub BOXED_LOCAL,
|
||||
perf,
|
||||
"using `Box<T>` where unnecessary"
|
||||
|
@ -44,7 +44,7 @@ fn is_non_trait_box(ty: Ty<'_>) -> bool {
|
|||
|
||||
struct EscapeDelegate<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
set: NodeSet,
|
||||
set: HirIdSet,
|
||||
too_large_for_stack: u64,
|
||||
}
|
||||
|
||||
|
@ -66,11 +66,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
_: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
_: Span,
|
||||
node_id: NodeId,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
// If the method is an impl for a trait, don't warn
|
||||
let parent_id = cx.tcx.hir().get_parent(node_id);
|
||||
let parent_node = cx.tcx.hir().find(parent_id);
|
||||
// If the method is an impl for a trait, don't warn.
|
||||
let parent_id = cx.tcx.hir().get_parent_item(hir_id);
|
||||
let parent_node = cx.tcx.hir().find_by_hir_id(parent_id);
|
||||
|
||||
if let Some(Node::Item(item)) = parent_node {
|
||||
if let ItemKind::Impl(_, _, _, _, Some(..), _, _) = item.node {
|
||||
|
@ -80,11 +80,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
|
||||
let mut v = EscapeDelegate {
|
||||
cx,
|
||||
set: NodeSet::default(),
|
||||
set: HirIdSet::default(),
|
||||
too_large_for_stack: self.too_large_for_stack,
|
||||
};
|
||||
|
||||
let fn_def_id = cx.tcx.hir().local_def_id(node_id);
|
||||
let fn_def_id = cx.tcx.hir().local_def_id_from_hir_id(hir_id);
|
||||
let region_scope_tree = &cx.tcx.region_scope_tree(fn_def_id);
|
||||
ExprUseVisitor::new(&mut v, cx.tcx, cx.param_env, region_scope_tree, cx.tables, None).consume_body(body);
|
||||
|
||||
|
@ -92,7 +92,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
span_lint(
|
||||
cx,
|
||||
BOXED_LOCAL,
|
||||
cx.tcx.hir().span(node),
|
||||
cx.tcx.hir().span_by_hir_id(node),
|
||||
"local variable doesn't need to be boxed here",
|
||||
);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
fn consume(&mut self, _: NodeId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
|
||||
fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
if let Move(DirectRefMove) | Move(CaptureMove) = mode {
|
||||
// moved out or in. clearly can't be localized
|
||||
|
@ -111,13 +111,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
fn matched_pat(&mut self, _: &Pat, _: &cmt_<'tcx>, _: MatchMode) {}
|
||||
fn consume_pat(&mut self, consume_pat: &Pat, cmt: &cmt_<'tcx>, _: ConsumeMode) {
|
||||
let map = &self.cx.tcx.hir();
|
||||
if map.is_argument(consume_pat.id) {
|
||||
if map.is_argument(map.hir_to_node_id(consume_pat.hir_id)) {
|
||||
// Skip closure arguments
|
||||
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(consume_pat.id)) {
|
||||
if let Some(Node::Expr(..)) = map.find_by_hir_id(map.get_parent_node_by_hir_id(consume_pat.hir_id)) {
|
||||
return;
|
||||
}
|
||||
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
|
||||
self.set.insert(consume_pat.id);
|
||||
self.set.insert(consume_pat.hir_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
if let ExprKind::Box(..) = ex.node {
|
||||
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
|
||||
// let x = box (...)
|
||||
self.set.insert(consume_pat.id);
|
||||
self.set.insert(consume_pat.hir_id);
|
||||
}
|
||||
// TODO Box::new
|
||||
// TODO vec![]
|
||||
|
@ -143,14 +143,14 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
if self.set.contains(&lid) {
|
||||
// let y = x where x is known
|
||||
// remove x, insert y
|
||||
self.set.insert(consume_pat.id);
|
||||
self.set.insert(consume_pat.hir_id);
|
||||
self.set.remove(&lid);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn borrow(
|
||||
&mut self,
|
||||
_: NodeId,
|
||||
_: HirId,
|
||||
_: Span,
|
||||
cmt: &cmt_<'tcx>,
|
||||
_: ty::Region<'_>,
|
||||
|
@ -159,32 +159,31 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
|||
) {
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
match loan_cause {
|
||||
// x.foo()
|
||||
// Used without autodereffing (i.e. x.clone())
|
||||
// `x.foo()`
|
||||
// Used without autoderef-ing (i.e., `x.clone()`).
|
||||
LoanCause::AutoRef |
|
||||
|
||||
// &x
|
||||
// foo(&x) where no extra autoreffing is happening
|
||||
// `&x`
|
||||
// `foo(&x)` where no extra autoref-ing is happening.
|
||||
LoanCause::AddrOf |
|
||||
|
||||
// `match x` can move
|
||||
// `match x` can move.
|
||||
LoanCause::MatchDiscriminant => {
|
||||
self.set.remove(&lid);
|
||||
}
|
||||
|
||||
// do nothing for matches, etc. These can't escape
|
||||
// Do nothing for matches, etc. These can't escape.
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
|
||||
fn mutate(&mut self, _: NodeId, _: Span, _: &cmt_<'tcx>, _: MutateMode) {}
|
||||
fn decl_without_init(&mut self, _: HirId, _: Span) {}
|
||||
fn mutate(&mut self, _: HirId, _: Span, _: &cmt_<'tcx>, _: MutateMode) {}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
|
||||
fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
|
||||
// Large types need to be boxed to avoid stack
|
||||
// overflows.
|
||||
// Large types need to be boxed to avoid stack overflows.
|
||||
if ty.is_box() {
|
||||
self.cx.layout_of(ty.boxed_ty()).ok().map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
|
||||
} else {
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintContext, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
use crate::utils::{is_adjusted, iter_input_pats, snippet_opt, span_lint_and_then, type_is_unsafe_function};
|
||||
|
||||
pub struct EtaPass;
|
||||
|
||||
/// **What it does:** Checks for closures which just call another function where
|
||||
/// the function can be called directly. `unsafe` functions or calls where types
|
||||
/// get adjusted are ignored.
|
||||
///
|
||||
/// **Why is this bad?** Needlessly creating a closure adds code for no benefit
|
||||
/// and gives the optimizer more work.
|
||||
///
|
||||
/// **Known problems:** If creating the closure inside the closure has a side-
|
||||
/// effect then moving the closure creation out will change when that side-
|
||||
/// effect runs.
|
||||
/// See https://github.com/rust-lang/rust-clippy/issues/1439 for more
|
||||
/// details.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// xs.map(|x| foo(x))
|
||||
/// ```
|
||||
/// where `foo(_)` is a plain function that takes the exact argument type of
|
||||
/// `x`.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for closures which just call another function where
|
||||
/// the function can be called directly. `unsafe` functions or calls where types
|
||||
/// get adjusted are ignored.
|
||||
///
|
||||
/// **Why is this bad?** Needlessly creating a closure adds code for no benefit
|
||||
/// and gives the optimizer more work.
|
||||
///
|
||||
/// **Known problems:** If creating the closure inside the closure has a side-
|
||||
/// effect then moving the closure creation out will change when that side-
|
||||
/// effect runs.
|
||||
/// See rust-lang/rust-clippy#1439 for more details.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// xs.map(|x| foo(x))
|
||||
/// ```
|
||||
/// where `foo(_)` is a plain function that takes the exact argument type of
|
||||
/// `x`.
|
||||
pub REDUNDANT_CLOSURE,
|
||||
style,
|
||||
"redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)"
|
||||
"redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
|
||||
}
|
||||
|
||||
impl LintPass for EtaPass {
|
||||
|
@ -45,6 +45,10 @@ impl LintPass for EtaPass {
|
|||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EtaPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if in_external_macro(cx.sess(), expr.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
match expr.node {
|
||||
ExprKind::Call(_, ref args) | ExprKind::MethodCall(_, _, ref args) => {
|
||||
for arg in args {
|
||||
|
@ -129,18 +133,13 @@ fn get_ufcs_type_name(
|
|||
let actual_type_of_self = &cx.tables.node_type(self_arg.hir_id).sty;
|
||||
|
||||
if let Some(trait_id) = cx.tcx.trait_of_item(method_def_id) {
|
||||
//if the method expectes &self, ufcs requires explicit borrowing so closure can't be removed
|
||||
return match (expected_type_of_self, actual_type_of_self) {
|
||||
(ty::Ref(_, _, _), ty::Ref(_, _, _)) => Some(cx.tcx.item_path_str(trait_id)),
|
||||
(l, r) => match (l, r) {
|
||||
(ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => None,
|
||||
(_, _) => Some(cx.tcx.item_path_str(trait_id)),
|
||||
},
|
||||
};
|
||||
if match_borrow_depth(expected_type_of_self, actual_type_of_self) {
|
||||
return Some(cx.tcx.item_path_str(trait_id));
|
||||
}
|
||||
}
|
||||
|
||||
cx.tcx.impl_of_method(method_def_id).and_then(|_| {
|
||||
//a type may implicitly implement other types methods (e.g. Deref)
|
||||
//a type may implicitly implement other type's methods (e.g. Deref)
|
||||
if match_types(expected_type_of_self, actual_type_of_self) {
|
||||
return Some(get_type_name(cx, &actual_type_of_self));
|
||||
}
|
||||
|
@ -148,6 +147,16 @@ fn get_ufcs_type_name(
|
|||
})
|
||||
}
|
||||
|
||||
fn match_borrow_depth(lhs: &ty::TyKind<'_>, rhs: &ty::TyKind<'_>) -> bool {
|
||||
match (lhs, rhs) {
|
||||
(ty::Ref(_, t1, _), ty::Ref(_, t2, _)) => match_borrow_depth(&t1.sty, &t2.sty),
|
||||
(l, r) => match (l, r) {
|
||||
(ty::Ref(_, _, _), _) | (_, ty::Ref(_, _, _)) => false,
|
||||
(_, _) => true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn match_types(lhs: &ty::TyKind<'_>, rhs: &ty::TyKind<'_>) -> bool {
|
||||
match (lhs, rhs) {
|
||||
(ty::Bool, ty::Bool)
|
||||
|
@ -173,7 +182,7 @@ fn get_type_name(cx: &LateContext<'_, '_>, kind: &ty::TyKind<'_>) -> String {
|
|||
|
||||
fn compare_inputs(closure_inputs: &mut dyn Iterator<Item = &Arg>, call_args: &mut dyn Iterator<Item = &Expr>) -> bool {
|
||||
for (closure_input, function_arg) in closure_inputs.zip(call_args) {
|
||||
if let PatKind::Binding(_, _, _, ident, _) = closure_input.pat.node {
|
||||
if let PatKind::Binding(_, _, ident, _) = closure_input.pat.node {
|
||||
// XXXManishearth Should I be checking the binding mode here?
|
||||
if let ExprKind::Path(QPath::Resolved(None, ref p)) = function_arg.node {
|
||||
if p.segments.len() != 1 {
|
||||
|
|
|
@ -7,48 +7,48 @@ use rustc::ty;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast;
|
||||
|
||||
/// **What it does:** Checks for a read and a write to the same variable where
|
||||
/// whether the read occurs before or after the write depends on the evaluation
|
||||
/// order of sub-expressions.
|
||||
///
|
||||
/// **Why is this bad?** It is often confusing to read. In addition, the
|
||||
/// sub-expression evaluation order for Rust is not well documented.
|
||||
///
|
||||
/// **Known problems:** Code which intentionally depends on the evaluation
|
||||
/// order, or which is correct for any evaluation order.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut x = 0;
|
||||
/// let a = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
/// } + x;
|
||||
/// // Unclear whether a is 1 or 2.
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for a read and a write to the same variable where
|
||||
/// whether the read occurs before or after the write depends on the evaluation
|
||||
/// order of sub-expressions.
|
||||
///
|
||||
/// **Why is this bad?** It is often confusing to read. In addition, the
|
||||
/// sub-expression evaluation order for Rust is not well documented.
|
||||
///
|
||||
/// **Known problems:** Code which intentionally depends on the evaluation
|
||||
/// order, or which is correct for any evaluation order.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut x = 0;
|
||||
/// let a = {
|
||||
/// x = 1;
|
||||
/// 1
|
||||
/// } + x;
|
||||
/// // Unclear whether a is 1 or 2.
|
||||
/// ```
|
||||
pub EVAL_ORDER_DEPENDENCE,
|
||||
complexity,
|
||||
"whether a variable read occurs before a write depends on sub-expression evaluation order"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for diverging calls that are not match arms or
|
||||
/// statements.
|
||||
///
|
||||
/// **Why is this bad?** It is often confusing to read. In addition, the
|
||||
/// sub-expression evaluation order for Rust is not well documented.
|
||||
///
|
||||
/// **Known problems:** Someone might want to use `some_bool || panic!()` as a
|
||||
/// shorthand.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let a = b() || panic!() || c();
|
||||
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
|
||||
/// let x = (a, b, c, panic!());
|
||||
/// // can simply be replaced by `panic!()`
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for diverging calls that are not match arms or
|
||||
/// statements.
|
||||
///
|
||||
/// **Why is this bad?** It is often confusing to read. In addition, the
|
||||
/// sub-expression evaluation order for Rust is not well documented.
|
||||
///
|
||||
/// **Known problems:** Someone might want to use `some_bool || panic!()` as a
|
||||
/// shorthand.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let a = b() || panic!() || c();
|
||||
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
|
||||
/// let x = (a, b, c, panic!());
|
||||
/// // can simply be replaced by `panic!()`
|
||||
/// ```
|
||||
pub DIVERGING_SUB_EXPRESSION,
|
||||
complexity,
|
||||
"whether an expression contains a diverging sub expression"
|
||||
|
@ -186,13 +186,13 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
|
|||
/// When such a read is found, the lint is triggered.
|
||||
fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
|
||||
let map = &vis.cx.tcx.hir();
|
||||
let mut cur_id = vis.write_expr.id;
|
||||
let mut cur_id = vis.write_expr.hir_id;
|
||||
loop {
|
||||
let parent_id = map.get_parent_node(cur_id);
|
||||
let parent_id = map.get_parent_node_by_hir_id(cur_id);
|
||||
if parent_id == cur_id {
|
||||
break;
|
||||
}
|
||||
let parent_node = match map.find(parent_id) {
|
||||
let parent_node = match map.find_by_hir_id(parent_id) {
|
||||
Some(parent) => parent,
|
||||
None => break,
|
||||
};
|
||||
|
@ -224,7 +224,7 @@ enum StopEarly {
|
|||
}
|
||||
|
||||
fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr) -> StopEarly {
|
||||
if expr.id == vis.last_expr.id {
|
||||
if expr.hir_id == vis.last_expr.hir_id {
|
||||
return StopEarly::KeepGoing;
|
||||
}
|
||||
|
||||
|
@ -286,7 +286,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt) -> St
|
|||
/// A visitor that looks for reads from a variable.
|
||||
struct ReadVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
/// The id of the variable we're looking for.
|
||||
/// The ID of the variable we're looking for.
|
||||
var: ast::NodeId,
|
||||
/// The expressions where the write to the variable occurred (for reporting
|
||||
/// in the lint).
|
||||
|
@ -298,7 +298,7 @@ struct ReadVisitor<'a, 'tcx: 'a> {
|
|||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr) {
|
||||
if expr.id == self.last_expr.id {
|
||||
if expr.hir_id == self.last_expr.hir_id {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -351,11 +351,11 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns true if `expr` is the LHS of an assignment, like `expr = ...`.
|
||||
/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
|
||||
fn is_in_assignment_position(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
||||
if let Some(parent) = get_parent_expr(cx, expr) {
|
||||
if let ExprKind::Assign(ref lhs, _) = parent.node {
|
||||
return lhs.id == expr.id;
|
||||
return lhs.hir_id == expr.hir_id;
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
|
@ -11,25 +11,25 @@ use std::fmt;
|
|||
use syntax::ast::*;
|
||||
use syntax_pos::symbol::Symbol;
|
||||
|
||||
/// **What it does:** Checks for float literals with a precision greater
|
||||
/// than that supported by the underlying type
|
||||
///
|
||||
/// **Why is this bad?** Rust will truncate the literal silently.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let v: f32 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789
|
||||
///
|
||||
/// // Good
|
||||
/// let v: f64 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789_9
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for float literals with a precision greater
|
||||
/// than that supported by the underlying type
|
||||
///
|
||||
/// **Why is this bad?** Rust will truncate the literal silently.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// let v: f32 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789
|
||||
///
|
||||
/// // Good
|
||||
/// let v: f64 = 0.123_456_789_9;
|
||||
/// println!("{}", v); // 0.123_456_789_9
|
||||
/// ```
|
||||
pub EXCESSIVE_PRECISION,
|
||||
style,
|
||||
"excessive precision for float literal"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::utils::{is_expn_of, match_def_path, opt_def_id, resolve_node, span_lint, span_lint_and_sugg};
|
||||
use crate::utils::{is_expn_of, match_def_path, resolve_node, span_lint, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
|
@ -6,19 +6,19 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::ast::LitKind;
|
||||
|
||||
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
|
||||
/// replaced with `(e)print!()` / `(e)println!()`
|
||||
///
|
||||
/// **Why is this bad?** Using `(e)println! is clearer and more concise
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
|
||||
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `write!()` / `writeln()!` which can be
|
||||
/// replaced with `(e)print!()` / `(e)println!()`
|
||||
///
|
||||
/// **Why is this bad?** Using `(e)println! is clearer and more concise
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
|
||||
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
|
||||
/// ```
|
||||
pub EXPLICIT_WRITE,
|
||||
complexity,
|
||||
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
|
||||
|
@ -53,7 +53,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
if let ExprKind::Call(ref dest_fun, _) = write_args[0].node;
|
||||
if let ExprKind::Path(ref qpath) = dest_fun.node;
|
||||
if let Some(dest_fun_id) =
|
||||
opt_def_id(resolve_node(cx, qpath, dest_fun.hir_id));
|
||||
resolve_node(cx, qpath, dest_fun.hir_id).opt_def_id();
|
||||
if let Some(dest_name) = if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stdout"]) {
|
||||
Some("stdout")
|
||||
} else if match_def_path(cx.tcx, dest_fun_id, &["std", "io", "stdio", "stderr"]) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::utils::paths::{BEGIN_PANIC, BEGIN_PANIC_FMT, FROM_TRAIT, OPTION, RESULT};
|
||||
use crate::utils::{is_expn_of, match_def_path, method_chain_args, opt_def_id, span_lint_and_then, walk_ptrs_ty};
|
||||
use crate::utils::{is_expn_of, match_def_path, method_chain_args, span_lint_and_then, walk_ptrs_ty};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
|
@ -7,22 +7,22 @@ use rustc::ty;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax_pos::Span;
|
||||
|
||||
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
|
||||
///
|
||||
/// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct Foo(i32);
|
||||
/// impl From<String> for Foo {
|
||||
/// fn from(s: String) -> Self {
|
||||
/// Foo(s.parse().unwrap())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
|
||||
///
|
||||
/// **Why is this bad?** `TryFrom` should be used if there's a possibility of failure.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct Foo(i32);
|
||||
/// impl From<String> for Foo {
|
||||
/// fn from(s: String) -> Self {
|
||||
/// Foo(s.parse().unwrap())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub FALLIBLE_IMPL_FROM,
|
||||
nursery,
|
||||
"Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
|
||||
|
@ -43,7 +43,7 @@ impl LintPass for FallibleImplFrom {
|
|||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for FallibleImplFrom {
|
||||
fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::Item) {
|
||||
// check for `impl From<???> for ..`
|
||||
let impl_def_id = cx.tcx.hir().local_def_id(item.id);
|
||||
let impl_def_id = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
|
||||
if_chain! {
|
||||
if let hir::ItemKind::Impl(.., ref impl_items) = item.node;
|
||||
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_def_id);
|
||||
|
@ -71,7 +71,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it
|
|||
if_chain! {
|
||||
if let ExprKind::Call(ref func_expr, _) = expr.node;
|
||||
if let ExprKind::Path(QPath::Resolved(_, ref path)) = func_expr.node;
|
||||
if let Some(path_def_id) = opt_def_id(path.def);
|
||||
if let Some(path_def_id) = path.def.opt_def_id();
|
||||
if match_def_path(self.tcx, path_def_id, &BEGIN_PANIC) ||
|
||||
match_def_path(self.tcx, path_def_id, &BEGIN_PANIC_FMT);
|
||||
if is_expn_of(expr.span, "unreachable").is_none();
|
||||
|
@ -105,7 +105,7 @@ fn lint_impl_body<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, impl_span: Span, impl_it
|
|||
then {
|
||||
// check the body for `begin_panic` or `unwrap`
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let impl_item_def_id = cx.tcx.hir().local_def_id(impl_item.id.node_id);
|
||||
let impl_item_def_id = cx.tcx.hir().local_def_id_from_hir_id(impl_item.id.hir_id);
|
||||
let mut fpu = FindPanicUnwrap {
|
||||
tcx: cx.tcx,
|
||||
tables: cx.tcx.typeck_tables_of(impl_item_def_id),
|
||||
|
|
|
@ -1,33 +1,34 @@
|
|||
use crate::utils::paths;
|
||||
use crate::utils::{
|
||||
in_macro, is_expn_of, last_path_segment, match_def_path, match_type, opt_def_id, resolve_node, snippet,
|
||||
span_lint_and_then, walk_ptrs_ty,
|
||||
in_macro, is_expn_of, last_path_segment, match_def_path, match_type, resolve_node, snippet, span_lint_and_then,
|
||||
walk_ptrs_ty,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for the use of `format!("string literal with no
|
||||
/// argument")` and `format!("{}", foo)` where `foo` is a string.
|
||||
///
|
||||
/// **Why is this bad?** There is no point of doing that. `format!("foo")` can
|
||||
/// be replaced by `"foo".to_owned()` if you really need a `String`. The even
|
||||
/// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
|
||||
/// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
|
||||
/// if `foo: &str`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
/// format!("foo")
|
||||
/// format!("{}", foo)
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of `format!("string literal with no
|
||||
/// argument")` and `format!("{}", foo)` where `foo` is a string.
|
||||
///
|
||||
/// **Why is this bad?** There is no point of doing that. `format!("foo")` can
|
||||
/// be replaced by `"foo".to_owned()` if you really need a `String`. The even
|
||||
/// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
|
||||
/// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
|
||||
/// if `foo: &str`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
/// format!("foo")
|
||||
/// format!("{}", foo)
|
||||
/// ```
|
||||
pub USELESS_FORMAT,
|
||||
complexity,
|
||||
"useless use of `format!`"
|
||||
|
@ -57,7 +58,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
ExprKind::Call(ref fun, ref args) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(ref qpath) = fun.node;
|
||||
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, fun.hir_id));
|
||||
if let Some(fun_def_id) = resolve_node(cx, qpath, fun.hir_id).opt_def_id();
|
||||
let new_v1 = match_def_path(cx.tcx, fun_def_id, &paths::FMT_ARGUMENTS_NEWV1);
|
||||
let new_v1_fmt = match_def_path(
|
||||
cx.tcx,
|
||||
|
@ -82,14 +83,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
};
|
||||
|
||||
span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |db| {
|
||||
db.span_suggestion(
|
||||
expr.span,
|
||||
message,
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
span_useless_format(cx, span, message, sugg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -98,14 +92,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
if let ExprKind::Tup(ref tup) = matchee.node {
|
||||
if tup.is_empty() {
|
||||
let sugg = format!("{}.to_string()", snippet(cx, expr.span, "<expr>").into_owned());
|
||||
span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |db| {
|
||||
db.span_suggestion(
|
||||
span,
|
||||
"consider using .to_string()",
|
||||
sugg,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
});
|
||||
span_useless_format(cx, span, "consider using .to_string()", sugg);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -115,6 +102,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
}
|
||||
|
||||
fn span_useless_format<'a, 'tcx: 'a, T: LintContext<'tcx>>(cx: &'a T, span: Span, help: &str, mut sugg: String) {
|
||||
let to_replace = span.source_callsite();
|
||||
|
||||
// The callsite span contains the statement semicolon for some reason.
|
||||
let snippet = snippet(cx, to_replace, "..");
|
||||
if snippet.ends_with(';') {
|
||||
sugg.push(';');
|
||||
}
|
||||
|
||||
span_lint_and_then(cx, USELESS_FORMAT, span, "useless use of `format!`", |db| {
|
||||
db.span_suggestion(
|
||||
to_replace,
|
||||
help,
|
||||
sugg,
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks if the expressions matches `&[""]`
|
||||
fn check_single_piece(expr: &Expr) -> bool {
|
||||
if_chain! {
|
||||
|
@ -153,7 +159,7 @@ fn get_single_string_arg<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option
|
|||
if let ExprKind::Call(_, ref args) = exprs[0].node;
|
||||
if args.len() == 2;
|
||||
if let ExprKind::Path(ref qpath) = args[1].node;
|
||||
if let Some(fun_def_id) = opt_def_id(resolve_node(cx, qpath, args[1].hir_id));
|
||||
if let Some(fun_def_id) = resolve_node(cx, qpath, args[1].hir_id).opt_def_id();
|
||||
if match_def_path(cx.tcx, fun_def_id, &paths::DISPLAY_FMT_METHOD);
|
||||
then {
|
||||
let ty = walk_ptrs_ty(cx.tables.pat_ty(&pat[0]));
|
||||
|
|
|
@ -4,75 +4,75 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use syntax::ast;
|
||||
use syntax::ptr::P;
|
||||
|
||||
/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
|
||||
/// operators.
|
||||
///
|
||||
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
|
||||
/// confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
|
||||
/// operators.
|
||||
///
|
||||
/// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
|
||||
/// confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
|
||||
/// ```
|
||||
pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
style,
|
||||
"suspicious formatting of `*=`, `-=` or `!=`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
|
||||
/// is followed immediately by a newline or the `else` seems to be missing.
|
||||
///
|
||||
/// **Why is this bad?** This is probably some refactoring remnant, even if the
|
||||
/// code is correct, it might look confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } else
|
||||
///
|
||||
/// { // this is the `else` block of the previous `if`, but should it be?
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } else
|
||||
///
|
||||
/// if bar { // this is the `else` block of the previous `if`, but should it be?
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for formatting of `else`. It lints if the `else`
|
||||
/// is followed immediately by a newline or the `else` seems to be missing.
|
||||
///
|
||||
/// **Why is this bad?** This is probably some refactoring remnant, even if the
|
||||
/// code is correct, it might look confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } else
|
||||
///
|
||||
/// { // this is the `else` block of the previous `if`, but should it be?
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } else
|
||||
///
|
||||
/// if bar { // this is the `else` block of the previous `if`, but should it be?
|
||||
/// }
|
||||
/// ```
|
||||
pub SUSPICIOUS_ELSE_FORMATTING,
|
||||
style,
|
||||
"suspicious formatting of `else`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// **Why is this bad?** This could lead to unexpected results.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// **Why is this bad?** This could lead to unexpected results.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
pub POSSIBLE_MISSING_COMMA,
|
||||
correctness,
|
||||
"possible missing comma in array"
|
||||
|
|
|
@ -8,75 +8,74 @@ use rustc::ty;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use syntax::ast;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for functions with too many parameters.
|
||||
///
|
||||
/// **Why is this bad?** Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for functions with too many parameters.
|
||||
///
|
||||
/// **Why is this bad?** Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
complexity,
|
||||
"functions with too many arguments"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for functions with a large amount of lines.
|
||||
///
|
||||
/// **Why is this bad?** Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ``` rust
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for functions with a large amount of lines.
|
||||
///
|
||||
/// **Why is this bad?** Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ``` rust
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// }
|
||||
/// ```
|
||||
pub TOO_MANY_LINES,
|
||||
pedantic,
|
||||
"functions with too many lines"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for public functions that dereferences raw pointer
|
||||
/// arguments but are not marked unsafe.
|
||||
///
|
||||
/// **Why is this bad?** The function should probably be marked `unsafe`, since
|
||||
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
|
||||
/// valid.
|
||||
///
|
||||
/// **Known problems:**
|
||||
///
|
||||
/// * It does not check functions recursively so if the pointer is passed to a
|
||||
/// private non-`unsafe` function which does the dereferencing, the lint won't
|
||||
/// trigger.
|
||||
/// * It only checks for arguments whose type are raw pointers, not raw pointers
|
||||
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
|
||||
/// `some_argument.get_raw_ptr()`).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for public functions that dereferences raw pointer
|
||||
/// arguments but are not marked unsafe.
|
||||
///
|
||||
/// **Why is this bad?** The function should probably be marked `unsafe`, since
|
||||
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
|
||||
/// valid.
|
||||
///
|
||||
/// **Known problems:**
|
||||
///
|
||||
/// * It does not check functions recursively so if the pointer is passed to a
|
||||
/// private non-`unsafe` function which does the dereferencing, the lint won't
|
||||
/// trigger.
|
||||
/// * It only checks for arguments whose type are raw pointers, not raw pointers
|
||||
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
|
||||
/// `some_argument.get_raw_ptr()`).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo(x: *const u8) {
|
||||
/// println!("{}", unsafe { *x });
|
||||
/// }
|
||||
/// ```
|
||||
pub NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
correctness,
|
||||
"public functions dereferencing raw pointer arguments but not marked `unsafe`"
|
||||
|
@ -112,9 +111,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
|
|||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Body,
|
||||
span: Span,
|
||||
nodeid: ast::NodeId,
|
||||
hir_id: hir::HirId,
|
||||
) {
|
||||
let is_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(nodeid)) {
|
||||
let is_impl = if let Some(hir::Node::Item(item)) = cx
|
||||
.tcx
|
||||
.hir()
|
||||
.find_by_hir_id(cx.tcx.hir().get_parent_node_by_hir_id(hir_id))
|
||||
{
|
||||
matches!(item.node, hir::ItemKind::Impl(_, _, _, _, Some(_), _, _))
|
||||
} else {
|
||||
false
|
||||
|
@ -146,8 +149,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
|
|||
}
|
||||
}
|
||||
|
||||
self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
|
||||
self.check_line_number(cx, span);
|
||||
self.check_raw_ptr(cx, unsafety, decl, body, hir_id);
|
||||
self.check_line_number(cx, span, body);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
|
||||
|
@ -159,7 +162,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
|
|||
|
||||
if let hir::TraitMethod::Provided(eid) = *eid {
|
||||
let body = cx.tcx.hir().body(eid);
|
||||
self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.id);
|
||||
self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,12 +181,12 @@ impl<'a, 'tcx> Functions {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span) {
|
||||
fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span, body: &'tcx hir::Body) {
|
||||
if in_external_macro(cx.sess(), span) {
|
||||
return;
|
||||
}
|
||||
|
||||
let code_snippet = snippet(cx, span, "..");
|
||||
let code_snippet = snippet(cx, body.value.span, "..");
|
||||
let mut line_count: u64 = 0;
|
||||
let mut in_comment = false;
|
||||
let mut code_in_line;
|
||||
|
@ -250,10 +253,11 @@ impl<'a, 'tcx> Functions {
|
|||
unsafety: hir::Unsafety,
|
||||
decl: &'tcx hir::FnDecl,
|
||||
body: &'tcx hir::Body,
|
||||
nodeid: ast::NodeId,
|
||||
hir_id: hir::HirId,
|
||||
) {
|
||||
let expr = &body.value;
|
||||
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
|
||||
let node_id = cx.tcx.hir().hir_to_node_id(hir_id);
|
||||
if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(node_id) {
|
||||
let raw_ptrs = iter_input_pats(decl, body)
|
||||
.zip(decl.inputs.iter())
|
||||
.filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
|
||||
|
@ -273,8 +277,8 @@ impl<'a, 'tcx> Functions {
|
|||
}
|
||||
}
|
||||
|
||||
fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
|
||||
if let (&hir::PatKind::Binding(_, id, _, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
|
||||
fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<hir::HirId> {
|
||||
if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
|
@ -283,7 +287,7 @@ fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
|
|||
|
||||
struct DerefVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
ptrs: FxHashSet<ast::NodeId>,
|
||||
ptrs: FxHashSet<hir::HirId>,
|
||||
tables: &'a ty::TypeckTables<'tcx>,
|
||||
}
|
||||
|
||||
|
@ -324,7 +328,7 @@ impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
|
|||
fn check_arg(&self, ptr: &hir::Expr) {
|
||||
if let hir::ExprKind::Path(ref qpath) = ptr.node {
|
||||
if let Def::Local(id) = self.cx.tables.qpath_def(qpath, ptr.hir_id) {
|
||||
if self.ptrs.contains(&id) {
|
||||
if self.ptrs.contains(&self.cx.tcx.hir().node_to_hir_id(id)) {
|
||||
span_lint(
|
||||
self.cx,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
|
|
|
@ -1,25 +1,24 @@
|
|||
use crate::utils::{
|
||||
in_macro, match_def_path, match_trait_method, same_tys, snippet, snippet_with_macro_callsite, span_lint_and_then,
|
||||
};
|
||||
use crate::utils::{opt_def_id, paths, resolve_node};
|
||||
use crate::utils::{paths, resolve_node};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast::NodeId;
|
||||
|
||||
/// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // format!() returns a `String`
|
||||
/// let s: String = format!("hello").into();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for always-identical `Into`/`From`/`IntoIter` conversions.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // format!() returns a `String`
|
||||
/// let s: String = format!("hello").into();
|
||||
/// ```
|
||||
pub IDENTITY_CONVERSION,
|
||||
complexity,
|
||||
"using always-identical `Into`/`From`/`IntoIter` conversions"
|
||||
|
@ -27,7 +26,7 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct IdentityConversion {
|
||||
try_desugar_arm: Vec<NodeId>,
|
||||
try_desugar_arm: Vec<HirId>,
|
||||
}
|
||||
|
||||
impl LintPass for IdentityConversion {
|
||||
|
@ -46,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
|
|||
return;
|
||||
}
|
||||
|
||||
if Some(&e.id) == self.try_desugar_arm.last() {
|
||||
if Some(&e.hir_id) == self.try_desugar_arm.last() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,7 +56,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
|
|||
_ => return,
|
||||
};
|
||||
if let ExprKind::Call(_, ref args) = e.node {
|
||||
self.try_desugar_arm.push(args[0].id);
|
||||
self.try_desugar_arm.push(args[0].hir_id);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -99,7 +98,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
|
|||
|
||||
ExprKind::Call(ref path, ref args) => {
|
||||
if let ExprKind::Path(ref qpath) = path.node {
|
||||
if let Some(def_id) = opt_def_id(resolve_node(cx, qpath, path.hir_id)) {
|
||||
if let Some(def_id) = resolve_node(cx, qpath, path.hir_id).opt_def_id() {
|
||||
if match_def_path(cx.tcx, def_id, &paths::FROM_FROM[..]) {
|
||||
let a = cx.tables.expr_ty(e);
|
||||
let b = cx.tables.expr_ty(&args[0]);
|
||||
|
@ -126,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IdentityConversion {
|
|||
}
|
||||
|
||||
fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if Some(&e.id) == self.try_desugar_arm.last() {
|
||||
if Some(&e.hir_id) == self.try_desugar_arm.last() {
|
||||
self.try_desugar_arm.pop();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,27 @@
|
|||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{clip, in_macro, snippet, span_lint, unsext};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for identity operations, e.g. `x + 0`.
|
||||
///
|
||||
/// **Why is this bad?** This code can be removed without changing the
|
||||
/// meaning. So it just obscures what's going on. Delete it mercilessly.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x / 1 + 0 * 1 - 0 | 0
|
||||
/// ```
|
||||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{clip, in_macro, snippet, span_lint, unsext};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for identity operations, e.g., `x + 0`.
|
||||
///
|
||||
/// **Why is this bad?** This code can be removed without changing the
|
||||
/// meaning. So it just obscures what's going on. Delete it mercilessly.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x / 1 + 0 * 1 - 0 | 0
|
||||
/// ```
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
"using identity operations, e.g. `x + 0` or `y / 1`"
|
||||
"using identity operations, e.g., `x + 0` or `y / 1`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -7,32 +7,32 @@ use syntax::ast::*;
|
|||
|
||||
use crate::utils::span_help_and_lint;
|
||||
|
||||
/// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
|
||||
/// else branch.
|
||||
///
|
||||
/// **Why is this bad?** Negations reduce the readability of statements.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if !v.is_empty() {
|
||||
/// a()
|
||||
/// } else {
|
||||
/// b()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// if v.is_empty() {
|
||||
/// b()
|
||||
/// } else {
|
||||
/// a()
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
|
||||
/// else branch.
|
||||
///
|
||||
/// **Why is this bad?** Negations reduce the readability of statements.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if !v.is_empty() {
|
||||
/// a()
|
||||
/// } else {
|
||||
/// b()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// if v.is_empty() {
|
||||
/// b()
|
||||
/// } else {
|
||||
/// a()
|
||||
/// }
|
||||
/// ```
|
||||
pub IF_NOT_ELSE,
|
||||
pedantic,
|
||||
"`if` branches that could be swapped so no negation operation is necessary on the condition"
|
||||
|
|
|
@ -1,33 +1,33 @@
|
|||
use crate::utils::{in_macro, is_expn_of, snippet_opt, span_lint_and_then};
|
||||
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, MatchSource};
|
||||
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, MatchSource};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::{ast::NodeId, source_map::Span};
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for missing return statements at the end of a block.
|
||||
///
|
||||
/// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
|
||||
/// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
|
||||
/// the last returning statement because the only difference is a missing `;`. Especially in bigger
|
||||
/// code with multiple return paths having a `return` keyword makes it easier to find the
|
||||
/// corresponding statements.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(x: usize) {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
/// add return
|
||||
/// ```rust
|
||||
/// fn foo(x: usize) {
|
||||
/// return x;
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for missing return statements at the end of a block.
|
||||
///
|
||||
/// **Why is this bad?** Actually omitting the return keyword is idiomatic Rust code. Programmers
|
||||
/// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
|
||||
/// the last returning statement because the only difference is a missing `;`. Especially in bigger
|
||||
/// code with multiple return paths having a `return` keyword makes it easier to find the
|
||||
/// corresponding statements.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(x: usize) {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
/// add return
|
||||
/// ```rust
|
||||
/// fn foo(x: usize) {
|
||||
/// return x;
|
||||
/// }
|
||||
/// ```
|
||||
pub IMPLICIT_RETURN,
|
||||
restriction,
|
||||
"use a return statement like `return expr` instead of an expression"
|
||||
|
@ -128,7 +128,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
_: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
span: Span,
|
||||
_: NodeId,
|
||||
_: HirId,
|
||||
) {
|
||||
let def_id = cx.tcx.hir().body_owner_def_id(body.id());
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
|
|
@ -10,75 +10,76 @@ use rustc::ty;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::RangeLimits;
|
||||
|
||||
/// **What it does:** Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// **Why is this bad?** This will always panic at runtime.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
///
|
||||
/// // Good
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// **Why is this bad?** This will always panic at runtime.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```no_run
|
||||
/// # #![allow(const_err)]
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
///
|
||||
/// // Good
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
pub OUT_OF_BOUNDS_INDEXING,
|
||||
correctness,
|
||||
"out of bounds constant indexing"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for usage of indexing or slicing. Arrays are special cased, this lint
|
||||
/// does report on arrays if we can tell that slicing operations are in bounds and does not
|
||||
/// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
|
||||
///
|
||||
/// **Why is this bad?** Indexing and slicing can panic at runtime and there are
|
||||
/// safe alternatives.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Vector
|
||||
/// let x = vec![0; 5];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[2];
|
||||
/// &x[2..100];
|
||||
/// &x[2..];
|
||||
/// &x[..100];
|
||||
///
|
||||
/// // Good
|
||||
/// x.get(2);
|
||||
/// x.get(2..100);
|
||||
/// x.get(2..);
|
||||
/// x.get(..100);
|
||||
///
|
||||
/// // Array
|
||||
/// let y = [0, 1, 2, 3];
|
||||
///
|
||||
/// // Bad
|
||||
/// &y[10..100];
|
||||
/// &y[10..];
|
||||
/// &y[..100];
|
||||
///
|
||||
/// // Good
|
||||
/// &y[2..];
|
||||
/// &y[..2];
|
||||
/// &y[0..3];
|
||||
/// y.get(10);
|
||||
/// y.get(10..100);
|
||||
/// y.get(10..);
|
||||
/// y.get(..100);
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of indexing or slicing. Arrays are special cased, this lint
|
||||
/// does report on arrays if we can tell that slicing operations are in bounds and does not
|
||||
/// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
|
||||
///
|
||||
/// **Why is this bad?** Indexing and slicing can panic at runtime and there are
|
||||
/// safe alternatives.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Vector
|
||||
/// let x = vec![0; 5];
|
||||
///
|
||||
/// // Bad
|
||||
/// x[2];
|
||||
/// &x[2..100];
|
||||
/// &x[2..];
|
||||
/// &x[..100];
|
||||
///
|
||||
/// // Good
|
||||
/// x.get(2);
|
||||
/// x.get(2..100);
|
||||
/// x.get(2..);
|
||||
/// x.get(..100);
|
||||
///
|
||||
/// // Array
|
||||
/// let y = [0, 1, 2, 3];
|
||||
///
|
||||
/// // Bad
|
||||
/// &y[10..100];
|
||||
/// &y[10..];
|
||||
/// &y[..100];
|
||||
///
|
||||
/// // Good
|
||||
/// &y[2..];
|
||||
/// &y[..2];
|
||||
/// &y[0..3];
|
||||
/// y.get(10);
|
||||
/// y.get(10..100);
|
||||
/// y.get(10..);
|
||||
/// y.get(..100);
|
||||
/// ```
|
||||
pub INDEXING_SLICING,
|
||||
restriction,
|
||||
"indexing/slicing usage"
|
||||
|
@ -102,7 +103,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
|
|||
if let ExprKind::Index(ref array, ref index) = &expr.node {
|
||||
let ty = cx.tables.expr_ty(array);
|
||||
if let Some(range) = higher::range(cx, index) {
|
||||
// Ranged indexes, i.e. &x[n..m], &x[n..], &x[..n] and &x[..]
|
||||
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
|
||||
if let ty::Array(_, s) = ty.sty {
|
||||
let size: u128 = s.assert_usize(cx.tcx).unwrap().into();
|
||||
|
||||
|
@ -148,7 +149,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
|
|||
|
||||
utils::span_help_and_lint(cx, INDEXING_SLICING, expr.span, "slicing may panic.", help_msg);
|
||||
} else {
|
||||
// Catchall non-range index, i.e. [n] or [n << m]
|
||||
// Catchall non-range index, i.e., [n] or [n << m]
|
||||
if let ty::Array(..) = ty.sty {
|
||||
// Index is a constant uint.
|
||||
if let Some(..) = constant(cx, cx.tables, index) {
|
||||
|
|
|
@ -5,36 +5,36 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for matches being used to destructure a single-variant enum
|
||||
/// or tuple struct where a `let` will suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Wrapper {
|
||||
/// Data(i32),
|
||||
/// }
|
||||
///
|
||||
/// let wrapper = Wrapper::Data(42);
|
||||
///
|
||||
/// let data = match wrapper {
|
||||
/// Wrapper::Data(i) => i,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
/// ```rust
|
||||
/// enum Wrapper {
|
||||
/// Data(i32),
|
||||
/// }
|
||||
///
|
||||
/// let wrapper = Wrapper::Data(42);
|
||||
/// let Wrapper::Data(data) = wrapper;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for matches being used to destructure a single-variant enum
|
||||
/// or tuple struct where a `let` will suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `let` doesn't nest, whereas a `match` does.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Wrapper {
|
||||
/// Data(i32),
|
||||
/// }
|
||||
///
|
||||
/// let wrapper = Wrapper::Data(42);
|
||||
///
|
||||
/// let data = match wrapper {
|
||||
/// Wrapper::Data(i) => i,
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
/// ```rust
|
||||
/// enum Wrapper {
|
||||
/// Data(i32),
|
||||
/// }
|
||||
///
|
||||
/// let wrapper = Wrapper::Data(42);
|
||||
/// let Wrapper::Data(data) = wrapper;
|
||||
/// ```
|
||||
pub INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
style,
|
||||
"a match statement with a single infallible arm instead of a `let`"
|
||||
|
|
|
@ -1,38 +1,41 @@
|
|||
use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for iteration that is guaranteed to be infinite.
|
||||
///
|
||||
/// **Why is this bad?** While there may be places where this is acceptable
|
||||
/// (e.g. in event streams), in most cases this is simply an error.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// repeat(1_u8).iter().collect::<Vec<_>>()
|
||||
/// ```
|
||||
use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for iteration that is guaranteed to be infinite.
|
||||
///
|
||||
/// **Why is this bad?** While there may be places where this is acceptable
|
||||
/// (e.g., in event streams), in most cases this is simply an error.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```no_run
|
||||
/// use std::iter;
|
||||
///
|
||||
/// iter::repeat(1_u8).collect::<Vec<_>>();
|
||||
/// ```
|
||||
pub INFINITE_ITER,
|
||||
correctness,
|
||||
"infinite iteration"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for iteration that may be infinite.
|
||||
///
|
||||
/// **Why is this bad?** While there may be places where this is acceptable
|
||||
/// (e.g. in event streams), in most cases this is simply an error.
|
||||
///
|
||||
/// **Known problems:** The code may have a condition to stop iteration, but
|
||||
/// this lint is not clever enough to analyze it.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for iteration that may be infinite.
|
||||
///
|
||||
/// **Why is this bad?** While there may be places where this is acceptable
|
||||
/// (e.g., in event streams), in most cases this is simply an error.
|
||||
///
|
||||
/// **Known problems:** The code may have a condition to stop iteration, but
|
||||
/// this lint is not clever enough to analyze it.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
|
||||
/// ```
|
||||
pub MAYBE_INFINITE_ITER,
|
||||
pedantic,
|
||||
"possible infinite iteration"
|
||||
|
@ -120,8 +123,8 @@ use self::Heuristic::{All, Always, Any, First};
|
|||
/// a slice of (method name, number of args, heuristic, bounds) tuples
|
||||
/// that will be used to determine whether the method in question
|
||||
/// returns an infinite or possibly infinite iterator. The finiteness
|
||||
/// is an upper bound, e.g. some methods can return a possibly
|
||||
/// infinite iterator at worst, e.g. `take_while`.
|
||||
/// is an upper bound, e.g., some methods can return a possibly
|
||||
/// infinite iterator at worst, e.g., `take_while`.
|
||||
static HEURISTICS: &[(&str, usize, Heuristic, Finiteness)] = &[
|
||||
("zip", 2, All, Infinite),
|
||||
("chain", 2, Any, Infinite),
|
||||
|
|
|
@ -8,33 +8,33 @@ use rustc_data_structures::fx::FxHashMap;
|
|||
use std::default::Default;
|
||||
use syntax_pos::Span;
|
||||
|
||||
/// **What it does:** Checks for multiple inherent implementations of a struct
|
||||
///
|
||||
/// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct X;
|
||||
/// impl X {
|
||||
/// fn one() {}
|
||||
/// }
|
||||
/// impl X {
|
||||
/// fn other() {}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct X;
|
||||
/// impl X {
|
||||
/// fn one() {}
|
||||
/// fn other() {}
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for multiple inherent implementations of a struct
|
||||
///
|
||||
/// **Why is this bad?** Splitting the implementation of a type makes the code harder to navigate.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct X;
|
||||
/// impl X {
|
||||
/// fn one() {}
|
||||
/// }
|
||||
/// impl X {
|
||||
/// fn other() {}
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct X;
|
||||
/// impl X {
|
||||
/// fn one() {}
|
||||
/// fn other() {}
|
||||
/// }
|
||||
/// ```
|
||||
pub MULTIPLE_INHERENT_IMPL,
|
||||
restriction,
|
||||
"Multiple inherent impl that could be grouped"
|
||||
|
|
|
@ -8,21 +8,21 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::ast::{Attribute, Name};
|
||||
|
||||
/// **What it does:** Checks for `#[inline]` on trait methods without bodies
|
||||
///
|
||||
/// **Why is this bad?** Only implementations of trait methods may be inlined.
|
||||
/// The inline attribute is ignored for trait methods without bodies.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// trait Animal {
|
||||
/// #[inline]
|
||||
/// fn name(&self) -> &'static str;
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `#[inline]` on trait methods without bodies
|
||||
///
|
||||
/// **Why is this bad?** Only implementations of trait methods may be inlined.
|
||||
/// The inline attribute is ignored for trait methods without bodies.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// trait Animal {
|
||||
/// #[inline]
|
||||
/// fn name(&self) -> &'static str;
|
||||
/// }
|
||||
/// ```
|
||||
pub INLINE_FN_WITHOUT_BODY,
|
||||
correctness,
|
||||
"use of `#[inline]` on trait methods without bodies"
|
||||
|
|
|
@ -7,24 +7,24 @@ use syntax::ast::*;
|
|||
|
||||
use crate::utils::{snippet_opt, span_lint_and_then};
|
||||
|
||||
/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x >= y + 1
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// x > y
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
|
||||
///
|
||||
///
|
||||
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x >= y + 1
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// x > y
|
||||
/// ```
|
||||
pub INT_PLUS_ONE,
|
||||
complexity,
|
||||
"instead of using x >= y + 1, use x > y"
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
use crate::utils::{match_def_path, opt_def_id, paths, span_help_and_lint};
|
||||
use crate::utils::{match_def_path, paths, span_help_and_lint};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for creation of references to zeroed or uninitialized memory.
|
||||
///
|
||||
/// **Why is this bad?** Creation of null references is undefined behavior.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let bad_ref: &usize = std::mem::zeroed();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for creation of references to zeroed or uninitialized memory.
|
||||
///
|
||||
/// **Why is this bad?** Creation of null references is undefined behavior.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```no_run
|
||||
/// let bad_ref: &usize = unsafe { std::mem::zeroed() };
|
||||
/// ```
|
||||
pub INVALID_REF,
|
||||
correctness,
|
||||
"creation of invalid reference"
|
||||
|
@ -45,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidRef {
|
|||
if let ExprKind::Path(ref qpath) = path.node;
|
||||
if args.len() == 0;
|
||||
if let ty::Ref(..) = cx.tables.expr_ty(expr).sty;
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
|
||||
if let Some(def_id) = cx.tables.qpath_def(qpath, path.hir_id).opt_def_id();
|
||||
then {
|
||||
let msg = if match_def_path(cx.tcx, def_id, &paths::MEM_ZEROED) |
|
||||
match_def_path(cx.tcx, def_id, &paths::INIT)
|
||||
|
|
|
@ -6,29 +6,29 @@ use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast::*;
|
||||
|
||||
/// **What it does:** Checks for items declared after some statement in a block.
|
||||
///
|
||||
/// **Why is this bad?** Items live for the entire scope they are declared
|
||||
/// in. But statements are processed in order. This might cause confusion as
|
||||
/// it's hard to figure out which item is meant in a statement.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// foo(); // prints "foo"
|
||||
/// fn foo() {
|
||||
/// println!("foo");
|
||||
/// }
|
||||
/// foo(); // prints "foo"
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for items declared after some statement in a block.
|
||||
///
|
||||
/// **Why is this bad?** Items live for the entire scope they are declared
|
||||
/// in. But statements are processed in order. This might cause confusion as
|
||||
/// it's hard to figure out which item is meant in a statement.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo() {
|
||||
/// println!("cake");
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// foo(); // prints "foo"
|
||||
/// fn foo() {
|
||||
/// println!("foo");
|
||||
/// }
|
||||
/// foo(); // prints "foo"
|
||||
/// }
|
||||
/// ```
|
||||
pub ITEMS_AFTER_STATEMENTS,
|
||||
pedantic,
|
||||
"blocks where an item comes after a statement"
|
||||
|
|
|
@ -7,23 +7,23 @@ use rustc::ty::layout::LayoutOf;
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for large size differences between variants on
|
||||
/// `enum`s.
|
||||
///
|
||||
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a
|
||||
/// large variant
|
||||
/// can penalize the memory layout of that enum.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Test {
|
||||
/// A(i32),
|
||||
/// B([i32; 8000]),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for large size differences between variants on
|
||||
/// `enum`s.
|
||||
///
|
||||
/// **Why is this bad?** Enum size is bounded by the largest variant. Having a
|
||||
/// large variant
|
||||
/// can penalize the memory layout of that enum.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// enum Test {
|
||||
/// A(i32),
|
||||
/// B([i32; 8000]),
|
||||
/// }
|
||||
/// ```
|
||||
pub LARGE_ENUM_VARIANT,
|
||||
perf,
|
||||
"large size difference between variants on an enum"
|
||||
|
@ -54,7 +54,7 @@ impl LintPass for LargeEnumVariant {
|
|||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LargeEnumVariant {
|
||||
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &Item) {
|
||||
let did = cx.tcx.hir().local_def_id(item.id);
|
||||
let did = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
|
||||
if let ItemKind::Enum(ref def, _) = item.node {
|
||||
let ty = cx.tcx.type_of(did);
|
||||
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
|
||||
|
|
|
@ -9,62 +9,61 @@ use rustc_errors::Applicability;
|
|||
use syntax::ast::{Lit, LitKind, Name};
|
||||
use syntax::source_map::{Span, Spanned};
|
||||
|
||||
/// **What it does:** Checks for getting the length of something via `.len()`
|
||||
/// just to compare to zero, and suggests using `.is_empty()` where applicable.
|
||||
///
|
||||
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
|
||||
/// than calculating their length. Notably, for slices, getting the length
|
||||
/// requires a subtraction whereas `.is_empty()` is just a comparison. So it is
|
||||
/// good to get into the habit of using `.is_empty()`, and having it is cheap.
|
||||
/// Besides, it makes the intent clearer than a manual comparison.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x.len() == 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// if y.len() != 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
/// instead use
|
||||
/// ```rust
|
||||
/// if x.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// if !y.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
pub LEN_ZERO,
|
||||
style,
|
||||
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` \
|
||||
could be used instead"
|
||||
/// **What it does:** Checks for getting the length of something via `.len()`
|
||||
/// just to compare to zero, and suggests using `.is_empty()` where applicable.
|
||||
///
|
||||
/// **Why is this bad?** Some structures can answer `.is_empty()` much faster
|
||||
/// than calculating their length. Notably, for slices, getting the length
|
||||
/// requires a subtraction whereas `.is_empty()` is just a comparison. So it is
|
||||
/// good to get into the habit of using `.is_empty()`, and having it is cheap.
|
||||
/// Besides, it makes the intent clearer than a manual comparison.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// if x.len() == 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// if y.len() != 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
/// instead use
|
||||
/// ```ignore
|
||||
/// if x.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// if !y.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub LEN_ZERO,
|
||||
style,
|
||||
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for items that implement `.len()` but not
|
||||
/// `.is_empty()`.
|
||||
///
|
||||
/// **Why is this bad?** It is good custom to have both methods, because for
|
||||
/// some data structures, asking about the length will be a costly operation,
|
||||
/// whereas `.is_empty()` can usually answer in constant time. Also it used to
|
||||
/// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
|
||||
/// lint will ignore such entities.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// impl X {
|
||||
/// pub fn len(&self) -> usize {
|
||||
/// ..
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for items that implement `.len()` but not
|
||||
/// `.is_empty()`.
|
||||
///
|
||||
/// **Why is this bad?** It is good custom to have both methods, because for
|
||||
/// some data structures, asking about the length will be a costly operation,
|
||||
/// whereas `.is_empty()` can usually answer in constant time. Also it used to
|
||||
/// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
|
||||
/// lint will ignore such entities.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// impl X {
|
||||
/// pub fn len(&self) -> usize {
|
||||
/// ..
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub LEN_WITHOUT_IS_EMPTY,
|
||||
style,
|
||||
"traits or impls with a public `len` method but no corresponding `is_empty` method"
|
||||
|
@ -132,7 +131,7 @@ fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item, trait_items
|
|||
item.ident.name == name
|
||||
&& if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self && {
|
||||
let did = cx.tcx.hir().local_def_id(item.id.node_id);
|
||||
let did = cx.tcx.hir().local_def_id_from_hir_id(item.id.hir_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
|
@ -149,9 +148,11 @@ fn check_trait_items(cx: &LateContext<'_, '_>, visited_trait: &Item, trait_items
|
|||
}
|
||||
}
|
||||
|
||||
if cx.access_levels.is_exported(visited_trait.id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
|
||||
let trait_node_id = cx.tcx.hir().hir_to_node_id(visited_trait.hir_id);
|
||||
|
||||
if cx.access_levels.is_exported(trait_node_id) && trait_items.iter().any(|i| is_named_self(cx, i, "len")) {
|
||||
let mut current_and_super_traits = FxHashSet::default();
|
||||
let visited_trait_def_id = cx.tcx.hir().local_def_id(visited_trait.id);
|
||||
let visited_trait_def_id = cx.tcx.hir().local_def_id_from_hir_id(visited_trait.hir_id);
|
||||
fill_trait_set(visited_trait_def_id, &mut current_and_super_traits, cx);
|
||||
|
||||
let is_empty_method_found = current_and_super_traits
|
||||
|
@ -183,7 +184,7 @@ fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item, impl_items: &[ImplIte
|
|||
item.ident.name == name
|
||||
&& if let AssociatedItemKind::Method { has_self } = item.kind {
|
||||
has_self && {
|
||||
let did = cx.tcx.hir().local_def_id(item.id.node_id);
|
||||
let did = cx.tcx.hir().local_def_id_from_hir_id(item.id.hir_id);
|
||||
cx.tcx.fn_sig(did).inputs().skip_binder().len() == 1
|
||||
}
|
||||
} else {
|
||||
|
@ -192,7 +193,10 @@ fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item, impl_items: &[ImplIte
|
|||
}
|
||||
|
||||
let is_empty = if let Some(is_empty) = impl_items.iter().find(|i| is_named_self(cx, i, "is_empty")) {
|
||||
if cx.access_levels.is_exported(is_empty.id.node_id) {
|
||||
if cx
|
||||
.access_levels
|
||||
.is_exported(cx.tcx.hir().hir_to_node_id(is_empty.id.hir_id))
|
||||
{
|
||||
return;
|
||||
} else {
|
||||
"a private"
|
||||
|
@ -202,8 +206,8 @@ fn check_impl_items(cx: &LateContext<'_, '_>, item: &Item, impl_items: &[ImplIte
|
|||
};
|
||||
|
||||
if let Some(i) = impl_items.iter().find(|i| is_named_self(cx, i, "len")) {
|
||||
if cx.access_levels.is_exported(i.id.node_id) {
|
||||
let def_id = cx.tcx.hir().local_def_id(item.id);
|
||||
if cx.access_levels.is_exported(cx.tcx.hir().hir_to_node_id(i.id.hir_id)) {
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(item.hir_id);
|
||||
let ty = cx.tcx.type_of(def_id);
|
||||
|
||||
span_lint(
|
||||
|
@ -270,9 +274,9 @@ fn check_len(
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if this type has an `is_empty` method.
|
||||
/// Checks if this type has an `is_empty` method.
|
||||
fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
||||
/// Get an `AssociatedItem` and return true if it matches `is_empty(self)`.
|
||||
/// Gets an `AssociatedItem` and return true if it matches `is_empty(self)`.
|
||||
fn is_is_empty(cx: &LateContext<'_, '_>, item: &ty::AssociatedItem) -> bool {
|
||||
if let ty::AssociatedKind::Method = item.kind {
|
||||
if item.ident.name == "is_empty" {
|
||||
|
@ -287,7 +291,7 @@ fn has_is_empty(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check the inherent impl's items for an `is_empty(self)` method.
|
||||
/// Checks the inherent impl's items for an `is_empty(self)` method.
|
||||
fn has_is_empty_impl(cx: &LateContext<'_, '_>, id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.inherent_impls(id)
|
||||
|
|
|
@ -6,48 +6,47 @@ use rustc::hir::BindingAnnotation;
|
|||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast;
|
||||
|
||||
/// **What it does:** Checks for variable declarations immediately followed by a
|
||||
/// conditional affectation.
|
||||
///
|
||||
/// **Why is this bad?** This is not idiomatic Rust.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// let foo;
|
||||
///
|
||||
/// if bar() {
|
||||
/// foo = 42;
|
||||
/// } else {
|
||||
/// foo = 0;
|
||||
/// }
|
||||
///
|
||||
/// let mut baz = None;
|
||||
///
|
||||
/// if bar() {
|
||||
/// baz = Some(42);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// should be written
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let foo = if bar() {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 0
|
||||
/// };
|
||||
///
|
||||
/// let baz = if bar() {
|
||||
/// Some(42)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for variable declarations immediately followed by a
|
||||
/// conditional affectation.
|
||||
///
|
||||
/// **Why is this bad?** This is not idiomatic Rust.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust,ignore
|
||||
/// let foo;
|
||||
///
|
||||
/// if bar() {
|
||||
/// foo = 42;
|
||||
/// } else {
|
||||
/// foo = 0;
|
||||
/// }
|
||||
///
|
||||
/// let mut baz = None;
|
||||
///
|
||||
/// if bar() {
|
||||
/// baz = Some(42);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// should be written
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let foo = if bar() {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 0
|
||||
/// };
|
||||
///
|
||||
/// let baz = if bar() {
|
||||
/// Some(42)
|
||||
/// } else {
|
||||
/// None
|
||||
/// };
|
||||
/// ```
|
||||
pub USELESS_LET_IF_SEQ,
|
||||
style,
|
||||
"unidiomatic `let mut` declaration followed by initialization in `if`"
|
||||
|
@ -73,7 +72,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
|
|||
if_chain! {
|
||||
if let Some(expr) = it.peek();
|
||||
if let hir::StmtKind::Local(ref local) = stmt.node;
|
||||
if let hir::PatKind::Binding(mode, canonical_id, _, ident, None) = local.pat.node;
|
||||
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.node;
|
||||
if let hir::StmtKind::Expr(ref if_) = expr.node;
|
||||
if let hir::ExprKind::If(ref cond, ref then, ref else_) = if_.node;
|
||||
if !used_in_expr(cx, canonical_id, cond);
|
||||
|
@ -142,7 +141,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetIfSeq {
|
|||
|
||||
struct UsedVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
id: ast::NodeId,
|
||||
id: hir::HirId,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
|
@ -151,7 +150,7 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
|||
if_chain! {
|
||||
if let hir::ExprKind::Path(ref qpath) = expr.node;
|
||||
if let Def::Local(local_id) = self.cx.tables.qpath_def(qpath, expr.hir_id);
|
||||
if self.id == local_id;
|
||||
if self.id == self.cx.tcx.hir().node_to_hir_id(local_id);
|
||||
then {
|
||||
self.used = true;
|
||||
return;
|
||||
|
@ -166,7 +165,7 @@ impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for UsedVisitor<'a, 'tcx> {
|
|||
|
||||
fn check_assign<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
decl: ast::NodeId,
|
||||
decl: hir::HirId,
|
||||
block: &'tcx hir::Block,
|
||||
) -> Option<&'tcx hir::Expr> {
|
||||
if_chain! {
|
||||
|
@ -176,7 +175,7 @@ fn check_assign<'a, 'tcx>(
|
|||
if let hir::ExprKind::Assign(ref var, ref value) = expr.node;
|
||||
if let hir::ExprKind::Path(ref qpath) = var.node;
|
||||
if let Def::Local(local_id) = cx.tables.qpath_def(qpath, var.hir_id);
|
||||
if decl == local_id;
|
||||
if decl == cx.tcx.hir().node_to_hir_id(local_id);
|
||||
then {
|
||||
let mut v = UsedVisitor {
|
||||
cx,
|
||||
|
@ -199,7 +198,7 @@ fn check_assign<'a, 'tcx>(
|
|||
None
|
||||
}
|
||||
|
||||
fn used_in_expr<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, id: ast::NodeId, expr: &'tcx hir::Expr) -> bool {
|
||||
fn used_in_expr<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, id: hir::HirId, expr: &'tcx hir::Expr) -> bool {
|
||||
let mut v = UsedVisitor { cx, id, used: false };
|
||||
hir::intravisit::walk_expr(&mut v, expr);
|
||||
v.used
|
||||
|
|
|
@ -9,10 +9,9 @@
|
|||
#![recursion_limit = "256"]
|
||||
#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(try_from)]
|
||||
|
||||
// FIXME: switch to something more ergonomic here, once available.
|
||||
// (currently there is no way to opt into sysroot crates w/o `extern crate`)
|
||||
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
|
||||
#[allow(unused_extern_crates)]
|
||||
extern crate fmt_macros;
|
||||
#[allow(unused_extern_crates)]
|
||||
|
@ -40,7 +39,7 @@ use toml;
|
|||
///
|
||||
/// Every lint declaration consists of 4 parts:
|
||||
///
|
||||
/// 1. The documentation above the lint, which is used for the website
|
||||
/// 1. The documentation, which is used for the website
|
||||
/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
|
||||
/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
|
||||
/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
|
||||
|
@ -61,22 +60,22 @@ use toml;
|
|||
/// # use clippy_lints::declare_clippy_lint;
|
||||
/// use rustc::declare_tool_lint;
|
||||
///
|
||||
/// /// **What it does:** Checks for ... (describe what the lint matches).
|
||||
/// ///
|
||||
/// /// **Why is this bad?** Supply the reason for linting the code.
|
||||
/// ///
|
||||
/// /// **Known problems:** None. (Or describe where it could go wrong.)
|
||||
/// ///
|
||||
/// /// **Example:**
|
||||
/// ///
|
||||
/// /// ```rust
|
||||
/// /// // Bad
|
||||
/// /// Insert a short example of code that triggers the lint
|
||||
/// ///
|
||||
/// /// // Good
|
||||
/// /// Insert a short example of improved code that doesn't trigger the lint
|
||||
/// /// ```
|
||||
/// declare_clippy_lint! {
|
||||
/// /// **What it does:** Checks for ... (describe what the lint matches).
|
||||
/// ///
|
||||
/// /// **Why is this bad?** Supply the reason for linting the code.
|
||||
/// ///
|
||||
/// /// **Known problems:** None. (Or describe where it could go wrong.)
|
||||
/// ///
|
||||
/// /// **Example:**
|
||||
/// ///
|
||||
/// /// ```rust
|
||||
/// /// // Bad
|
||||
/// /// Insert a short example of code that triggers the lint
|
||||
/// ///
|
||||
/// /// // Good
|
||||
/// /// Insert a short example of improved code that doesn't trigger the lint
|
||||
/// /// ```
|
||||
/// pub LINT_NAME,
|
||||
/// pedantic,
|
||||
/// "description"
|
||||
|
@ -85,35 +84,55 @@ use toml;
|
|||
/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
|
||||
#[macro_export]
|
||||
macro_rules! declare_clippy_lint {
|
||||
{ pub $name:tt, style, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Warn, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, correctness, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Deny, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
$(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, complexity, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Warn, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, perf, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Warn, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, pedantic, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Allow, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Allow, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, restriction, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Allow, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Allow, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, cargo, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Allow, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Allow, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, nursery, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Allow, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Allow, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, internal, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Allow, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Allow, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
{ pub $name:tt, internal_warn, $description:tt } => {
|
||||
declare_tool_lint! { pub clippy::$name, Warn, $description, report_in_external_macro: true }
|
||||
{ $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -133,11 +152,11 @@ pub mod block_in_if_condition;
|
|||
pub mod booleans;
|
||||
pub mod bytecount;
|
||||
pub mod cargo_common_metadata;
|
||||
pub mod cognitive_complexity;
|
||||
pub mod collapsible_if;
|
||||
pub mod const_static_lifetime;
|
||||
pub mod copies;
|
||||
pub mod copy_iterator;
|
||||
pub mod cyclomatic_complexity;
|
||||
pub mod dbg_macro;
|
||||
pub mod default_trait_access;
|
||||
pub mod derive;
|
||||
|
@ -253,7 +272,7 @@ pub mod zero_div_zero;
|
|||
pub use crate::utils::conf::Conf;
|
||||
|
||||
mod reexport {
|
||||
crate use syntax::ast::{Name, NodeId};
|
||||
crate use syntax::ast::Name;
|
||||
}
|
||||
|
||||
/// Register all pre expansion lints
|
||||
|
@ -388,7 +407,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
);
|
||||
store.register_removed(
|
||||
"assign_ops",
|
||||
"using compound assignment operators (e.g. `+=`) is harmless",
|
||||
"using compound assignment operators (e.g., `+=`) is harmless",
|
||||
);
|
||||
store.register_removed(
|
||||
"if_let_redundant_pattern_matching",
|
||||
|
@ -459,7 +478,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
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)
|
||||
box cognitive_complexity::CognitiveComplexity::new(conf.cognitive_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);
|
||||
|
@ -564,6 +583,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
matches::WILDCARD_ENUM_MATCH_ARM,
|
||||
mem_forget::MEM_FORGET,
|
||||
methods::CLONE_ON_REF_PTR,
|
||||
methods::GET_UNWRAP,
|
||||
methods::OPTION_UNWRAP_USED,
|
||||
methods::RESULT_UNWRAP_USED,
|
||||
methods::WRONG_PUB_SELF_CONVENTION,
|
||||
|
@ -647,11 +667,11 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
booleans::LOGIC_BUG,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
collapsible_if::COLLAPSIBLE_IF,
|
||||
const_static_lifetime::CONST_STATIC_LIFETIME,
|
||||
copies::IFS_SAME_COND,
|
||||
copies::IF_SAME_THEN_ELSE,
|
||||
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
|
||||
derive::DERIVE_HASH_XOR_EQ,
|
||||
double_comparison::DOUBLE_COMPARISONS,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
|
@ -730,7 +750,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
methods::CLONE_ON_COPY,
|
||||
methods::EXPECT_FUN_CALL,
|
||||
methods::FILTER_NEXT,
|
||||
methods::GET_UNWRAP,
|
||||
methods::INTO_ITER_ON_ARRAY,
|
||||
methods::INTO_ITER_ON_REF,
|
||||
methods::ITER_CLONED_COLLECT,
|
||||
|
@ -889,7 +908,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
matches::SINGLE_MATCH,
|
||||
mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::GET_UNWRAP,
|
||||
methods::INTO_ITER_ON_REF,
|
||||
methods::ITER_CLONED_COLLECT,
|
||||
methods::ITER_SKIP_NEXT,
|
||||
|
@ -943,7 +961,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
assign_ops::MISREFACTORED_ASSIGN_OP,
|
||||
attrs::DEPRECATED_CFG_ATTR,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
cyclomatic_complexity::CYCLOMATIC_COMPLEXITY,
|
||||
cognitive_complexity::COGNITIVE_COMPLEXITY,
|
||||
double_comparison::DOUBLE_COMPARISONS,
|
||||
double_parens::DOUBLE_PARENS,
|
||||
duration_subsec::DURATION_SUBSEC,
|
||||
|
@ -1112,6 +1130,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
pub fn register_renamed(ls: &mut rustc::lint::LintStore) {
|
||||
ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
|
||||
ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
|
||||
ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
|
||||
}
|
||||
|
||||
// only exists to let the dogfood integration test works.
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use crate::reexport::*;
|
||||
use crate::utils::{last_path_segment, span_lint};
|
||||
use matches::matches;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::intravisit::*;
|
||||
|
@ -10,45 +8,48 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|||
use syntax::source_map::Span;
|
||||
use syntax::symbol::keywords;
|
||||
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
/// relying on lifetime elision.
|
||||
///
|
||||
/// **Why is this bad?** The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// **Known problems:** Potential false negatives: we bail out if the function
|
||||
/// has a `where` clause where lifetimes are mentioned.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
use crate::reexport::*;
|
||||
use crate::utils::{last_path_segment, span_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
pub NEEDLESS_LIFETIMES,
|
||||
complexity,
|
||||
"using explicit lifetimes for references in function arguments when elision rules \
|
||||
would allow omitting them"
|
||||
/// **What it does:** Checks for lifetime annotations which can be removed by
|
||||
/// relying on lifetime elision.
|
||||
///
|
||||
/// **Why is this bad?** The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// **Known problems:** Potential false negatives: we bail out if the function
|
||||
/// has a `where` clause where lifetimes are mentioned.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_LIFETIMES,
|
||||
complexity,
|
||||
"using explicit lifetimes for references in function arguments when elision rules \
|
||||
would allow omitting them"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for lifetimes in generics that are never used
|
||||
/// anywhere else.
|
||||
///
|
||||
/// **Why is this bad?** The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn unused_lifetime<'a>(x: u8) {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for lifetimes in generics that are never used
|
||||
/// anywhere else.
|
||||
///
|
||||
/// **Why is this bad?** The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn unused_lifetime<'a>(x: u8) {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXTRA_UNUSED_LIFETIMES,
|
||||
complexity,
|
||||
"unused lifetimes in function definitions"
|
||||
|
@ -319,7 +320,7 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
_ => false,
|
||||
})
|
||||
{
|
||||
let hir_id = self.cx.tcx.hir().node_to_hir_id(ty.id);
|
||||
let hir_id = ty.hir_id;
|
||||
match self.cx.tables.qpath_def(qpath, hir_id) {
|
||||
Def::TyAlias(def_id) | Def::Struct(def_id) => {
|
||||
let generics = self.cx.tcx.generics_of(def_id);
|
||||
|
@ -384,7 +385,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Are any lifetimes mentioned in the `where` clause? If yes, we don't try to
|
||||
/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
|
||||
/// reason about elision.
|
||||
fn has_where_lifetimes<'a, 'tcx: 'a>(cx: &LateContext<'a, 'tcx>, where_clause: &'tcx WhereClause) -> bool {
|
||||
for predicate in &where_clause.predicates {
|
||||
|
|
|
@ -9,95 +9,95 @@ use rustc_errors::Applicability;
|
|||
use syntax::ast::*;
|
||||
use syntax_pos;
|
||||
|
||||
/// **What it does:** Warns if a long integral or floating-point constant does
|
||||
/// not contain underscores.
|
||||
///
|
||||
/// **Why is this bad?** Reading long numbers is difficult without separators.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 61864918973511
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if a long integral or floating-point constant does
|
||||
/// not contain underscores.
|
||||
///
|
||||
/// **Why is this bad?** Reading long numbers is difficult without separators.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: u64 = 61864918973511;
|
||||
/// ```
|
||||
pub UNREADABLE_LITERAL,
|
||||
style,
|
||||
"long integer literal without underscores"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns for mistyped suffix in literals
|
||||
///
|
||||
/// **Why is this bad?** This is most probably a typo
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// - Recommends a signed suffix, even though the number might be too big and an unsigned
|
||||
/// suffix is required
|
||||
/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 2_32
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns for mistyped suffix in literals
|
||||
///
|
||||
/// **Why is this bad?** This is most probably a typo
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// - Recommends a signed suffix, even though the number might be too big and an unsigned
|
||||
/// suffix is required
|
||||
/// - Does not match on `_128` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 2_32;
|
||||
/// ```
|
||||
pub MISTYPED_LITERAL_SUFFIXES,
|
||||
correctness,
|
||||
"mistyped literal suffix"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if an integral or floating-point constant is
|
||||
/// grouped inconsistently with underscores.
|
||||
///
|
||||
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
|
||||
/// grouped digits.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 618_64_9189_73_511
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if an integral or floating-point constant is
|
||||
/// grouped inconsistently with underscores.
|
||||
///
|
||||
/// **Why is this bad?** Readers may incorrectly interpret inconsistently
|
||||
/// grouped digits.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: u64 = 618_64_9189_73_511;
|
||||
/// ```
|
||||
pub INCONSISTENT_DIGIT_GROUPING,
|
||||
style,
|
||||
"integer literals with digits grouped inconsistently"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if the digits of an integral or floating-point
|
||||
/// constant are grouped into groups that
|
||||
/// are too large.
|
||||
///
|
||||
/// **Why is this bad?** Negatively impacts readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 6186491_8973511
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if the digits of an integral or floating-point
|
||||
/// constant are grouped into groups that
|
||||
/// are too large.
|
||||
///
|
||||
/// **Why is this bad?** Negatively impacts readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: u64 = 6186491_8973511;
|
||||
/// ```
|
||||
pub LARGE_DIGIT_GROUPS,
|
||||
pedantic,
|
||||
"grouping digits into groups that are too large"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if there is a better representation for a numeric literal.
|
||||
///
|
||||
/// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
|
||||
/// readable than a decimal representation.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// `255` => `0xFF`
|
||||
/// `65_535` => `0xFFFF`
|
||||
/// `4_042_322_160` => `0xF0F0_F0F0`
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if there is a better representation for a numeric literal.
|
||||
///
|
||||
/// **Why is this bad?** Especially for big powers of 2 a hexadecimal representation is more
|
||||
/// readable than a decimal representation.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// `255` => `0xFF`
|
||||
/// `65_535` => `0xFFFF`
|
||||
/// `4_042_322_160` => `0xF0F0_F0F0`
|
||||
pub DECIMAL_LITERAL_REPRESENTATION,
|
||||
restriction,
|
||||
"using decimal representation when hexadecimal would be better"
|
||||
|
@ -112,7 +112,7 @@ pub(super) enum Radix {
|
|||
}
|
||||
|
||||
impl Radix {
|
||||
/// Return a reasonable digit group size for this radix.
|
||||
/// Returns a reasonable digit group size for this radix.
|
||||
crate fn suggest_grouping(&self) -> usize {
|
||||
match *self {
|
||||
Radix::Binary | Radix::Hexadecimal => 4,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,29 +14,29 @@ use syntax::source_map::Span;
|
|||
#[derive(Clone)]
|
||||
pub struct Pass;
|
||||
|
||||
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
|
||||
/// `iterator.cloned()` instead
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.map(|i| *i);
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.cloned();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `iterator.map(|x| x.clone())` and suggests
|
||||
/// `iterator.cloned()` instead
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more concisely
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.map(|i| *i);
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x = vec![42, 43];
|
||||
/// let y = x.iter();
|
||||
/// let z = y.cloned();
|
||||
/// ```
|
||||
pub MAP_CLONE,
|
||||
style,
|
||||
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
|
||||
|
|
|
@ -11,67 +11,67 @@ use syntax::source_map::Span;
|
|||
#[derive(Clone)]
|
||||
pub struct Pass;
|
||||
|
||||
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
|
||||
/// or closure that returns the unit type.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more clearly with
|
||||
/// an if let statement
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// }
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `option.map(f)` where f is a function
|
||||
/// or closure that returns the unit type.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more clearly with
|
||||
/// an if let statement
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// }
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// }
|
||||
/// ```
|
||||
pub OPTION_MAP_UNIT_FN,
|
||||
complexity,
|
||||
"using `option.map(f)`, where f is a function or closure that returns ()"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for usage of `result.map(f)` where f is a function
|
||||
/// or closure that returns the unit type.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more clearly with
|
||||
/// an if let statement
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// }
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `result.map(f)` where f is a function
|
||||
/// or closure that returns the unit type.
|
||||
///
|
||||
/// **Why is this bad?** Readability, this can be written more clearly with
|
||||
/// an if let statement
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// }
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// }
|
||||
/// ```
|
||||
pub RESULT_MAP_UNIT_FN,
|
||||
complexity,
|
||||
"using `result.map(f)`, where f is a function or closure that returns ()"
|
||||
|
|
|
@ -18,192 +18,192 @@ use std::ops::Deref;
|
|||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for matches with a single arm where an `if let`
|
||||
/// will usually suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for matches with a single arm where an `if let`
|
||||
/// will usually suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
pub SINGLE_MATCH,
|
||||
style,
|
||||
"a match statement with a single nontrivial arm (i.e. where the other arm is `_ => {}`) instead of `if let`"
|
||||
"a match statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for matches with a two arms where an `if let else` will
|
||||
/// usually suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
|
||||
///
|
||||
/// **Known problems:** Personal style preferences may differ.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Using `match`:
|
||||
///
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => bar(other_ref),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `if let` with `else`:
|
||||
///
|
||||
/// ```rust
|
||||
/// if let Some(ref foo) = x {
|
||||
/// bar(foo);
|
||||
/// } else {
|
||||
/// bar(other_ref);
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for matches with a two arms where an `if let else` will
|
||||
/// usually suffice.
|
||||
///
|
||||
/// **Why is this bad?** Just readability – `if let` nests less than a `match`.
|
||||
///
|
||||
/// **Known problems:** Personal style preferences may differ.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Using `match`:
|
||||
///
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => bar(other_ref),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `if let` with `else`:
|
||||
///
|
||||
/// ```ignore
|
||||
/// if let Some(ref foo) = x {
|
||||
/// bar(foo);
|
||||
/// } else {
|
||||
/// bar(other_ref);
|
||||
/// }
|
||||
/// ```
|
||||
pub SINGLE_MATCH_ELSE,
|
||||
pedantic,
|
||||
"a match statement with a two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for matches where all arms match a reference,
|
||||
/// suggesting to remove the reference and deref the matched expression
|
||||
/// instead. It also checks for `if let &foo = bar` blocks.
|
||||
///
|
||||
/// **Why is this bad?** It just makes the code less readable. That reference
|
||||
/// destructuring adds nothing to the code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// &A(ref y) => foo(y),
|
||||
/// &B => bar(),
|
||||
/// _ => frob(&x),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for matches where all arms match a reference,
|
||||
/// suggesting to remove the reference and deref the matched expression
|
||||
/// instead. It also checks for `if let &foo = bar` blocks.
|
||||
///
|
||||
/// **Why is this bad?** It just makes the code less readable. That reference
|
||||
/// destructuring adds nothing to the code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// match x {
|
||||
/// &A(ref y) => foo(y),
|
||||
/// &B => bar(),
|
||||
/// _ => frob(&x),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_REF_PATS,
|
||||
style,
|
||||
"a match or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for matches where match expression is a `bool`. It
|
||||
/// suggests to replace the expression with an `if...else` block.
|
||||
///
|
||||
/// **Why is this bad?** It makes the code less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let condition: bool = true;
|
||||
/// match condition {
|
||||
/// true => foo(),
|
||||
/// false => bar(),
|
||||
/// }
|
||||
/// ```
|
||||
/// Use if/else instead:
|
||||
/// ```rust
|
||||
/// let condition: bool = true;
|
||||
/// if condition {
|
||||
/// foo();
|
||||
/// } else {
|
||||
/// bar();
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for matches where match expression is a `bool`. It
|
||||
/// suggests to replace the expression with an `if...else` block.
|
||||
///
|
||||
/// **Why is this bad?** It makes the code less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let condition: bool = true;
|
||||
/// match condition {
|
||||
/// true => foo(),
|
||||
/// false => bar(),
|
||||
/// }
|
||||
/// ```
|
||||
/// Use if/else instead:
|
||||
/// ```ignore
|
||||
/// let condition: bool = true;
|
||||
/// if condition {
|
||||
/// foo();
|
||||
/// } else {
|
||||
/// bar();
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_BOOL,
|
||||
style,
|
||||
"a match on a boolean expression instead of an `if..else` block"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for overlapping match arms.
|
||||
///
|
||||
/// **Why is this bad?** It is likely to be an error and if not, makes the code
|
||||
/// less obvious.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 5;
|
||||
/// match x {
|
||||
/// 1...10 => println!("1 ... 10"),
|
||||
/// 5...15 => println!("5 ... 15"),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for overlapping match arms.
|
||||
///
|
||||
/// **Why is this bad?** It is likely to be an error and if not, makes the code
|
||||
/// less obvious.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 5;
|
||||
/// match x {
|
||||
/// 1...10 => println!("1 ... 10"),
|
||||
/// 5...15 => println!("5 ... 15"),
|
||||
/// _ => (),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_OVERLAPPING_ARM,
|
||||
style,
|
||||
"a match with overlapping arms"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for arm which matches all errors with `Err(_)`
|
||||
/// and take drastic actions like `panic!`.
|
||||
///
|
||||
/// **Why is this bad?** It is generally a bad practice, just like
|
||||
/// catching all exceptions in java with `catch(Exception)`
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: Result(i32, &str) = Ok(3);
|
||||
/// match x {
|
||||
/// Ok(_) => println!("ok"),
|
||||
/// Err(_) => panic!("err"),
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for arm which matches all errors with `Err(_)`
|
||||
/// and take drastic actions like `panic!`.
|
||||
///
|
||||
/// **Why is this bad?** It is generally a bad practice, just like
|
||||
/// catching all exceptions in java with `catch(Exception)`
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: Result<i32, &str> = Ok(3);
|
||||
/// match x {
|
||||
/// Ok(_) => println!("ok"),
|
||||
/// Err(_) => panic!("err"),
|
||||
/// }
|
||||
/// ```
|
||||
pub MATCH_WILD_ERR_ARM,
|
||||
style,
|
||||
"a match with `Err(_)` arm and take drastic actions"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for match which is used to add a reference to an
|
||||
/// `Option` value.
|
||||
///
|
||||
/// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: Option<()> = None;
|
||||
/// let r: Option<&()> = match x {
|
||||
/// None => None,
|
||||
/// Some(ref v) => Some(v),
|
||||
/// };
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for match which is used to add a reference to an
|
||||
/// `Option` value.
|
||||
///
|
||||
/// **Why is this bad?** Using `as_ref()` or `as_mut()` instead is shorter.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: Option<()> = None;
|
||||
/// let r: Option<&()> = match x {
|
||||
/// None => None,
|
||||
/// Some(ref v) => Some(v),
|
||||
/// };
|
||||
/// ```
|
||||
pub MATCH_AS_REF,
|
||||
complexity,
|
||||
"a match on an Option value instead of using `as_ref()` or `as_mut`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for wildcard enum matches using `_`.
|
||||
///
|
||||
/// **Why is this bad?** New enum variants added by library updates can be missed.
|
||||
///
|
||||
/// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
|
||||
/// variants, and also may not use correct path to enum if it's not present in the current scope.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// A => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for wildcard enum matches using `_`.
|
||||
///
|
||||
/// **Why is this bad?** New enum variants added by library updates can be missed.
|
||||
///
|
||||
/// **Known problems:** Suggested replacements may be incorrect if guards exhaustively cover some
|
||||
/// variants, and also may not use correct path to enum if it's not present in the current scope.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// match x {
|
||||
/// A => {},
|
||||
/// _ => {},
|
||||
/// }
|
||||
/// ```
|
||||
pub WILDCARD_ENUM_MATCH_ARM,
|
||||
restriction,
|
||||
"a wildcard enum match arm using `_`"
|
||||
|
@ -266,7 +266,7 @@ fn check_single_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &
|
|||
return;
|
||||
};
|
||||
let ty = cx.tables.expr_ty(ex);
|
||||
if ty.sty != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.id) {
|
||||
if ty.sty != ty::Bool || is_allowed(cx, MATCH_BOOL, ex.hir_id) {
|
||||
check_single_match_single_pattern(cx, ex, arms, expr, els);
|
||||
check_single_match_opt_like(cx, ex, arms, expr, ty, els);
|
||||
}
|
||||
|
@ -322,7 +322,7 @@ fn check_single_match_opt_like(
|
|||
ty: Ty<'_>,
|
||||
els: Option<&Expr>,
|
||||
) {
|
||||
// list of candidate Enums we know will never get any more members
|
||||
// list of candidate `Enum`s we know will never get any more members
|
||||
let candidates = &[
|
||||
(&paths::COW, "Borrowed"),
|
||||
(&paths::COW, "Cow::Borrowed"),
|
||||
|
@ -335,7 +335,7 @@ fn check_single_match_opt_like(
|
|||
|
||||
let path = match arms[1].pats[0].node {
|
||||
PatKind::TupleStruct(ref path, ref inner, _) => {
|
||||
// contains any non wildcard patterns? e.g. Err(err)
|
||||
// Contains any non wildcard patterns (e.g., `Err(err)`)?
|
||||
if !inner.iter().all(is_wild) {
|
||||
return;
|
||||
}
|
||||
|
@ -354,7 +354,7 @@ fn check_single_match_opt_like(
|
|||
}
|
||||
|
||||
fn check_match_bool(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &Expr) {
|
||||
// type of expression == bool
|
||||
// Type of expression is `bool`.
|
||||
if cx.tables.expr_ty(ex).sty == ty::Bool {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
@ -482,7 +482,7 @@ fn check_wild_enum_match(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm]) {
|
|||
for pat in &arm.pats {
|
||||
if let PatKind::Wild = pat.node {
|
||||
wildcard_span = Some(pat.span);
|
||||
} else if let PatKind::Binding(_, _, _, ident, None) = pat.node {
|
||||
} else if let PatKind::Binding(_, _, ident, None) = pat.node {
|
||||
wildcard_span = Some(pat.span);
|
||||
wildcard_ident = Some(ident);
|
||||
}
|
||||
|
@ -570,13 +570,15 @@ fn check_match_ref_pats(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr:
|
|||
if has_only_ref_pats(arms) {
|
||||
let mut suggs = Vec::new();
|
||||
let (title, msg) = if let ExprKind::AddrOf(Mutability::MutImmutable, ref inner) = ex.node {
|
||||
suggs.push((ex.span, Sugg::hir(cx, inner, "..").to_string()));
|
||||
let span = ex.span.source_callsite();
|
||||
suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
|
||||
(
|
||||
"you don't need to add `&` to both the expression and the patterns",
|
||||
"try",
|
||||
)
|
||||
} else {
|
||||
suggs.push((ex.span, Sugg::hir(cx, ex, "..").deref().to_string()));
|
||||
let span = ex.span.source_callsite();
|
||||
suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
|
||||
(
|
||||
"you don't need to add `&` to all patterns",
|
||||
"instead of prefixing all patterns with `&`, you can dereference the expression",
|
||||
|
@ -637,7 +639,7 @@ fn check_match_as_ref(cx: &LateContext<'_, '_>, ex: &Expr, arms: &[Arm], expr: &
|
|||
}
|
||||
}
|
||||
|
||||
/// Get all arms that are unbounded `PatRange`s.
|
||||
/// Gets all arms that are unbounded `PatRange`s.
|
||||
fn all_ranges<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arms: &'tcx [Arm]) -> Vec<SpannedRange<Constant>> {
|
||||
arms.iter()
|
||||
.flat_map(|arm| {
|
||||
|
@ -685,7 +687,7 @@ pub struct SpannedRange<T> {
|
|||
|
||||
type TypedRanges = Vec<SpannedRange<u128>>;
|
||||
|
||||
/// Get all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
|
||||
/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
|
||||
/// and other types than
|
||||
/// `Uint` and `Int` probably don't make sense.
|
||||
fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::utils::{match_def_path, opt_def_id, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
|
||||
use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::{Expr, ExprKind};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
|
@ -7,19 +7,21 @@ use rustc_errors::Applicability;
|
|||
|
||||
use std::iter;
|
||||
|
||||
/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
|
||||
///
|
||||
/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
|
||||
/// is unspecified.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// mem::discriminant(&"hello");
|
||||
/// mem::discriminant(&&Some(2));
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for calls of `mem::discriminant()` on a non-enum type.
|
||||
///
|
||||
/// **Why is this bad?** The value of `mem::discriminant()` on non-enum types
|
||||
/// is unspecified.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::mem;
|
||||
///
|
||||
/// mem::discriminant(&"hello");
|
||||
/// mem::discriminant(&&Some(2));
|
||||
/// ```
|
||||
pub MEM_DISCRIMINANT_NON_ENUM,
|
||||
correctness,
|
||||
"calling mem::descriminant on non-enum type"
|
||||
|
@ -43,7 +45,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemDiscriminant {
|
|||
if let ExprKind::Call(ref func, ref func_args) = expr.node;
|
||||
// is `mem::discriminant`
|
||||
if let ExprKind::Path(ref func_qpath) = func.node;
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
|
||||
if let Some(def_id) = cx.tables.qpath_def(func_qpath, func.hir_id).opt_def_id();
|
||||
if match_def_path(cx.tcx, def_id, &paths::MEM_DISCRIMINANT);
|
||||
// type is non-enum
|
||||
let ty_param = cx.tables.node_substs(func.hir_id).type_at(0);
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
use crate::utils::{match_def_path, opt_def_id, paths, span_lint};
|
||||
use crate::utils::{match_def_path, paths, span_lint};
|
||||
use rustc::hir::{Expr, ExprKind};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is
|
||||
/// `Drop`.
|
||||
///
|
||||
/// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its
|
||||
/// destructor, possibly causing leaks.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// mem::forget(Rc::new(55))
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usage of `std::mem::forget(t)` where `t` is
|
||||
/// `Drop`.
|
||||
///
|
||||
/// **Why is this bad?** `std::mem::forget(t)` prevents `t` from running its
|
||||
/// destructor, possibly causing leaks.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// mem::forget(Rc::new(55))
|
||||
/// ```
|
||||
pub MEM_FORGET,
|
||||
restriction,
|
||||
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
|
||||
|
@ -37,7 +37,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemForget {
|
|||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
|
||||
if let ExprKind::Call(ref path_expr, ref args) = e.node {
|
||||
if let ExprKind::Path(ref qpath) = path_expr.node {
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path_expr.hir_id)) {
|
||||
if let Some(def_id) = cx.tables.qpath_def(qpath, path_expr.hir_id).opt_def_id() {
|
||||
if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
|
||||
let forgot_ty = cx.tables.expr_ty(&args[0]);
|
||||
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
use crate::utils::{match_def_path, match_qpath, opt_def_id, paths, snippet_with_applicability, span_lint_and_sugg};
|
||||
use crate::utils::{match_def_path, match_qpath, paths, snippet_with_applicability, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::{Expr, ExprKind, MutMutable, QPath};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for `mem::replace()` on an `Option` with
|
||||
/// `None`.
|
||||
///
|
||||
/// **Why is this bad?** `Option` already has the method `take()` for
|
||||
/// taking its current value (Some(..) or None) and replacing it with
|
||||
/// `None`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut an_option = Some(0);
|
||||
/// let replaced = mem::replace(&mut an_option, None);
|
||||
/// ```
|
||||
/// Is better expressed with:
|
||||
/// ```rust
|
||||
/// let mut an_option = Some(0);
|
||||
/// let taken = an_option.take();
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for `mem::replace()` on an `Option` with
|
||||
/// `None`.
|
||||
///
|
||||
/// **Why is this bad?** `Option` already has the method `take()` for
|
||||
/// taking its current value (Some(..) or None) and replacing it with
|
||||
/// `None`.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::mem;
|
||||
///
|
||||
/// let mut an_option = Some(0);
|
||||
/// let replaced = mem::replace(&mut an_option, None);
|
||||
/// ```
|
||||
/// Is better expressed with:
|
||||
/// ```rust
|
||||
/// let mut an_option = Some(0);
|
||||
/// let taken = an_option.take();
|
||||
/// ```
|
||||
pub MEM_REPLACE_OPTION_WITH_NONE,
|
||||
style,
|
||||
"replacing an `Option` with `None` instead of `take()`"
|
||||
|
@ -49,7 +51,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
|
|||
if let ExprKind::Call(ref func, ref func_args) = expr.node;
|
||||
if func_args.len() == 2;
|
||||
if let ExprKind::Path(ref func_qpath) = func.node;
|
||||
if let Some(def_id) = opt_def_id(cx.tables.qpath_def(func_qpath, func.hir_id));
|
||||
if let Some(def_id) = cx.tables.qpath_def(func_qpath, func.hir_id).opt_def_id();
|
||||
if match_def_path(cx.tcx, def_id, &paths::MEM_REPLACE);
|
||||
|
||||
// Check that second argument is `Option::None`
|
||||
|
|
File diff suppressed because it is too large
Load diff
122
clippy_lints/src/methods/option_map_unwrap_or.rs
Normal file
122
clippy_lints/src/methods/option_map_unwrap_or.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use crate::utils::paths;
|
||||
use crate::utils::{is_copy, match_type, snippet, span_lint, span_note_and_lint};
|
||||
use rustc::hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
|
||||
use rustc::hir::{self, *};
|
||||
use rustc::lint::LateContext;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use syntax::symbol::Symbol;
|
||||
|
||||
use super::OPTION_MAP_UNWRAP_OR;
|
||||
|
||||
/// lint use of `map().unwrap_or()` for `Option`s
|
||||
pub(super) fn lint<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
expr: &hir::Expr,
|
||||
map_args: &'tcx [hir::Expr],
|
||||
unwrap_args: &'tcx [hir::Expr],
|
||||
) {
|
||||
// lint if the caller of `map()` is an `Option`
|
||||
if match_type(cx, cx.tables.expr_ty(&map_args[0]), &paths::OPTION) {
|
||||
if !is_copy(cx, cx.tables.expr_ty(&unwrap_args[1])) {
|
||||
// Do not lint if the `map` argument uses identifiers in the `map`
|
||||
// argument that are also used in the `unwrap_or` argument
|
||||
|
||||
let mut unwrap_visitor = UnwrapVisitor {
|
||||
cx,
|
||||
identifiers: FxHashSet::default(),
|
||||
};
|
||||
unwrap_visitor.visit_expr(&unwrap_args[1]);
|
||||
|
||||
let mut map_expr_visitor = MapExprVisitor {
|
||||
cx,
|
||||
identifiers: unwrap_visitor.identifiers,
|
||||
found_identifier: false,
|
||||
};
|
||||
map_expr_visitor.visit_expr(&map_args[1]);
|
||||
|
||||
if map_expr_visitor.found_identifier {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// get snippets for args to map() and unwrap_or()
|
||||
let map_snippet = snippet(cx, map_args[1].span, "..");
|
||||
let unwrap_snippet = snippet(cx, unwrap_args[1].span, "..");
|
||||
// lint message
|
||||
// comparing the snippet from source to raw text ("None") below is safe
|
||||
// because we already have checked the type.
|
||||
let arg = if unwrap_snippet == "None" { "None" } else { "a" };
|
||||
let suggest = if unwrap_snippet == "None" {
|
||||
"and_then(f)"
|
||||
} else {
|
||||
"map_or(a, f)"
|
||||
};
|
||||
let msg = &format!(
|
||||
"called `map(f).unwrap_or({})` on an Option value. \
|
||||
This can be done more directly by calling `{}` instead",
|
||||
arg, suggest
|
||||
);
|
||||
// lint, with note if neither arg is > 1 line and both map() and
|
||||
// unwrap_or() have the same span
|
||||
let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
|
||||
let same_span = map_args[1].span.ctxt() == unwrap_args[1].span.ctxt();
|
||||
if same_span && !multiline {
|
||||
let suggest = if unwrap_snippet == "None" {
|
||||
format!("and_then({})", map_snippet)
|
||||
} else {
|
||||
format!("map_or({}, {})", unwrap_snippet, map_snippet)
|
||||
};
|
||||
let note = format!(
|
||||
"replace `map({}).unwrap_or({})` with `{}`",
|
||||
map_snippet, unwrap_snippet, suggest
|
||||
);
|
||||
span_note_and_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg, expr.span, ¬e);
|
||||
} else if same_span && multiline {
|
||||
span_lint(cx, OPTION_MAP_UNWRAP_OR, expr.span, msg);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
struct UnwrapVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
identifiers: FxHashSet<Symbol>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
|
||||
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
|
||||
self.identifiers.insert(ident(path));
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
||||
struct MapExprVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
identifiers: FxHashSet<Symbol>,
|
||||
found_identifier: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
|
||||
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
|
||||
if self.identifiers.contains(&ident(path)) {
|
||||
self.found_identifier = true;
|
||||
return;
|
||||
}
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
|
||||
NestedVisitorMap::All(&self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
||||
fn ident(path: &Path) -> Symbol {
|
||||
path.segments
|
||||
.last()
|
||||
.expect("segments should be composed of at least 1 element")
|
||||
.ident
|
||||
.name
|
||||
}
|
|
@ -5,7 +5,6 @@ use rustc::hir;
|
|||
use rustc::hir::def::Def;
|
||||
use rustc::hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc::lint::LateContext;
|
||||
use syntax::ast;
|
||||
|
||||
use if_chain::if_chain;
|
||||
|
||||
|
@ -18,7 +17,7 @@ pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr
|
|||
|
||||
if let hir::ExprKind::Closure(_, _, body_id, ..) = args[1].node {
|
||||
let body = cx.tcx.hir().body(body_id);
|
||||
let arg_id = body.arguments[0].pat.id;
|
||||
let arg_id = body.arguments[0].pat.hir_id;
|
||||
let mutates_arg = match mutated_variables(&body.value, cx) {
|
||||
Some(used_mutably) => used_mutably.contains(&arg_id),
|
||||
None => true,
|
||||
|
@ -56,7 +55,7 @@ pub(super) fn lint(cx: &LateContext<'_, '_>, expr: &hir::Expr, args: &[hir::Expr
|
|||
// returns (found_mapping, found_filtering)
|
||||
fn check_expression<'a, 'tcx: 'a>(
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
arg_id: ast::NodeId,
|
||||
arg_id: hir::HirId,
|
||||
expr: &'tcx hir::Expr,
|
||||
) -> (bool, bool) {
|
||||
match &expr.node {
|
||||
|
@ -69,7 +68,7 @@ fn check_expression<'a, 'tcx: 'a>(
|
|||
if let hir::ExprKind::Path(path) = &args[0].node;
|
||||
if let Def::Local(ref local) = cx.tables.qpath_def(path, args[0].hir_id);
|
||||
then {
|
||||
if arg_id == *local {
|
||||
if arg_id == cx.tcx.hir().node_to_hir_id(*local) {
|
||||
return (false, false)
|
||||
}
|
||||
}
|
||||
|
@ -113,7 +112,7 @@ fn check_expression<'a, 'tcx: 'a>(
|
|||
|
||||
struct ReturnVisitor<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
arg_id: ast::NodeId,
|
||||
arg_id: hir::HirId,
|
||||
// Found a non-None return that isn't Some(input)
|
||||
found_mapping: bool,
|
||||
// Found a return that isn't Some
|
||||
|
@ -121,7 +120,7 @@ struct ReturnVisitor<'a, 'tcx: 'a> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx: 'a> ReturnVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: ast::NodeId) -> ReturnVisitor<'a, 'tcx> {
|
||||
fn new(cx: &'a LateContext<'a, 'tcx>, arg_id: hir::HirId) -> ReturnVisitor<'a, 'tcx> {
|
||||
ReturnVisitor {
|
||||
cx,
|
||||
arg_id,
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
use crate::consts::{constant_simple, Constant};
|
||||
use crate::utils::{match_def_path, opt_def_id, paths, span_lint};
|
||||
use crate::utils::{match_def_path, paths, span_lint};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
/// **What it does:** Checks for expressions where `std::cmp::min` and `max` are
|
||||
/// used to clamp values, but switched so that the result is constant.
|
||||
///
|
||||
/// **Why is this bad?** This is in all probability not the intended outcome. At
|
||||
/// the least it hurts readability of the code.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// min(0, max(100, x))
|
||||
/// ```
|
||||
/// It will always be equal to `0`. Probably the author meant to clamp the value
|
||||
/// between 0 and 100, but has erroneously swapped `min` and `max`.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for expressions where `std::cmp::min` and `max` are
|
||||
/// used to clamp values, but switched so that the result is constant.
|
||||
///
|
||||
/// **Why is this bad?** This is in all probability not the intended outcome. At
|
||||
/// the least it hurts readability of the code.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// min(0, max(100, x))
|
||||
/// ```
|
||||
/// It will always be equal to `0`. Probably the author meant to clamp the value
|
||||
/// between 0 and 100, but has erroneously swapped `min` and `max`.
|
||||
pub MIN_MAX,
|
||||
correctness,
|
||||
"`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
|
||||
|
@ -72,7 +72,7 @@ enum MinMax {
|
|||
fn min_max<'a>(cx: &LateContext<'_, '_>, expr: &'a Expr) -> Option<(MinMax, Constant, &'a Expr)> {
|
||||
if let ExprKind::Call(ref path, ref args) = expr.node {
|
||||
if let ExprKind::Path(ref qpath) = path.node {
|
||||
opt_def_id(cx.tables.qpath_def(qpath, path.hir_id)).and_then(|def_id| {
|
||||
cx.tables.qpath_def(qpath, path.hir_id).opt_def_id().and_then(|def_id| {
|
||||
if match_def_path(cx.tcx, def_id, &paths::CMP_MIN) {
|
||||
fetch_const(cx, args, MinMax::Min)
|
||||
} else if match_def_path(cx.tcx, def_id, &paths::CMP_MAX) {
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
use crate::consts::{constant, Constant};
|
||||
use crate::reexport::*;
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_item_name, get_parent_expr, implements_trait, in_constant, in_macro, is_integer_literal, iter_input_pats,
|
||||
last_path_segment, match_qpath, match_trait_method, paths, snippet, span_lint, span_lint_and_then, walk_ptrs_ty,
|
||||
SpanlessEq,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use matches::matches;
|
||||
use rustc::hir::intravisit::FnKind;
|
||||
|
@ -17,208 +9,216 @@ use rustc_errors::Applicability;
|
|||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::{ExpnFormat, Span};
|
||||
|
||||
/// **What it does:** Checks for function arguments and let bindings denoted as
|
||||
/// `ref`.
|
||||
///
|
||||
/// **Why is this bad?** The `ref` declaration makes the function take an owned
|
||||
/// value, but turns the argument into a reference (which means that the value
|
||||
/// is destroyed when exiting the function). This adds not much value: either
|
||||
/// take a reference type, or take an owned value and create references in the
|
||||
/// body.
|
||||
///
|
||||
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
|
||||
/// type of `x` is more obvious with the former.
|
||||
///
|
||||
/// **Known problems:** If the argument is dereferenced within the function,
|
||||
/// removing the `ref` will lead to errors. This can be fixed by removing the
|
||||
/// dereferences, e.g. changing `*x` to `x` within the function.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(ref x: u8) -> bool {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
get_item_name, get_parent_expr, implements_trait, in_constant, in_macro, is_integer_literal, iter_input_pats,
|
||||
last_path_segment, match_qpath, match_trait_method, paths, snippet, span_lint, span_lint_and_then, walk_ptrs_ty,
|
||||
SpanlessEq,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for function arguments and let bindings denoted as
|
||||
/// `ref`.
|
||||
///
|
||||
/// **Why is this bad?** The `ref` declaration makes the function take an owned
|
||||
/// value, but turns the argument into a reference (which means that the value
|
||||
/// is destroyed when exiting the function). This adds not much value: either
|
||||
/// take a reference type, or take an owned value and create references in the
|
||||
/// body.
|
||||
///
|
||||
/// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
|
||||
/// type of `x` is more obvious with the former.
|
||||
///
|
||||
/// **Known problems:** If the argument is dereferenced within the function,
|
||||
/// removing the `ref` will lead to errors. This can be fixed by removing the
|
||||
/// dereferences, e.g., changing `*x` to `x` within the function.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(ref x: u8) -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub TOPLEVEL_REF_ARG,
|
||||
style,
|
||||
"an entire binding declared as `ref`, in a function argument or a `let` statement"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for comparisons to NaN.
|
||||
///
|
||||
/// **Why is this bad?** NaN does not compare meaningfully to anything – not
|
||||
/// even itself – so those comparisons are simply wrong.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x == NAN
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for comparisons to NaN.
|
||||
///
|
||||
/// **Why is this bad?** NaN does not compare meaningfully to anything – not
|
||||
/// even itself – so those comparisons are simply wrong.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// x == NAN
|
||||
/// ```
|
||||
pub CMP_NAN,
|
||||
correctness,
|
||||
"comparisons to NAN, which will always return false, probably not intended"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for (in-)equality comparisons on floating-point
|
||||
/// values (apart from zero), except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// **Why is this bad?** Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// y == 1.23f64
|
||||
/// y != x // where both are floats
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for (in-)equality comparisons on floating-point
|
||||
/// values (apart from zero), except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// **Why is this bad?** Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// y == 1.23f64
|
||||
/// y != x // where both are floats
|
||||
/// ```
|
||||
pub FLOAT_CMP,
|
||||
correctness,
|
||||
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for conversions to owned values just for the sake
|
||||
/// of a comparison.
|
||||
///
|
||||
/// **Why is this bad?** The comparison can operate on a reference, so creating
|
||||
/// an owned value effectively throws it away directly afterwards, which is
|
||||
/// needlessly consuming code and heap space.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.to_owned() == y
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for conversions to owned values just for the sake
|
||||
/// of a comparison.
|
||||
///
|
||||
/// **Why is this bad?** The comparison can operate on a reference, so creating
|
||||
/// an owned value effectively throws it away directly afterwards, which is
|
||||
/// needlessly consuming code and heap space.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.to_owned() == y
|
||||
/// ```
|
||||
pub CMP_OWNED,
|
||||
perf,
|
||||
"creating owned instances for comparing with others, e.g. `x == \"foo\".to_string()`"
|
||||
"creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for getting the remainder of a division by one.
|
||||
///
|
||||
/// **Why is this bad?** The result can only ever be zero. No one will write
|
||||
/// such code deliberately, unless trying to win an Underhanded Rust
|
||||
/// Contest. Even for that contest, it's probably a bad idea. Use something more
|
||||
/// underhanded.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x % 1
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for getting the remainder of a division by one.
|
||||
///
|
||||
/// **Why is this bad?** The result can only ever be zero. No one will write
|
||||
/// such code deliberately, unless trying to win an Underhanded Rust
|
||||
/// Contest. Even for that contest, it's probably a bad idea. Use something more
|
||||
/// underhanded.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// x % 1
|
||||
/// ```
|
||||
pub MODULO_ONE,
|
||||
correctness,
|
||||
"taking a number modulo 1, which always returns 0"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for patterns in the form `name @ _`.
|
||||
///
|
||||
/// **Why is this bad?** It's almost always more readable to just use direct
|
||||
/// bindings.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y @ _ => (), // easier written as `y`,
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for patterns in the form `name @ _`.
|
||||
///
|
||||
/// **Why is this bad?** It's almost always more readable to just use direct
|
||||
/// bindings.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// match v {
|
||||
/// Some(x) => (),
|
||||
/// y @ _ => (), // easier written as `y`,
|
||||
/// }
|
||||
/// ```
|
||||
pub REDUNDANT_PATTERN,
|
||||
style,
|
||||
"using `name @ _` in a pattern"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for the use of bindings with a single leading
|
||||
/// underscore.
|
||||
///
|
||||
/// **Why is this bad?** A single leading underscore is usually used to indicate
|
||||
/// that a binding will not be used. Using such a binding breaks this
|
||||
/// expectation.
|
||||
///
|
||||
/// **Known problems:** The lint does not work properly with desugaring and
|
||||
/// macro, it has been allowed in the mean time.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _x = 0;
|
||||
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading
|
||||
/// // underscore. We should rename `_x` to `x`
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of bindings with a single leading
|
||||
/// underscore.
|
||||
///
|
||||
/// **Why is this bad?** A single leading underscore is usually used to indicate
|
||||
/// that a binding will not be used. Using such a binding breaks this
|
||||
/// expectation.
|
||||
///
|
||||
/// **Known problems:** The lint does not work properly with desugaring and
|
||||
/// macro, it has been allowed in the mean time.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _x = 0;
|
||||
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading
|
||||
/// // underscore. We should rename `_x` to `x`
|
||||
/// ```
|
||||
pub USED_UNDERSCORE_BINDING,
|
||||
pedantic,
|
||||
"using a binding which is prefixed with an underscore"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for the use of short circuit boolean conditions as
|
||||
/// a
|
||||
/// statement.
|
||||
///
|
||||
/// **Why is this bad?** Using a short circuit boolean condition as a statement
|
||||
/// may hide the fact that the second part is executed or not depending on the
|
||||
/// outcome of the first part.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// f() && g(); // We should write `if f() { g(); }`.
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for the use of short circuit boolean conditions as
|
||||
/// a
|
||||
/// statement.
|
||||
///
|
||||
/// **Why is this bad?** Using a short circuit boolean condition as a statement
|
||||
/// may hide the fact that the second part is executed or not depending on the
|
||||
/// outcome of the first part.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// f() && g(); // We should write `if f() { g(); }`.
|
||||
/// ```
|
||||
pub SHORT_CIRCUIT_STATEMENT,
|
||||
complexity,
|
||||
"using a short circuit boolean condition as a statement"
|
||||
}
|
||||
|
||||
/// **What it does:** Catch casts from `0` to some pointer type
|
||||
///
|
||||
/// **Why is this bad?** This generally means `null` and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// 0 as *const u32
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Catch casts from `0` to some pointer type
|
||||
///
|
||||
/// **Why is this bad?** This generally means `null` and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// 0 as *const u32
|
||||
/// ```
|
||||
pub ZERO_PTR,
|
||||
style,
|
||||
"using 0 as *{const, mut} T"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for (in-)equality comparisons on floating-point
|
||||
/// value and constant, except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// **Why is this bad?** Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// const ONE = 1.00f64;
|
||||
/// x == ONE // where both are floats
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for (in-)equality comparisons on floating-point
|
||||
/// value and constant, except in functions called `*eq*` (which probably
|
||||
/// implement equality for a type involving floats).
|
||||
///
|
||||
/// **Why is this bad?** Floating point calculations are usually imprecise, so
|
||||
/// asking if two values are *exactly* equal is asking for trouble. For a good
|
||||
/// guide on what to do, see [the floating point
|
||||
/// guide](http://www.floating-point-gui.de/errors/comparison).
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// const ONE = 1.00f64;
|
||||
/// x == ONE // where both are floats
|
||||
/// ```
|
||||
pub FLOAT_CMP_CONST,
|
||||
restriction,
|
||||
"using `==` or `!=` on float constants instead of comparing difference with an epsilon"
|
||||
|
@ -256,7 +256,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
decl: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
_: Span,
|
||||
_: NodeId,
|
||||
_: HirId,
|
||||
) {
|
||||
if let FnKind::Closure(_) = k {
|
||||
// Does not apply to closures
|
||||
|
@ -461,7 +461,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
|
|||
}
|
||||
|
||||
fn check_nan(cx: &LateContext<'_, '_>, path: &Path, expr: &Expr) {
|
||||
if !in_constant(cx, expr.id) {
|
||||
if !in_constant(cx, expr.hir_id) {
|
||||
if let Some(seg) = path.segments.last() {
|
||||
if seg.ident.name == "NAN" {
|
||||
span_lint(
|
||||
|
@ -553,7 +553,7 @@ fn check_to_owned(cx: &LateContext<'_, '_>, expr: &Expr, other: &Expr) {
|
|||
lint_span,
|
||||
"this creates an owned instance just for comparison",
|
||||
|db| {
|
||||
// this also catches PartialEq implementations that call to_owned
|
||||
// This also catches `PartialEq` implementations that call `to_owned`.
|
||||
if other_gets_derefed {
|
||||
db.span_label(lint_span, "try implementing the comparison without allocating");
|
||||
return;
|
||||
|
@ -591,9 +591,8 @@ fn is_used(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// Test whether an expression is in a macro expansion (e.g. something
|
||||
/// generated by
|
||||
/// `#[derive(...)`] or the like).
|
||||
/// Tests whether an expression is in a macro expansion (e.g., something
|
||||
/// generated by `#[derive(...)]` or the like).
|
||||
fn in_attributes_expansion(expr: &Expr) -> bool {
|
||||
expr.span
|
||||
.ctxt()
|
||||
|
@ -602,7 +601,7 @@ fn in_attributes_expansion(expr: &Expr) -> bool {
|
|||
.map_or(false, |info| matches!(info.format, ExpnFormat::MacroAttribute(_)))
|
||||
}
|
||||
|
||||
/// Test whether `def` is a variable defined outside a macro.
|
||||
/// Tests 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, _, _) => !in_macro(cx.tcx.hir().span(id)),
|
||||
|
@ -616,7 +615,7 @@ fn check_cast(cx: &LateContext<'_, '_>, span: Span, e: &Expr, ty: &Ty) {
|
|||
if let ExprKind::Lit(ref lit) = e.node;
|
||||
if let LitKind::Int(value, ..) = lit.node;
|
||||
if value == 0;
|
||||
if !in_constant(cx, e.id);
|
||||
if !in_constant(cx, e.hir_id);
|
||||
then {
|
||||
let msg = match mutbl {
|
||||
Mutability::MutMutable => "`0 as *mut _` detected. Consider using `ptr::null_mut()`",
|
||||
|
|
|
@ -9,163 +9,164 @@ use syntax::ast::*;
|
|||
use syntax::source_map::Span;
|
||||
use syntax::visit::{walk_expr, FnKind, Visitor};
|
||||
|
||||
/// **What it does:** Checks for structure field patterns bound to wildcards.
|
||||
///
|
||||
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
|
||||
/// the fields that are actually bound.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let { a: _, b: ref b, c: _ } = ..
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for structure field patterns bound to wildcards.
|
||||
///
|
||||
/// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
|
||||
/// the fields that are actually bound.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let { a: _, b: ref b, c: _ } = ..
|
||||
/// ```
|
||||
pub UNNEEDED_FIELD_PATTERN,
|
||||
style,
|
||||
"struct fields bound to a wildcard instead of using `..`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for function arguments having the similar names
|
||||
/// differing by an underscore.
|
||||
///
|
||||
/// **Why is this bad?** It affects code readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(a: i32, _a: i32) {}
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for function arguments having the similar names
|
||||
/// differing by an underscore.
|
||||
///
|
||||
/// **Why is this bad?** It affects code readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(a: i32, _a: i32) {}
|
||||
/// ```
|
||||
pub DUPLICATE_UNDERSCORE_ARGUMENT,
|
||||
style,
|
||||
"function arguments having names which only differ by an underscore"
|
||||
}
|
||||
|
||||
/// **What it does:** Detects closures called in the same expression where they
|
||||
/// are defined.
|
||||
///
|
||||
/// **Why is this bad?** It is unnecessarily adding to the expression's
|
||||
/// complexity.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// (|| 42)()
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects closures called in the same expression where they
|
||||
/// are defined.
|
||||
///
|
||||
/// **Why is this bad?** It is unnecessarily adding to the expression's
|
||||
/// complexity.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// (|| 42)()
|
||||
/// ```
|
||||
pub REDUNDANT_CLOSURE_CALL,
|
||||
complexity,
|
||||
"throwaway closures called in the expression they are defined"
|
||||
}
|
||||
|
||||
/// **What it does:** Detects expressions of the form `--x`.
|
||||
///
|
||||
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
|
||||
/// decremented.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// --x;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects expressions of the form `--x`.
|
||||
///
|
||||
/// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
|
||||
/// decremented.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut x = 3;
|
||||
/// --x;
|
||||
/// ```
|
||||
pub DOUBLE_NEG,
|
||||
style,
|
||||
"`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns on hexadecimal literals with mixed-case letter
|
||||
/// digits.
|
||||
///
|
||||
/// **Why is this bad?** It looks confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let y = 0x1a9BAcD;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns on hexadecimal literals with mixed-case letter
|
||||
/// digits.
|
||||
///
|
||||
/// **Why is this bad?** It looks confusing.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let y = 0x1a9BAcD;
|
||||
/// ```
|
||||
pub MIXED_CASE_HEX_LITERALS,
|
||||
style,
|
||||
"hex literals whose letter digits are not consistently upper- or lowercased"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if literal suffixes are not separated by an
|
||||
/// underscore.
|
||||
///
|
||||
/// **Why is this bad?** It is much less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let y = 123832i32;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if literal suffixes are not separated by an
|
||||
/// underscore.
|
||||
///
|
||||
/// **Why is this bad?** It is much less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let y = 123832i32;
|
||||
/// ```
|
||||
pub UNSEPARATED_LITERAL_SUFFIX,
|
||||
pedantic,
|
||||
"literals whose suffix is not separated by an underscore"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if an integral constant literal starts with `0`.
|
||||
///
|
||||
/// **Why is this bad?** In some languages (including the infamous C language
|
||||
/// and most of its
|
||||
/// family), this marks an octal constant. In Rust however, this is a decimal
|
||||
/// constant. This could
|
||||
/// be confusing for both the writer and a reader of the constant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// In Rust:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let a = 0123;
|
||||
/// println!("{}", a);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// prints `123`, while in C:
|
||||
///
|
||||
/// ```c
|
||||
/// #include <stdio.h>
|
||||
///
|
||||
/// int main() {
|
||||
/// int a = 0123;
|
||||
/// printf("%d\n", a);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if an integral constant literal starts with `0`.
|
||||
///
|
||||
/// **Why is this bad?** In some languages (including the infamous C language
|
||||
/// and most of its
|
||||
/// family), this marks an octal constant. In Rust however, this is a decimal
|
||||
/// constant. This could
|
||||
/// be confusing for both the writer and a reader of the constant.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// In Rust:
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let a = 0123;
|
||||
/// println!("{}", a);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// prints `123`, while in C:
|
||||
///
|
||||
/// ```c
|
||||
/// #include <stdio.h>
|
||||
///
|
||||
/// int main() {
|
||||
/// int a = 0123;
|
||||
/// printf("%d\n", a);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
|
||||
pub ZERO_PREFIXED_LITERAL,
|
||||
complexity,
|
||||
"integer literals starting with `0`"
|
||||
}
|
||||
|
||||
/// **What it does:** Warns if a generic shadows a built-in type.
|
||||
///
|
||||
/// **Why is this bad?** This gives surprising type errors.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// impl<u32> Foo<u32> {
|
||||
/// fn impl_func(&self) -> u32 {
|
||||
/// 42
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if a generic shadows a built-in type.
|
||||
///
|
||||
/// **Why is this bad?** This gives surprising type errors.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl<u32> Foo<u32> {
|
||||
/// fn impl_func(&self) -> u32 {
|
||||
/// 42
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub BUILTIN_TYPE_SHADOW,
|
||||
style,
|
||||
"shadowing a builtin type"
|
||||
|
@ -193,7 +194,7 @@ impl LintPass for MiscEarly {
|
|||
}
|
||||
}
|
||||
|
||||
// Used to find `return` statements or equivalents e.g. `?`
|
||||
// Used to find `return` statements or equivalents e.g., `?`
|
||||
struct ReturnVisitor {
|
||||
found_return: bool,
|
||||
}
|
||||
|
|
|
@ -1,58 +1,57 @@
|
|||
use crate::utils::{is_entrypoint_fn, span_lint};
|
||||
use crate::utils::{is_entrypoint_fn, span_lint, trait_ref_of_method};
|
||||
use rustc::hir;
|
||||
use rustc::hir::intravisit::FnKind;
|
||||
use rustc::hir::{Body, Constness, FnDecl};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::hir::{Body, Constness, FnDecl, HirId};
|
||||
use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_mir::transform::qualify_min_const_fn::is_min_const_fn;
|
||||
use syntax::ast::NodeId;
|
||||
use syntax_pos::Span;
|
||||
|
||||
/// **What it does:**
|
||||
///
|
||||
/// Suggests the use of `const` in functions and methods where possible.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
///
|
||||
/// Not having the function const prevents callers of the function from being const as well.
|
||||
///
|
||||
/// **Known problems:**
|
||||
///
|
||||
/// Const functions are currently still being worked on, with some features only being available
|
||||
/// on nightly. This lint does not consider all edge cases currently and the suggestions may be
|
||||
/// incorrect if you are using this lint on stable.
|
||||
///
|
||||
/// Also, the lint only runs one pass over the code. Consider these two non-const functions:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn a() -> i32 {
|
||||
/// 0
|
||||
/// }
|
||||
/// fn b() -> i32 {
|
||||
/// a()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
|
||||
/// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
|
||||
/// will suggest to make `b` const, too.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be a const fn:
|
||||
///
|
||||
/// ```rust
|
||||
/// const fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
///
|
||||
/// Suggests the use of `const` in functions and methods where possible.
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
///
|
||||
/// Not having the function const prevents callers of the function from being const as well.
|
||||
///
|
||||
/// **Known problems:**
|
||||
///
|
||||
/// Const functions are currently still being worked on, with some features only being available
|
||||
/// on nightly. This lint does not consider all edge cases currently and the suggestions may be
|
||||
/// incorrect if you are using this lint on stable.
|
||||
///
|
||||
/// Also, the lint only runs one pass over the code. Consider these two non-const functions:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn a() -> i32 {
|
||||
/// 0
|
||||
/// }
|
||||
/// fn b() -> i32 {
|
||||
/// a()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
|
||||
/// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
|
||||
/// will suggest to make `b` const, too.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be a const fn:
|
||||
///
|
||||
/// ```rust
|
||||
/// const fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// ```
|
||||
pub MISSING_CONST_FOR_FN,
|
||||
nursery,
|
||||
"Lint functions definitions that could be made `const fn`"
|
||||
|
@ -79,11 +78,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
|
|||
_: &FnDecl,
|
||||
_: &Body,
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
let def_id = cx.tcx.hir().local_def_id(node_id);
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(hir_id);
|
||||
|
||||
if is_entrypoint_fn(cx, def_id) {
|
||||
if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -96,7 +95,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingConstForFn {
|
|||
}
|
||||
},
|
||||
FnKind::Method(_, sig, ..) => {
|
||||
if already_const(sig.header) {
|
||||
if trait_ref_of_method(cx, hir_id).is_some() || already_const(sig.header) {
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -14,16 +14,16 @@ use syntax::ast;
|
|||
use syntax::attr;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Warns if there is missing doc for any documentable item
|
||||
/// (public or private).
|
||||
///
|
||||
/// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
|
||||
/// allowed-by-default lint for
|
||||
/// public members, but has no way to enforce documentation of private items.
|
||||
/// This lint fixes that.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Warns if there is missing doc for any documentable item
|
||||
/// (public or private).
|
||||
///
|
||||
/// **Why is this bad?** Doc is good. *rustc* has a `MISSING_DOCS`
|
||||
/// allowed-by-default lint for
|
||||
/// public members, but has no way to enforce documentation of private items.
|
||||
/// This lint fixes that.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
pub MISSING_DOCS_IN_PRIVATE_ITEMS,
|
||||
restriction,
|
||||
"detects missing documentation for public and private members"
|
||||
|
@ -124,7 +124,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
|||
hir::ItemKind::Fn(..) => {
|
||||
// ignore main()
|
||||
if it.ident.name == "main" {
|
||||
let def_id = cx.tcx.hir().local_def_id(it.id);
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(it.hir_id);
|
||||
let def_key = cx.tcx.hir().def_key(def_id);
|
||||
if def_key.parent == Some(hir::def_id::CRATE_DEF_INDEX) {
|
||||
return;
|
||||
|
@ -162,7 +162,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
|||
|
||||
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.hir().local_def_id(impl_item.id);
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(impl_item.hir_id);
|
||||
match cx.tcx.associated_item(def_id).container {
|
||||
ty::TraitContainer(_) => return,
|
||||
ty::ImplContainer(cid) => {
|
||||
|
|
|
@ -5,52 +5,52 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use syntax::ast;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** it lints if an exported function, method, trait method with default impl,
|
||||
/// or trait method impl is not `#[inline]`.
|
||||
///
|
||||
/// **Why is this bad?** In general, it is not. Functions can be inlined across
|
||||
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
|
||||
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
|
||||
/// might intend for most of the methods in their public API to be able to be inlined across
|
||||
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make
|
||||
/// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
|
||||
/// then opt out for specific methods where this might not make sense.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo() {} // missing #[inline]
|
||||
/// fn ok() {} // ok
|
||||
/// #[inline] pub fn bar() {} // ok
|
||||
/// #[inline(always)] pub fn baz() {} // ok
|
||||
///
|
||||
/// pub trait Bar {
|
||||
/// fn bar(); // ok
|
||||
/// fn def_bar() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// struct Baz;
|
||||
/// impl Baz {
|
||||
/// fn priv() {} // ok
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for Baz {
|
||||
/// fn bar() {} // ok - Baz is not exported
|
||||
/// }
|
||||
///
|
||||
/// pub struct PubBaz;
|
||||
/// impl PubBaz {
|
||||
/// fn priv() {} // ok
|
||||
/// pub not_ptriv() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for PubBaz {
|
||||
/// fn bar() {} // missing #[inline]
|
||||
/// fn def_bar() {} // missing #[inline]
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** it lints if an exported function, method, trait method with default impl,
|
||||
/// or trait method impl is not `#[inline]`.
|
||||
///
|
||||
/// **Why is this bad?** In general, it is not. Functions can be inlined across
|
||||
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
|
||||
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
|
||||
/// might intend for most of the methods in their public API to be able to be inlined across
|
||||
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make
|
||||
/// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
|
||||
/// then opt out for specific methods where this might not make sense.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo() {} // missing #[inline]
|
||||
/// fn ok() {} // ok
|
||||
/// #[inline] pub fn bar() {} // ok
|
||||
/// #[inline(always)] pub fn baz() {} // ok
|
||||
///
|
||||
/// pub trait Bar {
|
||||
/// fn bar(); // ok
|
||||
/// fn def_bar() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// struct Baz;
|
||||
/// impl Baz {
|
||||
/// fn priv() {} // ok
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for Baz {
|
||||
/// fn bar() {} // ok - Baz is not exported
|
||||
/// }
|
||||
///
|
||||
/// pub struct PubBaz;
|
||||
/// impl PubBaz {
|
||||
/// fn priv() {} // ok
|
||||
/// pub not_ptriv() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for PubBaz {
|
||||
/// fn bar() {} // missing #[inline]
|
||||
/// fn def_bar() {} // missing #[inline]
|
||||
/// }
|
||||
/// ```
|
||||
pub MISSING_INLINE_IN_PUBLIC_ITEMS,
|
||||
restriction,
|
||||
"detects missing #[inline] attribute for public callables (functions, trait methods, methods...)"
|
||||
|
@ -95,7 +95,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
return;
|
||||
}
|
||||
|
||||
if !cx.access_levels.is_exported(it.id) {
|
||||
if !cx.access_levels.is_exported(cx.tcx.hir().hir_to_node_id(it.hir_id)) {
|
||||
return;
|
||||
}
|
||||
match it.node {
|
||||
|
@ -115,7 +115,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
// trait method with default body needs inline in case
|
||||
// an impl is not provided
|
||||
let desc = "a default trait method";
|
||||
let item = cx.tcx.hir().expect_trait_item(tit.id.node_id);
|
||||
let item = cx.tcx.hir().expect_trait_item(tit.id.hir_id);
|
||||
check_missing_inline_attrs(cx, &item.attrs, item.span, desc);
|
||||
}
|
||||
},
|
||||
|
@ -146,7 +146,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
}
|
||||
|
||||
// If the item being implemented is not exported, then we don't need #[inline]
|
||||
if !cx.access_levels.is_exported(impl_item.id) {
|
||||
let node_id = cx.tcx.hir().hir_to_node_id(impl_item.hir_id);
|
||||
if !cx.access_levels.is_exported(node_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -155,7 +156,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) | hir::ImplItemKind::Existential(_) => return,
|
||||
};
|
||||
|
||||
let def_id = cx.tcx.hir().local_def_id(impl_item.id);
|
||||
let def_id = cx.tcx.hir().local_def_id_from_hir_id(impl_item.hir_id);
|
||||
let trait_def_id = match cx.tcx.associated_item(def_id).container {
|
||||
TraitContainer(cid) => Some(cid),
|
||||
ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
|
||||
|
|
|
@ -8,24 +8,24 @@ use syntax::{ast::*, source_map::DUMMY_SP};
|
|||
use cargo_metadata;
|
||||
use itertools::Itertools;
|
||||
|
||||
/// **What it does:** Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// **Why is this bad?** This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// **Known problems:** Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.4 and v0.2.8, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "3.1.0"
|
||||
/// ansi_term = "0.11.0"
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// **Why is this bad?** This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// **Known problems:** Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.4 and v0.2.8, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "3.1.0"
|
||||
/// ansi_term = "0.11.0"
|
||||
/// ```
|
||||
pub MULTIPLE_CRATE_VERSIONS,
|
||||
cargo,
|
||||
"multiple versions of the same crate being used"
|
||||
|
|
|
@ -5,22 +5,22 @@ use rustc::lint::{in_external_macro, LateContext, LateLintPass, LintArray, LintC
|
|||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for instances of `mut mut` references.
|
||||
///
|
||||
/// **Why is this bad?** Multiple `mut`s don't add anything meaningful to the
|
||||
/// source. This is either a copy'n'paste error, or it shows a fundamental
|
||||
/// misunderstanding of references.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = &mut &mut y;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for instances of `mut mut` references.
|
||||
///
|
||||
/// **Why is this bad?** Multiple `mut`s don't add anything meaningful to the
|
||||
/// source. This is either a copy'n'paste error, or it shows a fundamental
|
||||
/// misunderstanding of references.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = &mut &mut y;
|
||||
/// ```
|
||||
pub MUT_MUT,
|
||||
pedantic,
|
||||
"usage of double-mut refs, e.g. `&mut &mut ...`"
|
||||
"usage of double-mut refs, e.g., `&mut &mut ...`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
|
|
@ -5,19 +5,19 @@ use rustc::ty::subst::Subst;
|
|||
use rustc::ty::{self, Ty};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Detects giving a mutable reference to a function that only
|
||||
/// requires an immutable reference.
|
||||
///
|
||||
/// **Why is this bad?** The immutable reference rules out all other references
|
||||
/// to the value. Also the code misleads about the intent of the call site.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// my_vec.push(&mut value)
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Detects giving a mutable reference to a function that only
|
||||
/// requires an immutable reference.
|
||||
///
|
||||
/// **Why is this bad?** The immutable reference rules out all other references
|
||||
/// to the value. Also the code misleads about the intent of the call site.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// my_vec.push(&mut value)
|
||||
/// ```
|
||||
pub UNNECESSARY_MUT_PASSED,
|
||||
style,
|
||||
"an argument passed as a mutable reference although the callee only demands an immutable reference"
|
||||
|
|
|
@ -9,41 +9,41 @@ use rustc::ty::{self, Ty};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use syntax::ast;
|
||||
|
||||
/// **What it does:** Checks for usages of `Mutex<X>` where an atomic will do.
|
||||
///
|
||||
/// **Why is this bad?** Using a mutex just to make access to a plain bool or
|
||||
/// reference sequential is shooting flies with cannons.
|
||||
/// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
|
||||
/// faster.
|
||||
///
|
||||
/// **Known problems:** This lint cannot detect if the mutex is actually used
|
||||
/// for waiting before a critical section.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Mutex::new(&y);
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `Mutex<X>` where an atomic will do.
|
||||
///
|
||||
/// **Why is this bad?** Using a mutex just to make access to a plain bool or
|
||||
/// reference sequential is shooting flies with cannons.
|
||||
/// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
|
||||
/// faster.
|
||||
///
|
||||
/// **Known problems:** This lint cannot detect if the mutex is actually used
|
||||
/// for waiting before a critical section.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Mutex::new(&y);
|
||||
/// ```
|
||||
pub MUTEX_ATOMIC,
|
||||
perf,
|
||||
"using a mutex where an atomic value could be used instead"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral
|
||||
/// type.
|
||||
///
|
||||
/// **Why is this bad?** Using a mutex just to make access to a plain integer
|
||||
/// sequential is
|
||||
/// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
|
||||
///
|
||||
/// **Known problems:** This lint cannot detect if the mutex is actually used
|
||||
/// for waiting before a critical section.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Mutex::new(0usize);
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for usages of `Mutex<X>` where `X` is an integral
|
||||
/// type.
|
||||
///
|
||||
/// **Why is this bad?** Using a mutex just to make access to a plain integer
|
||||
/// sequential is
|
||||
/// shooting flies with cannons. `std::sync::atomic::AtomicUsize` is leaner and faster.
|
||||
///
|
||||
/// **Known problems:** This lint cannot detect if the mutex is actually used
|
||||
/// for waiting before a critical section.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = Mutex::new(0usize);
|
||||
/// ```
|
||||
pub MUTEX_INTEGER,
|
||||
nursery,
|
||||
"using a mutex for an integer type"
|
||||
|
|
|
@ -11,47 +11,47 @@ use rustc_errors::Applicability;
|
|||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::Spanned;
|
||||
|
||||
/// **What it does:** Checks for expressions of the form `if c { true } else {
|
||||
/// false }`
|
||||
/// (or vice versa) and suggest using the condition directly.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
/// **Known problems:** Maybe false positives: Sometimes, the two branches are
|
||||
/// painstakingly documented (which we of course do not detect), so they *may*
|
||||
/// have some value. Even then, the documentation can be rewritten to match the
|
||||
/// shorter code.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x {
|
||||
/// false
|
||||
/// } else {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for expressions of the form `if c { true } else {
|
||||
/// false }`
|
||||
/// (or vice versa) and suggest using the condition directly.
|
||||
///
|
||||
/// **Why is this bad?** Redundant code.
|
||||
///
|
||||
/// **Known problems:** Maybe false positives: Sometimes, the two branches are
|
||||
/// painstakingly documented (which we of course do not detect), so they *may*
|
||||
/// have some value. Even then, the documentation can be rewritten to match the
|
||||
/// shorter code.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x {
|
||||
/// false
|
||||
/// } else {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_BOOL,
|
||||
complexity,
|
||||
"if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }`"
|
||||
"if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for expressions of the form `x == true`,
|
||||
/// `x != true` and order comparisons such as `x < true` (or vice versa) and
|
||||
/// suggest using the variable directly.
|
||||
///
|
||||
/// **Why is this bad?** Unnecessary code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x == true {} // could be `if x { }`
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for expressions of the form `x == true`,
|
||||
/// `x != true` and order comparisons such as `x < true` (or vice versa) and
|
||||
/// suggest using the variable directly.
|
||||
///
|
||||
/// **Why is this bad?** Unnecessary code.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if x == true {} // could be `if x { }`
|
||||
/// ```
|
||||
pub BOOL_COMPARISON,
|
||||
complexity,
|
||||
"comparing a variable to a boolean, e.g. `if x == true` or `if x != true`"
|
||||
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -126,8 +126,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
|
|||
}
|
||||
|
||||
fn parent_node_is_if_expr<'a, 'b>(expr: &Expr, cx: &LateContext<'a, 'b>) -> bool {
|
||||
let parent_id = cx.tcx.hir().get_parent_node(expr.id);
|
||||
let parent_node = cx.tcx.hir().get(parent_id);
|
||||
let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(expr.hir_id);
|
||||
let parent_node = cx.tcx.hir().get_by_hir_id(parent_id);
|
||||
|
||||
if let rustc::hir::Node::Expr(e) = parent_node {
|
||||
if let ExprKind::If(_, _, _) = e.node {
|
||||
|
@ -225,23 +225,23 @@ fn check_comparison<'a, 'tcx>(
|
|||
use self::Expression::*;
|
||||
|
||||
if let ExprKind::Binary(_, ref left_side, ref right_side) = e.node {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
|
||||
(Bool(true), Other) => left_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Bool(true)) => right_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
|
||||
}),
|
||||
(Bool(false), Other) => left_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Bool(false)) => right_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Other) => no_literal.map_or((), |(h, m)| {
|
||||
let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
|
||||
if l_ty.is_bool() && r_ty.is_bool() {
|
||||
let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
|
||||
if l_ty.is_bool() && r_ty.is_bool() {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
|
||||
(Bool(true), Other) => left_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Bool(true)) => right_true.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
|
||||
}),
|
||||
(Bool(false), Other) => left_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, right_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Bool(false)) => right_false.map_or((), |(h, m)| {
|
||||
suggest_bool_comparison(cx, e, left_side, applicability, m, h)
|
||||
}),
|
||||
(Other, Other) => no_literal.map_or((), |(h, m)| {
|
||||
let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
|
||||
let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
|
@ -253,9 +253,9 @@ fn check_comparison<'a, 'tcx>(
|
|||
h(left_side, right_side).to_string(),
|
||||
applicability,
|
||||
)
|
||||
}
|
||||
}),
|
||||
_ => (),
|
||||
}),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,27 +4,26 @@
|
|||
|
||||
use crate::utils::{in_macro, snippet_opt, span_lint_and_then};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::{BindingAnnotation, Expr, ExprKind, Item, MutImmutable, Pat, PatKind};
|
||||
use rustc::hir::{BindingAnnotation, Expr, ExprKind, HirId, Item, MutImmutable, Pat, PatKind};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::ty;
|
||||
use rustc::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast::NodeId;
|
||||
|
||||
/// **What it does:** Checks for address of operations (`&`) that are going to
|
||||
/// be dereferenced immediately by the compiler.
|
||||
///
|
||||
/// **Why is this bad?** Suggests that the receiver of the expression borrows
|
||||
/// the expression.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: &i32 = &&&&&&5;
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for address of operations (`&`) that are going to
|
||||
/// be dereferenced immediately by the compiler.
|
||||
///
|
||||
/// **Why is this bad?** Suggests that the receiver of the expression borrows
|
||||
/// the expression.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x: &i32 = &&&&&&5;
|
||||
/// ```
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
pub NEEDLESS_BORROW,
|
||||
nursery,
|
||||
"taking a reference that is going to be automatically dereferenced"
|
||||
|
@ -32,7 +31,7 @@ declare_clippy_lint! {
|
|||
|
||||
#[derive(Default)]
|
||||
pub struct NeedlessBorrow {
|
||||
derived_item: Option<NodeId>,
|
||||
derived_item: Option<HirId>,
|
||||
}
|
||||
|
||||
impl LintPass for NeedlessBorrow {
|
||||
|
@ -119,13 +118,13 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBorrow {
|
|||
fn check_item(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if item.attrs.iter().any(|a| a.check_name("automatically_derived")) {
|
||||
debug_assert!(self.derived_item.is_none());
|
||||
self.derived_item = Some(item.id);
|
||||
self.derived_item = Some(item.hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item_post(&mut self, _: &LateContext<'a, 'tcx>, item: &'tcx Item) {
|
||||
if let Some(id) = self.derived_item {
|
||||
if item.id == id {
|
||||
if item.hir_id == id {
|
||||
self.derived_item = None;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,43 +9,43 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
|
||||
/// **What it does:** Checks for useless borrowed references.
|
||||
///
|
||||
/// **Why is this bad?** It is mostly useless and make the code look more
|
||||
/// complex than it
|
||||
/// actually is.
|
||||
///
|
||||
/// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
|
||||
/// For instance in the following snippet:
|
||||
/// ```rust
|
||||
/// enum Animal {
|
||||
/// Cat(u64),
|
||||
/// Dog(u64),
|
||||
/// }
|
||||
///
|
||||
/// fn foo(a: &Animal, b: &Animal) {
|
||||
/// match (a, b) {
|
||||
/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime
|
||||
/// mismatch error
|
||||
/// (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// There is a lifetime mismatch error for `k` (indeed a and b have distinct
|
||||
/// lifetime).
|
||||
/// This can be fixed by using the `&ref` pattern.
|
||||
/// However, the code can also be fixed by much cleaner ways
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
|
||||
/// ```
|
||||
/// This closure takes a reference on something that has been matched as a
|
||||
/// reference and
|
||||
/// de-referenced.
|
||||
/// As such, it could just be |a| a.is_empty()
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for useless borrowed references.
|
||||
///
|
||||
/// **Why is this bad?** It is mostly useless and make the code look more
|
||||
/// complex than it
|
||||
/// actually is.
|
||||
///
|
||||
/// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
|
||||
/// For instance in the following snippet:
|
||||
/// ```rust
|
||||
/// enum Animal {
|
||||
/// Cat(u64),
|
||||
/// Dog(u64),
|
||||
/// }
|
||||
///
|
||||
/// fn foo(a: &Animal, b: &Animal) {
|
||||
/// match (a, b) {
|
||||
/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime
|
||||
/// mismatch error
|
||||
/// (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// There is a lifetime mismatch error for `k` (indeed a and b have distinct
|
||||
/// lifetime).
|
||||
/// This can be fixed by using the `&ref` pattern.
|
||||
/// However, the code can also be fixed by much cleaner ways
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut v = Vec::<String>::new();
|
||||
/// let _ = v.iter_mut().filter(|&ref a| a.is_empty());
|
||||
/// ```
|
||||
/// This closure takes a reference on something that has been matched as a
|
||||
/// reference and
|
||||
/// de-referenced.
|
||||
/// As such, it could just be |a| a.is_empty()
|
||||
pub NEEDLESS_BORROWED_REFERENCE,
|
||||
complexity,
|
||||
"taking a needless borrowed reference"
|
||||
|
|
|
@ -35,66 +35,66 @@ use syntax::source_map::{original_sp, DUMMY_SP};
|
|||
|
||||
use crate::utils::{in_macro, snippet, snippet_block, span_help_and_lint, trim_multiline};
|
||||
|
||||
/// **What it does:** The lint checks for `if`-statements appearing in loops
|
||||
/// that contain a `continue` statement in either their main blocks or their
|
||||
/// `else`-blocks, when omitting the `else`-block possibly with some
|
||||
/// rearrangement of code can make the code easier to understand.
|
||||
///
|
||||
/// **Why is this bad?** Having explicit `else` blocks for `if` statements
|
||||
/// containing `continue` in their THEN branch adds unnecessary branching and
|
||||
/// nesting to the code. Having an else block containing just `continue` can
|
||||
/// also be better written by grouping the statements following the whole `if`
|
||||
/// statement within the THEN block and omitting the else block completely.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
/// // ...
|
||||
/// } else {
|
||||
/// continue;
|
||||
/// }
|
||||
/// println!("Hello, world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
/// // ...
|
||||
/// println!("Hello, world");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// As another example, the following code
|
||||
///
|
||||
/// ```rust
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// } else {
|
||||
/// // Do something useful
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// }
|
||||
/// // Do something useful
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** The lint checks for `if`-statements appearing in loops
|
||||
/// that contain a `continue` statement in either their main blocks or their
|
||||
/// `else`-blocks, when omitting the `else`-block possibly with some
|
||||
/// rearrangement of code can make the code easier to understand.
|
||||
///
|
||||
/// **Why is this bad?** Having explicit `else` blocks for `if` statements
|
||||
/// containing `continue` in their THEN branch adds unnecessary branching and
|
||||
/// nesting to the code. Having an else block containing just `continue` can
|
||||
/// also be better written by grouping the statements following the whole `if`
|
||||
/// statement within the THEN block and omitting the else block completely.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
/// // ...
|
||||
/// } else {
|
||||
/// continue;
|
||||
/// }
|
||||
/// println!("Hello, world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
/// // ...
|
||||
/// println!("Hello, world");
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// As another example, the following code
|
||||
///
|
||||
/// ```rust
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// } else {
|
||||
/// // Do something useful
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// }
|
||||
/// // Do something useful
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_CONTINUE,
|
||||
pedantic,
|
||||
"`continue` statements that can be replaced by a rearrangement of code"
|
||||
|
|
|
@ -17,34 +17,33 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use std::borrow::Cow;
|
||||
use syntax::ast::NodeId;
|
||||
use syntax::errors::DiagnosticBuilder;
|
||||
use syntax_pos::Span;
|
||||
|
||||
/// **What it does:** Checks for functions taking arguments by value, but not
|
||||
/// consuming them in its
|
||||
/// body.
|
||||
///
|
||||
/// **Why is this bad?** Taking arguments by reference is more flexible and can
|
||||
/// sometimes avoid
|
||||
/// unnecessary allocations.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// * This lint suggests taking an argument by reference,
|
||||
/// however sometimes it is better to let users decide the argument type
|
||||
/// (by using `Borrow` trait, for example), depending on how the function is used.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(v: Vec<i32>) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// // should be
|
||||
/// fn foo(v: &[i32]) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for functions taking arguments by value, but not
|
||||
/// consuming them in its
|
||||
/// body.
|
||||
///
|
||||
/// **Why is this bad?** Taking arguments by reference is more flexible and can
|
||||
/// sometimes avoid
|
||||
/// unnecessary allocations.
|
||||
///
|
||||
/// **Known problems:**
|
||||
/// * This lint suggests taking an argument by reference,
|
||||
/// however sometimes it is better to let users decide the argument type
|
||||
/// (by using `Borrow` trait, for example), depending on how the function is used.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn foo(v: Vec<i32>) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// // should be
|
||||
/// fn foo(v: &[i32]) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_PASS_BY_VALUE,
|
||||
pedantic,
|
||||
"functions taking arguments by value, but not consuming them in its body"
|
||||
|
@ -81,7 +80,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
decl: &'tcx FnDecl,
|
||||
body: &'tcx Body,
|
||||
span: Span,
|
||||
node_id: NodeId,
|
||||
hir_id: HirId,
|
||||
) {
|
||||
if in_macro(span) {
|
||||
return;
|
||||
|
@ -103,7 +102,11 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
}
|
||||
|
||||
// Exclude non-inherent impls
|
||||
if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(node_id)) {
|
||||
if let Some(Node::Item(item)) = cx
|
||||
.tcx
|
||||
.hir()
|
||||
.find_by_hir_id(cx.tcx.hir().get_parent_node_by_hir_id(hir_id))
|
||||
{
|
||||
if matches!(item.node, ItemKind::Impl(_, _, _, _, Some(_), _, _) |
|
||||
ItemKind::Trait(..))
|
||||
{
|
||||
|
@ -122,7 +125,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
|
||||
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
|
||||
|
||||
let fn_def_id = cx.tcx.hir().local_def_id(node_id);
|
||||
let fn_def_id = cx.tcx.hir().local_def_id_from_hir_id(hir_id);
|
||||
|
||||
let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.to_vec())
|
||||
.filter(|p| !p.is_global())
|
||||
|
@ -173,7 +176,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
|
||||
//
|
||||
// * Exclude a type that is specifically bounded by `Borrow`.
|
||||
// * Exclude a type whose reference also fulfills its bound. (e.g. `std::convert::AsRef`,
|
||||
// * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
|
||||
// `serde::Serialize`)
|
||||
let (implements_borrow_trait, all_borrowable_trait) = {
|
||||
let preds = preds
|
||||
|
@ -322,10 +325,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
|
||||
struct MovedVariablesCtxt<'a, 'tcx: 'a> {
|
||||
cx: &'a LateContext<'a, 'tcx>,
|
||||
moved_vars: FxHashSet<NodeId>,
|
||||
moved_vars: FxHashSet<HirId>,
|
||||
/// Spans which need to be prefixed with `*` for dereferencing the
|
||||
/// suggested additional reference.
|
||||
spans_need_deref: FxHashMap<NodeId, FxHashSet<Span>>,
|
||||
spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
|
||||
|
@ -337,7 +340,7 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn move_common(&mut self, _consume_id: NodeId, _span: Span, cmt: &mc::cmt_<'tcx>) {
|
||||
fn move_common(&mut self, _consume_id: HirId, _span: Span, cmt: &mc::cmt_<'tcx>) {
|
||||
let cmt = unwrap_downcast_or_interior(cmt);
|
||||
|
||||
if let mc::Categorization::Local(vid) = cmt.cat {
|
||||
|
@ -349,16 +352,16 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
|
|||
let cmt = unwrap_downcast_or_interior(cmt);
|
||||
|
||||
if let mc::Categorization::Local(vid) = cmt.cat {
|
||||
let mut id = matched_pat.id;
|
||||
let mut id = matched_pat.hir_id;
|
||||
loop {
|
||||
let parent = self.cx.tcx.hir().get_parent_node(id);
|
||||
let parent = self.cx.tcx.hir().get_parent_node_by_hir_id(id);
|
||||
if id == parent {
|
||||
// no parent
|
||||
return;
|
||||
}
|
||||
id = parent;
|
||||
|
||||
if let Some(node) = self.cx.tcx.hir().find(id) {
|
||||
if let Some(node) = self.cx.tcx.hir().find_by_hir_id(id) {
|
||||
match node {
|
||||
Node::Expr(e) => {
|
||||
// `match` and `if let`
|
||||
|
@ -395,7 +398,7 @@ impl<'a, 'tcx> MovedVariablesCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
|
||||
fn consume(&mut self, consume_id: NodeId, consume_span: Span, cmt: &mc::cmt_<'tcx>, mode: euv::ConsumeMode) {
|
||||
fn consume(&mut self, consume_id: HirId, consume_span: Span, cmt: &mc::cmt_<'tcx>, mode: euv::ConsumeMode) {
|
||||
if let euv::ConsumeMode::Move(_) = mode {
|
||||
self.move_common(consume_id, consume_span, cmt);
|
||||
}
|
||||
|
@ -403,7 +406,7 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
|
|||
|
||||
fn matched_pat(&mut self, matched_pat: &Pat, cmt: &mc::cmt_<'tcx>, mode: euv::MatchMode) {
|
||||
if let euv::MatchMode::MovingMatch = mode {
|
||||
self.move_common(matched_pat.id, matched_pat.span, cmt);
|
||||
self.move_common(matched_pat.hir_id, matched_pat.span, cmt);
|
||||
} else {
|
||||
self.non_moving_pat(matched_pat, cmt);
|
||||
}
|
||||
|
@ -411,13 +414,13 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
|
|||
|
||||
fn consume_pat(&mut self, consume_pat: &Pat, cmt: &mc::cmt_<'tcx>, mode: euv::ConsumeMode) {
|
||||
if let euv::ConsumeMode::Move(_) = mode {
|
||||
self.move_common(consume_pat.id, consume_pat.span, cmt);
|
||||
self.move_common(consume_pat.hir_id, consume_pat.span, cmt);
|
||||
}
|
||||
}
|
||||
|
||||
fn borrow(
|
||||
&mut self,
|
||||
_: NodeId,
|
||||
_: HirId,
|
||||
_: Span,
|
||||
_: &mc::cmt_<'tcx>,
|
||||
_: ty::Region<'_>,
|
||||
|
@ -426,9 +429,9 @@ impl<'a, 'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt<'a, 'tcx> {
|
|||
) {
|
||||
}
|
||||
|
||||
fn mutate(&mut self, _: NodeId, _: Span, _: &mc::cmt_<'tcx>, _: euv::MutateMode) {}
|
||||
fn mutate(&mut self, _: HirId, _: Span, _: &mc::cmt_<'tcx>, _: euv::MutateMode) {}
|
||||
|
||||
fn decl_without_init(&mut self, _: NodeId, _: Span) {}
|
||||
fn decl_without_init(&mut self, _: HirId, _: Span) {}
|
||||
}
|
||||
|
||||
fn unwrap_downcast_or_interior<'a, 'tcx>(mut cmt: &'a mc::cmt_<'tcx>) -> mc::cmt_<'tcx> {
|
||||
|
|
|
@ -4,23 +4,23 @@ use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
|||
use rustc::ty;
|
||||
use rustc::{declare_tool_lint, lint_array};
|
||||
|
||||
/// **What it does:** Checks for needlessly including a base struct on update
|
||||
/// when all fields are changed anyway.
|
||||
///
|
||||
/// **Why is this bad?** This will cost resources (because the base has to be
|
||||
/// somewhere), and make the code less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 0,
|
||||
/// ..zero_point
|
||||
/// }
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for needlessly including a base struct on update
|
||||
/// when all fields are changed anyway.
|
||||
///
|
||||
/// **Why is this bad?** This will cost resources (because the base has to be
|
||||
/// somewhere), and make the code less readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 0,
|
||||
/// ..zero_point
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_UPDATE,
|
||||
complexity,
|
||||
"using `Foo { ..base }` when there are no missing fields"
|
||||
|
|
|
@ -5,38 +5,38 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
|
||||
use crate::utils::{self, paths, span_lint};
|
||||
|
||||
/// **What it does:**
|
||||
/// Checks for the usage of negated comparison operators on types which only implement
|
||||
/// `PartialOrd` (e.g. `f64`).
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// These operators make it easy to forget that the underlying types actually allow not only three
|
||||
/// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
|
||||
/// especially easy to miss if the operator based comparison result is negated.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = 1.0;
|
||||
/// let b = std::f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = !(a <= b);
|
||||
///
|
||||
/// // Good
|
||||
/// let a = 1.0;
|
||||
/// let b = std::f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = match a.partial_cmp(&b) {
|
||||
/// None | Some(Ordering::Greater) => true,
|
||||
/// _ => false,
|
||||
/// };
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:**
|
||||
/// Checks for the usage of negated comparison operators on types which only implement
|
||||
/// `PartialOrd` (e.g., `f64`).
|
||||
///
|
||||
/// **Why is this bad?**
|
||||
/// These operators make it easy to forget that the underlying types actually allow not only three
|
||||
/// potential Orderings (Less, Equal, Greater) but also a fourth one (Uncomparable). This is
|
||||
/// especially easy to miss if the operator based comparison result is negated.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::cmp::Ordering;
|
||||
///
|
||||
/// // Bad
|
||||
/// let a = 1.0;
|
||||
/// let b = std::f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = !(a <= b);
|
||||
///
|
||||
/// // Good
|
||||
/// let a = 1.0;
|
||||
/// let b = std::f64::NAN;
|
||||
///
|
||||
/// let _not_less_or_equal = match a.partial_cmp(&b) {
|
||||
/// None | Some(Ordering::Greater) => true,
|
||||
/// _ => false,
|
||||
/// };
|
||||
/// ```
|
||||
pub NEG_CMP_OP_ON_PARTIAL_ORD,
|
||||
complexity,
|
||||
"The use of negated comparison operators on partially ordered types may produce confusing code."
|
||||
|
|
|
@ -7,17 +7,17 @@ use syntax::source_map::{Span, Spanned};
|
|||
use crate::consts::{self, Constant};
|
||||
use crate::utils::span_lint;
|
||||
|
||||
/// **What it does:** Checks for multiplication by -1 as a form of negation.
|
||||
///
|
||||
/// **Why is this bad?** It's more readable to just negate.
|
||||
///
|
||||
/// **Known problems:** This only catches integers (for now).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x * -1
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for multiplication by -1 as a form of negation.
|
||||
///
|
||||
/// **Why is this bad?** It's more readable to just negate.
|
||||
///
|
||||
/// **Known problems:** This only catches integers (for now).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// x * -1
|
||||
/// ```
|
||||
pub NEG_MULTIPLY,
|
||||
style,
|
||||
"multiplying integers with -1"
|
||||
|
|
|
@ -11,74 +11,74 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
/// **What it does:** Checks for types with a `fn new() -> Self` method and no
|
||||
/// implementation of
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
|
||||
///
|
||||
/// It detects both the case when a manual
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
|
||||
/// implementation is required and also when it can be created with
|
||||
/// `#[derive(Default)]`
|
||||
///
|
||||
/// **Why is this bad?** The user might expect to be able to use
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
|
||||
/// type can be constructed without arguments.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Foo(Bar);
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo(Bar::new())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead, use:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Foo(Bar);
|
||||
///
|
||||
/// impl Default for Foo {
|
||||
/// fn default() -> Self {
|
||||
/// Foo(Bar::new())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or, if
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
|
||||
/// can be derived by `#[derive(Default)]`:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead, use:
|
||||
///
|
||||
/// ```rust
|
||||
/// #[derive(Default)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You can also have `new()` call `Default::default()`.
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for types with a `fn new() -> Self` method and no
|
||||
/// implementation of
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
|
||||
///
|
||||
/// It detects both the case when a manual
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
|
||||
/// implementation is required and also when it can be created with
|
||||
/// `#[derive(Default)]`
|
||||
///
|
||||
/// **Why is this bad?** The user might expect to be able to use
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
|
||||
/// type can be constructed without arguments.
|
||||
///
|
||||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct Foo(Bar);
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo(Bar::new())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead, use:
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct Foo(Bar);
|
||||
///
|
||||
/// impl Default for Foo {
|
||||
/// fn default() -> Self {
|
||||
/// Foo(Bar::new())
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or, if
|
||||
/// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html)
|
||||
/// can be derived by `#[derive(Default)]`:
|
||||
///
|
||||
/// ```rust
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Instead, use:
|
||||
///
|
||||
/// ```rust
|
||||
/// #[derive(Default)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Foo
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// You can also have `new()` call `Default::default()`.
|
||||
pub NEW_WITHOUT_DEFAULT,
|
||||
style,
|
||||
"`fn new() -> Self` method without `Default` implementation"
|
||||
|
@ -110,7 +110,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
|
|||
}
|
||||
if let hir::ImplItemKind::Method(ref sig, _) = impl_item.node {
|
||||
let name = impl_item.ident.name;
|
||||
let id = impl_item.id;
|
||||
let id = impl_item.hir_id;
|
||||
let node_id = cx.tcx.hir().hir_to_node_id(id);
|
||||
if sig.header.constness == hir::Constness::Const {
|
||||
// can't be implemented by default
|
||||
return;
|
||||
|
@ -128,8 +129,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NewWithoutDefault {
|
|||
// impl of `Default`
|
||||
return;
|
||||
}
|
||||
if sig.decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(id) {
|
||||
let self_did = cx.tcx.hir().local_def_id(cx.tcx.hir().get_parent(id));
|
||||
if sig.decl.inputs.is_empty() && name == "new" && cx.access_levels.is_reachable(node_id) {
|
||||
let self_did = cx.tcx.hir().local_def_id_from_hir_id(cx.tcx.hir().get_parent_item(id));
|
||||
let self_ty = cx.tcx.type_of(self_did);
|
||||
if_chain! {
|
||||
if same_tys(cx, self_ty, return_ty(cx, id));
|
||||
|
|
|
@ -6,37 +6,37 @@ use rustc::{declare_tool_lint, lint_array};
|
|||
use rustc_errors::Applicability;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// **What it does:** Checks for statements which have no effect.
|
||||
///
|
||||
/// **Why is this bad?** Similar to dead code, these statements are actually
|
||||
/// executed. However, as they have no effect, all they do is make the code less
|
||||
/// readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 0;
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for statements which have no effect.
|
||||
///
|
||||
/// **Why is this bad?** Similar to dead code, these statements are actually
|
||||
/// executed. However, as they have no effect, all they do is make the code less
|
||||
/// readable.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 0;
|
||||
/// ```
|
||||
pub NO_EFFECT,
|
||||
complexity,
|
||||
"statements with no effect"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks for expression statements that can be reduced to a
|
||||
/// sub-expression.
|
||||
///
|
||||
/// **Why is this bad?** Expressions by themselves often have no side-effects.
|
||||
/// Having such expressions reduces readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// compute_array()[0];
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for expression statements that can be reduced to a
|
||||
/// sub-expression.
|
||||
///
|
||||
/// **Why is this bad?** Expressions by themselves often have no side-effects.
|
||||
/// Having such expressions reduces readability.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// compute_array()[0];
|
||||
/// ```
|
||||
pub UNNECESSARY_OPERATION,
|
||||
complexity,
|
||||
"outer expressions with no effect"
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! Checks for uses of const which the type is not Freeze (Cell-free).
|
||||
//! Checks for uses of const which the type is not `Freeze` (`Cell`-free).
|
||||
//!
|
||||
//! This lint is **deny** by default.
|
||||
|
||||
use crate::utils::{in_constant, in_macro, is_copy, span_lint_and_then};
|
||||
use std::ptr;
|
||||
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, Lint, LintArray, LintPass};
|
||||
|
@ -11,72 +12,73 @@ use rustc::ty::{self, TypeFlags};
|
|||
use rustc::{declare_tool_lint, lint_array};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
use std::ptr;
|
||||
use syntax_pos::{Span, DUMMY_SP};
|
||||
|
||||
/// **What it does:** Checks for declaration of `const` items which is interior
|
||||
/// mutable (e.g. contains a `Cell`, `Mutex`, `AtomicXxxx` etc).
|
||||
///
|
||||
/// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
///
|
||||
/// The `const` should better be replaced by a `static` item if a global
|
||||
/// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
|
||||
///
|
||||
/// **Known problems:** A "non-constant" const item is a legacy way to supply an
|
||||
/// initialized value to downstream `static` items (e.g. the
|
||||
/// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
|
||||
/// and this lint should be suppressed.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
///
|
||||
/// // Bad.
|
||||
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
|
||||
/// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
|
||||
/// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
|
||||
///
|
||||
/// // Good.
|
||||
/// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
|
||||
/// STATIC_ATOM.store(9, SeqCst);
|
||||
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
|
||||
/// ```
|
||||
use crate::utils::{in_constant, in_macro, is_copy, span_lint_and_then};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for declaration of `const` items which is interior
|
||||
/// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
|
||||
///
|
||||
/// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
///
|
||||
/// The `const` should better be replaced by a `static` item if a global
|
||||
/// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
|
||||
///
|
||||
/// **Known problems:** A "non-constant" const item is a legacy way to supply an
|
||||
/// initialized value to downstream `static` items (e.g., the
|
||||
/// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
|
||||
/// and this lint should be suppressed.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
///
|
||||
/// // Bad.
|
||||
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
|
||||
/// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
|
||||
/// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
|
||||
///
|
||||
/// // Good.
|
||||
/// static STATIC_ATOM: AtomicUsize = AtomicUsize::new(15);
|
||||
/// STATIC_ATOM.store(9, SeqCst);
|
||||
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
|
||||
/// ```
|
||||
pub DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
correctness,
|
||||
"declaring const with interior mutability"
|
||||
}
|
||||
|
||||
/// **What it does:** Checks if `const` items which is interior mutable (e.g.
|
||||
/// contains a `Cell`, `Mutex`, `AtomicXxxx` etc) has been borrowed directly.
|
||||
///
|
||||
/// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
///
|
||||
/// The `const` value should be stored inside a `static` item.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
|
||||
///
|
||||
/// // Bad.
|
||||
/// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
|
||||
/// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
|
||||
///
|
||||
/// // Good.
|
||||
/// static STATIC_ATOM: AtomicUsize = CONST_ATOM;
|
||||
/// STATIC_ATOM.store(9, SeqCst);
|
||||
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
|
||||
/// ```
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks if `const` items which is interior mutable (e.g.,
|
||||
/// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
|
||||
///
|
||||
/// **Why is this bad?** Consts are copied everywhere they are referenced, i.e.,
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
///
|
||||
/// The `const` value should be stored inside a `static` item.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
|
||||
/// const CONST_ATOM: AtomicUsize = AtomicUsize::new(12);
|
||||
///
|
||||
/// // Bad.
|
||||
/// CONST_ATOM.store(6, SeqCst); // the content of the atomic is unchanged
|
||||
/// assert_eq!(CONST_ATOM.load(SeqCst), 12); // because the CONST_ATOM in these lines are distinct
|
||||
///
|
||||
/// // Good.
|
||||
/// static STATIC_ATOM: AtomicUsize = CONST_ATOM;
|
||||
/// STATIC_ATOM.store(9, SeqCst);
|
||||
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
|
||||
/// ```
|
||||
pub BORROW_INTERIOR_MUTABLE_CONST,
|
||||
correctness,
|
||||
"referencing const with interior mutability"
|
||||
|
@ -108,8 +110,8 @@ impl Source {
|
|||
|
||||
fn verify_ty_bound<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, ty: ty::Ty<'tcx>, source: Source) {
|
||||
if ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP) || is_copy(cx, ty) {
|
||||
// an UnsafeCell is !Copy, and an UnsafeCell is also the only type which
|
||||
// is !Freeze, thus if our type is Copy we can be sure it must be Freeze
|
||||
// An `UnsafeCell` is `!Copy`, and an `UnsafeCell` is also the only type which
|
||||
// is `!Freeze`, thus if our type is `Copy` we can be sure it must be `Freeze`
|
||||
// as well.
|
||||
return;
|
||||
}
|
||||
|
@ -177,9 +179,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst {
|
|||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem) {
|
||||
if let ImplItemKind::Const(hir_ty, ..) = &impl_item.node {
|
||||
let item_node_id = cx.tcx.hir().get_parent_node(impl_item.id);
|
||||
let item = cx.tcx.hir().expect_item(item_node_id);
|
||||
// ensure the impl is an inherent impl.
|
||||
let item_hir_id = cx.tcx.hir().get_parent_node_by_hir_id(impl_item.hir_id);
|
||||
let item = cx.tcx.hir().expect_item_by_hir_id(item_hir_id);
|
||||
// Ensure the impl is an inherent impl.
|
||||
if let ItemKind::Impl(_, _, _, _, None, _, _) = item.node {
|
||||
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
|
||||
verify_ty_bound(
|
||||
|
@ -197,29 +199,29 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst {
|
|||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if let ExprKind::Path(qpath) = &expr.node {
|
||||
// Only lint if we use the const item inside a function.
|
||||
if in_constant(cx, expr.id) {
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure it is a const item.
|
||||
// Make sure it is a const item.
|
||||
match cx.tables.qpath_def(qpath, expr.hir_id) {
|
||||
Def::Const(_) | Def::AssociatedConst(_) => {},
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// climb up to resolve any field access and explicit referencing.
|
||||
// Climb up to resolve any field access and explicit referencing.
|
||||
let mut cur_expr = expr;
|
||||
let mut dereferenced_expr = expr;
|
||||
let mut needs_check_adjustment = true;
|
||||
loop {
|
||||
let parent_id = cx.tcx.hir().get_parent_node(cur_expr.id);
|
||||
if parent_id == cur_expr.id {
|
||||
let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(cur_expr.hir_id);
|
||||
if parent_id == cur_expr.hir_id {
|
||||
break;
|
||||
}
|
||||
if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find(parent_id) {
|
||||
if let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find_by_hir_id(parent_id) {
|
||||
match &parent_expr.node {
|
||||
ExprKind::AddrOf(..) => {
|
||||
// `&e` => `e` must be referenced
|
||||
// `&e` => `e` must be referenced.
|
||||
needs_check_adjustment = false;
|
||||
},
|
||||
ExprKind::Field(..) => {
|
||||
|
@ -260,7 +262,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCopyConst {
|
|||
adjustments[i - 1].target
|
||||
}
|
||||
} else {
|
||||
// No borrow adjustments = the entire const is moved.
|
||||
// No borrow adjustments means the entire const is moved.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue