mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-27 07:00:55 +00:00
Merge branch 'origin/master' into flat-map
This commit is contained in:
commit
05d9f884e1
213 changed files with 3350 additions and 1588 deletions
6
.github/PULL_REQUEST_TEMPLATE
vendored
6
.github/PULL_REQUEST_TEMPLATE
vendored
|
@ -16,9 +16,11 @@ checked during review or continuous integration.
|
|||
- [ ] Followed [lint naming conventions][lint_naming]
|
||||
- [ ] Added passing UI tests (including committed `.stderr` file)
|
||||
- [ ] `cargo test` passes locally
|
||||
- [ ] Executed `util/dev update_lints`
|
||||
- [ ] Executed `./util/dev update_lints`
|
||||
- [ ] Added lint documentation
|
||||
- [ ] Run `cargo fmt`
|
||||
- [ ] Run `./util/dev fmt`
|
||||
|
||||
[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
|
||||
|
||||
Note that you can skip the above if you are just opening a WIP PR in
|
||||
order to get feedback.
|
||||
|
|
28
.travis.yml
28
.travis.yml
|
@ -17,6 +17,7 @@ branches:
|
|||
env:
|
||||
global:
|
||||
- RUST_BACKTRACE=1
|
||||
- secure: "OKulfkA5OGd/d1IhvBKzRkHQwMcWjzrzbimo7+5NhkUkWxndAzl+719TB3wWvIh1i2wXXrEXsyZkXM5FtRrHm55v1VKQ5ibjEvFg1w3NIg81iDyoLq186fLqywvxGkOAFPrsePPsBj5USd5xvhwwbrjO6L7/RK6Z8shBwOSc41s="
|
||||
|
||||
install:
|
||||
- |
|
||||
|
@ -45,7 +46,7 @@ matrix:
|
|||
- os: linux
|
||||
env: BASE_TESTS=true
|
||||
- os: windows
|
||||
env: CARGO_INCREMENTAL=0 BASE_TESTS=true
|
||||
env: CARGO_INCREMENTAL=0 BASE_TESTS=true OS_WINDOWS=true
|
||||
|
||||
# Builds that are only executed when a PR is r+ed or a try build is started
|
||||
# We don't want to run these always because they go towards
|
||||
|
@ -81,9 +82,6 @@ matrix:
|
|||
if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try)
|
||||
- env: INTEGRATION=chronotope/chrono
|
||||
if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try)
|
||||
allow_failures:
|
||||
- os: windows
|
||||
env: CARGO_INCREMENTAL=0 BASE_TESTS=true
|
||||
# prevent these jobs with default env vars
|
||||
exclude:
|
||||
- os: linux
|
||||
|
@ -91,10 +89,28 @@ matrix:
|
|||
- os: windows
|
||||
|
||||
script:
|
||||
- |
|
||||
if [ "$TRAVIS_BRANCH" == "auto" ] || [ "$TRAVIS_BRANCH" == "try" ]; then
|
||||
pr=$(echo $TRAVIS_COMMIT_MESSAGE | grep -o "#[0-9]*" | head -1 | sed 's/^#//g')
|
||||
output=$(curl -H "Authorization: token $GITHUB_API_TOKEN" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$pr" | \
|
||||
python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
|
||||
grep "^changelog: " | \
|
||||
sed "s/changelog: //g")
|
||||
if [ -z "$output" ]; then
|
||||
echo "ERROR: PR body must contain 'changelog: ...'"
|
||||
exit 1
|
||||
elif [ "$output" = "none" ]; then
|
||||
echo "WARNING: changelog is 'none'"
|
||||
fi
|
||||
fi
|
||||
- |
|
||||
rm rust-toolchain
|
||||
./setup-toolchain.sh
|
||||
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
|
||||
if [ "$TRAVIS_OS_NAME" == "windows" ]; then
|
||||
export PATH=$PATH:$(rustc --print sysroot)/bin
|
||||
else
|
||||
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
|
||||
fi
|
||||
- |
|
||||
if [ -z ${INTEGRATION} ]; then
|
||||
travis_wait 30 ./ci/base-tests.sh && sleep 5
|
||||
|
@ -104,7 +120,7 @@ script:
|
|||
|
||||
after_success: |
|
||||
#!/bin/bash
|
||||
if [ $(uname) == Linux ]; then
|
||||
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
set -ex
|
||||
if [ -z ${INTEGRATION} ]; then
|
||||
./.github/deploy.sh
|
||||
|
|
|
@ -974,6 +974,8 @@ Released 2018-09-13
|
|||
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
|
||||
[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
|
||||
[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
|
||||
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
|
||||
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
|
||||
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
|
||||
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
|
||||
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
|
||||
|
@ -981,7 +983,6 @@ Released 2018-09-13
|
|||
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
|
||||
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
|
||||
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
|
||||
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
|
||||
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
|
||||
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
|
||||
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
|
||||
|
@ -999,6 +1000,7 @@ Released 2018-09-13
|
|||
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
|
||||
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
|
||||
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
|
||||
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
||||
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
|
||||
|
@ -1137,6 +1139,7 @@ Released 2018-09-13
|
|||
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
|
||||
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
|
||||
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
|
||||
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
|
||||
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
|
||||
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
|
||||
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
|
||||
|
|
|
@ -61,4 +61,5 @@ rustc-workspace-hack = "1.0.0"
|
|||
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
|
||||
|
||||
[features]
|
||||
deny-warnings = []
|
||||
debugging = []
|
||||
|
|
31
README.md
31
README.md
|
@ -6,7 +6,7 @@
|
|||
|
||||
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
||||
|
||||
[There are 306 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
[There are 309 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
|
||||
|
||||
|
@ -36,6 +36,7 @@ Table of contents:
|
|||
|
||||
* [Usage instructions](#usage)
|
||||
* [Configuration](#configuration)
|
||||
* [Contributing](#contributing)
|
||||
* [License](#license)
|
||||
|
||||
## Usage
|
||||
|
@ -52,7 +53,7 @@ subcommand.
|
|||
|
||||
#### Step 1: Install rustup
|
||||
|
||||
You can install [rustup](http://rustup.rs/) on supported platforms. This will help
|
||||
You can install [rustup](https://rustup.rs/) on supported platforms. This will help
|
||||
us install Clippy and its dependencies.
|
||||
|
||||
If you already have rustup installed, update to ensure you have the latest
|
||||
|
@ -79,6 +80,15 @@ Now you can run Clippy by invoking the following command:
|
|||
cargo clippy
|
||||
```
|
||||
|
||||
#### Automatically applying Clippy suggestions
|
||||
|
||||
Some Clippy lint suggestions can be automatically applied by `cargo fix`.
|
||||
Note that this is still experimental and only supported on the nightly channel:
|
||||
|
||||
```terminal
|
||||
cargo fix -Z unstable-options --clippy
|
||||
```
|
||||
|
||||
### Running Clippy from the command line without installing it
|
||||
|
||||
To have cargo compile your crate with Clippy without Clippy installation
|
||||
|
@ -88,8 +98,7 @@ in your code, you can use:
|
|||
cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
|
||||
```
|
||||
|
||||
*[Note](https://github.com/rust-lang/rust-clippy/wiki#a-word-of-warning):*
|
||||
Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
|
||||
*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
|
||||
|
||||
### Travis CI
|
||||
|
||||
|
@ -113,7 +122,7 @@ script:
|
|||
```
|
||||
|
||||
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.
|
||||
In this case you can try to conditionally install Clippy from the Git repo.
|
||||
|
||||
```yaml
|
||||
language: rust
|
||||
|
@ -121,12 +130,12 @@ rust:
|
|||
- nightly
|
||||
before_script:
|
||||
- rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
|
||||
# etc
|
||||
# etc.
|
||||
```
|
||||
|
||||
Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
|
||||
That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
|
||||
an error for clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
|
||||
an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
|
||||
line. (You can swap `clippy::all` with the specific lint category you are targeting.)
|
||||
|
||||
## Configuration
|
||||
|
@ -154,9 +163,9 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
|
|||
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
|
||||
lints prone to false positives.
|
||||
|
||||
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc)
|
||||
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
|
||||
|
||||
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc
|
||||
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
|
||||
|
||||
Note: `deny` produces errors instead of warnings.
|
||||
|
||||
|
@ -171,7 +180,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
|
|||
Copyright 2014-2019 The Rust Project Developers
|
||||
|
||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
|
||||
<LICENSE-MIT or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)>, at your
|
||||
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
|
||||
<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
|
||||
option. All files in the project carrying such notice may not be
|
||||
copied, modified, or distributed except according to those terms.
|
||||
|
|
|
@ -8,11 +8,10 @@ environment:
|
|||
- TARGET: x86_64-pc-windows-msvc
|
||||
|
||||
branches:
|
||||
# Only build AppVeyor on r+, try and the master branch
|
||||
# Only build AppVeyor on r+ and try branch
|
||||
only:
|
||||
- auto
|
||||
- try
|
||||
- master
|
||||
|
||||
install:
|
||||
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
|
||||
|
@ -23,6 +22,7 @@ install:
|
|||
- del rust-toolchain
|
||||
- cargo install rustup-toolchain-install-master --debug || echo "rustup-toolchain-install-master already installed"
|
||||
- rustup-toolchain-install-master %RUSTC_HASH% -f -n master
|
||||
- rustup component add rustfmt --toolchain nightly & exit 0 # Format test handles missing rustfmt
|
||||
- rustup default master
|
||||
- set PATH=%PATH%;C:\Users\appveyor\.rustup\toolchains\master\bin
|
||||
- rustc -V
|
||||
|
|
|
@ -7,8 +7,8 @@ if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
|||
remark -f *.md -f doc/*.md > /dev/null
|
||||
fi
|
||||
# build clippy in debug mode and run tests
|
||||
cargo build --features debugging
|
||||
cargo test --features debugging
|
||||
cargo build --features "debugging deny-warnings"
|
||||
cargo test --features "debugging deny-warnings"
|
||||
# for faster build, share target dir between subcrates
|
||||
export CARGO_TARGET_DIR=`pwd`/target/
|
||||
(cd clippy_lints && cargo test)
|
||||
|
@ -24,21 +24,23 @@ export CARGO_TARGET_DIR=`pwd`/target/
|
|||
# Perform various checks for lint registration
|
||||
./util/dev update_lints --check
|
||||
./util/dev --limit-stderr-length
|
||||
cargo +nightly fmt --all -- --check
|
||||
|
||||
# Check running clippy-driver without cargo
|
||||
(
|
||||
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
|
||||
|
||||
# Check sysroot handling
|
||||
sysroot=$(./target/debug/clippy-driver --print sysroot)
|
||||
test $sysroot = $(rustc --print sysroot)
|
||||
|
||||
sysroot=$(./target/debug/clippy-driver --sysroot /tmp --print sysroot)
|
||||
test $sysroot = /tmp
|
||||
if [ -z $OS_WINDOWS ]; then
|
||||
desired_sysroot=/tmp
|
||||
else
|
||||
desired_sysroot=C:/tmp
|
||||
fi
|
||||
sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
|
||||
test $sysroot = $desired_sysroot
|
||||
|
||||
sysroot=$(SYSROOT=/tmp ./target/debug/clippy-driver --print sysroot)
|
||||
test $sysroot = /tmp
|
||||
sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
|
||||
test $sysroot = $desired_sysroot
|
||||
|
||||
# Make sure this isn't set - clippy-driver should cope without it
|
||||
unset CARGO_MANIFEST_DIR
|
||||
|
@ -50,32 +52,3 @@ cargo +nightly fmt --all -- --check
|
|||
|
||||
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
|
||||
)
|
||||
|
||||
# make sure tests are formatted
|
||||
|
||||
# some lints are sensitive to formatting, exclude some files
|
||||
tests_need_reformatting="false"
|
||||
# switch to nightly
|
||||
rustup override set nightly
|
||||
# avoid loop spam and allow cmds with exit status != 0
|
||||
set +ex
|
||||
|
||||
# Excluding `ice-3891.rs` because the code triggers a rustc parse error which
|
||||
# makes rustfmt fail.
|
||||
for file in `find tests -not -path "tests/ui/crashes/ice-3891.rs" | grep "\.rs$"` ; do
|
||||
rustfmt ${file} --check
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "${file} needs reformatting!"
|
||||
tests_need_reformatting="true"
|
||||
fi
|
||||
done
|
||||
|
||||
set -ex # reset
|
||||
|
||||
if [ "${tests_need_reformatting}" == "true" ] ; then
|
||||
echo "Tests need reformatting!"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# switch back to master
|
||||
rustup override set master
|
||||
|
|
|
@ -9,4 +9,5 @@ clap = "2.33"
|
|||
itertools = "0.8"
|
||||
regex = "1"
|
||||
lazy_static = "1.0"
|
||||
shell-escape = "0.1"
|
||||
walkdir = "2"
|
||||
|
|
200
clippy_dev/src/fmt.rs
Normal file
200
clippy_dev/src/fmt.rs
Normal file
|
@ -0,0 +1,200 @@
|
|||
use shell_escape::escape;
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{self, Command};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CliError {
|
||||
CommandFailed(String),
|
||||
IoError(io::Error),
|
||||
ProjectRootNotFound,
|
||||
RustfmtNotInstalled,
|
||||
WalkDirError(walkdir::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for CliError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Self::IoError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<walkdir::Error> for CliError {
|
||||
fn from(error: walkdir::Error) -> Self {
|
||||
Self::WalkDirError(error)
|
||||
}
|
||||
}
|
||||
|
||||
struct FmtContext {
|
||||
check: bool,
|
||||
verbose: bool,
|
||||
}
|
||||
|
||||
pub fn run(check: bool, verbose: bool) {
|
||||
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
|
||||
let mut success = true;
|
||||
|
||||
let project_root = project_root()?;
|
||||
|
||||
rustfmt_test(context)?;
|
||||
|
||||
success &= cargo_fmt(context, project_root.as_path())?;
|
||||
success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
|
||||
success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
|
||||
|
||||
for entry in WalkDir::new(project_root.join("tests")) {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.extension() != Some("rs".as_ref())
|
||||
|| entry.file_name() == "ice-3891.rs"
|
||||
// Avoid rustfmt bug rust-lang/rustfmt#1873
|
||||
|| cfg!(windows) && entry.file_name() == "implicit_hasher.rs"
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
success &= rustfmt(context, &path)?;
|
||||
}
|
||||
|
||||
Ok(success)
|
||||
}
|
||||
|
||||
fn output_err(err: CliError) {
|
||||
match err {
|
||||
CliError::CommandFailed(command) => {
|
||||
eprintln!("error: A command failed! `{}`", command);
|
||||
},
|
||||
CliError::IoError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
},
|
||||
CliError::ProjectRootNotFound => {
|
||||
eprintln!("error: Can't determine root of project. Please run inside a Clippy working dir.");
|
||||
},
|
||||
CliError::RustfmtNotInstalled => {
|
||||
eprintln!("error: rustfmt nightly is not installed.");
|
||||
},
|
||||
CliError::WalkDirError(err) => {
|
||||
eprintln!("error: {}", err);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let context = FmtContext { check, verbose };
|
||||
let result = try_run(&context);
|
||||
let code = match result {
|
||||
Ok(true) => 0,
|
||||
Ok(false) => {
|
||||
eprintln!();
|
||||
eprintln!("Formatting check failed.");
|
||||
eprintln!("Run `./util/dev fmt` to update formatting.");
|
||||
1
|
||||
},
|
||||
Err(err) => {
|
||||
output_err(err);
|
||||
1
|
||||
},
|
||||
};
|
||||
process::exit(code);
|
||||
}
|
||||
|
||||
fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
|
||||
let arg_display: Vec<_> = args
|
||||
.iter()
|
||||
.map(|a| escape(a.as_ref().to_string_lossy()).to_owned())
|
||||
.collect();
|
||||
|
||||
format!(
|
||||
"cd {} && {} {}",
|
||||
escape(dir.as_ref().to_string_lossy()),
|
||||
escape(program.as_ref().to_string_lossy()),
|
||||
arg_display.join(" ")
|
||||
)
|
||||
}
|
||||
|
||||
fn exec(
|
||||
context: &FmtContext,
|
||||
program: impl AsRef<OsStr>,
|
||||
dir: impl AsRef<Path>,
|
||||
args: &[impl AsRef<OsStr>],
|
||||
) -> Result<bool, CliError> {
|
||||
if context.verbose {
|
||||
println!("{}", format_command(&program, &dir, args));
|
||||
}
|
||||
|
||||
let mut child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
|
||||
let code = child.wait()?;
|
||||
let success = code.success();
|
||||
|
||||
if !context.check && !success {
|
||||
return Err(CliError::CommandFailed(format_command(&program, &dir, args)));
|
||||
}
|
||||
|
||||
Ok(success)
|
||||
}
|
||||
|
||||
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
|
||||
let mut args = vec!["+nightly", "fmt", "--all"];
|
||||
if context.check {
|
||||
args.push("--");
|
||||
args.push("--check");
|
||||
}
|
||||
let success = exec(context, "cargo", path, &args)?;
|
||||
|
||||
Ok(success)
|
||||
}
|
||||
|
||||
fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
|
||||
let program = "rustfmt";
|
||||
let dir = std::env::current_dir()?;
|
||||
let args = &["+nightly", "--version"];
|
||||
|
||||
if context.verbose {
|
||||
println!("{}", format_command(&program, &dir, args));
|
||||
}
|
||||
|
||||
let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
} else if std::str::from_utf8(&output.stderr)
|
||||
.unwrap_or("")
|
||||
.starts_with("error: 'rustfmt' is not installed")
|
||||
{
|
||||
Err(CliError::RustfmtNotInstalled)
|
||||
} else {
|
||||
Err(CliError::CommandFailed(format_command(&program, &dir, args)))
|
||||
}
|
||||
}
|
||||
|
||||
fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
|
||||
let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
|
||||
if context.check {
|
||||
args.push("--check".as_ref());
|
||||
}
|
||||
let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
|
||||
if !success {
|
||||
eprintln!("rustfmt failed on {}", path.display());
|
||||
}
|
||||
Ok(success)
|
||||
}
|
||||
|
||||
fn project_root() -> Result<PathBuf, CliError> {
|
||||
let current_dir = std::env::current_dir()?;
|
||||
for path in current_dir.ancestors() {
|
||||
let result = std::fs::read_to_string(path.join("Cargo.toml"));
|
||||
if let Err(err) = &result {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let content = result?;
|
||||
if content.contains("[package]\nname = \"clippy\"") {
|
||||
return Ok(path.to_path_buf());
|
||||
}
|
||||
}
|
||||
|
||||
Err(CliError::ProjectRootNotFound)
|
||||
}
|
|
@ -4,6 +4,8 @@ extern crate regex;
|
|||
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use clippy_dev::*;
|
||||
|
||||
mod fmt;
|
||||
mod stderr_length_check;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
@ -14,6 +16,21 @@ enum UpdateMode {
|
|||
|
||||
fn main() {
|
||||
let matches = App::new("Clippy developer tooling")
|
||||
.subcommand(
|
||||
SubCommand::with_name("fmt")
|
||||
.about("Run rustfmt on all projects and tests")
|
||||
.arg(
|
||||
Arg::with_name("check")
|
||||
.long("check")
|
||||
.help("Use the rustfmt --check option"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbose")
|
||||
.short("v")
|
||||
.long("verbose")
|
||||
.help("Echo commands run"),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("update_lints")
|
||||
.about("Updates lint registration and information from the source code")
|
||||
|
@ -46,14 +63,21 @@ fn main() {
|
|||
if matches.is_present("limit-stderr-length") {
|
||||
stderr_length_check::check();
|
||||
}
|
||||
if let Some(matches) = matches.subcommand_matches("update_lints") {
|
||||
if matches.is_present("print-only") {
|
||||
print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints(&UpdateMode::Check);
|
||||
} else {
|
||||
update_lints(&UpdateMode::Change);
|
||||
}
|
||||
|
||||
match matches.subcommand() {
|
||||
("fmt", Some(matches)) => {
|
||||
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
|
||||
},
|
||||
("update_lints", Some(matches)) => {
|
||||
if matches.is_present("print-only") {
|
||||
print_lints();
|
||||
} else if matches.is_present("check") {
|
||||
update_lints(&UpdateMode::Check);
|
||||
} else {
|
||||
update_lints(&UpdateMode::Change);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::io::prelude::*;
|
|||
// The maximum length allowed for stderr files.
|
||||
//
|
||||
// We limit this because small files are easier to deal with than bigger files.
|
||||
const LIMIT: usize = 320;
|
||||
const LIMIT: usize = 275;
|
||||
|
||||
pub fn check() {
|
||||
let stderr_files = stderr_files();
|
||||
|
@ -23,16 +23,15 @@ pub fn check() {
|
|||
}
|
||||
|
||||
fn exceeding_stderr_files(files: impl Iterator<Item = walkdir::DirEntry>) -> impl Iterator<Item = String> {
|
||||
files
|
||||
.filter_map(|file| {
|
||||
let path = file.path().to_str().expect("Could not convert path to str").to_string();
|
||||
let linecount = count_linenumbers(&path);
|
||||
if linecount > LIMIT {
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
files.filter_map(|file| {
|
||||
let path = file.path().to_str().expect("Could not convert path to str").to_string();
|
||||
let linecount = count_linenumbers(&path);
|
||||
if linecount > LIMIT {
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn stderr_files() -> impl Iterator<Item = walkdir::DirEntry> {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_dummy" # rename to clippy before publishing
|
||||
version = "0.0.302"
|
||||
version = "0.0.303"
|
||||
authors = ["Manish Goregaokar <manishsmail@gmail.com>"]
|
||||
edition = "2018"
|
||||
readme = "crates-readme.md"
|
||||
|
|
|
@ -3,7 +3,7 @@ extern crate term;
|
|||
fn main() {
|
||||
if let Err(_) = foo() {
|
||||
eprintln!("error: Clippy is no longer available via crates.io\n");
|
||||
eprintln!("help: please run `rustup component add clippy-preview` instead");
|
||||
eprintln!("help: please run `rustup component add clippy` instead");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ fn foo() -> Result<(), ()> {
|
|||
write!(t, "please run `").map_err(|_| ())?;
|
||||
|
||||
t.attr(term::Attr::Bold).map_err(|_| ())?;
|
||||
write!(t, "rustup component add clippy-preview").map_err(|_| ())?;
|
||||
write!(t, "rustup component add clippy").map_err(|_| ())?;
|
||||
|
||||
t.reset().map_err(|_| ())?;
|
||||
t.fg(term::color::WHITE).map_err(|_| ())?;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
Installing clippy via crates.io is deprecated. Please use the following:
|
||||
|
||||
```terminal
|
||||
rustup component add clippy-preview
|
||||
rustup component add clippy
|
||||
```
|
||||
|
||||
on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.
|
||||
|
|
|
@ -27,7 +27,7 @@ semver = "0.9.0"
|
|||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
unicode-normalization = "0.1"
|
||||
pulldown-cmark = "0.5.2"
|
||||
pulldown-cmark = "0.5.3"
|
||||
url = "1.7.0"
|
||||
if_chain = "1.0.0"
|
||||
smallvec = { version = "0.6.5", features = ["union"] }
|
||||
|
|
|
@ -16,7 +16,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1
|
||||
/// # let a = 0;
|
||||
/// a + 1;
|
||||
/// ```
|
||||
pub INTEGER_ARITHMETIC,
|
||||
restriction,
|
||||
|
@ -33,7 +34,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + 1.0
|
||||
/// # let a = 0.0;
|
||||
/// a + 1.0;
|
||||
/// ```
|
||||
pub FLOAT_ARITHMETIC,
|
||||
restriction,
|
||||
|
|
|
@ -45,7 +45,8 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 5;
|
||||
/// ...
|
||||
/// let b = 2;
|
||||
/// // ...
|
||||
/// a += a + b;
|
||||
/// ```
|
||||
pub MISREFACTORED_ASSIGN_OP,
|
||||
|
|
|
@ -113,19 +113,19 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// #[inline(always)]
|
||||
///
|
||||
/// fn not_quite_good_code(..) { ... }
|
||||
///
|
||||
/// // Good (as inner attribute)
|
||||
/// #![inline(always)]
|
||||
///
|
||||
/// fn this_is_fine(..) { ... }
|
||||
/// fn this_is_fine() { }
|
||||
///
|
||||
/// // Bad
|
||||
/// #[inline(always)]
|
||||
///
|
||||
/// fn not_quite_good_code() { }
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[inline(always)]
|
||||
/// fn this_is_fine_too(..) { ... }
|
||||
/// fn this_is_fine_too() { }
|
||||
/// ```
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
nursery,
|
||||
|
|
|
@ -24,7 +24,8 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
|
||||
/// # let vec = vec![1_u8];
|
||||
/// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
|
||||
/// ```
|
||||
pub NAIVE_BYTECOUNT,
|
||||
perf,
|
||||
|
|
|
@ -27,6 +27,8 @@ declare_clippy_lint! {
|
|||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::convert::TryFrom;
|
||||
/// # let foo = 1;
|
||||
/// # let _ =
|
||||
/// i32::try_from(foo).is_ok()
|
||||
/// # ;
|
||||
|
@ -160,12 +162,12 @@ impl ConversionType {
|
|||
/// Creates a conversion type if the type is allowed & conversion is valid
|
||||
fn try_new(from: &str, to: &str) -> Option<Self> {
|
||||
if UINTS.contains(&from) {
|
||||
Some(ConversionType::FromUnsigned)
|
||||
Some(Self::FromUnsigned)
|
||||
} else if SINTS.contains(&from) {
|
||||
if UINTS.contains(&to) {
|
||||
Some(ConversionType::SignedToUnsigned)
|
||||
Some(Self::SignedToUnsigned)
|
||||
} else if SINTS.contains(&to) {
|
||||
Some(ConversionType::SignedToSigned)
|
||||
Some(Self::SignedToSigned)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -48,27 +48,25 @@ pub enum Constant {
|
|||
impl PartialEq for Constant {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => ls == rs,
|
||||
(&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r,
|
||||
(&Constant::Char(l), &Constant::Char(r)) => l == r,
|
||||
(&Constant::Int(l), &Constant::Int(r)) => l == r,
|
||||
(&Constant::F64(l), &Constant::F64(r)) => {
|
||||
(&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
|
||||
(&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
|
||||
(&Self::Char(l), &Self::Char(r)) => l == r,
|
||||
(&Self::Int(l), &Self::Int(r)) => l == r,
|
||||
(&Self::F64(l), &Self::F64(r)) => {
|
||||
// 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)) => {
|
||||
(&Self::F32(l), &Self::F32(r)) => {
|
||||
// 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,
|
||||
(&Constant::Vec(ref l), &Constant::Vec(ref r)) | (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) => {
|
||||
l == r
|
||||
},
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
|
||||
(&Self::Bool(l), &Self::Bool(r)) => l == r,
|
||||
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
|
||||
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
|
||||
// TODO: are there inter-type equalities?
|
||||
_ => false,
|
||||
}
|
||||
|
@ -82,38 +80,38 @@ impl Hash for Constant {
|
|||
{
|
||||
std::mem::discriminant(self).hash(state);
|
||||
match *self {
|
||||
Constant::Str(ref s) => {
|
||||
Self::Str(ref s) => {
|
||||
s.hash(state);
|
||||
},
|
||||
Constant::Binary(ref b) => {
|
||||
Self::Binary(ref b) => {
|
||||
b.hash(state);
|
||||
},
|
||||
Constant::Char(c) => {
|
||||
Self::Char(c) => {
|
||||
c.hash(state);
|
||||
},
|
||||
Constant::Int(i) => {
|
||||
Self::Int(i) => {
|
||||
i.hash(state);
|
||||
},
|
||||
Constant::F32(f) => {
|
||||
Self::F32(f) => {
|
||||
f64::from(f).to_bits().hash(state);
|
||||
},
|
||||
Constant::F64(f) => {
|
||||
Self::F64(f) => {
|
||||
f.to_bits().hash(state);
|
||||
},
|
||||
Constant::Bool(b) => {
|
||||
Self::Bool(b) => {
|
||||
b.hash(state);
|
||||
},
|
||||
Constant::Vec(ref v) | Constant::Tuple(ref v) => {
|
||||
Self::Vec(ref v) | Self::Tuple(ref v) => {
|
||||
v.hash(state);
|
||||
},
|
||||
Constant::Repeat(ref c, l) => {
|
||||
Self::Repeat(ref c, l) => {
|
||||
c.hash(state);
|
||||
l.hash(state);
|
||||
},
|
||||
Constant::RawPtr(u) => {
|
||||
Self::RawPtr(u) => {
|
||||
u.hash(state);
|
||||
},
|
||||
Constant::Err(ref s) => {
|
||||
Self::Err(ref s) => {
|
||||
s.hash(state);
|
||||
},
|
||||
}
|
||||
|
@ -123,25 +121,25 @@ impl Hash for Constant {
|
|||
impl Constant {
|
||||
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
|
||||
match (left, right) {
|
||||
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)),
|
||||
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Int(l), &Constant::Int(r)) => {
|
||||
(&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
|
||||
(&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
|
||||
(&Self::Int(l), &Self::Int(r)) => {
|
||||
if let ty::Int(int_ty) = cmp_type.sty {
|
||||
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
|
||||
} else {
|
||||
Some(l.cmp(&r))
|
||||
}
|
||||
},
|
||||
(&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r),
|
||||
(&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r),
|
||||
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)),
|
||||
(&Constant::Tuple(ref l), &Constant::Tuple(ref r)) | (&Constant::Vec(ref l), &Constant::Vec(ref r)) => l
|
||||
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
|
||||
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
|
||||
(&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
|
||||
(&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
|
||||
.iter()
|
||||
.zip(r.iter())
|
||||
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
|
||||
.find(|r| r.map_or(true, |o| o != Ordering::Equal))
|
||||
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
|
||||
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => {
|
||||
(&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
|
||||
match Self::partial_cmp(tcx, cmp_type, lv, rv) {
|
||||
Some(Equal) => Some(ls.cmp(rs)),
|
||||
x => x,
|
||||
|
@ -232,7 +230,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
|
|||
ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
|
||||
ExprKind::Repeat(ref value, _) => {
|
||||
let n = match self.tables.expr_ty(e).sty {
|
||||
ty::Array(_, n) => n.assert_usize(self.lcx.tcx).expect("array length"),
|
||||
ty::Array(_, n) => n.eval_usize(self.lcx.tcx, self.lcx.param_env),
|
||||
_ => span_bug!(e.span, "typeck error"),
|
||||
};
|
||||
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
|
||||
|
|
|
@ -13,7 +13,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Copy, Clone)]
|
||||
/// struct Countdown(u8);
|
||||
///
|
||||
|
|
|
@ -113,3 +113,12 @@ declare_deprecated_lint! {
|
|||
pub UNSAFE_VECTOR_INITIALIZATION,
|
||||
"the replacement suggested by this lint had substantially different behavior"
|
||||
}
|
||||
|
||||
/// **What it does:** Nothing. This lint has been deprecated.
|
||||
///
|
||||
/// **Deprecation reason:** This lint has been superseded by the warn-by-default
|
||||
/// `invalid_value` rustc lint.
|
||||
declare_clippy_lint! {
|
||||
pub INVALID_REF,
|
||||
"superseded by rustc lint `invalid_value`"
|
||||
}
|
||||
|
|
|
@ -49,12 +49,12 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// #[derive(Copy)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl Clone for Foo {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXPL_IMPL_CLONE_ON_COPY,
|
||||
|
|
|
@ -27,7 +27,7 @@ declare_clippy_lint! {
|
|||
/// /// 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) { .. }
|
||||
/// fn doit(foo_bar: usize) {}
|
||||
/// ```
|
||||
pub DOC_MARKDOWN,
|
||||
pedantic,
|
||||
|
|
|
@ -18,13 +18,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x == y || x < y
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x == y || x < y {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// x <= y
|
||||
/// # let x = 1;
|
||||
/// # let y = 2;
|
||||
/// if x <= y {}
|
||||
/// ```
|
||||
pub DOUBLE_COMPARISONS,
|
||||
complexity,
|
||||
|
@ -63,6 +67,7 @@ impl<'a, 'tcx> DoubleComparisons {
|
|||
);
|
||||
}};
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
match (op, lkind, rkind) {
|
||||
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
|
||||
lint_double_comparison!(<=)
|
||||
|
|
|
@ -13,9 +13,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ((0))
|
||||
/// foo((0))
|
||||
/// ((1, 2))
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// ((0));
|
||||
/// foo((0));
|
||||
/// ((1, 2));
|
||||
/// ```
|
||||
pub DOUBLE_PARENS,
|
||||
complexity,
|
||||
|
|
|
@ -20,6 +20,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::time::Duration;
|
||||
/// let dur = Duration::new(5, 0);
|
||||
/// let _micros = dur.subsec_nanos() / 1_000;
|
||||
/// let _millis = dur.subsec_nanos() / 1_000_000;
|
||||
|
|
|
@ -16,6 +16,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # fn a() {}
|
||||
/// # fn b() {}
|
||||
/// # let x: i32 = 1;
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
|
@ -26,6 +29,9 @@ declare_clippy_lint! {
|
|||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn a() {}
|
||||
/// # fn b() {}
|
||||
/// # let x: i32 = 1;
|
||||
/// if x.is_positive() {
|
||||
/// a();
|
||||
/// } else if x.is_negative() {
|
||||
|
|
|
@ -16,21 +16,32 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Known problems:** Some false negatives, eg.:
|
||||
/// ```rust
|
||||
/// let k = &key;
|
||||
/// if !m.contains_key(k) {
|
||||
/// m.insert(k.clone(), v);
|
||||
/// # use std::collections::HashMap;
|
||||
/// # let mut map = HashMap::new();
|
||||
/// # let v = 1;
|
||||
/// # let k = 1;
|
||||
/// if !map.contains_key(&k) {
|
||||
/// map.insert(k.clone(), v);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// if !m.contains_key(&k) {
|
||||
/// m.insert(k, v)
|
||||
/// # use std::collections::HashMap;
|
||||
/// # let mut map = HashMap::new();
|
||||
/// # let k = 1;
|
||||
/// # let v = 1;
|
||||
/// if !map.contains_key(&k) {
|
||||
/// map.insert(k, v);
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten as:
|
||||
/// can both be rewritten as:
|
||||
/// ```rust
|
||||
/// m.entry(k).or_insert(v);
|
||||
/// # use std::collections::HashMap;
|
||||
/// # let mut map = HashMap::new();
|
||||
/// # let k = 1;
|
||||
/// # let v = 1;
|
||||
/// map.entry(k).or_insert(v);
|
||||
/// ```
|
||||
pub MAP_ENTRY,
|
||||
perf,
|
||||
|
|
|
@ -160,6 +160,7 @@ fn check_variant(
|
|||
let name = var2str(var);
|
||||
if partial_match(item_name, &name) == item_name_chars
|
||||
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
|
||||
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
|
||||
{
|
||||
span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
|
||||
}
|
||||
|
@ -178,6 +179,9 @@ fn check_variant(
|
|||
let pre_camel = camel_case::until(pre);
|
||||
pre = &pre[..pre_camel];
|
||||
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
|
||||
if next.is_numeric() {
|
||||
return;
|
||||
}
|
||||
if next.is_lowercase() {
|
||||
let last = pre.len() - last.len_utf8();
|
||||
let last_camel = camel_case::until(&pre[..last]);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use rustc::hir::intravisit as visit;
|
||||
use rustc::hir::*;
|
||||
use rustc::hir::{self, *};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::middle::expr_use_visitor::*;
|
||||
use rustc::middle::mem_categorization::{cmt_, Categorization};
|
||||
|
@ -28,11 +28,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// }
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// ```
|
||||
pub BOXED_LOCAL,
|
||||
perf,
|
||||
|
@ -101,6 +100,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Replace with Map::is_argument(..) when it's fixed
|
||||
fn is_argument(map: &hir::map::Map<'_>, id: HirId) -> bool {
|
||||
match map.find(id) {
|
||||
Some(Node::Binding(_)) => (),
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
match map.find(map.get_parent_node(id)) {
|
||||
Some(Node::Arg(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
|
||||
if let Categorization::Local(lid) = cmt.cat {
|
||||
|
@ -113,11 +125,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.hir_id) {
|
||||
if is_argument(map, consume_pat.hir_id) {
|
||||
// Skip closure arguments
|
||||
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(consume_pat.hir_id)) {
|
||||
let parent_id = map.get_parent_node(consume_pat.hir_id);
|
||||
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
|
||||
self.set.insert(consume_pat.hir_id);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,9 @@ declare_clippy_lint! {
|
|||
/// shorthand.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,no_run
|
||||
/// # fn b() -> bool { true }
|
||||
/// # fn c() -> bool { true }
|
||||
/// let a = b() || panic!() || c();
|
||||
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
|
||||
/// let x = (a, b, c, panic!());
|
||||
|
|
|
@ -143,20 +143,20 @@ impl FloatFormat {
|
|||
fn new(s: &str) -> Self {
|
||||
s.chars()
|
||||
.find_map(|x| match x {
|
||||
'e' => Some(FloatFormat::LowerExp),
|
||||
'E' => Some(FloatFormat::UpperExp),
|
||||
'e' => Some(Self::LowerExp),
|
||||
'E' => Some(Self::UpperExp),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(FloatFormat::Normal)
|
||||
.unwrap_or(Self::Normal)
|
||||
}
|
||||
fn format<T>(&self, f: T) -> String
|
||||
where
|
||||
T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
|
||||
{
|
||||
match self {
|
||||
FloatFormat::LowerExp => format!("{:e}", f),
|
||||
FloatFormat::UpperExp => format!("{:E}", f),
|
||||
FloatFormat::Normal => format!("{}", f),
|
||||
Self::LowerExp => format!("{:e}", f),
|
||||
Self::UpperExp => format!("{:E}", f),
|
||||
Self::Normal => format!("{}", f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::io::Write;
|
||||
/// # let bar = "furchtbar";
|
||||
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
|
||||
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
|
||||
/// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
|
||||
/// ```
|
||||
pub EXPLICIT_WRITE,
|
||||
complexity,
|
||||
|
|
|
@ -26,8 +26,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Examples:**
|
||||
/// ```rust
|
||||
/// format!("foo")
|
||||
/// format!("{}", foo)
|
||||
/// # let foo = "foo";
|
||||
/// format!("foo");
|
||||
/// format!("{}", foo);
|
||||
/// ```
|
||||
pub USELESS_FORMAT,
|
||||
complexity,
|
||||
|
|
|
@ -23,8 +23,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # struct Color;
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
|
|
|
@ -17,7 +17,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x / 1 + 0 * 1 - 0 | 0
|
||||
/// # let x = 1;
|
||||
/// x / 1 + 0 * 1 - 0 | 0;
|
||||
/// ```
|
||||
pub IDENTITY_OP,
|
||||
complexity,
|
||||
|
|
|
@ -17,6 +17,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let v: Vec<usize> = vec![];
|
||||
/// # fn a() {}
|
||||
/// # fn b() {}
|
||||
/// if !v.is_empty() {
|
||||
/// a()
|
||||
/// } else {
|
||||
|
@ -27,6 +30,9 @@ declare_clippy_lint! {
|
|||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// # let v: Vec<usize> = vec![];
|
||||
/// # fn a() {}
|
||||
/// # fn b() {}
|
||||
/// if v.is_empty() {
|
||||
/// b()
|
||||
/// } else {
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
use crate::utils::{in_macro_or_desugar, is_expn_of, snippet_opt, span_lint_and_then};
|
||||
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, MatchSource};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_lint_pass, declare_tool_lint};
|
||||
use crate::utils::{
|
||||
in_macro_or_desugar, match_def_path,
|
||||
paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
|
||||
resolve_node, snippet_opt, span_lint_and_then,
|
||||
};
|
||||
use if_chain::if_chain;
|
||||
use rustc::{
|
||||
declare_lint_pass, declare_tool_lint,
|
||||
hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind},
|
||||
lint::{LateContext, LateLintPass, LintArray, LintPass},
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::source_map::Span;
|
||||
|
||||
|
@ -35,71 +42,84 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
|
||||
|
||||
impl ImplicitReturn {
|
||||
fn lint(cx: &LateContext<'_, '_>, outer_span: syntax_pos::Span, inner_span: syntax_pos::Span, msg: &str) {
|
||||
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
|
||||
if let Some(snippet) = snippet_opt(cx, inner_span) {
|
||||
db.span_suggestion(
|
||||
outer_span,
|
||||
msg,
|
||||
format!("return {}", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
static LINT_BREAK: &str = "change `break` to `return` as shown";
|
||||
static LINT_RETURN: &str = "add `return` as shown";
|
||||
|
||||
fn expr_match(cx: &LateContext<'_, '_>, expr: &rustc::hir::Expr) {
|
||||
match &expr.node {
|
||||
// loops could be using `break` instead of `return`
|
||||
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
|
||||
if let Some(expr) = &block.expr {
|
||||
Self::expr_match(cx, expr);
|
||||
}
|
||||
// only needed in the case of `break` with `;` at the end
|
||||
else if let Some(stmt) = block.stmts.last() {
|
||||
if let rustc::hir::StmtKind::Semi(expr, ..) = &stmt.node {
|
||||
// make sure it's a break, otherwise we want to skip
|
||||
if let ExprKind::Break(.., break_expr) = &expr.node {
|
||||
if let Some(break_expr) = break_expr {
|
||||
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// use `return` instead of `break`
|
||||
ExprKind::Break(.., break_expr) => {
|
||||
if let Some(break_expr) = break_expr {
|
||||
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
|
||||
}
|
||||
},
|
||||
ExprKind::Match(.., arms, source) => {
|
||||
let check_all_arms = match source {
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: has_else,
|
||||
} => *has_else,
|
||||
_ => true,
|
||||
};
|
||||
fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
|
||||
let outer_span = outer_span.source_callsite();
|
||||
let inner_span = inner_span.source_callsite();
|
||||
|
||||
if check_all_arms {
|
||||
for arm in arms {
|
||||
Self::expr_match(cx, &arm.body);
|
||||
}
|
||||
} else {
|
||||
Self::expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
|
||||
}
|
||||
},
|
||||
// skip if it already has a return statement
|
||||
ExprKind::Ret(..) => (),
|
||||
// everything else is missing `return`
|
||||
_ => {
|
||||
// make sure it's not just an unreachable expression
|
||||
if is_expn_of(expr.span, "unreachable").is_none() {
|
||||
Self::lint(cx, expr.span, expr.span, "add `return` as shown")
|
||||
}
|
||||
},
|
||||
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
|
||||
if let Some(snippet) = snippet_opt(cx, inner_span) {
|
||||
db.span_suggestion(
|
||||
outer_span,
|
||||
msg,
|
||||
format!("return {}", snippet),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr) {
|
||||
match &expr.node {
|
||||
// loops could be using `break` instead of `return`
|
||||
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
|
||||
if let Some(expr) = &block.expr {
|
||||
expr_match(cx, expr);
|
||||
}
|
||||
// only needed in the case of `break` with `;` at the end
|
||||
else if let Some(stmt) = block.stmts.last() {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(expr, ..) = &stmt.node;
|
||||
// make sure it's a break, otherwise we want to skip
|
||||
if let ExprKind::Break(.., break_expr) = &expr.node;
|
||||
if let Some(break_expr) = break_expr;
|
||||
then {
|
||||
lint(cx, expr.span, break_expr.span, LINT_BREAK);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// use `return` instead of `break`
|
||||
ExprKind::Break(.., break_expr) => {
|
||||
if let Some(break_expr) = break_expr {
|
||||
lint(cx, expr.span, break_expr.span, LINT_BREAK);
|
||||
}
|
||||
},
|
||||
ExprKind::Match(.., arms, source) => {
|
||||
let check_all_arms = match source {
|
||||
MatchSource::IfLetDesugar {
|
||||
contains_else_clause: has_else,
|
||||
} => *has_else,
|
||||
_ => true,
|
||||
};
|
||||
|
||||
if check_all_arms {
|
||||
for arm in arms {
|
||||
expr_match(cx, &arm.body);
|
||||
}
|
||||
} else {
|
||||
expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
|
||||
}
|
||||
},
|
||||
// skip if it already has a return statement
|
||||
ExprKind::Ret(..) => (),
|
||||
// make sure it's not a call that panics
|
||||
ExprKind::Call(expr, ..) => {
|
||||
if_chain! {
|
||||
if let ExprKind::Path(qpath) = &expr.node;
|
||||
if let Some(path_def_id) = resolve_node(cx, qpath, expr.hir_id).opt_def_id();
|
||||
if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
|
||||
match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
|
||||
then { }
|
||||
else {
|
||||
lint(cx, expr.span, expr.span, LINT_RETURN)
|
||||
}
|
||||
}
|
||||
},
|
||||
// everything else is missing `return`
|
||||
_ => lint(cx, expr.span, expr.span, LINT_RETURN),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
|
|||
// checking return type through MIR, HIR is not able to determine inferred closure return types
|
||||
// make sure it's not a macro
|
||||
if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
|
||||
Self::expr_match(cx, &body.value);
|
||||
expr_match(cx, &body.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** Hopefully none.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,no_run
|
||||
/// // Vector
|
||||
/// let x = vec![0; 5];
|
||||
///
|
||||
|
@ -94,7 +94,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
|
|||
if let Some(range) = higher::range(cx, index) {
|
||||
// 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();
|
||||
let size: u128 = s.eval_usize(cx.tcx, cx.param_env).into();
|
||||
|
||||
let const_range = to_const_range(cx, range, size);
|
||||
|
||||
|
|
|
@ -34,7 +34,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
|
||||
/// let infinite_iter = 0..;
|
||||
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
|
||||
/// ```
|
||||
pub MAYBE_INFINITE_ITER,
|
||||
pedantic,
|
||||
|
|
154
clippy_lints/src/inherent_to_string.rs
Normal file
154
clippy_lints/src/inherent_to_string.rs
Normal file
|
@ -0,0 +1,154 @@
|
|||
use if_chain::if_chain;
|
||||
use rustc::hir::{ImplItem, ImplItemKind};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
use crate::utils::{
|
||||
get_trait_def_id, implements_trait, in_macro_or_desugar, match_type, paths, return_ty, span_help_and_lint,
|
||||
trait_ref_of_method, walk_ptrs_ty,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What id does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
|
||||
///
|
||||
/// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// ** Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// pub struct A;
|
||||
///
|
||||
/// impl A {
|
||||
/// pub fn to_string(&self) -> String {
|
||||
/// "I am A".to_string()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
///
|
||||
/// impl fmt::Display for A {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "I am A")
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub INHERENT_TO_STRING,
|
||||
style,
|
||||
"type implements inherent method `to_string()`, but should instead implement the `Display` trait"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What id does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
|
||||
///
|
||||
/// **Why is this bad?** This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
|
||||
///
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// ** Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// // Bad
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
///
|
||||
/// impl A {
|
||||
/// pub fn to_string(&self) -> String {
|
||||
/// "I am A".to_string()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// impl fmt::Display for A {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "I am A, too")
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // Good
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// pub struct A;
|
||||
///
|
||||
/// impl fmt::Display for A {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "I am A")
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
correctness,
|
||||
"type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait "
|
||||
}
|
||||
|
||||
declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InherentToString {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx ImplItem) {
|
||||
if in_macro_or_desugar(impl_item.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
// Check if item is a method, called to_string and has a parameter 'self'
|
||||
if let ImplItemKind::Method(ref signature, _) = impl_item.node;
|
||||
if impl_item.ident.name.as_str() == "to_string";
|
||||
let decl = &signature.decl;
|
||||
if decl.implicit_self.has_implicit_self();
|
||||
|
||||
// Check if return type is String
|
||||
if match_type(cx, return_ty(cx, impl_item.hir_id), &paths::STRING);
|
||||
|
||||
// Filters instances of to_string which are required by a trait
|
||||
if trait_ref_of_method(cx, impl_item.hir_id).is_none();
|
||||
|
||||
then {
|
||||
show_lint(cx, impl_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn show_lint(cx: &LateContext<'_, '_>, item: &ImplItem) {
|
||||
let display_trait_id =
|
||||
get_trait_def_id(cx, &["core", "fmt", "Display"]).expect("Failed to get trait ID of `Display`!");
|
||||
|
||||
// Get the real type of 'self'
|
||||
let fn_def_id = cx.tcx.hir().local_def_id(item.hir_id);
|
||||
let self_type = cx.tcx.fn_sig(fn_def_id).input(0);
|
||||
let self_type = walk_ptrs_ty(self_type.skip_binder());
|
||||
|
||||
// Emit either a warning or an error
|
||||
if implements_trait(cx, self_type, display_trait_id, &[]) {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
item.span,
|
||||
&format!(
|
||||
"type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`",
|
||||
self_type.to_string()
|
||||
),
|
||||
&format!("remove the inherent method from type `{}`", self_type.to_string())
|
||||
);
|
||||
} else {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
INHERENT_TO_STRING,
|
||||
item.span,
|
||||
&format!(
|
||||
"implementation of inherent method `to_string(&self) -> String` for type `{}`",
|
||||
self_type.to_string()
|
||||
),
|
||||
&format!("implement trait `Display` for type `{}` instead", self_type.to_string()),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -17,13 +17,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x >= y + 1
|
||||
/// # let x = 1;
|
||||
/// # let y = 1;
|
||||
/// if x >= y + 1 {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// x > y
|
||||
/// # let x = 1;
|
||||
/// # let y = 1;
|
||||
/// if x > y {}
|
||||
/// ```
|
||||
pub INT_PLUS_ONE,
|
||||
complexity,
|
||||
|
|
|
@ -1,55 +0,0 @@
|
|||
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_lint_pass, declare_tool_lint};
|
||||
|
||||
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"
|
||||
}
|
||||
|
||||
const ZERO_REF_SUMMARY: &str = "reference to zeroed memory";
|
||||
const UNINIT_REF_SUMMARY: &str = "reference to uninitialized memory";
|
||||
const HELP: &str = "Creation of a null reference is undefined behavior; \
|
||||
see https://doc.rust-lang.org/reference/behavior-considered-undefined.html";
|
||||
|
||||
declare_lint_pass!(InvalidRef => [INVALID_REF]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(ref path, ref args) = expr.node;
|
||||
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) = cx.tables.qpath_res(qpath, path.hir_id).opt_def_id();
|
||||
then {
|
||||
let msg = if match_def_path(cx, def_id, &paths::MEM_ZEROED) |
|
||||
match_def_path(cx, def_id, &paths::INIT)
|
||||
{
|
||||
ZERO_REF_SUMMARY
|
||||
} else if match_def_path(cx, def_id, &paths::MEM_UNINIT) |
|
||||
match_def_path(cx, def_id, &paths::UNINIT)
|
||||
{
|
||||
UNINIT_REF_SUMMARY
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
span_help_and_lint(cx, INVALID_REF, expr.span, msg, HELP);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -244,7 +244,7 @@ fn check_len(
|
|||
LEN_ZERO,
|
||||
span,
|
||||
&format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
|
||||
"using `is_empty` is clearer and more explicit",
|
||||
&format!("using `{}is_empty` is clearer and more explicit", op),
|
||||
format!(
|
||||
"{}{}.is_empty()",
|
||||
op,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
// error-pattern:cargo-clippy
|
||||
|
||||
#![feature(box_syntax)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(slice_patterns)]
|
||||
|
@ -9,6 +10,7 @@
|
|||
#![recursion_limit = "512"]
|
||||
#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
|
||||
#![deny(rustc::internal)]
|
||||
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(concat_idents)]
|
||||
|
||||
|
@ -98,42 +100,42 @@ macro_rules! declare_clippy_lint {
|
|||
};
|
||||
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
|
||||
declare_tool_lint! {
|
||||
pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
$(#[$attr])* 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
|
||||
$(#[$attr])* pub clippy::$name, Warn, $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
|
||||
$(#[$attr])* 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
|
||||
$(#[$attr])* 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
|
||||
$(#[$attr])* 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
|
||||
$(#[$attr])* 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
|
||||
$(#[$attr])* pub clippy::$name, Allow, $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
|
||||
$(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -194,10 +196,10 @@ pub mod indexing_slicing;
|
|||
pub mod infallible_destructuring_match;
|
||||
pub mod infinite_iter;
|
||||
pub mod inherent_impl;
|
||||
pub mod inherent_to_string;
|
||||
pub mod inline_fn_without_body;
|
||||
pub mod int_plus_one;
|
||||
pub mod integer_division;
|
||||
pub mod invalid_ref;
|
||||
pub mod items_after_statements;
|
||||
pub mod large_enum_variant;
|
||||
pub mod len_zero;
|
||||
|
@ -205,6 +207,7 @@ pub mod let_if_seq;
|
|||
pub mod lifetimes;
|
||||
pub mod literal_representation;
|
||||
pub mod loops;
|
||||
pub mod main_recursion;
|
||||
pub mod map_clone;
|
||||
pub mod map_unit_fn;
|
||||
pub mod matches;
|
||||
|
@ -260,6 +263,7 @@ pub mod strings;
|
|||
pub mod suspicious_trait_impl;
|
||||
pub mod swap;
|
||||
pub mod temporary_assignment;
|
||||
pub mod trait_bounds;
|
||||
pub mod transmute;
|
||||
pub mod transmuting_null;
|
||||
pub mod trivially_copy_pass_by_ref;
|
||||
|
@ -469,6 +473,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
reg.register_late_lint_pass(box types::LetUnitValue);
|
||||
reg.register_late_lint_pass(box types::UnitCmp);
|
||||
reg.register_late_lint_pass(box loops::Loops);
|
||||
reg.register_late_lint_pass(box main_recursion::MainRecursion::default());
|
||||
reg.register_late_lint_pass(box lifetimes::Lifetimes);
|
||||
reg.register_late_lint_pass(box entry::HashMapPass);
|
||||
reg.register_late_lint_pass(box ranges::Ranges);
|
||||
|
@ -552,7 +557,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
reg.register_late_lint_pass(box bytecount::ByteCount);
|
||||
reg.register_late_lint_pass(box infinite_iter::InfiniteIter);
|
||||
reg.register_late_lint_pass(box inline_fn_without_body::InlineFnWithoutBody);
|
||||
reg.register_late_lint_pass(box invalid_ref::InvalidRef);
|
||||
reg.register_late_lint_pass(box identity_conversion::IdentityConversion::default());
|
||||
reg.register_late_lint_pass(box types::ImplicitHasher);
|
||||
reg.register_early_lint_pass(box redundant_static_lifetimes::RedundantStaticLifetimes);
|
||||
|
@ -584,6 +588,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
reg.register_late_lint_pass(box path_buf_push_overwrite::PathBufPushOverwrite);
|
||||
reg.register_late_lint_pass(box checked_conversions::CheckedConversions);
|
||||
reg.register_late_lint_pass(box integer_division::IntegerDivision);
|
||||
reg.register_late_lint_pass(box inherent_to_string::InherentToString);
|
||||
reg.register_late_lint_pass(box trait_bounds::TraitBounds);
|
||||
|
||||
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
|
||||
arithmetic::FLOAT_ARITHMETIC,
|
||||
|
@ -666,7 +672,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
utils::internal_lints::OUTER_EXPN_INFO,
|
||||
utils::internal_lints::OUTER_EXPN_EXPN_INFO,
|
||||
]);
|
||||
|
||||
reg.register_lint_group("clippy::all", Some("clippy"), vec![
|
||||
|
@ -725,9 +731,10 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
indexing_slicing::OUT_OF_BOUNDS_INDEXING,
|
||||
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
infinite_iter::INFINITE_ITER,
|
||||
inherent_to_string::INHERENT_TO_STRING,
|
||||
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
|
||||
int_plus_one::INT_PLUS_ONE,
|
||||
invalid_ref::INVALID_REF,
|
||||
large_enum_variant::LARGE_ENUM_VARIANT,
|
||||
len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
len_zero::LEN_ZERO,
|
||||
|
@ -753,6 +760,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
loops::WHILE_IMMUTABLE_CONDITION,
|
||||
loops::WHILE_LET_LOOP,
|
||||
loops::WHILE_LET_ON_ITERATOR,
|
||||
main_recursion::MAIN_RECURSION,
|
||||
map_clone::MAP_CLONE,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
|
@ -853,6 +861,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
swap::ALMOST_SWAPPED,
|
||||
swap::MANUAL_SWAP,
|
||||
temporary_assignment::TEMPORARY_ASSIGNMENT,
|
||||
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
|
||||
transmute::CROSSPOINTER_TRANSMUTE,
|
||||
transmute::TRANSMUTE_BYTES_TO_STR,
|
||||
transmute::TRANSMUTE_INT_TO_BOOL,
|
||||
|
@ -886,6 +895,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
unused_label::UNUSED_LABEL,
|
||||
unwrap::PANICKING_UNWRAP,
|
||||
unwrap::UNNECESSARY_UNWRAP,
|
||||
vec::USELESS_VEC,
|
||||
write::PRINTLN_EMPTY_STRING,
|
||||
write::PRINT_LITERAL,
|
||||
|
@ -913,6 +924,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
formatting::SUSPICIOUS_ELSE_FORMATTING,
|
||||
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
|
||||
inherent_to_string::INHERENT_TO_STRING,
|
||||
len_zero::LEN_WITHOUT_IS_EMPTY,
|
||||
len_zero::LEN_ZERO,
|
||||
let_if_seq::USELESS_LET_IF_SEQ,
|
||||
|
@ -922,6 +934,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
loops::FOR_KV_MAP,
|
||||
loops::NEEDLESS_RANGE_LOOP,
|
||||
loops::WHILE_LET_ON_ITERATOR,
|
||||
main_recursion::MAIN_RECURSION,
|
||||
map_clone::MAP_CLONE,
|
||||
matches::MATCH_BOOL,
|
||||
matches::MATCH_OVERLAPPING_ARM,
|
||||
|
@ -1033,6 +1046,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
reference::REF_IN_DEREF,
|
||||
swap::MANUAL_SWAP,
|
||||
temporary_assignment::TEMPORARY_ASSIGNMENT,
|
||||
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
|
||||
transmute::CROSSPOINTER_TRANSMUTE,
|
||||
transmute::TRANSMUTE_BYTES_TO_STR,
|
||||
transmute::TRANSMUTE_INT_TO_BOOL,
|
||||
|
@ -1050,6 +1064,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
types::UNNECESSARY_CAST,
|
||||
types::VEC_BOX,
|
||||
unused_label::UNUSED_LABEL,
|
||||
unwrap::UNNECESSARY_UNWRAP,
|
||||
zero_div_zero::ZERO_DIVIDED_BY_ZERO,
|
||||
]);
|
||||
|
||||
|
@ -1075,8 +1090,8 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
functions::NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
indexing_slicing::OUT_OF_BOUNDS_INDEXING,
|
||||
infinite_iter::INFINITE_ITER,
|
||||
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
|
||||
invalid_ref::INVALID_REF,
|
||||
literal_representation::MISTYPED_LITERAL_SUFFIXES,
|
||||
loops::FOR_LOOP_OVER_OPTION,
|
||||
loops::FOR_LOOP_OVER_RESULT,
|
||||
|
@ -1110,6 +1125,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
types::UNIT_CMP,
|
||||
unicode::ZERO_WIDTH_SPACE,
|
||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
unwrap::PANICKING_UNWRAP,
|
||||
]);
|
||||
|
||||
reg.register_lint_group("clippy::perf", Some("clippy_perf"), vec![
|
||||
|
@ -1146,8 +1162,6 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
|
|||
needless_borrow::NEEDLESS_BORROW,
|
||||
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
redundant_clone::REDUNDANT_CLONE,
|
||||
unwrap::PANICKING_UNWRAP,
|
||||
unwrap::UNNECESSARY_UNWRAP,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
/// ```rust
|
||||
/// fn unused_lifetime<'a>(x: u8) {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXTRA_UNUSED_LIFETIMES,
|
||||
|
@ -283,6 +283,8 @@ impl<'v, 't> RefVisitor<'v, 't> {
|
|||
if let Some(ref lt) = *lifetime {
|
||||
if lt.name == LifetimeName::Static {
|
||||
self.lts.push(RefLt::Static);
|
||||
} else if let LifetimeName::Param(ParamName::Fresh(_)) = lt.name {
|
||||
// Fresh lifetimes generated should be ignored.
|
||||
} else if lt.is_elided() {
|
||||
self.lts.push(RefLt::Unnamed);
|
||||
} else {
|
||||
|
@ -346,7 +348,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
|
|||
},
|
||||
TyKind::Def(item, _) => {
|
||||
let map = self.cx.tcx.hir();
|
||||
if let ItemKind::Existential(ref exist_ty) = map.expect_item(item.id).node {
|
||||
if let ItemKind::OpaqueTy(ref exist_ty) = map.expect_item(item.id).node {
|
||||
for bound in &exist_ty.bounds {
|
||||
if let GenericBound::Outlives(_) = *bound {
|
||||
self.record(&None);
|
||||
|
|
|
@ -115,8 +115,8 @@ impl Radix {
|
|||
/// Returns a reasonable digit group size for this radix.
|
||||
crate fn suggest_grouping(&self) -> usize {
|
||||
match *self {
|
||||
Radix::Binary | Radix::Hexadecimal => 4,
|
||||
Radix::Octal | Radix::Decimal => 3,
|
||||
Self::Binary | Self::Hexadecimal => 4,
|
||||
Self::Octal | Self::Decimal => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ enum WarningType {
|
|||
impl WarningType {
|
||||
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
|
||||
match self {
|
||||
WarningType::MistypedLiteralSuffix => span_lint_and_sugg(
|
||||
Self::MistypedLiteralSuffix => span_lint_and_sugg(
|
||||
cx,
|
||||
MISTYPED_LITERAL_SUFFIXES,
|
||||
span,
|
||||
|
@ -294,7 +294,7 @@ impl WarningType {
|
|||
grouping_hint.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
),
|
||||
WarningType::UnreadableLiteral => span_lint_and_sugg(
|
||||
Self::UnreadableLiteral => span_lint_and_sugg(
|
||||
cx,
|
||||
UNREADABLE_LITERAL,
|
||||
span,
|
||||
|
@ -303,7 +303,7 @@ impl WarningType {
|
|||
grouping_hint.to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
WarningType::LargeDigitGroups => span_lint_and_sugg(
|
||||
Self::LargeDigitGroups => span_lint_and_sugg(
|
||||
cx,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
span,
|
||||
|
@ -312,7 +312,7 @@ impl WarningType {
|
|||
grouping_hint.to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
WarningType::InconsistentDigitGrouping => span_lint_and_sugg(
|
||||
Self::InconsistentDigitGrouping => span_lint_and_sugg(
|
||||
cx,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
span,
|
||||
|
@ -321,7 +321,7 @@ impl WarningType {
|
|||
grouping_hint.to_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
),
|
||||
WarningType::DecimalRepresentation => span_lint_and_sugg(
|
||||
Self::DecimalRepresentation => span_lint_and_sugg(
|
||||
cx,
|
||||
DECIMAL_LITERAL_REPRESENTATION,
|
||||
span,
|
||||
|
|
|
@ -41,7 +41,9 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// ```rust
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
/// for i in 0..src.len() {
|
||||
/// dst[i + 64] = src[i];
|
||||
/// }
|
||||
|
@ -89,16 +91,18 @@ declare_clippy_lint! {
|
|||
/// types.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// ```rust
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// # let y = vec![1];
|
||||
/// for x in y.iter() {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten to
|
||||
/// ```rust
|
||||
/// # let y = vec![1];
|
||||
/// for x in &y {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXPLICIT_ITER_LOOP,
|
||||
|
@ -115,16 +119,18 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// ```rust
|
||||
/// # let y = vec![1];
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// for x in y.into_iter() {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten to
|
||||
/// ```ignore
|
||||
/// ```rust
|
||||
/// # let y = vec![1];
|
||||
/// for x in y {
|
||||
/// ..
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
pub EXPLICIT_INTO_ITER_LOOP,
|
||||
|
@ -217,18 +223,19 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** Sometimes the wrong binding is displayed (#383).
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,no_run
|
||||
/// # let y = Some(1);
|
||||
/// loop {
|
||||
/// let x = match y {
|
||||
/// Some(x) => x,
|
||||
/// None => break,
|
||||
/// }
|
||||
/// };
|
||||
/// // .. do something with x
|
||||
/// }
|
||||
/// // is easier written as
|
||||
/// while let Some(x) = y {
|
||||
/// // .. do something with x
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
pub WHILE_LET_LOOP,
|
||||
complexity,
|
||||
|
@ -264,8 +271,9 @@ declare_clippy_lint! {
|
|||
/// None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// let len = iterator.collect::<Vec<_>>().len();
|
||||
/// ```rust
|
||||
/// # let iterator = vec![1].into_iter();
|
||||
/// let len = iterator.clone().collect::<Vec<_>>().len();
|
||||
/// // should be
|
||||
/// let len = iterator.count();
|
||||
/// ```
|
||||
|
@ -309,8 +317,11 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```ignore
|
||||
/// for i in 0..v.len() { foo(v[i]);
|
||||
/// ```rust
|
||||
/// # let v = vec![1];
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
/// for i in 0..v.len() { foo(v[i]); }
|
||||
/// for i in 0..v.len() { bar(i, v[i]); }
|
||||
/// ```
|
||||
pub EXPLICIT_COUNTER_LOOP,
|
||||
|
@ -1242,7 +1253,7 @@ fn is_end_eq_array_len<'tcx>(
|
|||
if let ExprKind::Lit(ref lit) = end.node;
|
||||
if let ast::LitKind::Int(end_int, _) = lit.node;
|
||||
if let ty::Array(_, arr_len_const) = indexed_ty.sty;
|
||||
if let Some(arr_len) = arr_len_const.assert_usize(cx.tcx);
|
||||
if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
|
||||
then {
|
||||
return match limits {
|
||||
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
|
||||
|
@ -1364,7 +1375,7 @@ fn check_for_loop_arg(cx: &LateContext<'_, '_>, pat: &Pat, arg: &Expr, expr: &Ex
|
|||
match cx.tables.expr_ty(&args[0]).sty {
|
||||
// If the length is greater than 32 no traits are implemented for array and
|
||||
// therefore we cannot use `&`.
|
||||
ty::Array(_, size) if size.assert_usize(cx.tcx).expect("array size") > 32 => (),
|
||||
ty::Array(_, size) if size.eval_usize(cx.tcx, cx.param_env) > 32 => {},
|
||||
_ => lint_iter_method(cx, args, arg, method_name),
|
||||
};
|
||||
} else {
|
||||
|
@ -1977,7 +1988,7 @@ fn is_ref_iterable_type(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
|
|||
fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
|
||||
// IntoIterator is currently only implemented for array sizes <= 32 in rustc
|
||||
match ty.sty {
|
||||
ty::Array(_, n) => (0..=32).contains(&n.assert_usize(cx.tcx).expect("array length")),
|
||||
ty::Array(_, n) => (0..=32).contains(&n.eval_usize(cx.tcx, cx.param_env)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
62
clippy_lints/src/main_recursion.rs
Normal file
62
clippy_lints/src/main_recursion.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
use rustc::hir::{Crate, Expr, ExprKind, QPath};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, impl_lint_pass};
|
||||
use syntax::symbol::sym;
|
||||
|
||||
use crate::utils::{is_entrypoint_fn, snippet, span_help_and_lint};
|
||||
use if_chain::if_chain;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for recursion using the entrypoint.
|
||||
///
|
||||
/// **Why is this bad?** Apart from special setups (which we could detect following attributes like #![no_std]),
|
||||
/// recursing into main() seems like an unintuitive antipattern we should be able to detect.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```no_run
|
||||
/// fn main() {
|
||||
/// main();
|
||||
/// }
|
||||
/// ```
|
||||
pub MAIN_RECURSION,
|
||||
style,
|
||||
"recursion using the entrypoint"
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MainRecursion {
|
||||
has_no_std_attr: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(MainRecursion => [MAIN_RECURSION]);
|
||||
|
||||
impl LateLintPass<'_, '_> for MainRecursion {
|
||||
fn check_crate(&mut self, _: &LateContext<'_, '_>, krate: &Crate) {
|
||||
self.has_no_std_attr = krate.attrs.iter().any(|attr| attr.path == sym::no_std);
|
||||
}
|
||||
|
||||
fn check_expr_post(&mut self, cx: &LateContext<'_, '_>, expr: &Expr) {
|
||||
if self.has_no_std_attr {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func, _) = &expr.node;
|
||||
if let ExprKind::Path(path) = &func.node;
|
||||
if let QPath::Resolved(_, path) = &path;
|
||||
if let Some(def_id) = path.res.opt_def_id();
|
||||
if is_entrypoint_fn(cx, def_id);
|
||||
then {
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
MAIN_RECURSION,
|
||||
func.span,
|
||||
&format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
|
||||
"consider using another function for this recursion"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,20 +20,29 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// # fn do_stuff() -> Option<String> { Some(String::new()) }
|
||||
/// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
|
||||
/// # fn format_msg(foo: String) -> String { String::new() }
|
||||
/// let x: Option<String> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// # let x: Option<String> = do_stuff();
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)));
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Option<&str> = do_stuff();
|
||||
/// # fn do_stuff() -> Option<String> { Some(String::new()) }
|
||||
/// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
|
||||
/// # fn format_msg(foo: String) -> String { String::new() }
|
||||
/// let x: Option<String> = do_stuff();
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// log_err_msg(msg);
|
||||
/// }
|
||||
///
|
||||
/// # let x: Option<String> = do_stuff();
|
||||
/// if let Some(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// log_err_msg(format_msg(msg));
|
||||
/// }
|
||||
/// ```
|
||||
pub OPTION_MAP_UNIT_FN,
|
||||
|
@ -53,21 +62,29 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
|
||||
/// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
|
||||
/// # fn format_msg(foo: String) -> String { String::new() }
|
||||
/// let x: Result<String, String> = do_stuff();
|
||||
/// x.map(log_err_msg);
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)))
|
||||
/// # let x: Result<String, String> = do_stuff();
|
||||
/// x.map(|msg| log_err_msg(format_msg(msg)));
|
||||
/// ```
|
||||
///
|
||||
/// The correct use would be:
|
||||
///
|
||||
/// ```rust
|
||||
/// let x: Result<&str, &str> = do_stuff();
|
||||
/// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
|
||||
/// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
|
||||
/// # fn format_msg(foo: String) -> String { String::new() }
|
||||
/// let x: Result<String, String> = do_stuff();
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(msg)
|
||||
/// }
|
||||
/// log_err_msg(msg);
|
||||
/// };
|
||||
/// # let x: Result<String, String> = do_stuff();
|
||||
/// if let Ok(msg) = x {
|
||||
/// log_err_msg(format_msg(msg))
|
||||
/// }
|
||||
/// log_err_msg(format_msg(msg));
|
||||
/// };
|
||||
/// ```
|
||||
pub RESULT_MAP_UNIT_FN,
|
||||
complexity,
|
||||
|
|
|
@ -53,19 +53,25 @@ declare_clippy_lint! {
|
|||
/// Using `match`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn bar(foo: &usize) {}
|
||||
/// # let other_ref: usize = 1;
|
||||
/// # let x: Option<&usize> = Some(&1);
|
||||
/// match x {
|
||||
/// Some(ref foo) => bar(foo),
|
||||
/// _ => bar(other_ref),
|
||||
/// _ => bar(&other_ref),
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `if let` with `else`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn bar(foo: &usize) {}
|
||||
/// # let other_ref: usize = 1;
|
||||
/// # let x: Option<&usize> = Some(&1);
|
||||
/// if let Some(ref foo) = x {
|
||||
/// bar(foo);
|
||||
/// } else {
|
||||
/// bar(other_ref);
|
||||
/// bar(&other_ref);
|
||||
/// }
|
||||
/// ```
|
||||
pub SINGLE_MATCH_ELSE,
|
||||
|
@ -205,6 +211,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # enum Foo { A(usize), B(usize) }
|
||||
/// # let x = Foo::B(1);
|
||||
/// match x {
|
||||
/// A => {},
|
||||
/// _ => {},
|
||||
|
|
|
@ -14,6 +14,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::mem;
|
||||
/// # use std::rc::Rc;
|
||||
/// mem::forget(Rc::new(55))
|
||||
/// ```
|
||||
pub MEM_FORGET,
|
||||
|
|
|
@ -40,8 +40,19 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// Using unwrap on an `Option`:
|
||||
///
|
||||
/// ```rust
|
||||
/// x.unwrap()
|
||||
/// let opt = Some(1);
|
||||
/// opt.unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```rust
|
||||
/// let opt = Some(1);
|
||||
/// opt.expect("more helpful message");
|
||||
/// ```
|
||||
pub OPTION_UNWRAP_USED,
|
||||
restriction,
|
||||
|
@ -62,8 +73,18 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// Using unwrap on an `Option`:
|
||||
///
|
||||
/// ```rust
|
||||
/// x.unwrap()
|
||||
/// let res: Result<usize, ()> = Ok(1);
|
||||
/// res.unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```rust
|
||||
/// let res: Result<usize, ()> = Ok(1);
|
||||
/// res.expect("more helpful message");
|
||||
/// ```
|
||||
pub RESULT_UNWRAP_USED,
|
||||
restriction,
|
||||
|
@ -141,9 +162,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// impl X {
|
||||
/// pub fn as_str(self) -> &str {
|
||||
/// ..
|
||||
/// # struct X;
|
||||
/// impl<'a> X {
|
||||
/// pub fn as_str(self) -> &'a str {
|
||||
/// "foo"
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
|
@ -179,7 +201,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.map(|a| a + 1).unwrap_or(0)
|
||||
/// # let x = Some(1);
|
||||
/// x.map(|a| a + 1).unwrap_or(0);
|
||||
/// ```
|
||||
pub OPTION_MAP_UNWRAP_OR,
|
||||
pedantic,
|
||||
|
@ -196,7 +219,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.map(|a| a + 1).unwrap_or_else(some_function)
|
||||
/// # let x = Some(1);
|
||||
/// # fn some_function() -> usize { 1 }
|
||||
/// x.map(|a| a + 1).unwrap_or_else(some_function);
|
||||
/// ```
|
||||
pub OPTION_MAP_UNWRAP_OR_ELSE,
|
||||
pedantic,
|
||||
|
@ -213,7 +238,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.map(|a| a + 1).unwrap_or_else(some_function)
|
||||
/// # let x: Result<usize, ()> = Ok(1);
|
||||
/// # fn some_function(foo: ()) -> usize { 1 }
|
||||
/// x.map(|a| a + 1).unwrap_or_else(some_function);
|
||||
/// ```
|
||||
pub RESULT_MAP_UNWRAP_OR_ELSE,
|
||||
pedantic,
|
||||
|
@ -247,7 +274,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// iter.filter(|x| x == 0).next()
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().filter(|x| **x == 0).next();
|
||||
/// ```
|
||||
pub FILTER_NEXT,
|
||||
complexity,
|
||||
|
@ -264,7 +292,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// iter.map(|x| x.iter()).flatten()
|
||||
/// let vec = vec![vec![1]];
|
||||
/// vec.iter().map(|x| x.iter()).flatten();
|
||||
/// ```
|
||||
pub MAP_FLATTEN,
|
||||
pedantic,
|
||||
|
@ -283,7 +312,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// iter.filter(|x| x == 0).map(|x| x * 2)
|
||||
/// let vec = vec![1];
|
||||
/// vec.iter().filter(|x| **x == 0).map(|x| *x * 2);
|
||||
/// ```
|
||||
pub FILTER_MAP,
|
||||
pedantic,
|
||||
|
@ -343,7 +373,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// (0..3).find(|x| x == 2).map(|x| x * 2);
|
||||
/// (0..3).find(|x| *x == 2).map(|x| x * 2);
|
||||
/// ```
|
||||
/// Can be written as
|
||||
/// ```rust
|
||||
|
@ -365,7 +395,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// iter.find(|x| x == 0).is_some()
|
||||
/// # let vec = vec![1];
|
||||
/// vec.iter().find(|x| **x == 0).is_some();
|
||||
/// ```
|
||||
pub SEARCH_IS_SOME,
|
||||
complexity,
|
||||
|
@ -383,7 +414,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// name.chars().next() == Some('_')
|
||||
/// let name = "foo";
|
||||
/// name.chars().next() == Some('_');
|
||||
/// ```
|
||||
pub CHARS_NEXT_CMP,
|
||||
complexity,
|
||||
|
@ -403,15 +435,18 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// foo.unwrap_or(String::new())
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or(String::new());
|
||||
/// ```
|
||||
/// this can instead be written:
|
||||
/// ```rust
|
||||
/// foo.unwrap_or_else(String::new)
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or_else(String::new);
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
/// foo.unwrap_or_default()
|
||||
/// # let foo = Some(String::new());
|
||||
/// foo.unwrap_or_default();
|
||||
/// ```
|
||||
pub OR_FUN_CALL,
|
||||
perf,
|
||||
|
@ -429,15 +464,24 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// foo.expect(&format!("Err {}: {}", err_code, err_msg))
|
||||
/// # let foo = Some(String::new());
|
||||
/// # let err_code = "418";
|
||||
/// # let err_msg = "I'm a teapot";
|
||||
/// foo.expect(&format!("Err {}: {}", err_code, err_msg));
|
||||
/// ```
|
||||
/// or
|
||||
/// ```rust
|
||||
/// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str())
|
||||
/// # let foo = Some(String::new());
|
||||
/// # let err_code = "418";
|
||||
/// # let err_msg = "I'm a teapot";
|
||||
/// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
|
||||
/// ```
|
||||
/// this can instead be written:
|
||||
/// ```rust
|
||||
/// foo.unwrap_or_else(|_| panic!("Err {}: {}", err_code, err_msg))
|
||||
/// # let foo = Some(String::new());
|
||||
/// # let err_code = "418";
|
||||
/// # let err_msg = "I'm a teapot";
|
||||
/// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
|
||||
/// ```
|
||||
pub EXPECT_FUN_CALL,
|
||||
perf,
|
||||
|
@ -454,7 +498,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 42u64.clone()
|
||||
/// 42u64.clone();
|
||||
/// ```
|
||||
pub CLONE_ON_COPY,
|
||||
complexity,
|
||||
|
@ -472,7 +516,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.clone()
|
||||
/// # use std::rc::Rc;
|
||||
/// let x = Rc::new(1);
|
||||
/// x.clone();
|
||||
/// ```
|
||||
pub CLONE_ON_REF_PTR,
|
||||
restriction,
|
||||
|
@ -728,11 +774,13 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # fn do_stuff(x: &[i32]) {}
|
||||
/// let x: &[i32] = &[1, 2, 3, 4, 5];
|
||||
/// do_stuff(x.as_ref());
|
||||
/// ```
|
||||
/// The correct use would be:
|
||||
/// ```rust
|
||||
/// # fn do_stuff(x: &[i32]) {}
|
||||
/// let x: &[i32] = &[1, 2, 3, 4, 5];
|
||||
/// do_stuff(x);
|
||||
/// ```
|
||||
|
@ -911,7 +959,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Methods {
|
|||
["is_some", "position"] => lint_search_is_some(cx, expr, "position", arg_lists[1], arg_lists[0]),
|
||||
["is_some", "rposition"] => lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0]),
|
||||
["extend", ..] => lint_extend(cx, expr, arg_lists[0]),
|
||||
["as_ptr", "unwrap"] => lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0]),
|
||||
["as_ptr", "unwrap"] | ["as_ptr", "expect"] => {
|
||||
lint_cstring_as_ptr(cx, expr, &arg_lists[1][0], &arg_lists[0][0])
|
||||
},
|
||||
["nth", "iter"] => lint_iter_nth(cx, expr, arg_lists[1], false),
|
||||
["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true),
|
||||
["next", "skip"] => lint_iter_skip_next(cx, expr),
|
||||
|
@ -1799,7 +1849,7 @@ fn derefs_to_slice<'a, 'tcx>(
|
|||
ty::Slice(_) => true,
|
||||
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
|
||||
ty::Adt(..) => match_type(cx, ty, &paths::VEC),
|
||||
ty::Array(_, size) => size.assert_usize(cx.tcx).expect("array length") < 32,
|
||||
ty::Array(_, size) => size.eval_usize(cx.tcx, cx.param_env) < 32,
|
||||
ty::Ref(_, inner, _) => may_slice(cx, inner),
|
||||
_ => false,
|
||||
}
|
||||
|
@ -2327,13 +2377,20 @@ fn lint_chars_last_cmp_with_unwrap<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, info: &
|
|||
fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, _expr: &'tcx hir::Expr, arg: &'tcx hir::Expr) {
|
||||
if_chain! {
|
||||
if let hir::ExprKind::Lit(lit) = &arg.node;
|
||||
if let ast::LitKind::Str(r, _) = lit.node;
|
||||
if let ast::LitKind::Str(r, style) = lit.node;
|
||||
if r.as_str().len() == 1;
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
|
||||
let c = &snip[1..snip.len() - 1];
|
||||
let hint = format!("'{}'", if c == "'" { "\\'" } else { c });
|
||||
let ch = if let ast::StrStyle::Raw(nhash) = style {
|
||||
let nhash = nhash as usize;
|
||||
// for raw string: r##"a"##
|
||||
&snip[(nhash + 2)..(snip.len() - 1 - nhash)]
|
||||
} else {
|
||||
// for regular string: "a"
|
||||
&snip[1..(snip.len() - 1)]
|
||||
};
|
||||
let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SINGLE_CHAR_PATTERN,
|
||||
|
@ -2551,14 +2608,14 @@ impl SelfKind {
|
|||
let is_actually_self = |ty| is_self_ty(ty) || SpanlessEq::new(cx).eq_ty(ty, self_ty);
|
||||
if is_self(arg) {
|
||||
match self {
|
||||
SelfKind::Value => is_actually_self(ty),
|
||||
SelfKind::Ref | SelfKind::RefMut => {
|
||||
Self::Value => is_actually_self(ty),
|
||||
Self::Ref | Self::RefMut => {
|
||||
if allow_value_for_ref && is_actually_self(ty) {
|
||||
return true;
|
||||
}
|
||||
match ty.node {
|
||||
hir::TyKind::Rptr(_, ref mt_ty) => {
|
||||
let mutability_match = if self == SelfKind::Ref {
|
||||
let mutability_match = if self == Self::Ref {
|
||||
mt_ty.mutbl == hir::MutImmutable
|
||||
} else {
|
||||
mt_ty.mutbl == hir::MutMutable
|
||||
|
@ -2572,20 +2629,20 @@ impl SelfKind {
|
|||
}
|
||||
} else {
|
||||
match self {
|
||||
SelfKind::Value => false,
|
||||
SelfKind::Ref => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASREF_TRAIT),
|
||||
SelfKind::RefMut => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASMUT_TRAIT),
|
||||
SelfKind::No => true,
|
||||
Self::Value => false,
|
||||
Self::Ref => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASREF_TRAIT),
|
||||
Self::RefMut => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASMUT_TRAIT),
|
||||
Self::No => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn description(self) -> &'static str {
|
||||
match self {
|
||||
SelfKind::Value => "self by value",
|
||||
SelfKind::Ref => "self by reference",
|
||||
SelfKind::RefMut => "self by mutable reference",
|
||||
SelfKind::No => "no self",
|
||||
Self::Value => "self by value",
|
||||
Self::Ref => "self by reference",
|
||||
Self::RefMut => "self by mutable reference",
|
||||
Self::No => "no self",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2655,8 +2712,8 @@ fn single_segment_ty(ty: &hir::Ty) -> Option<&hir::PathSegment> {
|
|||
impl Convention {
|
||||
fn check(&self, other: &str) -> bool {
|
||||
match *self {
|
||||
Convention::Eq(this) => this == other,
|
||||
Convention::StartsWith(this) => other.starts_with(this) && this != other,
|
||||
Self::Eq(this) => this == other,
|
||||
Self::StartsWith(this) => other.starts_with(this) && this != other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2664,8 +2721,8 @@ impl Convention {
|
|||
impl fmt::Display for Convention {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Convention::Eq(this) => this.fmt(f),
|
||||
Convention::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
|
||||
Self::Eq(this) => this.fmt(f),
|
||||
Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2682,11 +2739,11 @@ impl OutType {
|
|||
fn matches(self, cx: &LateContext<'_, '_>, ty: &hir::FunctionRetTy) -> bool {
|
||||
let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.node, &hir::TyKind::Tup(vec![].into()));
|
||||
match (self, ty) {
|
||||
(OutType::Unit, &hir::DefaultReturn(_)) => true,
|
||||
(OutType::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
|
||||
(OutType::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
|
||||
(OutType::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
|
||||
(OutType::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
|
||||
(Self::Unit, &hir::DefaultReturn(_)) => true,
|
||||
(Self::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
|
||||
(Self::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
|
||||
(Self::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
|
||||
(Self::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc::ty;
|
|||
use rustc::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_errors::Applicability;
|
||||
use syntax::ast::LitKind;
|
||||
use syntax::source_map::{ExpnFormat, Span};
|
||||
use syntax::source_map::{ExpnKind, Span};
|
||||
|
||||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::sugg::Sugg;
|
||||
|
@ -101,7 +101,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.to_owned() == y
|
||||
/// # let x = "foo";
|
||||
/// # let y = String::from("foo");
|
||||
/// if x.to_owned() == y {}
|
||||
/// ```
|
||||
pub CMP_OWNED,
|
||||
perf,
|
||||
|
@ -184,7 +186,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// f() && g(); // We should write `if f() { g(); }`.
|
||||
/// ```
|
||||
pub SHORT_CIRCUIT_STATEMENT,
|
||||
|
@ -224,8 +226,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// const ONE = 1.00f64;
|
||||
/// x == ONE // where both are floats
|
||||
/// let x: f64 = 1.0;
|
||||
/// const ONE: f64 = 1.00;
|
||||
/// x == ONE; // where both are floats
|
||||
/// ```
|
||||
pub FLOAT_CMP_CONST,
|
||||
restriction,
|
||||
|
@ -364,6 +367,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
|
|||
if is_allowed(cx, left) || is_allowed(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow comparing the results of signum()
|
||||
if is_signum(cx, left) && is_signum(cx, right) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(name) = get_item_name(cx, expr) {
|
||||
let name = name.as_str();
|
||||
if name == "eq"
|
||||
|
@ -493,6 +502,25 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Return true if `expr` is the result of `signum()` invoked on a float value.
|
||||
fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
||||
// The negation of a signum is still a signum
|
||||
if let ExprKind::Unary(UnNeg, ref child_expr) = expr.node {
|
||||
return is_signum(cx, &child_expr);
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.node;
|
||||
if sym!(signum) == method_name.ident.name;
|
||||
// Check that the receiver of the signum() is a float (expressions[0] is the receiver of
|
||||
// the method call)
|
||||
then {
|
||||
return is_float(cx, &expressions[0]);
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_float(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
||||
matches!(walk_ptrs_ty(cx.tables.expr_ty(expr)).sty, ty::Float(_))
|
||||
}
|
||||
|
@ -596,10 +624,14 @@ fn is_used(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
|
|||
/// 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()
|
||||
.outer_expn_info()
|
||||
.map_or(false, |info| matches!(info.format, ExpnFormat::MacroAttribute(_)))
|
||||
use syntax::ext::hygiene::MacroKind;
|
||||
expr.span.ctxt().outer_expn_info().map_or(false, |info| {
|
||||
if let ExpnKind::Macro(MacroKind::Attr, _) = info.kind {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Tests whether `res` is a variable defined outside a macro.
|
||||
|
|
|
@ -53,7 +53,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// (|| 42)()
|
||||
/// ```
|
||||
pub REDUNDANT_CLOSURE_CALL,
|
||||
|
@ -305,7 +305,7 @@ impl EarlyLintPass for MiscEarlyLints {
|
|||
name makes code comprehension and documentation more difficult",
|
||||
arg_name[1..].to_owned()
|
||||
),
|
||||
);;
|
||||
);
|
||||
}
|
||||
} else {
|
||||
registered_names.insert(arg_name, arg.pat.span);
|
||||
|
|
|
@ -40,17 +40,27 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// # struct Foo {
|
||||
/// # random_number: usize,
|
||||
/// # }
|
||||
/// # impl Foo {
|
||||
/// fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Could be a const fn:
|
||||
///
|
||||
/// ```rust
|
||||
/// # struct Foo {
|
||||
/// # random_number: usize,
|
||||
/// # }
|
||||
/// # impl Foo {
|
||||
/// const fn new() -> Self {
|
||||
/// Self { random_number: 42 }
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
pub MISSING_CONST_FOR_FN,
|
||||
nursery,
|
||||
|
|
|
@ -146,9 +146,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
|||
hir::ItemKind::Struct(..) => "a struct",
|
||||
hir::ItemKind::Trait(..) => "a trait",
|
||||
hir::ItemKind::TraitAlias(..) => "a trait alias",
|
||||
hir::ItemKind::Ty(..) => "a type alias",
|
||||
hir::ItemKind::TyAlias(..) => "a type alias",
|
||||
hir::ItemKind::Union(..) => "a union",
|
||||
hir::ItemKind::Existential(..) => "an existential type",
|
||||
hir::ItemKind::OpaqueTy(..) => "an existential type",
|
||||
hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod(..)
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
|
@ -184,8 +184,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
|
|||
let desc = match impl_item.node {
|
||||
hir::ImplItemKind::Const(..) => "an associated constant",
|
||||
hir::ImplItemKind::Method(..) => "a method",
|
||||
hir::ImplItemKind::Type(_) => "an associated type",
|
||||
hir::ImplItemKind::Existential(_) => "an existential type",
|
||||
hir::ImplItemKind::TyAlias(_) => "an associated type",
|
||||
hir::ImplItemKind::OpaqueTy(_) => "an existential type",
|
||||
};
|
||||
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// struct Baz;
|
||||
/// impl Baz {
|
||||
/// fn priv() {} // ok
|
||||
/// fn private() {} // ok
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for Baz {
|
||||
|
@ -42,8 +42,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// pub struct PubBaz;
|
||||
/// impl PubBaz {
|
||||
/// fn priv() {} // ok
|
||||
/// pub not_ptriv() {} // missing #[inline]
|
||||
/// fn private() {} // ok
|
||||
/// pub fn not_ptrivate() {} // missing #[inline]
|
||||
/// }
|
||||
///
|
||||
/// impl Bar for PubBaz {
|
||||
|
@ -119,9 +119,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::TraitAlias(..)
|
||||
| hir::ItemKind::GlobalAsm(..)
|
||||
| hir::ItemKind::Ty(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Union(..)
|
||||
| hir::ItemKind::Existential(..)
|
||||
| hir::ItemKind::OpaqueTy(..)
|
||||
| hir::ItemKind::ExternCrate(..)
|
||||
| hir::ItemKind::ForeignMod(..)
|
||||
| hir::ItemKind::Impl(..)
|
||||
|
@ -142,7 +142,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
|
|||
|
||||
let desc = match impl_item.node {
|
||||
hir::ImplItemKind::Method(..) => "a method",
|
||||
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) | hir::ImplItemKind::Existential(_) => return,
|
||||
hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) | hir::ImplItemKind::OpaqueTy(_) => return,
|
||||
};
|
||||
|
||||
let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);
|
||||
|
|
|
@ -16,6 +16,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let mut y = 1;
|
||||
/// let x = &mut &mut y;
|
||||
/// ```
|
||||
pub MUT_MUT,
|
||||
|
|
|
@ -22,6 +22,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
/// # let y = 1;
|
||||
/// let x = Mutex::new(&y);
|
||||
/// ```
|
||||
pub MUTEX_ATOMIC,
|
||||
|
@ -42,6 +44,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::sync::Mutex;
|
||||
/// let x = Mutex::new(0usize);
|
||||
/// ```
|
||||
pub MUTEX_INTEGER,
|
||||
|
|
|
@ -24,7 +24,7 @@ declare_clippy_lint! {
|
|||
/// shorter code.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// if x {
|
||||
/// false
|
||||
/// } else {
|
||||
|
@ -46,7 +46,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// if x == true {} // could be `if x { }`
|
||||
/// ```
|
||||
pub BOOL_COMPARISON,
|
||||
|
@ -118,13 +118,11 @@ 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.hir_id);
|
||||
let parent_node = cx.tcx.hir().get(parent_id);
|
||||
|
||||
if let rustc::hir::Node::Expr(e) = parent_node {
|
||||
if higher::if_block(&e).is_some() {
|
||||
return true;
|
||||
}
|
||||
match parent_node {
|
||||
rustc::hir::Node::Expr(e) => higher::if_block(&e).is_some(),
|
||||
rustc::hir::Node::Arm(e) => higher::if_block(&e.body).is_some(),
|
||||
_ => false,
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
|
||||
|
|
|
@ -18,7 +18,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
|
||||
/// For instance in the following snippet:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// enum Animal {
|
||||
/// Cat(u64),
|
||||
/// Dog(u64),
|
||||
|
@ -26,8 +26,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// fn foo(a: &Animal, b: &Animal) {
|
||||
/// match (a, b) {
|
||||
/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime
|
||||
/// mismatch error
|
||||
/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error
|
||||
/// (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
|
||||
/// }
|
||||
/// }
|
||||
|
|
|
@ -57,6 +57,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # fn condition() -> bool { false }
|
||||
/// # fn update_condition() {}
|
||||
/// # let x = false;
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
|
@ -71,6 +74,9 @@ declare_clippy_lint! {
|
|||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn condition() -> bool { false }
|
||||
/// # fn update_condition() {}
|
||||
/// # let x = false;
|
||||
/// while condition() {
|
||||
/// update_condition();
|
||||
/// if x {
|
||||
|
@ -83,22 +89,26 @@ declare_clippy_lint! {
|
|||
/// As another example, the following code
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn waiting() -> bool { false }
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// } else {
|
||||
/// // Do something useful
|
||||
/// }
|
||||
/// # break;
|
||||
/// }
|
||||
/// ```
|
||||
/// Could be rewritten as
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn waiting() -> bool { false }
|
||||
/// loop {
|
||||
/// if waiting() {
|
||||
/// continue;
|
||||
/// }
|
||||
/// // Do something useful
|
||||
/// # break;
|
||||
/// }
|
||||
/// ```
|
||||
pub NEEDLESS_CONTINUE,
|
||||
|
|
|
@ -40,6 +40,9 @@ declare_clippy_lint! {
|
|||
/// fn foo(v: Vec<i32>) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // should be
|
||||
/// fn foo(v: &[i32]) {
|
||||
/// assert_eq!(v.len(), 42);
|
||||
|
@ -193,7 +196,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
|
|||
|
||||
if_chain! {
|
||||
if !is_self(arg);
|
||||
if !ty.is_mutable_pointer();
|
||||
if !ty.is_mutable_ptr();
|
||||
if !is_copy(cx, ty);
|
||||
if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
|
||||
if !implements_borrow_trait;
|
||||
|
|
|
@ -15,11 +15,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # struct Point {
|
||||
/// # x: i32,
|
||||
/// # y: i32,
|
||||
/// # z: i32,
|
||||
/// # }
|
||||
/// # let zero_point = Point { x: 0, y: 0, z: 0 };
|
||||
/// Point {
|
||||
/// x: 1,
|
||||
/// y: 0,
|
||||
/// y: 1,
|
||||
/// ..zero_point
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
pub NEEDLESS_UPDATE,
|
||||
complexity,
|
||||
|
|
|
@ -34,7 +34,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// compute_array()[0];
|
||||
/// ```
|
||||
pub UNNECESSARY_OPERATION,
|
||||
|
|
|
@ -84,6 +84,7 @@ declare_clippy_lint! {
|
|||
"referencing const with interior mutability"
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Copy, Clone)]
|
||||
enum Source {
|
||||
Item { item: Span },
|
||||
|
@ -94,12 +95,12 @@ enum Source {
|
|||
impl Source {
|
||||
fn lint(&self) -> (&'static Lint, &'static str, Span) {
|
||||
match self {
|
||||
Source::Item { item } | Source::Assoc { item, .. } => (
|
||||
Self::Item { item } | Self::Assoc { item, .. } => (
|
||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
"a const item should never be interior mutable",
|
||||
*item,
|
||||
),
|
||||
Source::Expr { expr } => (
|
||||
Self::Expr { expr } => (
|
||||
BORROW_INTERIOR_MUTABLE_CONST,
|
||||
"a const item with interior mutability should not be borrowed",
|
||||
*expr,
|
||||
|
|
|
@ -127,7 +127,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
|
|||
} else {
|
||||
create = true
|
||||
}
|
||||
create_arg = create_arg || (arg == Argument::True);;
|
||||
create_arg = create_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Append, arg) => {
|
||||
if append {
|
||||
|
@ -140,7 +140,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
|
|||
} else {
|
||||
append = true
|
||||
}
|
||||
append_arg = append_arg || (arg == Argument::True);;
|
||||
append_arg = append_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Truncate, arg) => {
|
||||
if truncate {
|
||||
|
@ -166,7 +166,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
|
|||
} else {
|
||||
read = true
|
||||
}
|
||||
read_arg = read_arg || (arg == Argument::True);;
|
||||
read_arg = read_arg || (arg == Argument::True);
|
||||
},
|
||||
(OpenOption::Write, arg) => {
|
||||
if write {
|
||||
|
@ -179,7 +179,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
|
|||
} else {
|
||||
write = true
|
||||
}
|
||||
write_arg = write_arg || (arg == Argument::True);;
|
||||
write_arg = write_arg || (arg == Argument::True);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// a + b < a
|
||||
/// # let a = 1;
|
||||
/// # let b = 2;
|
||||
/// a + b < a;
|
||||
/// ```
|
||||
pub OVERFLOW_CHECK_CONDITIONAL,
|
||||
complexity,
|
||||
|
|
|
@ -19,7 +19,7 @@ declare_clippy_lint! {
|
|||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// fn eq(&self, other: &Foo) -> bool { ... }
|
||||
/// fn eq(&self, other: &Foo) -> bool { true }
|
||||
/// fn ne(&self, other: &Foo) -> bool { !(self == other) }
|
||||
/// }
|
||||
/// ```
|
||||
|
|
|
@ -133,8 +133,8 @@ enum Method {
|
|||
impl Method {
|
||||
fn suggestion(self) -> &'static str {
|
||||
match self {
|
||||
Method::Offset => "add",
|
||||
Method::WrappingOffset => "wrapping_add",
|
||||
Self::Offset => "add",
|
||||
Self::WrappingOffset => "wrapping_add",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,8 +142,8 @@ impl Method {
|
|||
impl fmt::Display for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Method::Offset => write!(f, "offset"),
|
||||
Method::WrappingOffset => write!(f, "wrapping_offset"),
|
||||
Self::Offset => write!(f, "offset"),
|
||||
Self::WrappingOffset => write!(f, "wrapping_offset"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// x.iter().zip(0..x.len())
|
||||
/// # let x = vec![1];
|
||||
/// x.iter().zip(0..x.len());
|
||||
/// ```
|
||||
pub RANGE_ZIP_WITH_LEN,
|
||||
complexity,
|
||||
|
@ -60,7 +61,7 @@ declare_clippy_lint! {
|
|||
/// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// for x..(y+1) { .. }
|
||||
/// ```
|
||||
pub RANGE_PLUS_ONE,
|
||||
|
@ -78,7 +79,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// for x..=(y-1) { .. }
|
||||
/// ```
|
||||
pub RANGE_MINUS_ONE,
|
||||
|
|
|
@ -40,6 +40,7 @@ declare_clippy_lint! {
|
|||
/// * False-positive if there is a borrow preventing the value from moving out.
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn foo(x: String) {}
|
||||
/// let x = String::new();
|
||||
///
|
||||
/// let y = &x;
|
||||
|
@ -49,15 +50,22 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::path::Path;
|
||||
/// # #[derive(Clone)]
|
||||
/// # struct Foo;
|
||||
/// # impl Foo {
|
||||
/// # fn new() -> Self { Foo {} }
|
||||
/// # }
|
||||
/// # fn call(x: Foo) {}
|
||||
/// {
|
||||
/// let x = Foo::new();
|
||||
/// call(x.clone());
|
||||
/// call(x.clone()); // this can just pass `x`
|
||||
/// }
|
||||
///
|
||||
/// ["lorem", "ipsum"].join(" ").to_string()
|
||||
/// ["lorem", "ipsum"].join(" ").to_string();
|
||||
///
|
||||
/// Path::new("/a/b").join("c").to_path_buf()
|
||||
/// Path::new("/a/b").join("c").to_path_buf();
|
||||
/// ```
|
||||
pub REDUNDANT_CLONE,
|
||||
nursery,
|
||||
|
@ -132,7 +140,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
|
|||
let pred_arg = if_chain! {
|
||||
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
|
||||
is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
|
||||
if *res == mir::Place::Base(mir::PlaceBase::Local(cloned));
|
||||
if res.base == mir::PlaceBase::Local(cloned);
|
||||
if match_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
|
||||
if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
|
||||
|| match_type(cx, pred_arg_ty, &paths::OS_STRING);
|
||||
|
@ -218,7 +226,7 @@ fn is_call_with_ref_arg<'tcx>(
|
|||
if_chain! {
|
||||
if let TerminatorKind::Call { func, args, destination, .. } = kind;
|
||||
if args.len() == 1;
|
||||
if let mir::Operand::Move(mir::Place::Base(mir::PlaceBase::Local(local))) = &args[0];
|
||||
if let mir::Operand::Move(mir::Place { base: mir::PlaceBase::Local(local), .. }) = &args[0];
|
||||
if let ty::FnDef(def_id, _) = func.ty(&*mir, cx.tcx).sty;
|
||||
if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
|
||||
if !is_copy(cx, inner_ty);
|
||||
|
@ -244,7 +252,14 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
|
|||
stmts
|
||||
.rev()
|
||||
.find_map(|stmt| {
|
||||
if let mir::StatementKind::Assign(mir::Place::Base(mir::PlaceBase::Local(local)), v) = &stmt.kind {
|
||||
if let mir::StatementKind::Assign(
|
||||
mir::Place {
|
||||
base: mir::PlaceBase::Local(local),
|
||||
..
|
||||
},
|
||||
v,
|
||||
) = &stmt.kind
|
||||
{
|
||||
if *local == to {
|
||||
return Some(v);
|
||||
}
|
||||
|
@ -271,28 +286,34 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
|
|||
fn base_local_and_movability<'tcx>(
|
||||
cx: &LateContext<'_, 'tcx>,
|
||||
mir: &mir::Body<'tcx>,
|
||||
mut place: &mir::Place<'tcx>,
|
||||
place: &mir::Place<'tcx>,
|
||||
) -> Option<(mir::Local, CannotMoveOut)> {
|
||||
use rustc::mir::Place::*;
|
||||
use rustc::mir::Place;
|
||||
use rustc::mir::PlaceBase;
|
||||
use rustc::mir::PlaceRef;
|
||||
use rustc::mir::Projection;
|
||||
|
||||
// Dereference. You cannot move things out from a borrowed value.
|
||||
let mut deref = false;
|
||||
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
|
||||
let mut field = false;
|
||||
|
||||
loop {
|
||||
match place {
|
||||
Base(PlaceBase::Local(local)) => return Some((*local, deref || field)),
|
||||
Projection(proj) => {
|
||||
place = &proj.base;
|
||||
deref = deref || matches!(proj.elem, mir::ProjectionElem::Deref);
|
||||
if !field && matches!(proj.elem, mir::ProjectionElem::Field(..)) {
|
||||
field = has_drop(cx, place.ty(&mir.local_decls, cx.tcx).ty);
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
let PlaceRef {
|
||||
base: place_base,
|
||||
mut projection,
|
||||
} = place.as_ref();
|
||||
if let PlaceBase::Local(local) = place_base {
|
||||
while let Some(box Projection { base, elem }) = projection {
|
||||
projection = base;
|
||||
deref = matches!(elem, mir::ProjectionElem::Deref);
|
||||
field = !field
|
||||
&& matches!(elem, mir::ProjectionElem::Field(..))
|
||||
&& has_drop(cx, Place::ty_from(place_base, projection, &mir.local_decls, cx.tcx).ty);
|
||||
}
|
||||
|
||||
Some((*local, deref || field))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -81,7 +81,8 @@ impl EarlyLintPass for RedundantStaticLifetimes {
|
|||
if !in_macro_or_desugar(item.span) {
|
||||
if let ItemKind::Const(ref var_type, _) = item.node {
|
||||
self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
|
||||
// Don't check associated consts because `'static` cannot be elided on those (issue #2438)
|
||||
// Don't check associated consts because `'static` cannot be elided on those (issue
|
||||
// #2438)
|
||||
}
|
||||
|
||||
if let ItemKind::Static(ref var_type, _, _) = item.node {
|
||||
|
|
|
@ -15,7 +15,7 @@ declare_clippy_lint! {
|
|||
/// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// let a = f(*&mut b);
|
||||
/// let c = *&d;
|
||||
/// ```
|
||||
|
@ -64,8 +64,8 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
/// ```rust
|
||||
/// struct Point(u32, u32);
|
||||
/// let point = Foo(30, 20);
|
||||
/// let x = (&point).x;
|
||||
/// let point = Point(30, 20);
|
||||
/// let x = (&point).0;
|
||||
/// ```
|
||||
pub REF_IN_DEREF,
|
||||
complexity,
|
||||
|
|
|
@ -16,12 +16,14 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use core::sync::atomic::{ATOMIC_ISIZE_INIT, AtomicIsize};
|
||||
/// static FOO: AtomicIsize = ATOMIC_ISIZE_INIT;
|
||||
/// ```
|
||||
///
|
||||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use core::sync::atomic::AtomicIsize;
|
||||
/// static FOO: AtomicIsize = AtomicIsize::new(0);
|
||||
/// ```
|
||||
pub REPLACE_CONSTS,
|
||||
|
|
|
@ -86,7 +86,7 @@ declare_clippy_lint! {
|
|||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
enum RetReplacement {
|
||||
Empty,
|
||||
Unit,
|
||||
Block,
|
||||
}
|
||||
|
||||
declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);
|
||||
|
@ -139,7 +139,7 @@ impl Return {
|
|||
// a match expr, check all arms
|
||||
ast::ExprKind::Match(_, ref arms) => {
|
||||
for arm in arms {
|
||||
self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Unit);
|
||||
self.check_final_expr(cx, &arm.body, Some(arm.body.span), RetReplacement::Block);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
|
@ -176,12 +176,12 @@ impl Return {
|
|||
);
|
||||
});
|
||||
},
|
||||
RetReplacement::Unit => {
|
||||
RetReplacement::Block => {
|
||||
span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
|
||||
db.span_suggestion(
|
||||
ret_span,
|
||||
"replace `return` with the unit type",
|
||||
"()".to_string(),
|
||||
"replace `return` with an empty block",
|
||||
"{}".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
});
|
||||
|
@ -317,7 +317,7 @@ fn attr_is_cfg(attr: &ast::Attribute) -> bool {
|
|||
|
||||
// get the def site
|
||||
fn get_def(span: Span) -> Option<Span> {
|
||||
span.ctxt().outer_expn_info().and_then(|info| info.def_site)
|
||||
span.ctxt().outer_expn_info().and_then(|info| Some(info.def_site))
|
||||
}
|
||||
|
||||
// is this expr a `()` unit?
|
||||
|
|
|
@ -20,6 +20,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let x = 1;
|
||||
/// let x = &x;
|
||||
/// ```
|
||||
pub SHADOW_SAME,
|
||||
|
@ -41,10 +42,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = 2;
|
||||
/// let x = x + 1;
|
||||
/// ```
|
||||
/// use different variable name:
|
||||
/// ```rust
|
||||
/// let x = 2;
|
||||
/// let y = x + 1;
|
||||
/// ```
|
||||
pub SHADOW_REUSE,
|
||||
|
@ -67,6 +70,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let y = 1;
|
||||
/// # let z = 2;
|
||||
/// let x = y;
|
||||
/// let x = z; // shadows the earlier binding
|
||||
/// ```
|
||||
|
|
|
@ -19,6 +19,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use core::iter::repeat;
|
||||
/// # let len = 4;
|
||||
/// let mut vec1 = Vec::with_capacity(len);
|
||||
/// vec1.resize(len, 0);
|
||||
///
|
||||
|
|
|
@ -48,7 +48,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// ```rust
|
||||
/// let x = "Hello".to_owned();
|
||||
/// x + ", World"
|
||||
/// x + ", World";
|
||||
/// ```
|
||||
pub STRING_ADD,
|
||||
restriction,
|
||||
|
@ -138,6 +138,9 @@ fn is_add(cx: &LateContext<'_, '_>, src: &Expr, target: &Expr) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
// Max length a b"foo" string can take
|
||||
const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
|
||||
|
||||
declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
|
||||
|
@ -173,6 +176,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
|
|||
);
|
||||
} else if callsite == expanded
|
||||
&& lit_content.as_str().chars().all(|c| c.is_ascii())
|
||||
&& lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
|
||||
&& !in_macro_or_desugar(args[0].span)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -20,12 +20,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let mut a = 42;
|
||||
/// let mut b = 1337;
|
||||
///
|
||||
/// let t = b;
|
||||
/// b = a;
|
||||
/// a = t;
|
||||
/// ```
|
||||
/// Use std::mem::swap():
|
||||
/// ```rust
|
||||
/// let mut a = 1;
|
||||
/// let mut b = 2;
|
||||
/// std::mem::swap(&mut a, &mut b);
|
||||
/// ```
|
||||
pub MANUAL_SWAP,
|
||||
|
|
77
clippy_lints/src/trait_bounds.rs
Normal file
77
clippy_lints/src/trait_bounds.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::utils::{in_macro, snippet, span_help_and_lint, SpanlessHash};
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
use rustc::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TraitBounds;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** This lint warns about unnecessary type repetitions in trait bounds
|
||||
///
|
||||
/// **Why is this bad?** Repeating the type for every bound makes the code
|
||||
/// less readable than combining the bounds
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
|
||||
/// ```
|
||||
///
|
||||
/// Could be written as:
|
||||
///
|
||||
/// ```rust
|
||||
/// pub fn foo<T>(t: T) where T: Copy + Clone {}
|
||||
/// ```
|
||||
pub TYPE_REPETITION_IN_BOUNDS,
|
||||
complexity,
|
||||
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TraitBounds {
|
||||
fn check_generics(&mut self, cx: &LateContext<'a, 'tcx>, gen: &'tcx Generics) {
|
||||
if in_macro(gen.span) {
|
||||
return;
|
||||
}
|
||||
let hash = |ty| -> u64 {
|
||||
let mut hasher = SpanlessHash::new(cx, cx.tables);
|
||||
hasher.hash_ty(ty);
|
||||
hasher.finish()
|
||||
};
|
||||
let mut map = FxHashMap::default();
|
||||
for bound in &gen.where_clause.predicates {
|
||||
if let WherePredicate::BoundPredicate(ref p) = bound {
|
||||
let h = hash(&p.bounded_ty);
|
||||
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()) {
|
||||
let mut hint_string = format!(
|
||||
"consider combining the bounds: `{}:",
|
||||
snippet(cx, p.bounded_ty.span, "_")
|
||||
);
|
||||
for b in v.iter() {
|
||||
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
|
||||
let path = &poly_trait_ref.trait_ref.path;
|
||||
hint_string.push_str(&format!(" {} +", path));
|
||||
}
|
||||
}
|
||||
for b in p.bounds.iter() {
|
||||
if let GenericBound::Trait(ref poly_trait_ref, _) = b {
|
||||
let path = &poly_trait_ref.trait_ref.path;
|
||||
hint_string.push_str(&format!(" {} +", path));
|
||||
}
|
||||
}
|
||||
hint_string.truncate(hint_string.len() - 2);
|
||||
hint_string.push('`');
|
||||
span_help_and_lint(
|
||||
cx,
|
||||
TYPE_REPETITION_IN_BOUNDS,
|
||||
p.span,
|
||||
"this type has already been used as a bound predicate",
|
||||
&hint_string,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -36,8 +36,8 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// core::intrinsics::transmute(t) // where the result type is the same as `t`'s
|
||||
/// ```rust,ignore
|
||||
/// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
|
||||
/// ```
|
||||
pub USELESS_TRANSMUTE,
|
||||
complexity,
|
||||
|
@ -53,7 +53,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// core::intrinsics::transmute(t) // where the result type is the same as
|
||||
/// // `*t` or `&t`'s
|
||||
/// ```
|
||||
|
@ -70,8 +70,10 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: &T = std::mem::transmute(p); // where p: *const T
|
||||
/// ```rust,ignore
|
||||
/// unsafe {
|
||||
/// let _: &T = std::mem::transmute(p); // where p: *const T
|
||||
/// }
|
||||
///
|
||||
/// // can be written:
|
||||
/// let _: &T = &*p;
|
||||
|
@ -99,7 +101,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: char = std::mem::transmute(x); // where x: u32
|
||||
/// let x = 1_u32;
|
||||
/// unsafe {
|
||||
/// let _: char = std::mem::transmute(x); // where x: u32
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _ = std::char::from_u32(x).unwrap();
|
||||
|
@ -127,7 +132,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: &str = std::mem::transmute(b); // where b: &[u8]
|
||||
/// let b: &[u8] = &[1_u8, 2_u8];
|
||||
/// unsafe {
|
||||
/// let _: &str = std::mem::transmute(b); // where b: &[u8]
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _ = std::str::from_utf8(b).unwrap();
|
||||
|
@ -146,7 +154,10 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: bool = std::mem::transmute(x); // where x: u8
|
||||
/// let x = 1_u8;
|
||||
/// unsafe {
|
||||
/// let _: bool = std::mem::transmute(x); // where x: u8
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _: bool = x != 0;
|
||||
|
@ -166,10 +177,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _: f32 = std::mem::transmute(x); // where x: u32
|
||||
/// unsafe {
|
||||
/// let _: f32 = std::mem::transmute(1_u32); // where x: u32
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _: f32 = f32::from_bits(x);
|
||||
/// let _: f32 = f32::from_bits(1_u32);
|
||||
/// ```
|
||||
pub TRANSMUTE_INT_TO_FLOAT,
|
||||
complexity,
|
||||
|
@ -195,7 +208,7 @@ declare_clippy_lint! {
|
|||
/// let _: &f32 = std::mem::transmute(&1u32);
|
||||
/// }
|
||||
/// // These can be respectively written:
|
||||
/// let _ = ptr as *const f32
|
||||
/// let _ = ptr as *const f32;
|
||||
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
|
||||
/// ```
|
||||
pub TRANSMUTE_PTR_TO_PTR,
|
||||
|
|
|
@ -39,14 +39,15 @@ declare_clippy_lint! {
|
|||
/// each other.
|
||||
///
|
||||
/// **Example:**
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo(v: &u32) {
|
||||
/// assert_eq!(v, 42);
|
||||
/// }
|
||||
/// // should be
|
||||
/// fn foo(v: u32) {
|
||||
/// assert_eq!(v, 42);
|
||||
/// }
|
||||
/// // Bad
|
||||
/// fn foo(v: &u32) {}
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// // Better
|
||||
/// fn foo(v: u32) {}
|
||||
/// ```
|
||||
pub TRIVIALLY_COPY_PASS_BY_REF,
|
||||
perf,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::utils::{match_qpath, paths, snippet, span_lint_and_sugg};
|
||||
use crate::utils::{in_macro_or_desugar, match_qpath, paths, snippet, snippet_with_macro_callsite, span_lint_and_sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc::hir::*;
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
|
||||
|
@ -67,10 +67,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TryErr {
|
|||
|
||||
then {
|
||||
let err_type = cx.tables.expr_ty(err_arg);
|
||||
let suggestion = if err_type == return_type {
|
||||
format!("return Err({})", snippet(cx, err_arg.span, "_"))
|
||||
let origin_snippet = if in_macro_or_desugar(err_arg.span) {
|
||||
snippet_with_macro_callsite(cx, err_arg.span, "_")
|
||||
} else {
|
||||
format!("return Err({}.into())", snippet(cx, err_arg.span, "_"))
|
||||
snippet(cx, err_arg.span, "_")
|
||||
};
|
||||
let suggestion = if err_type == return_type {
|
||||
format!("return Err({})", origin_snippet)
|
||||
} else {
|
||||
format!("return Err({}.into())", origin_snippet)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
|
|
|
@ -25,7 +25,7 @@ use crate::utils::paths;
|
|||
use crate::utils::{
|
||||
clip, comparisons, differing_macro_contexts, higher, in_constant, in_macro_or_desugar, int_bits, last_path_segment,
|
||||
match_def_path, match_path, multispan_sugg, same_tys, sext, snippet, snippet_opt, snippet_with_applicability,
|
||||
span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, unsext,
|
||||
snippet_with_macro_callsite, span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then, unsext,
|
||||
};
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -133,7 +133,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = LinkedList::new();
|
||||
/// # use std::collections::LinkedList;
|
||||
/// let x: LinkedList<usize> = LinkedList::new();
|
||||
/// ```
|
||||
pub LINKEDLIST,
|
||||
pedantic,
|
||||
|
@ -467,15 +468,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnitValue {
|
|||
if higher::is_from_for_desugar(local) {
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
LET_UNIT_VALUE,
|
||||
stmt.span,
|
||||
&format!(
|
||||
"this let-binding has unit value. Consider omitting `let {} =`",
|
||||
snippet(cx, local.pat.span, "..")
|
||||
),
|
||||
);
|
||||
span_lint_and_then(cx, LET_UNIT_VALUE, stmt.span, "this let-binding has unit value", |db| {
|
||||
if let Some(expr) = &local.init {
|
||||
let snip = snippet_with_macro_callsite(cx, expr.span, "()");
|
||||
db.span_suggestion(
|
||||
stmt.span,
|
||||
"omit the `let` binding",
|
||||
format!("{};", snip),
|
||||
Applicability::MachineApplicable, // snippet
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -557,7 +560,7 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// foo({
|
||||
/// let a = bar();
|
||||
/// baz(a);
|
||||
|
@ -621,9 +624,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
|
|||
}
|
||||
|
||||
fn is_questionmark_desugar_marked_call(expr: &Expr) -> bool {
|
||||
use syntax_pos::hygiene::CompilerDesugaringKind;
|
||||
use syntax_pos::hygiene::DesugaringKind;
|
||||
if let ExprKind::Call(ref callee, _) = expr.node {
|
||||
callee.span.is_compiler_desugaring(CompilerDesugaringKind::QuestionMark)
|
||||
callee.span.is_desugaring(DesugaringKind::QuestionMark)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -660,8 +663,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x = u64::MAX;
|
||||
/// x as f64
|
||||
/// let x = std::u64::MAX;
|
||||
/// x as f64;
|
||||
/// ```
|
||||
pub CAST_PRECISION_LOSS,
|
||||
pedantic,
|
||||
|
@ -682,7 +685,7 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let y: i8 = -1;
|
||||
/// y as u128 // will return 18446744073709551615
|
||||
/// y as u128; // will return 18446744073709551615
|
||||
/// ```
|
||||
pub CAST_SIGN_LOSS,
|
||||
pedantic,
|
||||
|
@ -690,7 +693,7 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for on casts between numerical types that may
|
||||
/// **What it does:** Checks for casts between numerical types that may
|
||||
/// truncate large values. This is expected behavior, so the cast is `Allow` by
|
||||
/// default.
|
||||
///
|
||||
|
@ -727,7 +730,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// u32::MAX as i32 // will yield a value of `-1`
|
||||
/// std::u32::MAX as i32; // will yield a value of `-1`
|
||||
/// ```
|
||||
pub CAST_POSSIBLE_WRAP,
|
||||
pedantic,
|
||||
|
@ -735,7 +738,7 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// **What it does:** Checks for on casts between numerical types that may
|
||||
/// **What it does:** Checks for casts between numerical types that may
|
||||
/// be replaced by safe conversion functions.
|
||||
///
|
||||
/// **Why is this bad?** Rust's `as` keyword will perform many kinds of
|
||||
|
@ -775,7 +778,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let _ = 2i32 as i32
|
||||
/// let _ = 2i32 as i32;
|
||||
/// ```
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
|
@ -789,7 +792,8 @@ declare_clippy_lint! {
|
|||
/// **Why is this bad?** Dereferencing the resulting pointer may be undefined
|
||||
/// behavior.
|
||||
///
|
||||
/// **Known problems:** None.
|
||||
/// **Known problems:** Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
|
||||
/// on the resulting pointer is fine.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
|
@ -1210,17 +1214,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts {
|
|||
if_chain! {
|
||||
if let ty::RawPtr(from_ptr_ty) = &cast_from.sty;
|
||||
if let ty::RawPtr(to_ptr_ty) = &cast_to.sty;
|
||||
if let Some(from_align) = cx.layout_of(from_ptr_ty.ty).ok().map(|a| a.align.abi);
|
||||
if let Some(to_align) = cx.layout_of(to_ptr_ty.ty).ok().map(|a| a.align.abi);
|
||||
if from_align < to_align;
|
||||
if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
|
||||
if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
|
||||
if from_layout.align.abi < to_layout.align.abi;
|
||||
// with c_void, we inherently need to trust the user
|
||||
if !is_c_void(cx, from_ptr_ty.ty);
|
||||
// when casting from a ZST, we don't know enough to properly lint
|
||||
if !from_layout.is_zst();
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
CAST_PTR_ALIGNMENT,
|
||||
expr.span,
|
||||
&format!("casting from `{}` to a more-strictly-aligned pointer (`{}`)", cast_from, cast_to)
|
||||
&format!(
|
||||
"casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
|
||||
cast_from,
|
||||
cast_to,
|
||||
from_layout.align.abi.bytes(),
|
||||
to_layout.align.abi.bytes(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1242,7 +1254,7 @@ fn lint_fn_to_numeric_cast(
|
|||
}
|
||||
match cast_from.sty {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
|
||||
|
||||
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
|
||||
|
@ -1286,6 +1298,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::rc::Rc;
|
||||
/// struct Foo {
|
||||
/// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
|
||||
/// }
|
||||
|
@ -1344,7 +1357,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeComplexity {
|
|||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
|
||||
match item.node {
|
||||
ImplItemKind::Const(ref ty, _) | ImplItemKind::Type(ref ty) => self.check_type(cx, ty),
|
||||
ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty),
|
||||
// methods are covered by check_fn
|
||||
_ => (),
|
||||
}
|
||||
|
@ -1449,13 +1462,13 @@ declare_clippy_lint! {
|
|||
/// **Known problems:** None.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// 'x' as u8
|
||||
/// ```
|
||||
///
|
||||
/// A better version, using the byte literal:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// b'x'
|
||||
/// ```
|
||||
pub CHAR_LIT_AS_U8,
|
||||
|
@ -1679,7 +1692,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// let x : u8 = ...; (x as u32) > 300
|
||||
/// let x: u8 = 1;
|
||||
/// (x as u32) > 300;
|
||||
/// ```
|
||||
pub INVALID_UPCAST_COMPARISONS,
|
||||
pedantic,
|
||||
|
@ -1716,10 +1730,10 @@ impl PartialEq for FullInt {
|
|||
impl PartialOrd for FullInt {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(match (self, other) {
|
||||
(&FullInt::S(s), &FullInt::S(o)) => s.cmp(&o),
|
||||
(&FullInt::U(s), &FullInt::U(o)) => s.cmp(&o),
|
||||
(&FullInt::S(s), &FullInt::U(o)) => Self::cmp_s_u(s, o),
|
||||
(&FullInt::U(s), &FullInt::S(o)) => Self::cmp_s_u(o, s).reverse(),
|
||||
(&Self::S(s), &Self::S(o)) => s.cmp(&o),
|
||||
(&Self::U(s), &Self::U(o)) => s.cmp(&o),
|
||||
(&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
|
||||
(&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1914,12 +1928,21 @@ declare_clippy_lint! {
|
|||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # use std::hash::Hash;
|
||||
/// # use std::hash::{Hash, BuildHasher};
|
||||
/// # trait Serialize {};
|
||||
/// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
|
||||
///
|
||||
/// pub fn foo(map: &mut HashMap<i32, i32>) { }
|
||||
/// ```
|
||||
/// could be rewritten as
|
||||
/// ```rust
|
||||
/// # use std::collections::HashMap;
|
||||
/// # use std::hash::{Hash, BuildHasher};
|
||||
/// # trait Serialize {};
|
||||
/// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
|
||||
///
|
||||
/// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
|
||||
/// ```
|
||||
pub IMPLICIT_HASHER,
|
||||
style,
|
||||
"missing generalization over different hashers"
|
||||
|
@ -2029,6 +2052,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher {
|
|||
vis.visit_ty(ty);
|
||||
|
||||
for target in &vis.found {
|
||||
if in_external_macro(cx.sess(), generics.span) {
|
||||
continue;
|
||||
}
|
||||
let generics_suggestion_span = generics.span.substitute_dummy({
|
||||
let pos = snippet_opt(cx, item.span.until(body.arguments[0].pat.span))
|
||||
.and_then(|snip| {
|
||||
|
|
|
@ -14,10 +14,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Why is this bad?** Using `if let` or `match` is more idiomatic.
|
||||
///
|
||||
/// **Known problems:** Limitations of the borrow checker might make unwrap() necessary sometimes?
|
||||
/// **Known problems:** None
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let option = Some(0);
|
||||
/// # fn do_something_with(_x: usize) {}
|
||||
/// if option.is_some() {
|
||||
/// do_something_with(option.unwrap())
|
||||
/// }
|
||||
|
@ -26,12 +28,14 @@ declare_clippy_lint! {
|
|||
/// Could be written:
|
||||
///
|
||||
/// ```rust
|
||||
/// # let option = Some(0);
|
||||
/// # fn do_something_with(_x: usize) {}
|
||||
/// if let Some(value) = option {
|
||||
/// do_something_with(value)
|
||||
/// }
|
||||
/// ```
|
||||
pub UNNECESSARY_UNWRAP,
|
||||
nursery,
|
||||
complexity,
|
||||
"checks for calls of unwrap[_err]() that cannot fail"
|
||||
}
|
||||
|
||||
|
@ -45,6 +49,8 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let option = Some(0);
|
||||
/// # fn do_something_with(_x: usize) {}
|
||||
/// if option.is_none() {
|
||||
/// do_something_with(option.unwrap())
|
||||
/// }
|
||||
|
@ -52,7 +58,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// This code will always panic. The if condition should probably be inverted.
|
||||
pub PANICKING_UNWRAP,
|
||||
nursery,
|
||||
correctness,
|
||||
"checks for calls of unwrap[_err]() that will always fail"
|
||||
}
|
||||
|
||||
|
|
|
@ -51,9 +51,11 @@ declare_lint_pass!(UseSelf => [USE_SELF]);
|
|||
|
||||
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
|
||||
|
||||
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path) {
|
||||
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path, last_segment: Option<&PathSegment>) {
|
||||
let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
|
||||
|
||||
// Path segments only include actual path, no methods or fields.
|
||||
let last_path_span = path.segments.last().expect(SEGMENTS_MSG).ident.span;
|
||||
let last_path_span = last_segment.ident.span;
|
||||
// Only take path up to the end of last_path_span.
|
||||
let span = path.span.with_hi(last_path_span.hi());
|
||||
|
||||
|
@ -80,22 +82,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TraitImplTyVisitor<'a, 'tcx> {
|
|||
let trait_ty = self.trait_type_walker.next();
|
||||
let impl_ty = self.impl_type_walker.next();
|
||||
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node {
|
||||
if_chain! {
|
||||
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node;
|
||||
|
||||
// The implementation and trait types don't match which means that
|
||||
// the concrete type was specified by the implementation
|
||||
if impl_ty != trait_ty {
|
||||
if let Some(impl_ty) = impl_ty {
|
||||
if self.item_type == impl_ty {
|
||||
let is_self_ty = if let def::Res::SelfTy(..) = path.res {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !is_self_ty {
|
||||
span_use_self_lint(self.cx, path);
|
||||
}
|
||||
}
|
||||
if impl_ty != trait_ty;
|
||||
if let Some(impl_ty) = impl_ty;
|
||||
if self.item_type == impl_ty;
|
||||
then {
|
||||
match path.res {
|
||||
def::Res::SelfTy(..) => {},
|
||||
_ => span_use_self_lint(self.cx, path, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,15 +218,34 @@ struct UseSelfVisitor<'a, 'tcx> {
|
|||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
|
||||
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
|
||||
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
|
||||
if self.item_path.res == path.res {
|
||||
span_use_self_lint(self.cx, path);
|
||||
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_did) = path.res {
|
||||
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_did) {
|
||||
span_use_self_lint(self.cx, path);
|
||||
if path.segments.len() >= 2 {
|
||||
let last_but_one = &path.segments[path.segments.len() - 2];
|
||||
if last_but_one.ident.name != kw::SelfUpper {
|
||||
let enum_def_id = match path.res {
|
||||
Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
|
||||
Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
|
||||
let variant_def_id = self.cx.tcx.parent(ctor_def_id);
|
||||
variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if self.item_path.res.opt_def_id() == enum_def_id {
|
||||
span_use_self_lint(self.cx, path, Some(last_but_one));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
|
||||
if self.item_path.res == path.res {
|
||||
span_use_self_lint(self.cx, path, None);
|
||||
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_def_id) = path.res {
|
||||
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
|
||||
span_use_self_lint(self.cx, path, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walk_path(self, path);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ declare_clippy_lint! {
|
|||
/// **What it does:** Generates clippy code that detects the offending pattern
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// // ./tests/ui/my_lint.rs
|
||||
/// fn foo() {
|
||||
/// // detect the following pattern
|
||||
|
@ -24,13 +24,14 @@ declare_clippy_lint! {
|
|||
/// // but ignore everything from here on
|
||||
/// #![clippy::author = "ignore"]
|
||||
/// }
|
||||
/// ()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Running `TESTNAME=ui/my_lint cargo uitest` will produce
|
||||
/// a `./tests/ui/new_lint.stdout` file with the generated code:
|
||||
///
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// // ./tests/ui/new_lint.stdout
|
||||
/// if_chain! {
|
||||
/// if let ExprKind::If(ref cond, ref then, None) = item.node,
|
||||
|
|
|
@ -44,15 +44,15 @@ pub enum Error {
|
|||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::Io(ref err) => err.fmt(f),
|
||||
Error::Toml(ref err) => err.fmt(f),
|
||||
Self::Io(ref err) => err.fmt(f),
|
||||
Self::Toml(ref err) => err.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Error::Io(e)
|
||||
Self::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -438,9 +438,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(fun);
|
||||
self.hash_exprs(args);
|
||||
},
|
||||
ExprKind::Cast(ref e, ref _ty) | ExprKind::Type(ref e, ref _ty) => {
|
||||
ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
|
||||
self.hash_expr(e);
|
||||
// TODO: _ty
|
||||
self.hash_ty(ty);
|
||||
},
|
||||
ExprKind::Closure(cap, _, eid, _, _) => {
|
||||
match cap {
|
||||
|
@ -512,7 +512,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_expr(e);
|
||||
}
|
||||
},
|
||||
ExprKind::Tup(ref v) | ExprKind::Array(ref v) => {
|
||||
ExprKind::Tup(ref tup) => {
|
||||
self.hash_exprs(tup);
|
||||
},
|
||||
ExprKind::Array(ref v) => {
|
||||
self.hash_exprs(v);
|
||||
},
|
||||
ExprKind::Unary(lop, ref le) => {
|
||||
|
@ -574,4 +577,100 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
|
||||
std::mem::discriminant(&lifetime.name).hash(&mut self.s);
|
||||
if let LifetimeName::Param(ref name) = lifetime.name {
|
||||
std::mem::discriminant(name).hash(&mut self.s);
|
||||
match name {
|
||||
ParamName::Plain(ref ident) => {
|
||||
ident.name.hash(&mut self.s);
|
||||
},
|
||||
ParamName::Fresh(ref size) => {
|
||||
size.hash(&mut self.s);
|
||||
},
|
||||
ParamName::Error => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_ty(&mut self, ty: &Ty) {
|
||||
self.hash_tykind(&ty.node);
|
||||
}
|
||||
|
||||
pub fn hash_tykind(&mut self, ty: &TyKind) {
|
||||
std::mem::discriminant(ty).hash(&mut self.s);
|
||||
match ty {
|
||||
TyKind::Slice(ty) => {
|
||||
self.hash_ty(ty);
|
||||
},
|
||||
TyKind::Array(ty, anon_const) => {
|
||||
self.hash_ty(ty);
|
||||
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
|
||||
},
|
||||
TyKind::Ptr(mut_ty) => {
|
||||
self.hash_ty(&mut_ty.ty);
|
||||
mut_ty.mutbl.hash(&mut self.s);
|
||||
},
|
||||
TyKind::Rptr(lifetime, mut_ty) => {
|
||||
self.hash_lifetime(lifetime);
|
||||
self.hash_ty(&mut_ty.ty);
|
||||
mut_ty.mutbl.hash(&mut self.s);
|
||||
},
|
||||
TyKind::BareFn(bfn) => {
|
||||
bfn.unsafety.hash(&mut self.s);
|
||||
bfn.abi.hash(&mut self.s);
|
||||
for arg in &bfn.decl.inputs {
|
||||
self.hash_ty(&arg);
|
||||
}
|
||||
match bfn.decl.output {
|
||||
FunctionRetTy::DefaultReturn(_) => {
|
||||
().hash(&mut self.s);
|
||||
},
|
||||
FunctionRetTy::Return(ref ty) => {
|
||||
self.hash_ty(ty);
|
||||
},
|
||||
}
|
||||
bfn.decl.c_variadic.hash(&mut self.s);
|
||||
},
|
||||
TyKind::Tup(ty_list) => {
|
||||
for ty in ty_list {
|
||||
self.hash_ty(ty);
|
||||
}
|
||||
},
|
||||
TyKind::Path(qpath) => match qpath {
|
||||
QPath::Resolved(ref maybe_ty, ref path) => {
|
||||
if let Some(ref ty) = maybe_ty {
|
||||
self.hash_ty(ty);
|
||||
}
|
||||
for segment in &path.segments {
|
||||
segment.ident.name.hash(&mut self.s);
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ref ty, ref segment) => {
|
||||
self.hash_ty(ty);
|
||||
segment.ident.name.hash(&mut self.s);
|
||||
},
|
||||
},
|
||||
TyKind::Def(_, arg_list) => {
|
||||
for arg in arg_list {
|
||||
match arg {
|
||||
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
|
||||
GenericArg::Type(ref ty) => self.hash_ty(&ty),
|
||||
GenericArg::Const(ref ca) => {
|
||||
self.hash_expr(&self.cx.tcx.hir().body(ca.value.body).value);
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
TyKind::TraitObject(_, lifetime) => {
|
||||
self.hash_lifetime(lifetime);
|
||||
},
|
||||
TyKind::Typeof(anon_const) => {
|
||||
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
|
||||
},
|
||||
TyKind::CVarArgs(lifetime) => self.hash_lifetime(lifetime),
|
||||
TyKind::Err | TyKind::Infer | TyKind::Never => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,14 +13,14 @@ declare_clippy_lint! {
|
|||
/// attribute
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// #[clippy::dump]
|
||||
/// extern crate foo;
|
||||
/// ```
|
||||
///
|
||||
/// prints
|
||||
///
|
||||
/// ```
|
||||
/// ```text
|
||||
/// item `foo`
|
||||
/// visibility inherited from outer item
|
||||
/// extern crate dylib source: "/path/to/foo.so"
|
||||
|
@ -63,8 +63,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DeepCodeInspector {
|
|||
print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
|
||||
},
|
||||
hir::ImplItemKind::Method(..) => println!("method"),
|
||||
hir::ImplItemKind::Type(_) => println!("associated type"),
|
||||
hir::ImplItemKind::Existential(_) => println!("existential type"),
|
||||
hir::ImplItemKind::TyAlias(_) => println!("associated type"),
|
||||
hir::ImplItemKind::OpaqueTy(_) => println!("existential type"),
|
||||
}
|
||||
}
|
||||
// fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx
|
||||
|
@ -360,10 +360,10 @@ fn print_item(cx: &LateContext<'_, '_>, item: &hir::Item) {
|
|||
hir::ItemKind::Mod(..) => println!("module"),
|
||||
hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi),
|
||||
hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm),
|
||||
hir::ItemKind::Ty(..) => {
|
||||
hir::ItemKind::TyAlias(..) => {
|
||||
println!("type alias for {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Existential(..) => {
|
||||
hir::ItemKind::OpaqueTy(..) => {
|
||||
println!("existential type with real type {:?}", cx.tcx.type_of(did));
|
||||
},
|
||||
hir::ItemKind::Enum(..) => {
|
||||
|
|
|
@ -38,7 +38,7 @@ declare_clippy_lint! {
|
|||
/// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
|
||||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// declare_lint! { pub LINT_1, ... }
|
||||
/// declare_lint! { pub LINT_2, ... }
|
||||
/// declare_lint! { pub FORGOTTEN_LINT, ... }
|
||||
|
@ -62,12 +62,12 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// cx.span_lint(LINT_NAME, "message");
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// utils::span_lint(cx, LINT_NAME, "message");
|
||||
/// ```
|
||||
pub COMPILER_LINT_FUNCTIONS,
|
||||
|
@ -85,17 +85,17 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// Bad:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// expr.span.ctxt().outer().expn_info()
|
||||
/// ```
|
||||
///
|
||||
/// Good:
|
||||
/// ```rust
|
||||
/// ```rust,ignore
|
||||
/// expr.span.ctxt().outer_expn_info()
|
||||
/// ```
|
||||
pub OUTER_EXPN_INFO,
|
||||
pub OUTER_EXPN_EXPN_INFO,
|
||||
internal,
|
||||
"using `cx.outer().expn_info()` instead of `cx.outer_expn_info()`"
|
||||
"using `cx.outer_expn().expn_info()` instead of `cx.outer_expn_info()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
|
||||
|
@ -280,7 +280,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
|
|||
|
||||
pub struct OuterExpnInfoPass;
|
||||
|
||||
impl_lint_pass!(OuterExpnInfoPass => [OUTER_EXPN_INFO]);
|
||||
impl_lint_pass!(OuterExpnInfoPass => [OUTER_EXPN_EXPN_INFO]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
|
||||
|
@ -288,7 +288,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass {
|
|||
let method_names: Vec<LocalInternedString> = method_names.iter().map(|s| s.as_str()).collect();
|
||||
let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect();
|
||||
if_chain! {
|
||||
if let ["expn_info", "outer"] = method_names.as_slice();
|
||||
if let ["expn_info", "outer_expn"] = method_names.as_slice();
|
||||
let args = arg_lists[1];
|
||||
if args.len() == 1;
|
||||
let self_arg = &args[0];
|
||||
|
@ -297,9 +297,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass {
|
|||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
OUTER_EXPN_INFO,
|
||||
OUTER_EXPN_EXPN_INFO,
|
||||
expr.span.trim_start(self_arg.span).unwrap_or(expr.span),
|
||||
"usage of `outer().expn_info()`",
|
||||
"usage of `outer_expn().expn_info()`",
|
||||
"try",
|
||||
".outer_expn_info()".to_string(),
|
||||
Applicability::MachineApplicable,
|
||||
|
|
|
@ -43,7 +43,7 @@ use rustc_errors::Applicability;
|
|||
use smallvec::SmallVec;
|
||||
use syntax::ast::{self, LitKind};
|
||||
use syntax::attr;
|
||||
use syntax::ext::hygiene::ExpnFormat;
|
||||
use syntax::ext::hygiene::ExpnKind;
|
||||
use syntax::source_map::{Span, DUMMY_SP};
|
||||
use syntax::symbol::{kw, Symbol};
|
||||
|
||||
|
@ -100,7 +100,7 @@ pub fn in_macro_or_desugar(span: Span) -> bool {
|
|||
/// Returns `true` if this `expn_info` was expanded by any macro.
|
||||
pub fn in_macro(span: Span) -> bool {
|
||||
if let Some(info) = span.ctxt().outer_expn_info() {
|
||||
if let ExpnFormat::CompilerDesugaring(..) = info.format {
|
||||
if let ExpnKind::Desugaring(..) = info.kind {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
|
@ -686,7 +686,7 @@ pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
|
|||
/// See also `is_direct_expn_of`.
|
||||
pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
|
||||
loop {
|
||||
let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.format.name(), ei.call_site));
|
||||
let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.kind.descr(), ei.call_site));
|
||||
|
||||
match span_name_span {
|
||||
Some((mac_name, new_span)) if mac_name.as_str() == name => return Some(new_span),
|
||||
|
@ -706,7 +706,7 @@ pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
|
|||
/// `bar!` by
|
||||
/// `is_direct_expn_of`.
|
||||
pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
|
||||
let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.format.name(), ei.call_site));
|
||||
let span_name_span = span.ctxt().outer_expn_info().map(|ei| (ei.kind.descr(), ei.call_site));
|
||||
|
||||
match span_name_span {
|
||||
Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span),
|
||||
|
|
|
@ -37,7 +37,6 @@ pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entr
|
|||
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
|
||||
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
|
||||
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
|
||||
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
|
||||
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
|
||||
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
|
||||
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
|
||||
|
@ -50,8 +49,6 @@ pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
|
|||
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
|
||||
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
|
||||
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
|
||||
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
|
||||
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
|
||||
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
|
||||
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
|
||||
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
|
||||
|
@ -109,7 +106,6 @@ pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_stri
|
|||
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
|
||||
pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
|
||||
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
|
||||
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
|
||||
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
|
||||
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
|
||||
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
|
||||
|
|
|
@ -77,6 +77,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// # let foo = "bar";
|
||||
/// println!("{:?}", foo);
|
||||
/// ```
|
||||
pub USE_DEBUG,
|
||||
|
@ -364,7 +365,9 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
|
|||
align: AlignUnknown,
|
||||
flags: 0,
|
||||
precision: CountImplied,
|
||||
precision_span: None,
|
||||
width: CountImplied,
|
||||
width_span: None,
|
||||
ty: "",
|
||||
};
|
||||
if !parser.eat(&token::Comma) {
|
||||
|
|
|
@ -15,7 +15,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// **Example:**
|
||||
/// ```rust
|
||||
/// 0.0f32 / 0.0
|
||||
/// 0.0f32 / 0.0;
|
||||
/// ```
|
||||
pub ZERO_DIVIDED_BY_ZERO,
|
||||
complexity,
|
||||
|
|
|
@ -345,16 +345,18 @@ list][lint_list].
|
|||
|
||||
### Running rustfmt
|
||||
|
||||
[Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust code according
|
||||
to style guidelines. Your code has to be formatted by `rustfmt` before a PR can be merged.
|
||||
[Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust
|
||||
code according to style guidelines. Your code has to be formatted by `rustfmt`
|
||||
before a PR can be merged. Clippy uses nightly `rustfmt` in the CI.
|
||||
|
||||
It can be installed via `rustup`:
|
||||
|
||||
```bash
|
||||
rustup component add rustfmt
|
||||
rustup component add rustfmt --toolchain=nightly
|
||||
```
|
||||
|
||||
Use `cargo fmt --all` to format the whole codebase.
|
||||
Use `./util/dev fmt` to format the whole codebase. Make sure that `rustfmt` is
|
||||
installed for the nightly toolchain.
|
||||
|
||||
### Debugging
|
||||
|
||||
|
@ -366,12 +368,14 @@ output in the `stdout` part.
|
|||
|
||||
Before submitting your PR make sure you followed all of the basic requirements:
|
||||
|
||||
<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
|
||||
|
||||
- [ ] Followed [lint naming conventions][lint_naming]
|
||||
- [ ] Added passing UI tests (including committed `.stderr` file)
|
||||
- [ ] `cargo test` passes locally
|
||||
- [ ] Executed `util/dev update_lints`
|
||||
- [ ] Executed `./util/dev update_lints`
|
||||
- [ ] Added lint documentation
|
||||
- [ ] Run `cargo fmt`
|
||||
- [ ] Run `./util/dev fmt`
|
||||
|
||||
### Cheatsheet
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue