Merge branch 'origin/master' into flat-map

This commit is contained in:
Jeremy Stucki 2019-08-11 19:22:40 +02:00
commit 05d9f884e1
No known key found for this signature in database
GPG key ID: EEFCA93148042655
213 changed files with 3350 additions and 1588 deletions

View file

@ -16,9 +16,11 @@ checked during review or continuous integration.
- [ ] Followed [lint naming conventions][lint_naming] - [ ] Followed [lint naming conventions][lint_naming]
- [ ] Added passing UI tests (including committed `.stderr` file) - [ ] Added passing UI tests (including committed `.stderr` file)
- [ ] `cargo test` passes locally - [ ] `cargo test` passes locally
- [ ] Executed `util/dev update_lints` - [ ] Executed `./util/dev update_lints`
- [ ] Added lint documentation - [ ] 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 Note that you can skip the above if you are just opening a WIP PR in
order to get feedback. order to get feedback.

View file

@ -17,6 +17,7 @@ branches:
env: env:
global: global:
- RUST_BACKTRACE=1 - RUST_BACKTRACE=1
- secure: "OKulfkA5OGd/d1IhvBKzRkHQwMcWjzrzbimo7+5NhkUkWxndAzl+719TB3wWvIh1i2wXXrEXsyZkXM5FtRrHm55v1VKQ5ibjEvFg1w3NIg81iDyoLq186fLqywvxGkOAFPrsePPsBj5USd5xvhwwbrjO6L7/RK6Z8shBwOSc41s="
install: install:
- | - |
@ -45,7 +46,7 @@ matrix:
- os: linux - os: linux
env: BASE_TESTS=true env: BASE_TESTS=true
- os: windows - 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 # 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 # 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) if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try)
- env: INTEGRATION=chronotope/chrono - env: INTEGRATION=chronotope/chrono
if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try) 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 # prevent these jobs with default env vars
exclude: exclude:
- os: linux - os: linux
@ -91,10 +89,28 @@ matrix:
- os: windows - os: windows
script: 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 rm rust-toolchain
./setup-toolchain.sh ./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 if [ -z ${INTEGRATION} ]; then
travis_wait 30 ./ci/base-tests.sh && sleep 5 travis_wait 30 ./ci/base-tests.sh && sleep 5
@ -104,7 +120,7 @@ script:
after_success: | after_success: |
#!/bin/bash #!/bin/bash
if [ $(uname) == Linux ]; then if [ "$TRAVIS_OS_NAME" == "linux" ]; then
set -ex set -ex
if [ -z ${INTEGRATION} ]; then if [ -z ${INTEGRATION} ]; then
./.github/deploy.sh ./.github/deploy.sh

View file

@ -974,6 +974,8 @@ Released 2018-09-13
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask [`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 [`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 [`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_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 [`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 [`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 [`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_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 [`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_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 [`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 [`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 [`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 [`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 [`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_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 [`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 [`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 [`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 [`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_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 [`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 [`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 [`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg

View file

@ -61,4 +61,5 @@ rustc-workspace-hack = "1.0.0"
rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"} rustc_tools_util = { version = "0.2.0", path = "rustc_tools_util"}
[features] [features]
deny-warnings = []
debugging = [] debugging = []

View file

@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. 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: 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) * [Usage instructions](#usage)
* [Configuration](#configuration) * [Configuration](#configuration)
* [Contributing](#contributing)
* [License](#license) * [License](#license)
## Usage ## Usage
@ -52,7 +53,7 @@ subcommand.
#### Step 1: Install rustup #### 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. us install Clippy and its dependencies.
If you already have rustup installed, update to ensure you have the latest 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 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 ### Running Clippy from the command line without installing it
To have cargo compile your crate with Clippy without Clippy installation 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 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):* *Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
### Travis CI ### 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. 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 ```yaml
language: rust language: rust
@ -121,12 +130,12 @@ rust:
- nightly - nightly
before_script: before_script:
- rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy - 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. 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 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.) line. (You can swap `clippy::all` with the specific lint category you are targeting.)
## Configuration ## 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 `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives. 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. 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 Copyright 2014-2019 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or 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 [https://www.apache.org/licenses/LICENSE-2.0](https://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 <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 option. All files in the project carrying such notice may not be
copied, modified, or distributed except according to those terms. copied, modified, or distributed except according to those terms.

View file

@ -8,11 +8,10 @@ environment:
- TARGET: x86_64-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc
branches: branches:
# Only build AppVeyor on r+, try and the master branch # Only build AppVeyor on r+ and try branch
only: only:
- auto - auto
- try - try
- master
install: install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/ - curl -sSf -o rustup-init.exe https://win.rustup.rs/
@ -23,6 +22,7 @@ install:
- del rust-toolchain - del rust-toolchain
- cargo install rustup-toolchain-install-master --debug || echo "rustup-toolchain-install-master already installed" - cargo install rustup-toolchain-install-master --debug || echo "rustup-toolchain-install-master already installed"
- rustup-toolchain-install-master %RUSTC_HASH% -f -n master - 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 - rustup default master
- set PATH=%PATH%;C:\Users\appveyor\.rustup\toolchains\master\bin - set PATH=%PATH%;C:\Users\appveyor\.rustup\toolchains\master\bin
- rustc -V - rustc -V

View file

@ -7,8 +7,8 @@ if [ "$TRAVIS_OS_NAME" == "linux" ]; then
remark -f *.md -f doc/*.md > /dev/null remark -f *.md -f doc/*.md > /dev/null
fi fi
# build clippy in debug mode and run tests # build clippy in debug mode and run tests
cargo build --features debugging cargo build --features "debugging deny-warnings"
cargo test --features debugging cargo test --features "debugging deny-warnings"
# for faster build, share target dir between subcrates # for faster build, share target dir between subcrates
export CARGO_TARGET_DIR=`pwd`/target/ export CARGO_TARGET_DIR=`pwd`/target/
(cd clippy_lints && cargo test) (cd clippy_lints && cargo test)
@ -24,21 +24,23 @@ export CARGO_TARGET_DIR=`pwd`/target/
# Perform various checks for lint registration # Perform various checks for lint registration
./util/dev update_lints --check ./util/dev update_lints --check
./util/dev --limit-stderr-length ./util/dev --limit-stderr-length
cargo +nightly fmt --all -- --check
# Check running clippy-driver without cargo # Check running clippy-driver without cargo
( (
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
# Check sysroot handling # Check sysroot handling
sysroot=$(./target/debug/clippy-driver --print sysroot) sysroot=$(./target/debug/clippy-driver --print sysroot)
test $sysroot = $(rustc --print sysroot) test $sysroot = $(rustc --print sysroot)
sysroot=$(./target/debug/clippy-driver --sysroot /tmp --print sysroot) if [ -z $OS_WINDOWS ]; then
test $sysroot = /tmp 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) sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
test $sysroot = /tmp test $sysroot = $desired_sysroot
# Make sure this isn't set - clippy-driver should cope without it # Make sure this isn't set - clippy-driver should cope without it
unset CARGO_MANIFEST_DIR unset CARGO_MANIFEST_DIR
@ -50,32 +52,3 @@ cargo +nightly fmt --all -- --check
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR # 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

View file

@ -9,4 +9,5 @@ clap = "2.33"
itertools = "0.8" itertools = "0.8"
regex = "1" regex = "1"
lazy_static = "1.0" lazy_static = "1.0"
shell-escape = "0.1"
walkdir = "2" walkdir = "2"

200
clippy_dev/src/fmt.rs Normal file
View 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)
}

View file

@ -4,6 +4,8 @@ extern crate regex;
use clap::{App, Arg, SubCommand}; use clap::{App, Arg, SubCommand};
use clippy_dev::*; use clippy_dev::*;
mod fmt;
mod stderr_length_check; mod stderr_length_check;
#[derive(PartialEq)] #[derive(PartialEq)]
@ -14,6 +16,21 @@ enum UpdateMode {
fn main() { fn main() {
let matches = App::new("Clippy developer tooling") 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(
SubCommand::with_name("update_lints") SubCommand::with_name("update_lints")
.about("Updates lint registration and information from the source code") .about("Updates lint registration and information from the source code")
@ -46,14 +63,21 @@ fn main() {
if matches.is_present("limit-stderr-length") { if matches.is_present("limit-stderr-length") {
stderr_length_check::check(); stderr_length_check::check();
} }
if let Some(matches) = matches.subcommand_matches("update_lints") {
if matches.is_present("print-only") { match matches.subcommand() {
print_lints(); ("fmt", Some(matches)) => {
} else if matches.is_present("check") { fmt::run(matches.is_present("check"), matches.is_present("verbose"));
update_lints(&UpdateMode::Check); },
} else { ("update_lints", Some(matches)) => {
update_lints(&UpdateMode::Change); if matches.is_present("print-only") {
} print_lints();
} else if matches.is_present("check") {
update_lints(&UpdateMode::Check);
} else {
update_lints(&UpdateMode::Change);
}
},
_ => {},
} }
} }

View file

@ -7,7 +7,7 @@ use std::io::prelude::*;
// The maximum length allowed for stderr files. // The maximum length allowed for stderr files.
// //
// We limit this because small files are easier to deal with than bigger 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() { pub fn check() {
let stderr_files = stderr_files(); 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> { fn exceeding_stderr_files(files: impl Iterator<Item = walkdir::DirEntry>) -> impl Iterator<Item = String> {
files files.filter_map(|file| {
.filter_map(|file| { let path = file.path().to_str().expect("Could not convert path to str").to_string();
let path = file.path().to_str().expect("Could not convert path to str").to_string(); let linecount = count_linenumbers(&path);
let linecount = count_linenumbers(&path); if linecount > LIMIT {
if linecount > LIMIT { Some(path)
Some(path) } else {
} else { None
None }
} })
})
} }
fn stderr_files() -> impl Iterator<Item = walkdir::DirEntry> { fn stderr_files() -> impl Iterator<Item = walkdir::DirEntry> {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_dummy" # rename to clippy before publishing name = "clippy_dummy" # rename to clippy before publishing
version = "0.0.302" version = "0.0.303"
authors = ["Manish Goregaokar <manishsmail@gmail.com>"] authors = ["Manish Goregaokar <manishsmail@gmail.com>"]
edition = "2018" edition = "2018"
readme = "crates-readme.md" readme = "crates-readme.md"

View file

@ -3,7 +3,7 @@ extern crate term;
fn main() { fn main() {
if let Err(_) = foo() { if let Err(_) = foo() {
eprintln!("error: Clippy is no longer available via crates.io\n"); 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); std::process::exit(1);
} }
@ -31,7 +31,7 @@ fn foo() -> Result<(), ()> {
write!(t, "please run `").map_err(|_| ())?; write!(t, "please run `").map_err(|_| ())?;
t.attr(term::Attr::Bold).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.reset().map_err(|_| ())?;
t.fg(term::color::WHITE).map_err(|_| ())?; t.fg(term::color::WHITE).map_err(|_| ())?;

View file

@ -1,7 +1,7 @@
Installing clippy via crates.io is deprecated. Please use the following: Installing clippy via crates.io is deprecated. Please use the following:
```terminal ```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. on a Rust version 1.29 or later. You may need to run `rustup self update` if it complains about a missing clippy binary.

View file

@ -27,7 +27,7 @@ semver = "0.9.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.5" toml = "0.5"
unicode-normalization = "0.1" unicode-normalization = "0.1"
pulldown-cmark = "0.5.2" pulldown-cmark = "0.5.3"
url = "1.7.0" url = "1.7.0"
if_chain = "1.0.0" if_chain = "1.0.0"
smallvec = { version = "0.6.5", features = ["union"] } smallvec = { version = "0.6.5", features = ["union"] }

View file

@ -16,7 +16,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// a + 1 /// # let a = 0;
/// a + 1;
/// ``` /// ```
pub INTEGER_ARITHMETIC, pub INTEGER_ARITHMETIC,
restriction, restriction,
@ -33,7 +34,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// a + 1.0 /// # let a = 0.0;
/// a + 1.0;
/// ``` /// ```
pub FLOAT_ARITHMETIC, pub FLOAT_ARITHMETIC,
restriction, restriction,

View file

@ -45,7 +45,8 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let mut a = 5; /// let mut a = 5;
/// ... /// let b = 2;
/// // ...
/// a += a + b; /// a += a + b;
/// ``` /// ```
pub MISREFACTORED_ASSIGN_OP, pub MISREFACTORED_ASSIGN_OP,

View file

@ -113,19 +113,19 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// // Bad
/// #[inline(always)]
///
/// fn not_quite_good_code(..) { ... }
///
/// // Good (as inner attribute) /// // Good (as inner attribute)
/// #![inline(always)] /// #![inline(always)]
/// ///
/// fn this_is_fine(..) { ... } /// fn this_is_fine() { }
///
/// // Bad
/// #[inline(always)]
///
/// fn not_quite_good_code() { }
/// ///
/// // Good (as outer attribute) /// // Good (as outer attribute)
/// #[inline(always)] /// #[inline(always)]
/// fn this_is_fine_too(..) { ... } /// fn this_is_fine_too() { }
/// ``` /// ```
pub EMPTY_LINE_AFTER_OUTER_ATTR, pub EMPTY_LINE_AFTER_OUTER_ATTR,
nursery, nursery,

View file

@ -24,7 +24,8 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ///
/// ```rust /// ```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, pub NAIVE_BYTECOUNT,
perf, perf,

View file

@ -27,6 +27,8 @@ declare_clippy_lint! {
/// Could be written: /// Could be written:
/// ///
/// ```rust /// ```rust
/// # use std::convert::TryFrom;
/// # let foo = 1;
/// # let _ = /// # let _ =
/// i32::try_from(foo).is_ok() /// i32::try_from(foo).is_ok()
/// # ; /// # ;
@ -160,12 +162,12 @@ impl ConversionType {
/// Creates a conversion type if the type is allowed & conversion is valid /// Creates a conversion type if the type is allowed & conversion is valid
fn try_new(from: &str, to: &str) -> Option<Self> { fn try_new(from: &str, to: &str) -> Option<Self> {
if UINTS.contains(&from) { if UINTS.contains(&from) {
Some(ConversionType::FromUnsigned) Some(Self::FromUnsigned)
} else if SINTS.contains(&from) { } else if SINTS.contains(&from) {
if UINTS.contains(&to) { if UINTS.contains(&to) {
Some(ConversionType::SignedToUnsigned) Some(Self::SignedToUnsigned)
} else if SINTS.contains(&to) { } else if SINTS.contains(&to) {
Some(ConversionType::SignedToSigned) Some(Self::SignedToSigned)
} else { } else {
None None
} }

View file

@ -48,27 +48,25 @@ pub enum Constant {
impl PartialEq for Constant { impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => ls == rs, (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
(&Constant::Binary(ref l), &Constant::Binary(ref r)) => l == r, (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
(&Constant::Char(l), &Constant::Char(r)) => l == r, (&Self::Char(l), &Self::Char(r)) => l == r,
(&Constant::Int(l), &Constant::Int(r)) => l == r, (&Self::Int(l), &Self::Int(r)) => l == r,
(&Constant::F64(l), &Constant::F64(r)) => { (&Self::F64(l), &Self::F64(r)) => {
// We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them. // `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
l.to_bits() == r.to_bits() 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 // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
// `Fw32 == Fw64`, so dont compare them. // `Fw32 == Fw64`, so dont compare them.
// `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs. // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
f64::from(l).to_bits() == f64::from(r).to_bits() f64::from(l).to_bits() == f64::from(r).to_bits()
}, },
(&Constant::Bool(l), &Constant::Bool(r)) => l == r, (&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Constant::Vec(ref l), &Constant::Vec(ref r)) | (&Constant::Tuple(ref l), &Constant::Tuple(ref r)) => { (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
l == r (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
},
(&Constant::Repeat(ref lv, ref ls), &Constant::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
// TODO: are there inter-type equalities? // TODO: are there inter-type equalities?
_ => false, _ => false,
} }
@ -82,38 +80,38 @@ impl Hash for Constant {
{ {
std::mem::discriminant(self).hash(state); std::mem::discriminant(self).hash(state);
match *self { match *self {
Constant::Str(ref s) => { Self::Str(ref s) => {
s.hash(state); s.hash(state);
}, },
Constant::Binary(ref b) => { Self::Binary(ref b) => {
b.hash(state); b.hash(state);
}, },
Constant::Char(c) => { Self::Char(c) => {
c.hash(state); c.hash(state);
}, },
Constant::Int(i) => { Self::Int(i) => {
i.hash(state); i.hash(state);
}, },
Constant::F32(f) => { Self::F32(f) => {
f64::from(f).to_bits().hash(state); f64::from(f).to_bits().hash(state);
}, },
Constant::F64(f) => { Self::F64(f) => {
f.to_bits().hash(state); f.to_bits().hash(state);
}, },
Constant::Bool(b) => { Self::Bool(b) => {
b.hash(state); b.hash(state);
}, },
Constant::Vec(ref v) | Constant::Tuple(ref v) => { Self::Vec(ref v) | Self::Tuple(ref v) => {
v.hash(state); v.hash(state);
}, },
Constant::Repeat(ref c, l) => { Self::Repeat(ref c, l) => {
c.hash(state); c.hash(state);
l.hash(state); l.hash(state);
}, },
Constant::RawPtr(u) => { Self::RawPtr(u) => {
u.hash(state); u.hash(state);
}, },
Constant::Err(ref s) => { Self::Err(ref s) => {
s.hash(state); s.hash(state);
}, },
} }
@ -123,25 +121,25 @@ impl Hash for Constant {
impl Constant { impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> { pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) { match (left, right) {
(&Constant::Str(ref ls), &Constant::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
(&Constant::Char(ref l), &Constant::Char(ref r)) => Some(l.cmp(r)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
(&Constant::Int(l), &Constant::Int(r)) => { (&Self::Int(l), &Self::Int(r)) => {
if let ty::Int(int_ty) = cmp_type.sty { if let ty::Int(int_ty) = cmp_type.sty {
Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty)))
} else { } else {
Some(l.cmp(&r)) Some(l.cmp(&r))
} }
}, },
(&Constant::F64(l), &Constant::F64(r)) => l.partial_cmp(&r), (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
(&Constant::F32(l), &Constant::F32(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
(&Constant::Bool(ref l), &Constant::Bool(ref r)) => Some(l.cmp(r)), (&Self::Bool(ref l), &Self::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::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => l
.iter() .iter()
.zip(r.iter()) .zip(r.iter())
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri)) .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
.find(|r| r.map_or(true, |o| o != Ordering::Equal)) .find(|r| r.map_or(true, |o| o != Ordering::Equal))
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))), .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) { match Self::partial_cmp(tcx, cmp_type, lv, rv) {
Some(Equal) => Some(ls.cmp(rs)), Some(Equal) => Some(ls.cmp(rs)),
x => x, x => x,
@ -232,7 +230,7 @@ impl<'c, 'cc> ConstEvalLateContext<'c, 'cc> {
ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple), ExprKind::Tup(ref tup) => self.multi(tup).map(Constant::Tuple),
ExprKind::Repeat(ref value, _) => { ExprKind::Repeat(ref value, _) => {
let n = match self.tables.expr_ty(e).sty { 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"), _ => span_bug!(e.span, "typeck error"),
}; };
self.expr(value).map(|v| Constant::Repeat(Box::new(v), n)) self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))

View file

@ -13,7 +13,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// #[derive(Copy, Clone)] /// #[derive(Copy, Clone)]
/// struct Countdown(u8); /// struct Countdown(u8);
/// ///

View file

@ -113,3 +113,12 @@ declare_deprecated_lint! {
pub UNSAFE_VECTOR_INITIALIZATION, pub UNSAFE_VECTOR_INITIALIZATION,
"the replacement suggested by this lint had substantially different behavior" "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`"
}

View file

@ -49,12 +49,12 @@ declare_clippy_lint! {
/// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925 /// **Known problems:** Bounds of generic types are sometimes wrong: https://github.com/rust-lang/rust/issues/26925
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// #[derive(Copy)] /// #[derive(Copy)]
/// struct Foo; /// struct Foo;
/// ///
/// impl Clone for Foo { /// impl Clone for Foo {
/// .. /// // ..
/// } /// }
/// ``` /// ```
pub EXPL_IMPL_CLONE_ON_COPY, pub EXPL_IMPL_CLONE_ON_COPY,

View file

@ -27,7 +27,7 @@ declare_clippy_lint! {
/// /// Do something with the foo_bar parameter. See also /// /// Do something with the foo_bar parameter. See also
/// /// that::other::module::foo. /// /// that::other::module::foo.
/// // ^ `foo_bar` and `that::other::module::foo` should be ticked. /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
/// fn doit(foo_bar) { .. } /// fn doit(foo_bar: usize) {}
/// ``` /// ```
pub DOC_MARKDOWN, pub DOC_MARKDOWN,
pedantic, pedantic,

View file

@ -18,13 +18,17 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x == y || x < y /// # let x = 1;
/// # let y = 2;
/// if x == y || x < y {}
/// ``` /// ```
/// ///
/// Could be written as: /// Could be written as:
/// ///
/// ```rust /// ```rust
/// x <= y /// # let x = 1;
/// # let y = 2;
/// if x <= y {}
/// ``` /// ```
pub DOUBLE_COMPARISONS, pub DOUBLE_COMPARISONS,
complexity, complexity,
@ -63,6 +67,7 @@ impl<'a, 'tcx> DoubleComparisons {
); );
}}; }};
} }
#[rustfmt::skip]
match (op, lkind, rkind) { match (op, lkind, rkind) {
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
lint_double_comparison!(<=) lint_double_comparison!(<=)

View file

@ -13,9 +13,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// ((0)) /// # fn foo(bar: usize) {}
/// foo((0)) /// ((0));
/// ((1, 2)) /// foo((0));
/// ((1, 2));
/// ``` /// ```
pub DOUBLE_PARENS, pub DOUBLE_PARENS,
complexity, complexity,

View file

@ -20,6 +20,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::time::Duration;
/// let dur = Duration::new(5, 0); /// let dur = Duration::new(5, 0);
/// let _micros = dur.subsec_nanos() / 1_000; /// let _micros = dur.subsec_nanos() / 1_000;
/// let _millis = dur.subsec_nanos() / 1_000_000; /// let _millis = dur.subsec_nanos() / 1_000_000;

View file

@ -16,6 +16,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # fn a() {}
/// # fn b() {}
/// # let x: i32 = 1;
/// if x.is_positive() { /// if x.is_positive() {
/// a(); /// a();
/// } else if x.is_negative() { /// } else if x.is_negative() {
@ -26,6 +29,9 @@ declare_clippy_lint! {
/// Could be written: /// Could be written:
/// ///
/// ```rust /// ```rust
/// # fn a() {}
/// # fn b() {}
/// # let x: i32 = 1;
/// if x.is_positive() { /// if x.is_positive() {
/// a(); /// a();
/// } else if x.is_negative() { /// } else if x.is_negative() {

View file

@ -16,21 +16,32 @@ declare_clippy_lint! {
/// ///
/// **Known problems:** Some false negatives, eg.: /// **Known problems:** Some false negatives, eg.:
/// ```rust /// ```rust
/// let k = &key; /// # use std::collections::HashMap;
/// if !m.contains_key(k) { /// # let mut map = HashMap::new();
/// m.insert(k.clone(), v); /// # let v = 1;
/// # let k = 1;
/// if !map.contains_key(&k) {
/// map.insert(k.clone(), v);
/// } /// }
/// ``` /// ```
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// if !m.contains_key(&k) { /// # use std::collections::HashMap;
/// m.insert(k, v) /// # 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 /// ```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, pub MAP_ENTRY,
perf, perf,

View file

@ -160,6 +160,7 @@ fn check_variant(
let name = var2str(var); let name = var2str(var);
if partial_match(item_name, &name) == item_name_chars 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).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"); 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); let pre_camel = camel_case::until(pre);
pre = &pre[..pre_camel]; pre = &pre[..pre_camel];
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() { while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
if next.is_numeric() {
return;
}
if next.is_lowercase() { if next.is_lowercase() {
let last = pre.len() - last.len_utf8(); let last = pre.len() - last.len_utf8();
let last_camel = camel_case::until(&pre[..last]); let last_camel = camel_case::until(&pre[..last]);

View file

@ -1,5 +1,5 @@
use rustc::hir::intravisit as visit; use rustc::hir::intravisit as visit;
use rustc::hir::*; use rustc::hir::{self, *};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::middle::expr_use_visitor::*; use rustc::middle::expr_use_visitor::*;
use rustc::middle::mem_categorization::{cmt_, Categorization}; use rustc::middle::mem_categorization::{cmt_, Categorization};
@ -28,11 +28,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// fn main() { /// # fn foo(bar: usize) {}
/// let x = Box::new(1); /// let x = Box::new(1);
/// foo(*x); /// foo(*x);
/// println!("{}", *x); /// println!("{}", *x);
/// }
/// ``` /// ```
pub BOXED_LOCAL, pub BOXED_LOCAL,
perf, 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> { impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) { fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
if let Categorization::Local(lid) = cmt.cat { if let 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 matched_pat(&mut self, _: &Pat, _: &cmt_<'tcx>, _: MatchMode) {}
fn consume_pat(&mut self, consume_pat: &Pat, cmt: &cmt_<'tcx>, _: ConsumeMode) { fn consume_pat(&mut self, consume_pat: &Pat, cmt: &cmt_<'tcx>, _: ConsumeMode) {
let map = &self.cx.tcx.hir(); 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 // 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; return;
} }
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) { if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
self.set.insert(consume_pat.hir_id); self.set.insert(consume_pat.hir_id);
} }

View file

@ -42,7 +42,9 @@ declare_clippy_lint! {
/// shorthand. /// shorthand.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,no_run
/// # fn b() -> bool { true }
/// # fn c() -> bool { true }
/// let a = b() || panic!() || c(); /// let a = b() || panic!() || c();
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false` /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
/// let x = (a, b, c, panic!()); /// let x = (a, b, c, panic!());

View file

@ -143,20 +143,20 @@ impl FloatFormat {
fn new(s: &str) -> Self { fn new(s: &str) -> Self {
s.chars() s.chars()
.find_map(|x| match x { .find_map(|x| match x {
'e' => Some(FloatFormat::LowerExp), 'e' => Some(Self::LowerExp),
'E' => Some(FloatFormat::UpperExp), 'E' => Some(Self::UpperExp),
_ => None, _ => None,
}) })
.unwrap_or(FloatFormat::Normal) .unwrap_or(Self::Normal)
} }
fn format<T>(&self, f: T) -> String fn format<T>(&self, f: T) -> String
where where
T: fmt::UpperExp + fmt::LowerExp + fmt::Display, T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
{ {
match self { match self {
FloatFormat::LowerExp => format!("{:e}", f), Self::LowerExp => format!("{:e}", f),
FloatFormat::UpperExp => format!("{:E}", f), Self::UpperExp => format!("{:E}", f),
FloatFormat::Normal => format!("{}", f), Self::Normal => format!("{}", f),
} }
} }
} }

View file

@ -16,8 +16,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::io::Write;
/// # let bar = "furchtbar";
/// // this would be clearer as `eprintln!("foo: {:?}", bar);` /// // 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, pub EXPLICIT_WRITE,
complexity, complexity,

View file

@ -26,8 +26,9 @@ declare_clippy_lint! {
/// ///
/// **Examples:** /// **Examples:**
/// ```rust /// ```rust
/// format!("foo") /// # let foo = "foo";
/// format!("{}", foo) /// format!("foo");
/// format!("{}", foo);
/// ``` /// ```
pub USELESS_FORMAT, pub USELESS_FORMAT,
complexity, complexity,

View file

@ -23,8 +23,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # struct Color;
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) { /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
/// .. /// // ..
/// } /// }
/// ``` /// ```
pub TOO_MANY_ARGUMENTS, pub TOO_MANY_ARGUMENTS,

View file

@ -17,7 +17,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x / 1 + 0 * 1 - 0 | 0 /// # let x = 1;
/// x / 1 + 0 * 1 - 0 | 0;
/// ``` /// ```
pub IDENTITY_OP, pub IDENTITY_OP,
complexity, complexity,

View file

@ -17,6 +17,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let v: Vec<usize> = vec![];
/// # fn a() {}
/// # fn b() {}
/// if !v.is_empty() { /// if !v.is_empty() {
/// a() /// a()
/// } else { /// } else {
@ -27,6 +30,9 @@ declare_clippy_lint! {
/// Could be written: /// Could be written:
/// ///
/// ```rust /// ```rust
/// # let v: Vec<usize> = vec![];
/// # fn a() {}
/// # fn b() {}
/// if v.is_empty() { /// if v.is_empty() {
/// b() /// b()
/// } else { /// } else {

View file

@ -1,7 +1,14 @@
use crate::utils::{in_macro_or_desugar, is_expn_of, snippet_opt, span_lint_and_then}; use crate::utils::{
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, MatchSource}; in_macro_or_desugar, match_def_path,
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
use rustc::{declare_lint_pass, declare_tool_lint}; 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 rustc_errors::Applicability;
use syntax::source_map::Span; use syntax::source_map::Span;
@ -35,71 +42,84 @@ declare_clippy_lint! {
declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]); declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
impl ImplicitReturn { static LINT_BREAK: &str = "change `break` to `return` as shown";
fn lint(cx: &LateContext<'_, '_>, outer_span: syntax_pos::Span, inner_span: syntax_pos::Span, msg: &str) { static LINT_RETURN: &str = "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: &rustc::hir::Expr) { fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
match &expr.node { let outer_span = outer_span.source_callsite();
// loops could be using `break` instead of `return` let inner_span = inner_span.source_callsite();
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,
};
if check_all_arms { span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
for arm in arms { if let Some(snippet) = snippet_opt(cx, inner_span) {
Self::expr_match(cx, &arm.body); db.span_suggestion(
} outer_span,
} else { msg,
Self::expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body); format!("return {}", snippet),
} Applicability::MachineApplicable,
}, );
// 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")
}
},
} }
});
}
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 // checking return type through MIR, HIR is not able to determine inferred closure return types
// make sure it's not a macro // make sure it's not a macro
if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) { if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
Self::expr_match(cx, &body.value); expr_match(cx, &body.value);
} }
} }
} }

View file

@ -47,7 +47,7 @@ declare_clippy_lint! {
/// **Known problems:** Hopefully none. /// **Known problems:** Hopefully none.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,no_run
/// // Vector /// // Vector
/// let x = vec![0; 5]; /// 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) { if let Some(range) = higher::range(cx, index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..] // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.sty { 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); let const_range = to_const_range(cx, range, size);

View file

@ -34,7 +34,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub MAYBE_INFINITE_ITER,
pedantic, pedantic,

View 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()),
);
}
}

View file

@ -17,13 +17,17 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x >= y + 1 /// # let x = 1;
/// # let y = 1;
/// if x >= y + 1 {}
/// ``` /// ```
/// ///
/// Could be written: /// Could be written as:
/// ///
/// ```rust /// ```rust
/// x > y /// # let x = 1;
/// # let y = 1;
/// if x > y {}
/// ``` /// ```
pub INT_PLUS_ONE, pub INT_PLUS_ONE,
complexity, complexity,

View file

@ -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);
}
}
}
}

View file

@ -244,7 +244,7 @@ fn check_len(
LEN_ZERO, LEN_ZERO,
span, span,
&format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }), &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!( format!(
"{}{}.is_empty()", "{}{}.is_empty()",
op, op,

View file

@ -1,6 +1,7 @@
// error-pattern:cargo-clippy // error-pattern:cargo-clippy
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(box_patterns)]
#![feature(never_type)] #![feature(never_type)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(slice_patterns)] #![feature(slice_patterns)]
@ -9,6 +10,7 @@
#![recursion_limit = "512"] #![recursion_limit = "512"]
#![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)] #![warn(rust_2018_idioms, trivial_casts, trivial_numeric_casts)]
#![deny(rustc::internal)] #![deny(rustc::internal)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![feature(crate_visibility_modifier)] #![feature(crate_visibility_modifier)]
#![feature(concat_idents)] #![feature(concat_idents)]
@ -98,42 +100,42 @@ macro_rules! declare_clippy_lint {
}; };
{ $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
declare_tool_lint! { 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 } => { { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
declare_tool_lint! { 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 infallible_destructuring_match;
pub mod infinite_iter; pub mod infinite_iter;
pub mod inherent_impl; pub mod inherent_impl;
pub mod inherent_to_string;
pub mod inline_fn_without_body; pub mod inline_fn_without_body;
pub mod int_plus_one; pub mod int_plus_one;
pub mod integer_division; pub mod integer_division;
pub mod invalid_ref;
pub mod items_after_statements; pub mod items_after_statements;
pub mod large_enum_variant; pub mod large_enum_variant;
pub mod len_zero; pub mod len_zero;
@ -205,6 +207,7 @@ pub mod let_if_seq;
pub mod lifetimes; pub mod lifetimes;
pub mod literal_representation; pub mod literal_representation;
pub mod loops; pub mod loops;
pub mod main_recursion;
pub mod map_clone; pub mod map_clone;
pub mod map_unit_fn; pub mod map_unit_fn;
pub mod matches; pub mod matches;
@ -260,6 +263,7 @@ pub mod strings;
pub mod suspicious_trait_impl; pub mod suspicious_trait_impl;
pub mod swap; pub mod swap;
pub mod temporary_assignment; pub mod temporary_assignment;
pub mod trait_bounds;
pub mod transmute; pub mod transmute;
pub mod transmuting_null; pub mod transmuting_null;
pub mod trivially_copy_pass_by_ref; 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::LetUnitValue);
reg.register_late_lint_pass(box types::UnitCmp); reg.register_late_lint_pass(box types::UnitCmp);
reg.register_late_lint_pass(box loops::Loops); 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 lifetimes::Lifetimes);
reg.register_late_lint_pass(box entry::HashMapPass); reg.register_late_lint_pass(box entry::HashMapPass);
reg.register_late_lint_pass(box ranges::Ranges); 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 bytecount::ByteCount);
reg.register_late_lint_pass(box infinite_iter::InfiniteIter); 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 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 identity_conversion::IdentityConversion::default());
reg.register_late_lint_pass(box types::ImplicitHasher); reg.register_late_lint_pass(box types::ImplicitHasher);
reg.register_early_lint_pass(box redundant_static_lifetimes::RedundantStaticLifetimes); 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 path_buf_push_overwrite::PathBufPushOverwrite);
reg.register_late_lint_pass(box checked_conversions::CheckedConversions); reg.register_late_lint_pass(box checked_conversions::CheckedConversions);
reg.register_late_lint_pass(box integer_division::IntegerDivision); 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![ reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
arithmetic::FLOAT_ARITHMETIC, 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::CLIPPY_LINTS_INTERNAL,
utils::internal_lints::COMPILER_LINT_FUNCTIONS, utils::internal_lints::COMPILER_LINT_FUNCTIONS,
utils::internal_lints::LINT_WITHOUT_LINT_PASS, 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![ 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, indexing_slicing::OUT_OF_BOUNDS_INDEXING,
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH, infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
infinite_iter::INFINITE_ITER, 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, inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
int_plus_one::INT_PLUS_ONE, int_plus_one::INT_PLUS_ONE,
invalid_ref::INVALID_REF,
large_enum_variant::LARGE_ENUM_VARIANT, large_enum_variant::LARGE_ENUM_VARIANT,
len_zero::LEN_WITHOUT_IS_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY,
len_zero::LEN_ZERO, 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_IMMUTABLE_CONDITION,
loops::WHILE_LET_LOOP, loops::WHILE_LET_LOOP,
loops::WHILE_LET_ON_ITERATOR, loops::WHILE_LET_ON_ITERATOR,
main_recursion::MAIN_RECURSION,
map_clone::MAP_CLONE, map_clone::MAP_CLONE,
map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_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::ALMOST_SWAPPED,
swap::MANUAL_SWAP, swap::MANUAL_SWAP,
temporary_assignment::TEMPORARY_ASSIGNMENT, temporary_assignment::TEMPORARY_ASSIGNMENT,
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
transmute::CROSSPOINTER_TRANSMUTE, transmute::CROSSPOINTER_TRANSMUTE,
transmute::TRANSMUTE_BYTES_TO_STR, transmute::TRANSMUTE_BYTES_TO_STR,
transmute::TRANSMUTE_INT_TO_BOOL, 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, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_io_amount::UNUSED_IO_AMOUNT, unused_io_amount::UNUSED_IO_AMOUNT,
unused_label::UNUSED_LABEL, unused_label::UNUSED_LABEL,
unwrap::PANICKING_UNWRAP,
unwrap::UNNECESSARY_UNWRAP,
vec::USELESS_VEC, vec::USELESS_VEC,
write::PRINTLN_EMPTY_STRING, write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL, write::PRINT_LITERAL,
@ -913,6 +924,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
formatting::SUSPICIOUS_ELSE_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING,
infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH, infallible_destructuring_match::INFALLIBLE_DESTRUCTURING_MATCH,
inherent_to_string::INHERENT_TO_STRING,
len_zero::LEN_WITHOUT_IS_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY,
len_zero::LEN_ZERO, len_zero::LEN_ZERO,
let_if_seq::USELESS_LET_IF_SEQ, 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::FOR_KV_MAP,
loops::NEEDLESS_RANGE_LOOP, loops::NEEDLESS_RANGE_LOOP,
loops::WHILE_LET_ON_ITERATOR, loops::WHILE_LET_ON_ITERATOR,
main_recursion::MAIN_RECURSION,
map_clone::MAP_CLONE, map_clone::MAP_CLONE,
matches::MATCH_BOOL, matches::MATCH_BOOL,
matches::MATCH_OVERLAPPING_ARM, matches::MATCH_OVERLAPPING_ARM,
@ -1033,6 +1046,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
reference::REF_IN_DEREF, reference::REF_IN_DEREF,
swap::MANUAL_SWAP, swap::MANUAL_SWAP,
temporary_assignment::TEMPORARY_ASSIGNMENT, temporary_assignment::TEMPORARY_ASSIGNMENT,
trait_bounds::TYPE_REPETITION_IN_BOUNDS,
transmute::CROSSPOINTER_TRANSMUTE, transmute::CROSSPOINTER_TRANSMUTE,
transmute::TRANSMUTE_BYTES_TO_STR, transmute::TRANSMUTE_BYTES_TO_STR,
transmute::TRANSMUTE_INT_TO_BOOL, transmute::TRANSMUTE_INT_TO_BOOL,
@ -1050,6 +1064,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
types::UNNECESSARY_CAST, types::UNNECESSARY_CAST,
types::VEC_BOX, types::VEC_BOX,
unused_label::UNUSED_LABEL, unused_label::UNUSED_LABEL,
unwrap::UNNECESSARY_UNWRAP,
zero_div_zero::ZERO_DIVIDED_BY_ZERO, 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, functions::NOT_UNSAFE_PTR_ARG_DEREF,
indexing_slicing::OUT_OF_BOUNDS_INDEXING, indexing_slicing::OUT_OF_BOUNDS_INDEXING,
infinite_iter::INFINITE_ITER, infinite_iter::INFINITE_ITER,
inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
inline_fn_without_body::INLINE_FN_WITHOUT_BODY, inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
invalid_ref::INVALID_REF,
literal_representation::MISTYPED_LITERAL_SUFFIXES, literal_representation::MISTYPED_LITERAL_SUFFIXES,
loops::FOR_LOOP_OVER_OPTION, loops::FOR_LOOP_OVER_OPTION,
loops::FOR_LOOP_OVER_RESULT, loops::FOR_LOOP_OVER_RESULT,
@ -1110,6 +1125,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
types::UNIT_CMP, types::UNIT_CMP,
unicode::ZERO_WIDTH_SPACE, unicode::ZERO_WIDTH_SPACE,
unused_io_amount::UNUSED_IO_AMOUNT, unused_io_amount::UNUSED_IO_AMOUNT,
unwrap::PANICKING_UNWRAP,
]); ]);
reg.register_lint_group("clippy::perf", Some("clippy_perf"), vec![ 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, needless_borrow::NEEDLESS_BORROW,
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE, path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
redundant_clone::REDUNDANT_CLONE, redundant_clone::REDUNDANT_CLONE,
unwrap::PANICKING_UNWRAP,
unwrap::UNNECESSARY_UNWRAP,
]); ]);
} }

View file

@ -47,7 +47,7 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// fn unused_lifetime<'a>(x: u8) { /// fn unused_lifetime<'a>(x: u8) {
/// .. /// // ..
/// } /// }
/// ``` /// ```
pub EXTRA_UNUSED_LIFETIMES, pub EXTRA_UNUSED_LIFETIMES,
@ -283,6 +283,8 @@ impl<'v, 't> RefVisitor<'v, 't> {
if let Some(ref lt) = *lifetime { if let Some(ref lt) = *lifetime {
if lt.name == LifetimeName::Static { if lt.name == LifetimeName::Static {
self.lts.push(RefLt::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() { } else if lt.is_elided() {
self.lts.push(RefLt::Unnamed); self.lts.push(RefLt::Unnamed);
} else { } else {
@ -346,7 +348,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
}, },
TyKind::Def(item, _) => { TyKind::Def(item, _) => {
let map = self.cx.tcx.hir(); 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 { for bound in &exist_ty.bounds {
if let GenericBound::Outlives(_) = *bound { if let GenericBound::Outlives(_) = *bound {
self.record(&None); self.record(&None);

View file

@ -115,8 +115,8 @@ impl Radix {
/// Returns a reasonable digit group size for this radix. /// Returns a reasonable digit group size for this radix.
crate fn suggest_grouping(&self) -> usize { crate fn suggest_grouping(&self) -> usize {
match *self { match *self {
Radix::Binary | Radix::Hexadecimal => 4, Self::Binary | Self::Hexadecimal => 4,
Radix::Octal | Radix::Decimal => 3, Self::Octal | Self::Decimal => 3,
} }
} }
} }
@ -285,7 +285,7 @@ enum WarningType {
impl WarningType { impl WarningType {
crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) { crate fn display(&self, grouping_hint: &str, cx: &EarlyContext<'_>, span: syntax_pos::Span) {
match self { match self {
WarningType::MistypedLiteralSuffix => span_lint_and_sugg( Self::MistypedLiteralSuffix => span_lint_and_sugg(
cx, cx,
MISTYPED_LITERAL_SUFFIXES, MISTYPED_LITERAL_SUFFIXES,
span, span,
@ -294,7 +294,7 @@ impl WarningType {
grouping_hint.to_string(), grouping_hint.to_string(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
), ),
WarningType::UnreadableLiteral => span_lint_and_sugg( Self::UnreadableLiteral => span_lint_and_sugg(
cx, cx,
UNREADABLE_LITERAL, UNREADABLE_LITERAL,
span, span,
@ -303,7 +303,7 @@ impl WarningType {
grouping_hint.to_owned(), grouping_hint.to_owned(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
), ),
WarningType::LargeDigitGroups => span_lint_and_sugg( Self::LargeDigitGroups => span_lint_and_sugg(
cx, cx,
LARGE_DIGIT_GROUPS, LARGE_DIGIT_GROUPS,
span, span,
@ -312,7 +312,7 @@ impl WarningType {
grouping_hint.to_owned(), grouping_hint.to_owned(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
), ),
WarningType::InconsistentDigitGrouping => span_lint_and_sugg( Self::InconsistentDigitGrouping => span_lint_and_sugg(
cx, cx,
INCONSISTENT_DIGIT_GROUPING, INCONSISTENT_DIGIT_GROUPING,
span, span,
@ -321,7 +321,7 @@ impl WarningType {
grouping_hint.to_owned(), grouping_hint.to_owned(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
), ),
WarningType::DecimalRepresentation => span_lint_and_sugg( Self::DecimalRepresentation => span_lint_and_sugg(
cx, cx,
DECIMAL_LITERAL_REPRESENTATION, DECIMAL_LITERAL_REPRESENTATION,
span, span,

View file

@ -41,7 +41,9 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```ignore /// ```rust
/// # let src = vec![1];
/// # let mut dst = vec![0; 65];
/// for i in 0..src.len() { /// for i in 0..src.len() {
/// dst[i + 64] = src[i]; /// dst[i + 64] = src[i];
/// } /// }
@ -89,16 +91,18 @@ declare_clippy_lint! {
/// types. /// types.
/// ///
/// **Example:** /// **Example:**
/// ```ignore /// ```rust
/// // with `y` a `Vec` or slice: /// // with `y` a `Vec` or slice:
/// # let y = vec![1];
/// for x in y.iter() { /// for x in y.iter() {
/// .. /// // ..
/// } /// }
/// ``` /// ```
/// can be rewritten to /// can be rewritten to
/// ```rust /// ```rust
/// # let y = vec![1];
/// for x in &y { /// for x in &y {
/// .. /// // ..
/// } /// }
/// ``` /// ```
pub EXPLICIT_ITER_LOOP, pub EXPLICIT_ITER_LOOP,
@ -115,16 +119,18 @@ declare_clippy_lint! {
/// **Known problems:** None /// **Known problems:** None
/// ///
/// **Example:** /// **Example:**
/// ```ignore /// ```rust
/// # let y = vec![1];
/// // with `y` a `Vec` or slice: /// // with `y` a `Vec` or slice:
/// for x in y.into_iter() { /// for x in y.into_iter() {
/// .. /// // ..
/// } /// }
/// ``` /// ```
/// can be rewritten to /// can be rewritten to
/// ```ignore /// ```rust
/// # let y = vec![1];
/// for x in y { /// for x in y {
/// .. /// // ..
/// } /// }
/// ``` /// ```
pub EXPLICIT_INTO_ITER_LOOP, pub EXPLICIT_INTO_ITER_LOOP,
@ -217,18 +223,19 @@ declare_clippy_lint! {
/// **Known problems:** Sometimes the wrong binding is displayed (#383). /// **Known problems:** Sometimes the wrong binding is displayed (#383).
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,no_run
/// # let y = Some(1);
/// loop { /// loop {
/// let x = match y { /// let x = match y {
/// Some(x) => x, /// Some(x) => x,
/// None => break, /// None => break,
/// } /// };
/// // .. do something with x /// // .. do something with x
/// } /// }
/// // is easier written as /// // is easier written as
/// while let Some(x) = y { /// while let Some(x) = y {
/// // .. do something with x /// // .. do something with x
/// } /// };
/// ``` /// ```
pub WHILE_LET_LOOP, pub WHILE_LET_LOOP,
complexity, complexity,
@ -264,8 +271,9 @@ declare_clippy_lint! {
/// None /// None
/// ///
/// **Example:** /// **Example:**
/// ```ignore /// ```rust
/// let len = iterator.collect::<Vec<_>>().len(); /// # let iterator = vec![1].into_iter();
/// let len = iterator.clone().collect::<Vec<_>>().len();
/// // should be /// // should be
/// let len = iterator.count(); /// let len = iterator.count();
/// ``` /// ```
@ -309,8 +317,11 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```ignore /// ```rust
/// for i in 0..v.len() { foo(v[i]); /// # 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]); } /// for i in 0..v.len() { bar(i, v[i]); }
/// ``` /// ```
pub EXPLICIT_COUNTER_LOOP, 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 ExprKind::Lit(ref lit) = end.node;
if let ast::LitKind::Int(end_int, _) = lit.node; if let ast::LitKind::Int(end_int, _) = lit.node;
if let ty::Array(_, arr_len_const) = indexed_ty.sty; 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 { then {
return match limits { return match limits {
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), 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 { match cx.tables.expr_ty(&args[0]).sty {
// If the length is greater than 32 no traits are implemented for array and // If the length is greater than 32 no traits are implemented for array and
// therefore we cannot use `&`. // 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), _ => lint_iter_method(cx, args, arg, method_name),
}; };
} else { } 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 { fn is_iterable_array<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'_, 'tcx>) -> bool {
// IntoIterator is currently only implemented for array sizes <= 32 in rustc // IntoIterator is currently only implemented for array sizes <= 32 in rustc
match ty.sty { 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, _ => false,
} }
} }

View 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"
)
}
}
}
}

View file

@ -20,20 +20,29 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ///
/// ```rust /// ```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(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: /// The correct use would be:
/// ///
/// ```rust /// ```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 { /// if let Some(msg) = x {
/// log_err_msg(msg) /// log_err_msg(msg);
/// } /// }
///
/// # let x: Option<String> = do_stuff();
/// if let Some(msg) = x { /// if let Some(msg) = x {
/// log_err_msg(format_msg(msg)) /// log_err_msg(format_msg(msg));
/// } /// }
/// ``` /// ```
pub OPTION_MAP_UNIT_FN, pub OPTION_MAP_UNIT_FN,
@ -53,21 +62,29 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ///
/// ```rust /// ```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(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: /// The correct use would be:
/// ///
/// ```rust /// ```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 { /// 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 { /// if let Ok(msg) = x {
/// log_err_msg(format_msg(msg)) /// log_err_msg(format_msg(msg));
/// } /// };
/// ``` /// ```
pub RESULT_MAP_UNIT_FN, pub RESULT_MAP_UNIT_FN,
complexity, complexity,

View file

@ -53,19 +53,25 @@ declare_clippy_lint! {
/// Using `match`: /// Using `match`:
/// ///
/// ```rust /// ```rust
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
/// match x { /// match x {
/// Some(ref foo) => bar(foo), /// Some(ref foo) => bar(foo),
/// _ => bar(other_ref), /// _ => bar(&other_ref),
/// } /// }
/// ``` /// ```
/// ///
/// Using `if let` with `else`: /// Using `if let` with `else`:
/// ///
/// ```rust /// ```rust
/// # fn bar(foo: &usize) {}
/// # let other_ref: usize = 1;
/// # let x: Option<&usize> = Some(&1);
/// if let Some(ref foo) = x { /// if let Some(ref foo) = x {
/// bar(foo); /// bar(foo);
/// } else { /// } else {
/// bar(other_ref); /// bar(&other_ref);
/// } /// }
/// ``` /// ```
pub SINGLE_MATCH_ELSE, pub SINGLE_MATCH_ELSE,
@ -205,6 +211,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
/// match x { /// match x {
/// A => {}, /// A => {},
/// _ => {}, /// _ => {},

View file

@ -14,6 +14,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::mem;
/// # use std::rc::Rc;
/// mem::forget(Rc::new(55)) /// mem::forget(Rc::new(55))
/// ``` /// ```
pub MEM_FORGET, pub MEM_FORGET,

View file

@ -40,8 +40,19 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
///
/// Using unwrap on an `Option`:
///
/// ```rust /// ```rust
/// x.unwrap() /// let opt = Some(1);
/// opt.unwrap();
/// ```
///
/// Better:
///
/// ```rust
/// let opt = Some(1);
/// opt.expect("more helpful message");
/// ``` /// ```
pub OPTION_UNWRAP_USED, pub OPTION_UNWRAP_USED,
restriction, restriction,
@ -62,8 +73,18 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// Using unwrap on an `Option`:
///
/// ```rust /// ```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, pub RESULT_UNWRAP_USED,
restriction, restriction,
@ -141,9 +162,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// impl X { /// # struct X;
/// pub fn as_str(self) -> &str { /// impl<'a> X {
/// .. /// pub fn as_str(self) -> &'a str {
/// "foo"
/// } /// }
/// } /// }
/// ``` /// ```
@ -179,7 +201,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub OPTION_MAP_UNWRAP_OR,
pedantic, pedantic,
@ -196,7 +219,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub OPTION_MAP_UNWRAP_OR_ELSE,
pedantic, pedantic,
@ -213,7 +238,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub RESULT_MAP_UNWRAP_OR_ELSE,
pedantic, pedantic,
@ -247,7 +274,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// iter.filter(|x| x == 0).next() /// # let vec = vec![1];
/// vec.iter().filter(|x| **x == 0).next();
/// ``` /// ```
pub FILTER_NEXT, pub FILTER_NEXT,
complexity, complexity,
@ -264,7 +292,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// iter.map(|x| x.iter()).flatten() /// let vec = vec![vec![1]];
/// vec.iter().map(|x| x.iter()).flatten();
/// ``` /// ```
pub MAP_FLATTEN, pub MAP_FLATTEN,
pedantic, pedantic,
@ -283,7 +312,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub FILTER_MAP,
pedantic, pedantic,
@ -343,7 +373,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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 /// Can be written as
/// ```rust /// ```rust
@ -365,7 +395,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// iter.find(|x| x == 0).is_some() /// # let vec = vec![1];
/// vec.iter().find(|x| **x == 0).is_some();
/// ``` /// ```
pub SEARCH_IS_SOME, pub SEARCH_IS_SOME,
complexity, complexity,
@ -383,7 +414,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// name.chars().next() == Some('_') /// let name = "foo";
/// name.chars().next() == Some('_');
/// ``` /// ```
pub CHARS_NEXT_CMP, pub CHARS_NEXT_CMP,
complexity, complexity,
@ -403,15 +435,18 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// foo.unwrap_or(String::new()) /// # let foo = Some(String::new());
/// foo.unwrap_or(String::new());
/// ``` /// ```
/// this can instead be written: /// this can instead be written:
/// ```rust /// ```rust
/// foo.unwrap_or_else(String::new) /// # let foo = Some(String::new());
/// foo.unwrap_or_else(String::new);
/// ``` /// ```
/// or /// or
/// ```rust /// ```rust
/// foo.unwrap_or_default() /// # let foo = Some(String::new());
/// foo.unwrap_or_default();
/// ``` /// ```
pub OR_FUN_CALL, pub OR_FUN_CALL,
perf, perf,
@ -429,15 +464,24 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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 /// or
/// ```rust /// ```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: /// this can instead be written:
/// ```rust /// ```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, pub EXPECT_FUN_CALL,
perf, perf,
@ -454,7 +498,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// 42u64.clone() /// 42u64.clone();
/// ``` /// ```
pub CLONE_ON_COPY, pub CLONE_ON_COPY,
complexity, complexity,
@ -472,7 +516,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x.clone() /// # use std::rc::Rc;
/// let x = Rc::new(1);
/// x.clone();
/// ``` /// ```
pub CLONE_ON_REF_PTR, pub CLONE_ON_REF_PTR,
restriction, restriction,
@ -728,11 +774,13 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5]; /// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x.as_ref()); /// do_stuff(x.as_ref());
/// ``` /// ```
/// The correct use would be: /// The correct use would be:
/// ```rust /// ```rust
/// # fn do_stuff(x: &[i32]) {}
/// let x: &[i32] = &[1, 2, 3, 4, 5]; /// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x); /// 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", "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]), ["is_some", "rposition"] => lint_search_is_some(cx, expr, "rposition", arg_lists[1], arg_lists[0]),
["extend", ..] => lint_extend(cx, expr, 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"] => lint_iter_nth(cx, expr, arg_lists[1], false),
["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true), ["nth", "iter_mut"] => lint_iter_nth(cx, expr, arg_lists[1], true),
["next", "skip"] => lint_iter_skip_next(cx, expr), ["next", "skip"] => lint_iter_skip_next(cx, expr),
@ -1799,7 +1849,7 @@ fn derefs_to_slice<'a, 'tcx>(
ty::Slice(_) => true, ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()), ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => match_type(cx, ty, &paths::VEC), 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), ty::Ref(_, inner, _) => may_slice(cx, inner),
_ => false, _ => 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) { fn lint_single_char_pattern<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, _expr: &'tcx hir::Expr, arg: &'tcx hir::Expr) {
if_chain! { if_chain! {
if let hir::ExprKind::Lit(lit) = &arg.node; 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; if r.as_str().len() == 1;
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability); let snip = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
let c = &snip[1..snip.len() - 1]; let ch = if let ast::StrStyle::Raw(nhash) = style {
let hint = format!("'{}'", if c == "'" { "\\'" } else { c }); 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( span_lint_and_sugg(
cx, cx,
SINGLE_CHAR_PATTERN, 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); let is_actually_self = |ty| is_self_ty(ty) || SpanlessEq::new(cx).eq_ty(ty, self_ty);
if is_self(arg) { if is_self(arg) {
match self { match self {
SelfKind::Value => is_actually_self(ty), Self::Value => is_actually_self(ty),
SelfKind::Ref | SelfKind::RefMut => { Self::Ref | Self::RefMut => {
if allow_value_for_ref && is_actually_self(ty) { if allow_value_for_ref && is_actually_self(ty) {
return true; return true;
} }
match ty.node { match ty.node {
hir::TyKind::Rptr(_, ref mt_ty) => { 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 mt_ty.mutbl == hir::MutImmutable
} else { } else {
mt_ty.mutbl == hir::MutMutable mt_ty.mutbl == hir::MutMutable
@ -2572,20 +2629,20 @@ impl SelfKind {
} }
} else { } else {
match self { match self {
SelfKind::Value => false, Self::Value => false,
SelfKind::Ref => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASREF_TRAIT), Self::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), Self::RefMut => is_as_ref_or_mut_trait(ty, self_ty, generics, &paths::ASMUT_TRAIT),
SelfKind::No => true, Self::No => true,
} }
} }
} }
fn description(self) -> &'static str { fn description(self) -> &'static str {
match self { match self {
SelfKind::Value => "self by value", Self::Value => "self by value",
SelfKind::Ref => "self by reference", Self::Ref => "self by reference",
SelfKind::RefMut => "self by mutable reference", Self::RefMut => "self by mutable reference",
SelfKind::No => "no self", Self::No => "no self",
} }
} }
} }
@ -2655,8 +2712,8 @@ fn single_segment_ty(ty: &hir::Ty) -> Option<&hir::PathSegment> {
impl Convention { impl Convention {
fn check(&self, other: &str) -> bool { fn check(&self, other: &str) -> bool {
match *self { match *self {
Convention::Eq(this) => this == other, Self::Eq(this) => this == other,
Convention::StartsWith(this) => other.starts_with(this) && this != other, Self::StartsWith(this) => other.starts_with(this) && this != other,
} }
} }
} }
@ -2664,8 +2721,8 @@ impl Convention {
impl fmt::Display for Convention { impl fmt::Display for Convention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self { match *self {
Convention::Eq(this) => this.fmt(f), Self::Eq(this) => this.fmt(f),
Convention::StartsWith(this) => this.fmt(f).and_then(|_| '*'.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 { 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())); let is_unit = |ty: &hir::Ty| SpanlessEq::new(cx).eq_ty_kind(&ty.node, &hir::TyKind::Tup(vec![].into()));
match (self, ty) { match (self, ty) {
(OutType::Unit, &hir::DefaultReturn(_)) => true, (Self::Unit, &hir::DefaultReturn(_)) => true,
(OutType::Unit, &hir::Return(ref ty)) if is_unit(ty) => true, (Self::Unit, &hir::Return(ref ty)) if is_unit(ty) => true,
(OutType::Bool, &hir::Return(ref ty)) if is_bool(ty) => true, (Self::Bool, &hir::Return(ref ty)) if is_bool(ty) => true,
(OutType::Any, &hir::Return(ref ty)) if !is_unit(ty) => true, (Self::Any, &hir::Return(ref ty)) if !is_unit(ty) => true,
(OutType::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)), (Self::Ref, &hir::Return(ref ty)) => matches!(ty.node, hir::TyKind::Rptr(_, _)),
_ => false, _ => false,
} }
} }

View file

@ -7,7 +7,7 @@ use rustc::ty;
use rustc::{declare_lint_pass, declare_tool_lint}; use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use syntax::ast::LitKind; use syntax::ast::LitKind;
use syntax::source_map::{ExpnFormat, Span}; use syntax::source_map::{ExpnKind, Span};
use crate::consts::{constant, Constant}; use crate::consts::{constant, Constant};
use crate::utils::sugg::Sugg; use crate::utils::sugg::Sugg;
@ -101,7 +101,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x.to_owned() == y /// # let x = "foo";
/// # let y = String::from("foo");
/// if x.to_owned() == y {}
/// ``` /// ```
pub CMP_OWNED, pub CMP_OWNED,
perf, perf,
@ -184,7 +186,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// f() && g(); // We should write `if f() { g(); }`. /// f() && g(); // We should write `if f() { g(); }`.
/// ``` /// ```
pub SHORT_CIRCUIT_STATEMENT, pub SHORT_CIRCUIT_STATEMENT,
@ -224,8 +226,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// const ONE = 1.00f64; /// let x: f64 = 1.0;
/// x == ONE // where both are floats /// const ONE: f64 = 1.00;
/// x == ONE; // where both are floats
/// ``` /// ```
pub FLOAT_CMP_CONST, pub FLOAT_CMP_CONST,
restriction, restriction,
@ -364,6 +367,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
if is_allowed(cx, left) || is_allowed(cx, right) { if is_allowed(cx, left) || is_allowed(cx, right) {
return; 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) { if let Some(name) = get_item_name(cx, expr) {
let name = name.as_str(); let name = name.as_str();
if name == "eq" 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 { fn is_float(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
matches!(walk_ptrs_ty(cx.tables.expr_ty(expr)).sty, ty::Float(_)) 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 /// Tests whether an expression is in a macro expansion (e.g., something
/// generated by `#[derive(...)]` or the like). /// generated by `#[derive(...)]` or the like).
fn in_attributes_expansion(expr: &Expr) -> bool { fn in_attributes_expansion(expr: &Expr) -> bool {
expr.span use syntax::ext::hygiene::MacroKind;
.ctxt() expr.span.ctxt().outer_expn_info().map_or(false, |info| {
.outer_expn_info() if let ExpnKind::Macro(MacroKind::Attr, _) = info.kind {
.map_or(false, |info| matches!(info.format, ExpnFormat::MacroAttribute(_))) true
} else {
false
}
})
} }
/// Tests whether `res` is a variable defined outside a macro. /// Tests whether `res` is a variable defined outside a macro.

View file

@ -53,7 +53,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// (|| 42)() /// (|| 42)()
/// ``` /// ```
pub REDUNDANT_CLOSURE_CALL, pub REDUNDANT_CLOSURE_CALL,
@ -305,7 +305,7 @@ impl EarlyLintPass for MiscEarlyLints {
name makes code comprehension and documentation more difficult", name makes code comprehension and documentation more difficult",
arg_name[1..].to_owned() arg_name[1..].to_owned()
), ),
);; );
} }
} else { } else {
registered_names.insert(arg_name, arg.pat.span); registered_names.insert(arg_name, arg.pat.span);

View file

@ -40,17 +40,27 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ///
/// ```rust /// ```rust
/// # struct Foo {
/// # random_number: usize,
/// # }
/// # impl Foo {
/// fn new() -> Self { /// fn new() -> Self {
/// Self { random_number: 42 } /// Self { random_number: 42 }
/// } /// }
/// # }
/// ``` /// ```
/// ///
/// Could be a const fn: /// Could be a const fn:
/// ///
/// ```rust /// ```rust
/// # struct Foo {
/// # random_number: usize,
/// # }
/// # impl Foo {
/// const fn new() -> Self { /// const fn new() -> Self {
/// Self { random_number: 42 } /// Self { random_number: 42 }
/// } /// }
/// # }
/// ``` /// ```
pub MISSING_CONST_FOR_FN, pub MISSING_CONST_FOR_FN,
nursery, nursery,

View file

@ -146,9 +146,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
hir::ItemKind::Struct(..) => "a struct", hir::ItemKind::Struct(..) => "a struct",
hir::ItemKind::Trait(..) => "a trait", hir::ItemKind::Trait(..) => "a trait",
hir::ItemKind::TraitAlias(..) => "a trait alias", 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::Union(..) => "a union",
hir::ItemKind::Existential(..) => "an existential type", hir::ItemKind::OpaqueTy(..) => "an existential type",
hir::ItemKind::ExternCrate(..) hir::ItemKind::ExternCrate(..)
| hir::ItemKind::ForeignMod(..) | hir::ItemKind::ForeignMod(..)
| hir::ItemKind::GlobalAsm(..) | hir::ItemKind::GlobalAsm(..)
@ -184,8 +184,8 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingDoc {
let desc = match impl_item.node { let desc = match impl_item.node {
hir::ImplItemKind::Const(..) => "an associated constant", hir::ImplItemKind::Const(..) => "an associated constant",
hir::ImplItemKind::Method(..) => "a method", hir::ImplItemKind::Method(..) => "a method",
hir::ImplItemKind::Type(_) => "an associated type", hir::ImplItemKind::TyAlias(_) => "an associated type",
hir::ImplItemKind::Existential(_) => "an existential type", hir::ImplItemKind::OpaqueTy(_) => "an existential type",
}; };
self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc); self.check_missing_docs_attrs(cx, &impl_item.attrs, impl_item.span, desc);
} }

View file

@ -33,7 +33,7 @@ declare_clippy_lint! {
/// ///
/// struct Baz; /// struct Baz;
/// impl Baz { /// impl Baz {
/// fn priv() {} // ok /// fn private() {} // ok
/// } /// }
/// ///
/// impl Bar for Baz { /// impl Bar for Baz {
@ -42,8 +42,8 @@ declare_clippy_lint! {
/// ///
/// pub struct PubBaz; /// pub struct PubBaz;
/// impl PubBaz { /// impl PubBaz {
/// fn priv() {} // ok /// fn private() {} // ok
/// pub not_ptriv() {} // missing #[inline] /// pub fn not_ptrivate() {} // missing #[inline]
/// } /// }
/// ///
/// impl Bar for PubBaz { /// impl Bar for PubBaz {
@ -119,9 +119,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
| hir::ItemKind::Struct(..) | hir::ItemKind::Struct(..)
| hir::ItemKind::TraitAlias(..) | hir::ItemKind::TraitAlias(..)
| hir::ItemKind::GlobalAsm(..) | hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::Ty(..) | hir::ItemKind::TyAlias(..)
| hir::ItemKind::Union(..) | hir::ItemKind::Union(..)
| hir::ItemKind::Existential(..) | hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::ExternCrate(..) | hir::ItemKind::ExternCrate(..)
| hir::ItemKind::ForeignMod(..) | hir::ItemKind::ForeignMod(..)
| hir::ItemKind::Impl(..) | hir::ItemKind::Impl(..)
@ -142,7 +142,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
let desc = match impl_item.node { let desc = match impl_item.node {
hir::ImplItemKind::Method(..) => "a method", 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); let def_id = cx.tcx.hir().local_def_id(impl_item.hir_id);

View file

@ -16,6 +16,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let mut y = 1;
/// let x = &mut &mut y; /// let x = &mut &mut y;
/// ``` /// ```
pub MUT_MUT, pub MUT_MUT,

View file

@ -22,6 +22,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::sync::Mutex;
/// # let y = 1;
/// let x = Mutex::new(&y); /// let x = Mutex::new(&y);
/// ``` /// ```
pub MUTEX_ATOMIC, pub MUTEX_ATOMIC,
@ -42,6 +44,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::sync::Mutex;
/// let x = Mutex::new(0usize); /// let x = Mutex::new(0usize);
/// ``` /// ```
pub MUTEX_INTEGER, pub MUTEX_INTEGER,

View file

@ -24,7 +24,7 @@ declare_clippy_lint! {
/// shorter code. /// shorter code.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// if x { /// if x {
/// false /// false
/// } else { /// } else {
@ -46,7 +46,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// if x == true {} // could be `if x { }` /// if x == true {} // could be `if x { }`
/// ``` /// ```
pub BOOL_COMPARISON, 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_id = cx.tcx.hir().get_parent_node(expr.hir_id);
let parent_node = cx.tcx.hir().get(parent_id); let parent_node = cx.tcx.hir().get(parent_id);
if let rustc::hir::Node::Expr(e) = parent_node { match parent_node {
if higher::if_block(&e).is_some() { rustc::hir::Node::Expr(e) => higher::if_block(&e).is_some(),
return true; rustc::hir::Node::Arm(e) => higher::if_block(&e.body).is_some(),
} _ => false,
} }
false
} }
declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]); declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);

View file

@ -18,7 +18,7 @@ declare_clippy_lint! {
/// ///
/// **Known problems:** It seems that the `&ref` pattern is sometimes useful. /// **Known problems:** It seems that the `&ref` pattern is sometimes useful.
/// For instance in the following snippet: /// For instance in the following snippet:
/// ```rust /// ```rust,ignore
/// enum Animal { /// enum Animal {
/// Cat(u64), /// Cat(u64),
/// Dog(u64), /// Dog(u64),
@ -26,8 +26,7 @@ declare_clippy_lint! {
/// ///
/// fn foo(a: &Animal, b: &Animal) { /// fn foo(a: &Animal, b: &Animal) {
/// match (a, b) { /// match (a, b) {
/// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime /// (&Animal::Cat(v), k) | (k, &Animal::Cat(v)) => (), // lifetime mismatch error
/// mismatch error
/// (&Animal::Dog(ref c), &Animal::Dog(_)) => () /// (&Animal::Dog(ref c), &Animal::Dog(_)) => ()
/// } /// }
/// } /// }

View file

@ -57,6 +57,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # fn condition() -> bool { false }
/// # fn update_condition() {}
/// # let x = false;
/// while condition() { /// while condition() {
/// update_condition(); /// update_condition();
/// if x { /// if x {
@ -71,6 +74,9 @@ declare_clippy_lint! {
/// Could be rewritten as /// Could be rewritten as
/// ///
/// ```rust /// ```rust
/// # fn condition() -> bool { false }
/// # fn update_condition() {}
/// # let x = false;
/// while condition() { /// while condition() {
/// update_condition(); /// update_condition();
/// if x { /// if x {
@ -83,22 +89,26 @@ declare_clippy_lint! {
/// As another example, the following code /// As another example, the following code
/// ///
/// ```rust /// ```rust
/// # fn waiting() -> bool { false }
/// loop { /// loop {
/// if waiting() { /// if waiting() {
/// continue; /// continue;
/// } else { /// } else {
/// // Do something useful /// // Do something useful
/// } /// }
/// # break;
/// } /// }
/// ``` /// ```
/// Could be rewritten as /// Could be rewritten as
/// ///
/// ```rust /// ```rust
/// # fn waiting() -> bool { false }
/// loop { /// loop {
/// if waiting() { /// if waiting() {
/// continue; /// continue;
/// } /// }
/// // Do something useful /// // Do something useful
/// # break;
/// } /// }
/// ``` /// ```
pub NEEDLESS_CONTINUE, pub NEEDLESS_CONTINUE,

View file

@ -40,6 +40,9 @@ declare_clippy_lint! {
/// fn foo(v: Vec<i32>) { /// fn foo(v: Vec<i32>) {
/// assert_eq!(v.len(), 42); /// assert_eq!(v.len(), 42);
/// } /// }
/// ```
///
/// ```rust
/// // should be /// // should be
/// fn foo(v: &[i32]) { /// fn foo(v: &[i32]) {
/// assert_eq!(v.len(), 42); /// assert_eq!(v.len(), 42);
@ -193,7 +196,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
if_chain! { if_chain! {
if !is_self(arg); if !is_self(arg);
if !ty.is_mutable_pointer(); if !ty.is_mutable_ptr();
if !is_copy(cx, ty); if !is_copy(cx, ty);
if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[])); if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
if !implements_borrow_trait; if !implements_borrow_trait;

View file

@ -15,11 +15,17 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # struct Point {
/// # x: i32,
/// # y: i32,
/// # z: i32,
/// # }
/// # let zero_point = Point { x: 0, y: 0, z: 0 };
/// Point { /// Point {
/// x: 1, /// x: 1,
/// y: 0, /// y: 1,
/// ..zero_point /// ..zero_point
/// } /// };
/// ``` /// ```
pub NEEDLESS_UPDATE, pub NEEDLESS_UPDATE,
complexity, complexity,

View file

@ -34,7 +34,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// compute_array()[0]; /// compute_array()[0];
/// ``` /// ```
pub UNNECESSARY_OPERATION, pub UNNECESSARY_OPERATION,

View file

@ -84,6 +84,7 @@ declare_clippy_lint! {
"referencing const with interior mutability" "referencing const with interior mutability"
} }
#[allow(dead_code)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum Source { enum Source {
Item { item: Span }, Item { item: Span },
@ -94,12 +95,12 @@ enum Source {
impl Source { impl Source {
fn lint(&self) -> (&'static Lint, &'static str, Span) { fn lint(&self) -> (&'static Lint, &'static str, Span) {
match self { match self {
Source::Item { item } | Source::Assoc { item, .. } => ( Self::Item { item } | Self::Assoc { item, .. } => (
DECLARE_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST,
"a const item should never be interior mutable", "a const item should never be interior mutable",
*item, *item,
), ),
Source::Expr { expr } => ( Self::Expr { expr } => (
BORROW_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST,
"a const item with interior mutability should not be borrowed", "a const item with interior mutability should not be borrowed",
*expr, *expr,

View file

@ -127,7 +127,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
} else { } else {
create = true create = true
} }
create_arg = create_arg || (arg == Argument::True);; create_arg = create_arg || (arg == Argument::True);
}, },
(OpenOption::Append, arg) => { (OpenOption::Append, arg) => {
if append { if append {
@ -140,7 +140,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
} else { } else {
append = true append = true
} }
append_arg = append_arg || (arg == Argument::True);; append_arg = append_arg || (arg == Argument::True);
}, },
(OpenOption::Truncate, arg) => { (OpenOption::Truncate, arg) => {
if truncate { if truncate {
@ -166,7 +166,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
} else { } else {
read = true read = true
} }
read_arg = read_arg || (arg == Argument::True);; read_arg = read_arg || (arg == Argument::True);
}, },
(OpenOption::Write, arg) => { (OpenOption::Write, arg) => {
if write { if write {
@ -179,7 +179,7 @@ fn check_open_options(cx: &LateContext<'_, '_>, options: &[(OpenOption, Argument
} else { } else {
write = true write = true
} }
write_arg = write_arg || (arg == Argument::True);; write_arg = write_arg || (arg == Argument::True);
}, },
} }
} }

View file

@ -14,7 +14,9 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// a + b < a /// # let a = 1;
/// # let b = 2;
/// a + b < a;
/// ``` /// ```
pub OVERFLOW_CHECK_CONDITIONAL, pub OVERFLOW_CHECK_CONDITIONAL,
complexity, complexity,

View file

@ -19,7 +19,7 @@ declare_clippy_lint! {
/// struct Foo; /// struct Foo;
/// ///
/// impl PartialEq for 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) } /// fn ne(&self, other: &Foo) -> bool { !(self == other) }
/// } /// }
/// ``` /// ```

View file

@ -133,8 +133,8 @@ enum Method {
impl Method { impl Method {
fn suggestion(self) -> &'static str { fn suggestion(self) -> &'static str {
match self { match self {
Method::Offset => "add", Self::Offset => "add",
Method::WrappingOffset => "wrapping_add", Self::WrappingOffset => "wrapping_add",
} }
} }
} }
@ -142,8 +142,8 @@ impl Method {
impl fmt::Display for Method { impl fmt::Display for Method {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Method::Offset => write!(f, "offset"), Self::Offset => write!(f, "offset"),
Method::WrappingOffset => write!(f, "wrapping_offset"), Self::WrappingOffset => write!(f, "wrapping_offset"),
} }
} }
} }

View file

@ -40,7 +40,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// x.iter().zip(0..x.len()) /// # let x = vec![1];
/// x.iter().zip(0..x.len());
/// ``` /// ```
pub RANGE_ZIP_WITH_LEN, pub RANGE_ZIP_WITH_LEN,
complexity, complexity,
@ -60,7 +61,7 @@ declare_clippy_lint! {
/// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`. /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// for x..(y+1) { .. } /// for x..(y+1) { .. }
/// ``` /// ```
pub RANGE_PLUS_ONE, pub RANGE_PLUS_ONE,
@ -78,7 +79,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// for x..=(y-1) { .. } /// for x..=(y-1) { .. }
/// ``` /// ```
pub RANGE_MINUS_ONE, pub RANGE_MINUS_ONE,

View file

@ -40,6 +40,7 @@ declare_clippy_lint! {
/// * False-positive if there is a borrow preventing the value from moving out. /// * False-positive if there is a borrow preventing the value from moving out.
/// ///
/// ```rust /// ```rust
/// # fn foo(x: String) {}
/// let x = String::new(); /// let x = String::new();
/// ///
/// let y = &x; /// let y = &x;
@ -49,15 +50,22 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::path::Path;
/// # #[derive(Clone)]
/// # struct Foo;
/// # impl Foo {
/// # fn new() -> Self { Foo {} }
/// # }
/// # fn call(x: Foo) {}
/// { /// {
/// let x = Foo::new(); /// let x = Foo::new();
/// call(x.clone()); /// call(x.clone());
/// call(x.clone()); // this can just pass `x` /// 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, pub REDUNDANT_CLONE,
nursery, nursery,
@ -132,7 +140,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for RedundantClone {
let pred_arg = if_chain! { let pred_arg = if_chain! {
if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) = if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, Some(res))) =
is_call_with_ref_arg(cx, mir, &pred_terminator.kind); 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_def_path(cx, pred_fn_def_id, &paths::DEREF_TRAIT_METHOD);
if match_type(cx, pred_arg_ty, &paths::PATH_BUF) if match_type(cx, pred_arg_ty, &paths::PATH_BUF)
|| match_type(cx, pred_arg_ty, &paths::OS_STRING); || match_type(cx, pred_arg_ty, &paths::OS_STRING);
@ -218,7 +226,7 @@ fn is_call_with_ref_arg<'tcx>(
if_chain! { if_chain! {
if let TerminatorKind::Call { func, args, destination, .. } = kind; if let TerminatorKind::Call { func, args, destination, .. } = kind;
if args.len() == 1; 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 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 let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
if !is_copy(cx, inner_ty); if !is_copy(cx, inner_ty);
@ -244,7 +252,14 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
stmts stmts
.rev() .rev()
.find_map(|stmt| { .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 { if *local == to {
return Some(v); return Some(v);
} }
@ -271,28 +286,34 @@ fn find_stmt_assigns_to<'a, 'tcx: 'a>(
fn base_local_and_movability<'tcx>( fn base_local_and_movability<'tcx>(
cx: &LateContext<'_, 'tcx>, cx: &LateContext<'_, 'tcx>,
mir: &mir::Body<'tcx>, mir: &mir::Body<'tcx>,
mut place: &mir::Place<'tcx>, place: &mir::Place<'tcx>,
) -> Option<(mir::Local, CannotMoveOut)> { ) -> Option<(mir::Local, CannotMoveOut)> {
use rustc::mir::Place::*; use rustc::mir::Place;
use rustc::mir::PlaceBase; use rustc::mir::PlaceBase;
use rustc::mir::PlaceRef;
use rustc::mir::Projection;
// Dereference. You cannot move things out from a borrowed value. // Dereference. You cannot move things out from a borrowed value.
let mut deref = false; let mut deref = false;
// Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509. // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
let mut field = false; let mut field = false;
loop { let PlaceRef {
match place { base: place_base,
Base(PlaceBase::Local(local)) => return Some((*local, deref || field)), mut projection,
Projection(proj) => { } = place.as_ref();
place = &proj.base; if let PlaceBase::Local(local) = place_base {
deref = deref || matches!(proj.elem, mir::ProjectionElem::Deref); while let Some(box Projection { base, elem }) = projection {
if !field && matches!(proj.elem, mir::ProjectionElem::Field(..)) { projection = base;
field = has_drop(cx, place.ty(&mir.local_decls, cx.tcx).ty); deref = matches!(elem, mir::ProjectionElem::Deref);
} field = !field
}, && matches!(elem, mir::ProjectionElem::Field(..))
_ => return None, && has_drop(cx, Place::ty_from(place_base, projection, &mir.local_decls, cx.tcx).ty);
} }
Some((*local, deref || field))
} else {
None
} }
} }

View file

@ -81,7 +81,8 @@ impl EarlyLintPass for RedundantStaticLifetimes {
if !in_macro_or_desugar(item.span) { if !in_macro_or_desugar(item.span) {
if let ItemKind::Const(ref var_type, _) = item.node { if let ItemKind::Const(ref var_type, _) = item.node {
self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime"); 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 { if let ItemKind::Static(ref var_type, _, _) = item.node {

View file

@ -15,7 +15,7 @@ declare_clippy_lint! {
/// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect. /// the suggested fix for `x = **&&y` is `x = *&y`, which is still incorrect.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// let a = f(*&mut b); /// let a = f(*&mut b);
/// let c = *&d; /// let c = *&d;
/// ``` /// ```
@ -64,8 +64,8 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// struct Point(u32, u32); /// struct Point(u32, u32);
/// let point = Foo(30, 20); /// let point = Point(30, 20);
/// let x = (&point).x; /// let x = (&point).0;
/// ``` /// ```
pub REF_IN_DEREF, pub REF_IN_DEREF,
complexity, complexity,

View file

@ -16,12 +16,14 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use core::sync::atomic::{ATOMIC_ISIZE_INIT, AtomicIsize};
/// static FOO: AtomicIsize = ATOMIC_ISIZE_INIT; /// static FOO: AtomicIsize = ATOMIC_ISIZE_INIT;
/// ``` /// ```
/// ///
/// Could be written: /// Could be written:
/// ///
/// ```rust /// ```rust
/// # use core::sync::atomic::AtomicIsize;
/// static FOO: AtomicIsize = AtomicIsize::new(0); /// static FOO: AtomicIsize = AtomicIsize::new(0);
/// ``` /// ```
pub REPLACE_CONSTS, pub REPLACE_CONSTS,

View file

@ -86,7 +86,7 @@ declare_clippy_lint! {
#[derive(PartialEq, Eq, Copy, Clone)] #[derive(PartialEq, Eq, Copy, Clone)]
enum RetReplacement { enum RetReplacement {
Empty, Empty,
Unit, Block,
} }
declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]); declare_lint_pass!(Return => [NEEDLESS_RETURN, LET_AND_RETURN, UNUSED_UNIT]);
@ -139,7 +139,7 @@ impl Return {
// a match expr, check all arms // a match expr, check all arms
ast::ExprKind::Match(_, ref arms) => { ast::ExprKind::Match(_, ref arms) => {
for arm in 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| { span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded return statement", |db| {
db.span_suggestion( db.span_suggestion(
ret_span, ret_span,
"replace `return` with the unit type", "replace `return` with an empty block",
"()".to_string(), "{}".to_string(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
}); });
@ -317,7 +317,7 @@ fn attr_is_cfg(attr: &ast::Attribute) -> bool {
// get the def site // get the def site
fn get_def(span: Span) -> Option<Span> { 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? // is this expr a `()` unit?

View file

@ -20,6 +20,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let x = 1;
/// let x = &x; /// let x = &x;
/// ``` /// ```
pub SHADOW_SAME, pub SHADOW_SAME,
@ -41,10 +42,12 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let x = 2;
/// let x = x + 1; /// let x = x + 1;
/// ``` /// ```
/// use different variable name: /// use different variable name:
/// ```rust /// ```rust
/// let x = 2;
/// let y = x + 1; /// let y = x + 1;
/// ``` /// ```
pub SHADOW_REUSE, pub SHADOW_REUSE,
@ -67,6 +70,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let y = 1;
/// # let z = 2;
/// let x = y; /// let x = y;
/// let x = z; // shadows the earlier binding /// let x = z; // shadows the earlier binding
/// ``` /// ```

View file

@ -19,6 +19,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use core::iter::repeat;
/// # let len = 4;
/// let mut vec1 = Vec::with_capacity(len); /// let mut vec1 = Vec::with_capacity(len);
/// vec1.resize(len, 0); /// vec1.resize(len, 0);
/// ///

View file

@ -48,7 +48,7 @@ declare_clippy_lint! {
/// ///
/// ```rust /// ```rust
/// let x = "Hello".to_owned(); /// let x = "Hello".to_owned();
/// x + ", World" /// x + ", World";
/// ``` /// ```
pub STRING_ADD, pub STRING_ADD,
restriction, 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]); declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
@ -173,6 +176,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
); );
} else if callsite == expanded } else if callsite == expanded
&& lit_content.as_str().chars().all(|c| c.is_ascii()) && 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) && !in_macro_or_desugar(args[0].span)
{ {
span_lint_and_sugg( span_lint_and_sugg(

View file

@ -20,12 +20,17 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let mut a = 42;
/// let mut b = 1337;
///
/// let t = b; /// let t = b;
/// b = a; /// b = a;
/// a = t; /// a = t;
/// ``` /// ```
/// Use std::mem::swap(): /// Use std::mem::swap():
/// ```rust /// ```rust
/// let mut a = 1;
/// let mut b = 2;
/// std::mem::swap(&mut a, &mut b); /// std::mem::swap(&mut a, &mut b);
/// ``` /// ```
pub MANUAL_SWAP, pub MANUAL_SWAP,

View 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,
);
}
}
}
}
}

View file

@ -36,8 +36,8 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// core::intrinsics::transmute(t) // where the result type is the same as `t`'s /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
/// ``` /// ```
pub USELESS_TRANSMUTE, pub USELESS_TRANSMUTE,
complexity, complexity,
@ -53,7 +53,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// core::intrinsics::transmute(t) // where the result type is the same as /// core::intrinsics::transmute(t) // where the result type is the same as
/// // `*t` or `&t`'s /// // `*t` or `&t`'s
/// ``` /// ```
@ -70,8 +70,10 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// let _: &T = std::mem::transmute(p); // where p: *const T /// unsafe {
/// let _: &T = std::mem::transmute(p); // where p: *const T
/// }
/// ///
/// // can be written: /// // can be written:
/// let _: &T = &*p; /// let _: &T = &*p;
@ -99,7 +101,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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: /// // should be:
/// let _ = std::char::from_u32(x).unwrap(); /// let _ = std::char::from_u32(x).unwrap();
@ -127,7 +132,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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: /// // should be:
/// let _ = std::str::from_utf8(b).unwrap(); /// let _ = std::str::from_utf8(b).unwrap();
@ -146,7 +154,10 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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: /// // should be:
/// let _: bool = x != 0; /// let _: bool = x != 0;
@ -166,10 +177,12 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let _: f32 = std::mem::transmute(x); // where x: u32 /// unsafe {
/// let _: f32 = std::mem::transmute(1_u32); // where x: u32
/// }
/// ///
/// // should be: /// // should be:
/// let _: f32 = f32::from_bits(x); /// let _: f32 = f32::from_bits(1_u32);
/// ``` /// ```
pub TRANSMUTE_INT_TO_FLOAT, pub TRANSMUTE_INT_TO_FLOAT,
complexity, complexity,
@ -195,7 +208,7 @@ declare_clippy_lint! {
/// let _: &f32 = std::mem::transmute(&1u32); /// let _: &f32 = std::mem::transmute(&1u32);
/// } /// }
/// // These can be respectively written: /// // These can be respectively written:
/// let _ = ptr as *const f32 /// let _ = ptr as *const f32;
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) }; /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
/// ``` /// ```
pub TRANSMUTE_PTR_TO_PTR, pub TRANSMUTE_PTR_TO_PTR,

View file

@ -39,14 +39,15 @@ declare_clippy_lint! {
/// each other. /// each other.
/// ///
/// **Example:** /// **Example:**
///
/// ```rust /// ```rust
/// fn foo(v: &u32) { /// // Bad
/// assert_eq!(v, 42); /// fn foo(v: &u32) {}
/// } /// ```
/// // should be ///
/// fn foo(v: u32) { /// ```rust
/// assert_eq!(v, 42); /// // Better
/// } /// fn foo(v: u32) {}
/// ``` /// ```
pub TRIVIALLY_COPY_PASS_BY_REF, pub TRIVIALLY_COPY_PASS_BY_REF,
perf, perf,

View file

@ -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 if_chain::if_chain;
use rustc::hir::*; use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass}; use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
@ -67,10 +67,15 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TryErr {
then { then {
let err_type = cx.tables.expr_ty(err_arg); let err_type = cx.tables.expr_ty(err_arg);
let suggestion = if err_type == return_type { let origin_snippet = if in_macro_or_desugar(err_arg.span) {
format!("return Err({})", snippet(cx, err_arg.span, "_")) snippet_with_macro_callsite(cx, err_arg.span, "_")
} else { } 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( span_lint_and_sugg(

View file

@ -25,7 +25,7 @@ use crate::utils::paths;
use crate::utils::{ use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, in_macro_or_desugar, int_bits, last_path_segment, 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, 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! { declare_clippy_lint! {
@ -133,7 +133,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let x = LinkedList::new(); /// # use std::collections::LinkedList;
/// let x: LinkedList<usize> = LinkedList::new();
/// ``` /// ```
pub LINKEDLIST, pub LINKEDLIST,
pedantic, pedantic,
@ -467,15 +468,17 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for LetUnitValue {
if higher::is_from_for_desugar(local) { if higher::is_from_for_desugar(local) {
return; return;
} }
span_lint( span_lint_and_then(cx, LET_UNIT_VALUE, stmt.span, "this let-binding has unit value", |db| {
cx, if let Some(expr) = &local.init {
LET_UNIT_VALUE, let snip = snippet_with_macro_callsite(cx, expr.span, "()");
stmt.span, db.span_suggestion(
&format!( stmt.span,
"this let-binding has unit value. Consider omitting `let {} =`", "omit the `let` binding",
snippet(cx, local.pat.span, "..") format!("{};", snip),
), Applicability::MachineApplicable, // snippet
); );
}
});
} }
} }
} }
@ -557,7 +560,7 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// foo({ /// foo({
/// let a = bar(); /// let a = bar();
/// baz(a); /// baz(a);
@ -621,9 +624,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnitArg {
} }
fn is_questionmark_desugar_marked_call(expr: &Expr) -> bool { 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 { if let ExprKind::Call(ref callee, _) = expr.node {
callee.span.is_compiler_desugaring(CompilerDesugaringKind::QuestionMark) callee.span.is_desugaring(DesugaringKind::QuestionMark)
} else { } else {
false false
} }
@ -660,8 +663,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let x = u64::MAX; /// let x = std::u64::MAX;
/// x as f64 /// x as f64;
/// ``` /// ```
pub CAST_PRECISION_LOSS, pub CAST_PRECISION_LOSS,
pedantic, pedantic,
@ -682,7 +685,7 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let y: i8 = -1; /// let y: i8 = -1;
/// y as u128 // will return 18446744073709551615 /// y as u128; // will return 18446744073709551615
/// ``` /// ```
pub CAST_SIGN_LOSS, pub CAST_SIGN_LOSS,
pedantic, pedantic,
@ -690,7 +693,7 @@ declare_clippy_lint! {
} }
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 /// truncate large values. This is expected behavior, so the cast is `Allow` by
/// default. /// default.
/// ///
@ -727,7 +730,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```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, pub CAST_POSSIBLE_WRAP,
pedantic, pedantic,
@ -735,7 +738,7 @@ declare_clippy_lint! {
} }
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. /// be replaced by safe conversion functions.
/// ///
/// **Why is this bad?** Rust's `as` keyword will perform many kinds of /// **Why is this bad?** Rust's `as` keyword will perform many kinds of
@ -775,7 +778,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let _ = 2i32 as i32 /// let _ = 2i32 as i32;
/// ``` /// ```
pub UNNECESSARY_CAST, pub UNNECESSARY_CAST,
complexity, complexity,
@ -789,7 +792,8 @@ declare_clippy_lint! {
/// **Why is this bad?** Dereferencing the resulting pointer may be undefined /// **Why is this bad?** Dereferencing the resulting pointer may be undefined
/// behavior. /// 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:** /// **Example:**
/// ```rust /// ```rust
@ -1210,17 +1214,25 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Casts {
if_chain! { if_chain! {
if let ty::RawPtr(from_ptr_ty) = &cast_from.sty; if let ty::RawPtr(from_ptr_ty) = &cast_from.sty;
if let ty::RawPtr(to_ptr_ty) = &cast_to.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 Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
if let Some(to_align) = cx.layout_of(to_ptr_ty.ty).ok().map(|a| a.align.abi); if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
if from_align < to_align; if from_layout.align.abi < to_layout.align.abi;
// with c_void, we inherently need to trust the user // with c_void, we inherently need to trust the user
if !is_c_void(cx, from_ptr_ty.ty); 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 { then {
span_lint( span_lint(
cx, cx,
CAST_PTR_ALIGNMENT, CAST_PTR_ALIGNMENT,
expr.span, 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 { match cast_from.sty {
ty::FnDef(..) | ty::FnPtr(_) => { 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 from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx); let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
@ -1286,6 +1298,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::rc::Rc;
/// struct Foo { /// struct Foo {
/// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>, /// 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) { fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx ImplItem) {
match item.node { 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 // methods are covered by check_fn
_ => (), _ => (),
} }
@ -1449,13 +1462,13 @@ declare_clippy_lint! {
/// **Known problems:** None. /// **Known problems:** None.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// 'x' as u8 /// 'x' as u8
/// ``` /// ```
/// ///
/// A better version, using the byte literal: /// A better version, using the byte literal:
/// ///
/// ```rust /// ```rust,ignore
/// b'x' /// b'x'
/// ``` /// ```
pub CHAR_LIT_AS_U8, pub CHAR_LIT_AS_U8,
@ -1679,7 +1692,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// let x : u8 = ...; (x as u32) > 300 /// let x: u8 = 1;
/// (x as u32) > 300;
/// ``` /// ```
pub INVALID_UPCAST_COMPARISONS, pub INVALID_UPCAST_COMPARISONS,
pedantic, pedantic,
@ -1716,10 +1730,10 @@ impl PartialEq for FullInt {
impl PartialOrd for FullInt { impl PartialOrd for FullInt {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(match (self, other) { Some(match (self, other) {
(&FullInt::S(s), &FullInt::S(o)) => s.cmp(&o), (&Self::S(s), &Self::S(o)) => s.cmp(&o),
(&FullInt::U(s), &FullInt::U(o)) => s.cmp(&o), (&Self::U(s), &Self::U(o)) => s.cmp(&o),
(&FullInt::S(s), &FullInt::U(o)) => Self::cmp_s_u(s, o), (&Self::S(s), &Self::U(o)) => Self::cmp_s_u(s, o),
(&FullInt::U(s), &FullInt::S(o)) => Self::cmp_s_u(o, s).reverse(), (&Self::U(s), &Self::S(o)) => Self::cmp_s_u(o, s).reverse(),
}) })
} }
} }
@ -1914,12 +1928,21 @@ declare_clippy_lint! {
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # use std::collections::HashMap; /// # use std::collections::HashMap;
/// # use std::hash::Hash; /// # use std::hash::{Hash, BuildHasher};
/// # trait Serialize {}; /// # trait Serialize {};
/// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { } /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
/// ///
/// pub fn foo(map: &mut HashMap<i32, i32>) { } /// 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, pub IMPLICIT_HASHER,
style, style,
"missing generalization over different hashers" "missing generalization over different hashers"
@ -2029,6 +2052,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitHasher {
vis.visit_ty(ty); vis.visit_ty(ty);
for target in &vis.found { for target in &vis.found {
if in_external_macro(cx.sess(), generics.span) {
continue;
}
let generics_suggestion_span = generics.span.substitute_dummy({ let generics_suggestion_span = generics.span.substitute_dummy({
let pos = snippet_opt(cx, item.span.until(body.arguments[0].pat.span)) let pos = snippet_opt(cx, item.span.until(body.arguments[0].pat.span))
.and_then(|snip| { .and_then(|snip| {

View file

@ -14,10 +14,12 @@ declare_clippy_lint! {
/// ///
/// **Why is this bad?** Using `if let` or `match` is more idiomatic. /// **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:** /// **Example:**
/// ```rust /// ```rust
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if option.is_some() { /// if option.is_some() {
/// do_something_with(option.unwrap()) /// do_something_with(option.unwrap())
/// } /// }
@ -26,12 +28,14 @@ declare_clippy_lint! {
/// Could be written: /// Could be written:
/// ///
/// ```rust /// ```rust
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if let Some(value) = option { /// if let Some(value) = option {
/// do_something_with(value) /// do_something_with(value)
/// } /// }
/// ``` /// ```
pub UNNECESSARY_UNWRAP, pub UNNECESSARY_UNWRAP,
nursery, complexity,
"checks for calls of unwrap[_err]() that cannot fail" "checks for calls of unwrap[_err]() that cannot fail"
} }
@ -45,6 +49,8 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let option = Some(0);
/// # fn do_something_with(_x: usize) {}
/// if option.is_none() { /// if option.is_none() {
/// do_something_with(option.unwrap()) /// do_something_with(option.unwrap())
/// } /// }
@ -52,7 +58,7 @@ declare_clippy_lint! {
/// ///
/// This code will always panic. The if condition should probably be inverted. /// This code will always panic. The if condition should probably be inverted.
pub PANICKING_UNWRAP, pub PANICKING_UNWRAP,
nursery, correctness,
"checks for calls of unwrap[_err]() that will always fail" "checks for calls of unwrap[_err]() that will always fail"
} }

View file

@ -51,9 +51,11 @@ declare_lint_pass!(UseSelf => [USE_SELF]);
const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element"; 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. // 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. // Only take path up to the end of last_path_span.
let span = path.span.with_hi(last_path_span.hi()); 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 trait_ty = self.trait_type_walker.next();
let impl_ty = self.impl_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 implementation and trait types don't match which means that
// the concrete type was specified by the implementation // the concrete type was specified by the implementation
if impl_ty != trait_ty { if impl_ty != trait_ty;
if let Some(impl_ty) = impl_ty { if let Some(impl_ty) = impl_ty;
if self.item_type == impl_ty { if self.item_type == impl_ty;
let is_self_ty = if let def::Res::SelfTy(..) = path.res { then {
true match path.res {
} else { def::Res::SelfTy(..) => {},
false _ => span_use_self_lint(self.cx, path, None)
};
if !is_self_ty {
span_use_self_lint(self.cx, path);
}
}
} }
} }
} }
@ -220,15 +218,34 @@ struct UseSelfVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) { fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper { if path.segments.len() >= 2 {
if self.item_path.res == path.res { let last_but_one = &path.segments[path.segments.len() - 2];
span_use_self_lint(self.cx, path); if last_but_one.ident.name != kw::SelfUpper {
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_did) = path.res { let enum_def_id = match path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_did) { Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
span_use_self_lint(self.cx, path); 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); walk_path(self, path);
} }

View file

@ -15,7 +15,7 @@ declare_clippy_lint! {
/// **What it does:** Generates clippy code that detects the offending pattern /// **What it does:** Generates clippy code that detects the offending pattern
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// // ./tests/ui/my_lint.rs /// // ./tests/ui/my_lint.rs
/// fn foo() { /// fn foo() {
/// // detect the following pattern /// // detect the following pattern
@ -24,13 +24,14 @@ declare_clippy_lint! {
/// // but ignore everything from here on /// // but ignore everything from here on
/// #![clippy::author = "ignore"] /// #![clippy::author = "ignore"]
/// } /// }
/// ()
/// } /// }
/// ``` /// ```
/// ///
/// Running `TESTNAME=ui/my_lint cargo uitest` will produce /// Running `TESTNAME=ui/my_lint cargo uitest` will produce
/// a `./tests/ui/new_lint.stdout` file with the generated code: /// a `./tests/ui/new_lint.stdout` file with the generated code:
/// ///
/// ```rust /// ```rust,ignore
/// // ./tests/ui/new_lint.stdout /// // ./tests/ui/new_lint.stdout
/// if_chain! { /// if_chain! {
/// if let ExprKind::If(ref cond, ref then, None) = item.node, /// if let ExprKind::If(ref cond, ref then, None) = item.node,

View file

@ -44,15 +44,15 @@ pub enum Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
match *self { match *self {
Error::Io(ref err) => err.fmt(f), Self::Io(ref err) => err.fmt(f),
Error::Toml(ref err) => err.fmt(f), Self::Toml(ref err) => err.fmt(f),
} }
} }
} }
impl From<io::Error> for Error { impl From<io::Error> for Error {
fn from(e: io::Error) -> Self { fn from(e: io::Error) -> Self {
Error::Io(e) Self::Io(e)
} }
} }

View file

@ -438,9 +438,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(fun); self.hash_expr(fun);
self.hash_exprs(args); 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); self.hash_expr(e);
// TODO: _ty self.hash_ty(ty);
}, },
ExprKind::Closure(cap, _, eid, _, _) => { ExprKind::Closure(cap, _, eid, _, _) => {
match cap { match cap {
@ -512,7 +512,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e); 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); self.hash_exprs(v);
}, },
ExprKind::Unary(lop, ref le) => { 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 => {},
}
}
} }

View file

@ -13,14 +13,14 @@ declare_clippy_lint! {
/// attribute /// attribute
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// #[clippy::dump] /// #[clippy::dump]
/// extern crate foo; /// extern crate foo;
/// ``` /// ```
/// ///
/// prints /// prints
/// ///
/// ``` /// ```text
/// item `foo` /// item `foo`
/// visibility inherited from outer item /// visibility inherited from outer item
/// extern crate dylib source: "/path/to/foo.so" /// 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); print_expr(cx, &cx.tcx.hir().body(body_id).value, 1);
}, },
hir::ImplItemKind::Method(..) => println!("method"), hir::ImplItemKind::Method(..) => println!("method"),
hir::ImplItemKind::Type(_) => println!("associated type"), hir::ImplItemKind::TyAlias(_) => println!("associated type"),
hir::ImplItemKind::Existential(_) => println!("existential type"), hir::ImplItemKind::OpaqueTy(_) => println!("existential type"),
} }
} }
// fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx // 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::Mod(..) => println!("module"),
hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi), hir::ItemKind::ForeignMod(ref fm) => println!("foreign module with abi: {}", fm.abi),
hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm), hir::ItemKind::GlobalAsm(ref asm) => println!("global asm: {:?}", asm),
hir::ItemKind::Ty(..) => { hir::ItemKind::TyAlias(..) => {
println!("type alias for {:?}", cx.tcx.type_of(did)); 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)); println!("existential type with real type {:?}", cx.tcx.type_of(did));
}, },
hir::ItemKind::Enum(..) => { hir::ItemKind::Enum(..) => {

View file

@ -38,7 +38,7 @@ declare_clippy_lint! {
/// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros. /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust,ignore
/// declare_lint! { pub LINT_1, ... } /// declare_lint! { pub LINT_1, ... }
/// declare_lint! { pub LINT_2, ... } /// declare_lint! { pub LINT_2, ... }
/// declare_lint! { pub FORGOTTEN_LINT, ... } /// declare_lint! { pub FORGOTTEN_LINT, ... }
@ -62,12 +62,12 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// Bad: /// Bad:
/// ```rust /// ```rust,ignore
/// cx.span_lint(LINT_NAME, "message"); /// cx.span_lint(LINT_NAME, "message");
/// ``` /// ```
/// ///
/// Good: /// Good:
/// ```rust /// ```rust,ignore
/// utils::span_lint(cx, LINT_NAME, "message"); /// utils::span_lint(cx, LINT_NAME, "message");
/// ``` /// ```
pub COMPILER_LINT_FUNCTIONS, pub COMPILER_LINT_FUNCTIONS,
@ -85,17 +85,17 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// Bad: /// Bad:
/// ```rust /// ```rust,ignore
/// expr.span.ctxt().outer().expn_info() /// expr.span.ctxt().outer().expn_info()
/// ``` /// ```
/// ///
/// Good: /// Good:
/// ```rust /// ```rust,ignore
/// expr.span.ctxt().outer_expn_info() /// expr.span.ctxt().outer_expn_info()
/// ``` /// ```
pub OUTER_EXPN_INFO, pub OUTER_EXPN_EXPN_INFO,
internal, 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]); declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
@ -280,7 +280,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for CompilerLintFunctions {
pub struct OuterExpnInfoPass; 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 { impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) { 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<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(); let method_names: Vec<&str> = method_names.iter().map(std::convert::AsRef::as_ref).collect();
if_chain! { 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]; let args = arg_lists[1];
if args.len() == 1; if args.len() == 1;
let self_arg = &args[0]; let self_arg = &args[0];
@ -297,9 +297,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for OuterExpnInfoPass {
then { then {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
OUTER_EXPN_INFO, OUTER_EXPN_EXPN_INFO,
expr.span.trim_start(self_arg.span).unwrap_or(expr.span), expr.span.trim_start(self_arg.span).unwrap_or(expr.span),
"usage of `outer().expn_info()`", "usage of `outer_expn().expn_info()`",
"try", "try",
".outer_expn_info()".to_string(), ".outer_expn_info()".to_string(),
Applicability::MachineApplicable, Applicability::MachineApplicable,

View file

@ -43,7 +43,7 @@ use rustc_errors::Applicability;
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::ast::{self, LitKind}; use syntax::ast::{self, LitKind};
use syntax::attr; use syntax::attr;
use syntax::ext::hygiene::ExpnFormat; use syntax::ext::hygiene::ExpnKind;
use syntax::source_map::{Span, DUMMY_SP}; use syntax::source_map::{Span, DUMMY_SP};
use syntax::symbol::{kw, Symbol}; 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. /// Returns `true` if this `expn_info` was expanded by any macro.
pub fn in_macro(span: Span) -> bool { pub fn in_macro(span: Span) -> bool {
if let Some(info) = span.ctxt().outer_expn_info() { if let Some(info) = span.ctxt().outer_expn_info() {
if let ExpnFormat::CompilerDesugaring(..) = info.format { if let ExpnKind::Desugaring(..) = info.kind {
false false
} else { } else {
true true
@ -686,7 +686,7 @@ pub fn is_adjusted(cx: &LateContext<'_, '_>, e: &Expr) -> bool {
/// See also `is_direct_expn_of`. /// See also `is_direct_expn_of`.
pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> { pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
loop { 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 { match span_name_span {
Some((mac_name, new_span)) if mac_name.as_str() == name => return Some(new_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 /// `bar!` by
/// `is_direct_expn_of`. /// `is_direct_expn_of`.
pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> { 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 { match span_name_span {
Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span), Some((mac_name, new_span)) if mac_name.as_str() == name => Some(new_span),

View file

@ -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 HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"]; pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"]; 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: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"]; pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; 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_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"]; pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"]; 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 MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"]; pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"]; 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 TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"]; 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 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: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"]; pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"]; pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];

View file

@ -77,6 +77,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// # let foo = "bar";
/// println!("{:?}", foo); /// println!("{:?}", foo);
/// ``` /// ```
pub USE_DEBUG, pub USE_DEBUG,
@ -364,7 +365,9 @@ fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (O
align: AlignUnknown, align: AlignUnknown,
flags: 0, flags: 0,
precision: CountImplied, precision: CountImplied,
precision_span: None,
width: CountImplied, width: CountImplied,
width_span: None,
ty: "", ty: "",
}; };
if !parser.eat(&token::Comma) { if !parser.eat(&token::Comma) {

View file

@ -15,7 +15,7 @@ declare_clippy_lint! {
/// ///
/// **Example:** /// **Example:**
/// ```rust /// ```rust
/// 0.0f32 / 0.0 /// 0.0f32 / 0.0;
/// ``` /// ```
pub ZERO_DIVIDED_BY_ZERO, pub ZERO_DIVIDED_BY_ZERO,
complexity, complexity,

View file

@ -345,16 +345,18 @@ list][lint_list].
### Running rustfmt ### Running rustfmt
[Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust code according [Rustfmt](https://github.com/rust-lang/rustfmt) is a tool for formatting Rust
to style guidelines. Your code has to be formatted by `rustfmt` before a PR can be merged. 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`: It can be installed via `rustup`:
```bash ```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 ### Debugging
@ -366,12 +368,14 @@ output in the `stdout` part.
Before submitting your PR make sure you followed all of the basic requirements: 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] - [ ] Followed [lint naming conventions][lint_naming]
- [ ] Added passing UI tests (including committed `.stderr` file) - [ ] Added passing UI tests (including committed `.stderr` file)
- [ ] `cargo test` passes locally - [ ] `cargo test` passes locally
- [ ] Executed `util/dev update_lints` - [ ] Executed `./util/dev update_lints`
- [ ] Added lint documentation - [ ] Added lint documentation
- [ ] Run `cargo fmt` - [ ] Run `./util/dev fmt`
### Cheatsheet ### Cheatsheet

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