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]
- [ ] Added passing UI tests (including committed `.stderr` file)
- [ ] `cargo test` passes locally
- [ ] Executed `util/dev update_lints`
- [ ] Executed `./util/dev update_lints`
- [ ] Added lint documentation
- [ ] Run `cargo fmt`
- [ ] Run `./util/dev fmt`
[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
Note that you can skip the above if you are just opening a WIP PR in
order to get feedback.

View file

@ -17,6 +17,7 @@ branches:
env:
global:
- RUST_BACKTRACE=1
- secure: "OKulfkA5OGd/d1IhvBKzRkHQwMcWjzrzbimo7+5NhkUkWxndAzl+719TB3wWvIh1i2wXXrEXsyZkXM5FtRrHm55v1VKQ5ibjEvFg1w3NIg81iDyoLq186fLqywvxGkOAFPrsePPsBj5USd5xvhwwbrjO6L7/RK6Z8shBwOSc41s="
install:
- |
@ -45,7 +46,7 @@ matrix:
- os: linux
env: BASE_TESTS=true
- os: windows
env: CARGO_INCREMENTAL=0 BASE_TESTS=true
env: CARGO_INCREMENTAL=0 BASE_TESTS=true OS_WINDOWS=true
# Builds that are only executed when a PR is r+ed or a try build is started
# We don't want to run these always because they go towards
@ -81,9 +82,6 @@ matrix:
if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try)
- env: INTEGRATION=chronotope/chrono
if: repo =~ /^rust-lang\/rust-clippy$/ AND branch IN (auto, try)
allow_failures:
- os: windows
env: CARGO_INCREMENTAL=0 BASE_TESTS=true
# prevent these jobs with default env vars
exclude:
- os: linux
@ -91,10 +89,28 @@ matrix:
- os: windows
script:
- |
if [ "$TRAVIS_BRANCH" == "auto" ] || [ "$TRAVIS_BRANCH" == "try" ]; then
pr=$(echo $TRAVIS_COMMIT_MESSAGE | grep -o "#[0-9]*" | head -1 | sed 's/^#//g')
output=$(curl -H "Authorization: token $GITHUB_API_TOKEN" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$pr" | \
python -c "import sys, json; print(json.load(sys.stdin)['body'])" | \
grep "^changelog: " | \
sed "s/changelog: //g")
if [ -z "$output" ]; then
echo "ERROR: PR body must contain 'changelog: ...'"
exit 1
elif [ "$output" = "none" ]; then
echo "WARNING: changelog is 'none'"
fi
fi
- |
rm rust-toolchain
./setup-toolchain.sh
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
if [ "$TRAVIS_OS_NAME" == "windows" ]; then
export PATH=$PATH:$(rustc --print sysroot)/bin
else
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
fi
- |
if [ -z ${INTEGRATION} ]; then
travis_wait 30 ./ci/base-tests.sh && sleep 5
@ -104,7 +120,7 @@ script:
after_success: |
#!/bin/bash
if [ $(uname) == Linux ]; then
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
set -ex
if [ -z ${INTEGRATION} ]; then
./.github/deploy.sh

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
[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
@ -981,7 +983,6 @@ Released 2018-09-13
[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
@ -999,6 +1000,7 @@ Released 2018-09-13
[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
@ -1137,6 +1139,7 @@ Released 2018-09-13
[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg

View file

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

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.
[There are 306 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are 309 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
@ -36,6 +36,7 @@ Table of contents:
* [Usage instructions](#usage)
* [Configuration](#configuration)
* [Contributing](#contributing)
* [License](#license)
## Usage
@ -52,7 +53,7 @@ subcommand.
#### Step 1: Install rustup
You can install [rustup](http://rustup.rs/) on supported platforms. This will help
You can install [rustup](https://rustup.rs/) on supported platforms. This will help
us install Clippy and its dependencies.
If you already have rustup installed, update to ensure you have the latest
@ -79,6 +80,15 @@ Now you can run Clippy by invoking the following command:
cargo clippy
```
#### Automatically applying Clippy suggestions
Some Clippy lint suggestions can be automatically applied by `cargo fix`.
Note that this is still experimental and only supported on the nightly channel:
```terminal
cargo fix -Z unstable-options --clippy
```
### Running Clippy from the command line without installing it
To have cargo compile your crate with Clippy without Clippy installation
@ -88,8 +98,7 @@ in your code, you can use:
cargo run --bin cargo-clippy --manifest-path=path_to_clippys_Cargo.toml
```
*[Note](https://github.com/rust-lang/rust-clippy/wiki#a-word-of-warning):*
Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
*Note:* Be sure that Clippy was compiled with the same version of rustc that cargo invokes here!
### Travis CI
@ -113,7 +122,7 @@ script:
```
If you are on nightly, It might happen that Clippy is not available for a certain nightly release.
In this case you can try to conditionally install Clippy from the git repo.
In this case you can try to conditionally install Clippy from the Git repo.
```yaml
language: rust
@ -121,12 +130,12 @@ rust:
- nightly
before_script:
- rustup component add clippy --toolchain=nightly || cargo install --git https://github.com/rust-lang/rust-clippy/ --force clippy
# etc
# etc.
```
Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
an error for clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
line. (You can swap `clippy::all` with the specific lint category you are targeting.)
## Configuration
@ -154,9 +163,9 @@ You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
`#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
lints prone to false positives.
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc)
* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc
* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
Note: `deny` produces errors instead of warnings.
@ -171,7 +180,7 @@ If you want to contribute to Clippy, you can find more information in [CONTRIBUT
Copyright 2014-2019 The Rust Project Developers
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
<LICENSE-MIT or [http://opensource.org/licenses/MIT](http://opensource.org/licenses/MIT)>, at your
[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
option. All files in the project carrying such notice may not be
copied, modified, or distributed except according to those terms.

View file

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

View file

@ -7,8 +7,8 @@ if [ "$TRAVIS_OS_NAME" == "linux" ]; then
remark -f *.md -f doc/*.md > /dev/null
fi
# build clippy in debug mode and run tests
cargo build --features debugging
cargo test --features debugging
cargo build --features "debugging deny-warnings"
cargo test --features "debugging deny-warnings"
# for faster build, share target dir between subcrates
export CARGO_TARGET_DIR=`pwd`/target/
(cd clippy_lints && cargo test)
@ -24,21 +24,23 @@ export CARGO_TARGET_DIR=`pwd`/target/
# Perform various checks for lint registration
./util/dev update_lints --check
./util/dev --limit-stderr-length
cargo +nightly fmt --all -- --check
# Check running clippy-driver without cargo
(
export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib
# Check sysroot handling
sysroot=$(./target/debug/clippy-driver --print sysroot)
test $sysroot = $(rustc --print sysroot)
sysroot=$(./target/debug/clippy-driver --sysroot /tmp --print sysroot)
test $sysroot = /tmp
if [ -z $OS_WINDOWS ]; then
desired_sysroot=/tmp
else
desired_sysroot=C:/tmp
fi
sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
test $sysroot = $desired_sysroot
sysroot=$(SYSROOT=/tmp ./target/debug/clippy-driver --print sysroot)
test $sysroot = /tmp
sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
test $sysroot = $desired_sysroot
# Make sure this isn't set - clippy-driver should cope without it
unset CARGO_MANIFEST_DIR
@ -50,32 +52,3 @@ cargo +nightly fmt --all -- --check
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
)
# make sure tests are formatted
# some lints are sensitive to formatting, exclude some files
tests_need_reformatting="false"
# switch to nightly
rustup override set nightly
# avoid loop spam and allow cmds with exit status != 0
set +ex
# Excluding `ice-3891.rs` because the code triggers a rustc parse error which
# makes rustfmt fail.
for file in `find tests -not -path "tests/ui/crashes/ice-3891.rs" | grep "\.rs$"` ; do
rustfmt ${file} --check
if [ $? -ne 0 ]; then
echo "${file} needs reformatting!"
tests_need_reformatting="true"
fi
done
set -ex # reset
if [ "${tests_need_reformatting}" == "true" ] ; then
echo "Tests need reformatting!"
exit 2
fi
# switch back to master
rustup override set master

View file

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

200
clippy_dev/src/fmt.rs Normal file
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 clippy_dev::*;
mod fmt;
mod stderr_length_check;
#[derive(PartialEq)]
@ -14,6 +16,21 @@ enum UpdateMode {
fn main() {
let matches = App::new("Clippy developer tooling")
.subcommand(
SubCommand::with_name("fmt")
.about("Run rustfmt on all projects and tests")
.arg(
Arg::with_name("check")
.long("check")
.help("Use the rustfmt --check option"),
)
.arg(
Arg::with_name("verbose")
.short("v")
.long("verbose")
.help("Echo commands run"),
),
)
.subcommand(
SubCommand::with_name("update_lints")
.about("Updates lint registration and information from the source code")
@ -46,14 +63,21 @@ fn main() {
if matches.is_present("limit-stderr-length") {
stderr_length_check::check();
}
if let Some(matches) = matches.subcommand_matches("update_lints") {
if matches.is_present("print-only") {
print_lints();
} else if matches.is_present("check") {
update_lints(&UpdateMode::Check);
} else {
update_lints(&UpdateMode::Change);
}
match matches.subcommand() {
("fmt", Some(matches)) => {
fmt::run(matches.is_present("check"), matches.is_present("verbose"));
},
("update_lints", Some(matches)) => {
if matches.is_present("print-only") {
print_lints();
} else if matches.is_present("check") {
update_lints(&UpdateMode::Check);
} else {
update_lints(&UpdateMode::Change);
}
},
_ => {},
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -24,7 +24,8 @@ declare_clippy_lint! {
/// **Example:**
///
/// ```rust
/// &my_data.filter(|&x| x == 0u8).count() // use bytecount::count instead
/// # let vec = vec![1_u8];
/// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
/// ```
pub NAIVE_BYTECOUNT,
perf,

View file

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

View file

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

View file

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

View file

@ -113,3 +113,12 @@ declare_deprecated_lint! {
pub UNSAFE_VECTOR_INITIALIZATION,
"the replacement suggested by this lint had substantially different behavior"
}
/// **What it does:** Nothing. This lint has been deprecated.
///
/// **Deprecation reason:** This lint has been superseded by the warn-by-default
/// `invalid_value` rustc lint.
declare_clippy_lint! {
pub INVALID_REF,
"superseded by rustc lint `invalid_value`"
}

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
///
/// **Example:**
/// ```rust
/// ```rust,ignore
/// #[derive(Copy)]
/// struct Foo;
///
/// impl Clone for Foo {
/// ..
/// // ..
/// }
/// ```
pub EXPL_IMPL_CLONE_ON_COPY,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -16,21 +16,32 @@ declare_clippy_lint! {
///
/// **Known problems:** Some false negatives, eg.:
/// ```rust
/// let k = &key;
/// if !m.contains_key(k) {
/// m.insert(k.clone(), v);
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let v = 1;
/// # let k = 1;
/// if !map.contains_key(&k) {
/// map.insert(k.clone(), v);
/// }
/// ```
///
/// **Example:**
/// ```rust
/// if !m.contains_key(&k) {
/// m.insert(k, v)
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let k = 1;
/// # let v = 1;
/// if !map.contains_key(&k) {
/// map.insert(k, v);
/// }
/// ```
/// can be rewritten as:
/// can both be rewritten as:
/// ```rust
/// m.entry(k).or_insert(v);
/// # use std::collections::HashMap;
/// # let mut map = HashMap::new();
/// # let k = 1;
/// # let v = 1;
/// map.entry(k).or_insert(v);
/// ```
pub MAP_ENTRY,
perf,

View file

@ -160,6 +160,7 @@ fn check_variant(
let name = var2str(var);
if partial_match(item_name, &name) == item_name_chars
&& name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
&& name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
{
span_lint(cx, lint, var.span, "Variant name starts with the enum's name");
}
@ -178,6 +179,9 @@ fn check_variant(
let pre_camel = camel_case::until(pre);
pre = &pre[..pre_camel];
while let Some((next, last)) = name[pre.len()..].chars().zip(pre.chars().rev()).next() {
if next.is_numeric() {
return;
}
if next.is_lowercase() {
let last = pre.len() - last.len_utf8();
let last_camel = camel_case::until(&pre[..last]);

View file

@ -1,5 +1,5 @@
use rustc::hir::intravisit as visit;
use rustc::hir::*;
use rustc::hir::{self, *};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::middle::expr_use_visitor::*;
use rustc::middle::mem_categorization::{cmt_, Categorization};
@ -28,11 +28,10 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust
/// fn main() {
/// let x = Box::new(1);
/// foo(*x);
/// println!("{}", *x);
/// }
/// # fn foo(bar: usize) {}
/// let x = Box::new(1);
/// foo(*x);
/// println!("{}", *x);
/// ```
pub BOXED_LOCAL,
perf,
@ -101,6 +100,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoxedLocal {
}
}
// TODO: Replace with Map::is_argument(..) when it's fixed
fn is_argument(map: &hir::map::Map<'_>, id: HirId) -> bool {
match map.find(id) {
Some(Node::Binding(_)) => (),
_ => return false,
}
match map.find(map.get_parent_node(id)) {
Some(Node::Arg(_)) => true,
_ => false,
}
}
impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn consume(&mut self, _: HirId, _: Span, cmt: &cmt_<'tcx>, mode: ConsumeMode) {
if let Categorization::Local(lid) = cmt.cat {
@ -113,11 +125,13 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
fn matched_pat(&mut self, _: &Pat, _: &cmt_<'tcx>, _: MatchMode) {}
fn consume_pat(&mut self, consume_pat: &Pat, cmt: &cmt_<'tcx>, _: ConsumeMode) {
let map = &self.cx.tcx.hir();
if map.is_argument(consume_pat.hir_id) {
if is_argument(map, consume_pat.hir_id) {
// Skip closure arguments
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(consume_pat.hir_id)) {
let parent_id = map.get_parent_node(consume_pat.hir_id);
if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
return;
}
if is_non_trait_box(cmt.ty) && !self.is_large_box(cmt.ty) {
self.set.insert(consume_pat.hir_id);
}

View file

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

View file

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

View file

@ -16,8 +16,10 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust
/// # use std::io::Write;
/// # let bar = "furchtbar";
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
/// writeln!(&mut io::stderr(), "foo: {:?}", bar).unwrap();
/// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
/// ```
pub EXPLICIT_WRITE,
complexity,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,14 @@
use crate::utils::{in_macro_or_desugar, is_expn_of, snippet_opt, span_lint_and_then};
use rustc::hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, MatchSource};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use crate::utils::{
in_macro_or_desugar, match_def_path,
paths::{BEGIN_PANIC, BEGIN_PANIC_FMT},
resolve_node, snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc::{
declare_lint_pass, declare_tool_lint,
hir::{intravisit::FnKind, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, StmtKind},
lint::{LateContext, LateLintPass, LintArray, LintPass},
};
use rustc_errors::Applicability;
use syntax::source_map::Span;
@ -35,71 +42,84 @@ declare_clippy_lint! {
declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
impl ImplicitReturn {
fn lint(cx: &LateContext<'_, '_>, outer_span: syntax_pos::Span, inner_span: syntax_pos::Span, msg: &str) {
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
db.span_suggestion(
outer_span,
msg,
format!("return {}", snippet),
Applicability::MachineApplicable,
);
}
});
}
static LINT_BREAK: &str = "change `break` to `return` as shown";
static LINT_RETURN: &str = "add `return` as shown";
fn expr_match(cx: &LateContext<'_, '_>, expr: &rustc::hir::Expr) {
match &expr.node {
// loops could be using `break` instead of `return`
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
if let Some(expr) = &block.expr {
Self::expr_match(cx, expr);
}
// only needed in the case of `break` with `;` at the end
else if let Some(stmt) = block.stmts.last() {
if let rustc::hir::StmtKind::Semi(expr, ..) = &stmt.node {
// make sure it's a break, otherwise we want to skip
if let ExprKind::Break(.., break_expr) = &expr.node {
if let Some(break_expr) = break_expr {
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
}
}
}
}
},
// use `return` instead of `break`
ExprKind::Break(.., break_expr) => {
if let Some(break_expr) = break_expr {
Self::lint(cx, expr.span, break_expr.span, "change `break` to `return` as shown");
}
},
ExprKind::Match(.., arms, source) => {
let check_all_arms = match source {
MatchSource::IfLetDesugar {
contains_else_clause: has_else,
} => *has_else,
_ => true,
};
fn lint(cx: &LateContext<'_, '_>, outer_span: Span, inner_span: Span, msg: &str) {
let outer_span = outer_span.source_callsite();
let inner_span = inner_span.source_callsite();
if check_all_arms {
for arm in arms {
Self::expr_match(cx, &arm.body);
}
} else {
Self::expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
}
},
// skip if it already has a return statement
ExprKind::Ret(..) => (),
// everything else is missing `return`
_ => {
// make sure it's not just an unreachable expression
if is_expn_of(expr.span, "unreachable").is_none() {
Self::lint(cx, expr.span, expr.span, "add `return` as shown")
}
},
span_lint_and_then(cx, IMPLICIT_RETURN, outer_span, "missing return statement", |db| {
if let Some(snippet) = snippet_opt(cx, inner_span) {
db.span_suggestion(
outer_span,
msg,
format!("return {}", snippet),
Applicability::MachineApplicable,
);
}
});
}
fn expr_match(cx: &LateContext<'_, '_>, expr: &Expr) {
match &expr.node {
// loops could be using `break` instead of `return`
ExprKind::Block(block, ..) | ExprKind::Loop(block, ..) => {
if let Some(expr) = &block.expr {
expr_match(cx, expr);
}
// only needed in the case of `break` with `;` at the end
else if let Some(stmt) = block.stmts.last() {
if_chain! {
if let StmtKind::Semi(expr, ..) = &stmt.node;
// make sure it's a break, otherwise we want to skip
if let ExprKind::Break(.., break_expr) = &expr.node;
if let Some(break_expr) = break_expr;
then {
lint(cx, expr.span, break_expr.span, LINT_BREAK);
}
}
}
},
// use `return` instead of `break`
ExprKind::Break(.., break_expr) => {
if let Some(break_expr) = break_expr {
lint(cx, expr.span, break_expr.span, LINT_BREAK);
}
},
ExprKind::Match(.., arms, source) => {
let check_all_arms = match source {
MatchSource::IfLetDesugar {
contains_else_clause: has_else,
} => *has_else,
_ => true,
};
if check_all_arms {
for arm in arms {
expr_match(cx, &arm.body);
}
} else {
expr_match(cx, &arms.first().expect("if let doesn't have a single arm").body);
}
},
// skip if it already has a return statement
ExprKind::Ret(..) => (),
// make sure it's not a call that panics
ExprKind::Call(expr, ..) => {
if_chain! {
if let ExprKind::Path(qpath) = &expr.node;
if let Some(path_def_id) = resolve_node(cx, qpath, expr.hir_id).opt_def_id();
if match_def_path(cx, path_def_id, &BEGIN_PANIC) ||
match_def_path(cx, path_def_id, &BEGIN_PANIC_FMT);
then { }
else {
lint(cx, expr.span, expr.span, LINT_RETURN)
}
}
},
// everything else is missing `return`
_ => lint(cx, expr.span, expr.span, LINT_RETURN),
}
}
@ -119,7 +139,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ImplicitReturn {
// checking return type through MIR, HIR is not able to determine inferred closure return types
// make sure it's not a macro
if !mir.return_ty().is_unit() && !in_macro_or_desugar(span) {
Self::expr_match(cx, &body.value);
expr_match(cx, &body.value);
}
}
}

View file

@ -47,7 +47,7 @@ declare_clippy_lint! {
/// **Known problems:** Hopefully none.
///
/// **Example:**
/// ```rust
/// ```rust,no_run
/// // Vector
/// let x = vec![0; 5];
///
@ -94,7 +94,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for IndexingSlicing {
if let Some(range) = higher::range(cx, index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.sty {
let size: u128 = s.assert_usize(cx.tcx).unwrap().into();
let size: u128 = s.eval_usize(cx.tcx, cx.param_env).into();
let const_range = to_const_range(cx, range, size);

View file

@ -34,7 +34,8 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust
/// [0..].iter().zip(infinite_iter.take_while(|x| x > 5))
/// let infinite_iter = 0..;
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
/// ```
pub MAYBE_INFINITE_ITER,
pedantic,

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:**
/// ```rust
/// x >= y + 1
/// # let x = 1;
/// # let y = 1;
/// if x >= y + 1 {}
/// ```
///
/// Could be written:
/// Could be written as:
///
/// ```rust
/// x > y
/// # let x = 1;
/// # let y = 1;
/// if x > y {}
/// ```
pub INT_PLUS_ONE,
complexity,

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,
span,
&format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
"using `is_empty` is clearer and more explicit",
&format!("using `{}is_empty` is clearer and more explicit", op),
format!(
"{}{}.is_empty()",
op,

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

@ -7,7 +7,7 @@ use rustc::ty;
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use syntax::ast::LitKind;
use syntax::source_map::{ExpnFormat, Span};
use syntax::source_map::{ExpnKind, Span};
use crate::consts::{constant, Constant};
use crate::utils::sugg::Sugg;
@ -101,7 +101,9 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust
/// x.to_owned() == y
/// # let x = "foo";
/// # let y = String::from("foo");
/// if x.to_owned() == y {}
/// ```
pub CMP_OWNED,
perf,
@ -184,7 +186,7 @@ declare_clippy_lint! {
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// ```rust,ignore
/// f() && g(); // We should write `if f() { g(); }`.
/// ```
pub SHORT_CIRCUIT_STATEMENT,
@ -224,8 +226,9 @@ declare_clippy_lint! {
///
/// **Example:**
/// ```rust
/// const ONE = 1.00f64;
/// x == ONE // where both are floats
/// let x: f64 = 1.0;
/// const ONE: f64 = 1.00;
/// x == ONE; // where both are floats
/// ```
pub FLOAT_CMP_CONST,
restriction,
@ -364,6 +367,12 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MiscLints {
if is_allowed(cx, left) || is_allowed(cx, right) {
return;
}
// Allow comparing the results of signum()
if is_signum(cx, left) && is_signum(cx, right) {
return;
}
if let Some(name) = get_item_name(cx, expr) {
let name = name.as_str();
if name == "eq"
@ -493,6 +502,25 @@ fn is_allowed<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) -> bool {
}
}
// Return true if `expr` is the result of `signum()` invoked on a float value.
fn is_signum(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
// The negation of a signum is still a signum
if let ExprKind::Unary(UnNeg, ref child_expr) = expr.node {
return is_signum(cx, &child_expr);
}
if_chain! {
if let ExprKind::MethodCall(ref method_name, _, ref expressions) = expr.node;
if sym!(signum) == method_name.ident.name;
// Check that the receiver of the signum() is a float (expressions[0] is the receiver of
// the method call)
then {
return is_float(cx, &expressions[0]);
}
}
false
}
fn is_float(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
matches!(walk_ptrs_ty(cx.tables.expr_ty(expr)).sty, ty::Float(_))
}
@ -596,10 +624,14 @@ fn is_used(cx: &LateContext<'_, '_>, expr: &Expr) -> bool {
/// Tests whether an expression is in a macro expansion (e.g., something
/// generated by `#[derive(...)]` or the like).
fn in_attributes_expansion(expr: &Expr) -> bool {
expr.span
.ctxt()
.outer_expn_info()
.map_or(false, |info| matches!(info.format, ExpnFormat::MacroAttribute(_)))
use syntax::ext::hygiene::MacroKind;
expr.span.ctxt().outer_expn_info().map_or(false, |info| {
if let ExpnKind::Macro(MacroKind::Attr, _) = info.kind {
true
} else {
false
}
})
}
/// Tests whether `res` is a variable defined outside a macro.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -19,7 +19,7 @@ declare_clippy_lint! {
/// struct Foo;
///
/// impl PartialEq for Foo {
/// fn eq(&self, other: &Foo) -> bool { ... }
/// fn eq(&self, other: &Foo) -> bool { true }
/// fn ne(&self, other: &Foo) -> bool { !(self == other) }
/// }
/// ```

View file

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

View file

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

View file

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

View file

@ -81,7 +81,8 @@ impl EarlyLintPass for RedundantStaticLifetimes {
if !in_macro_or_desugar(item.span) {
if let ItemKind::Const(ref var_type, _) = item.node {
self.visit_type(var_type, cx, "Constants have by default a `'static` lifetime");
// Don't check associated consts because `'static` cannot be elided on those (issue #2438)
// Don't check associated consts because `'static` cannot be elided on those (issue
// #2438)
}
if let ItemKind::Static(ref var_type, _, _) = item.node {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -48,7 +48,7 @@ declare_clippy_lint! {
///
/// ```rust
/// let x = "Hello".to_owned();
/// x + ", World"
/// x + ", World";
/// ```
pub STRING_ADD,
restriction,
@ -138,6 +138,9 @@ fn is_add(cx: &LateContext<'_, '_>, src: &Expr, target: &Expr) -> bool {
}
}
// Max length a b"foo" string can take
const MAX_LENGTH_BYTE_STRING_LIT: usize = 32;
declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES]);
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
@ -173,6 +176,7 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StringLitAsBytes {
);
} else if callsite == expanded
&& lit_content.as_str().chars().all(|c| c.is_ascii())
&& lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
&& !in_macro_or_desugar(args[0].span)
{
span_lint_and_sugg(

View file

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

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

View file

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

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

View file

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

View file

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

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";
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path) {
fn span_use_self_lint(cx: &LateContext<'_, '_>, path: &Path, last_segment: Option<&PathSegment>) {
let last_segment = last_segment.unwrap_or_else(|| path.segments.last().expect(SEGMENTS_MSG));
// Path segments only include actual path, no methods or fields.
let last_path_span = path.segments.last().expect(SEGMENTS_MSG).ident.span;
let last_path_span = last_segment.ident.span;
// Only take path up to the end of last_path_span.
let span = path.span.with_hi(last_path_span.hi());
@ -80,22 +82,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TraitImplTyVisitor<'a, 'tcx> {
let trait_ty = self.trait_type_walker.next();
let impl_ty = self.impl_type_walker.next();
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node {
if_chain! {
if let TyKind::Path(QPath::Resolved(_, path)) = &t.node;
// The implementation and trait types don't match which means that
// the concrete type was specified by the implementation
if impl_ty != trait_ty {
if let Some(impl_ty) = impl_ty {
if self.item_type == impl_ty {
let is_self_ty = if let def::Res::SelfTy(..) = path.res {
true
} else {
false
};
if !is_self_ty {
span_use_self_lint(self.cx, path);
}
}
if impl_ty != trait_ty;
if let Some(impl_ty) = impl_ty;
if self.item_type == impl_ty;
then {
match path.res {
def::Res::SelfTy(..) => {},
_ => span_use_self_lint(self.cx, path, None)
}
}
}
@ -220,15 +218,34 @@ struct UseSelfVisitor<'a, 'tcx> {
impl<'a, 'tcx> Visitor<'tcx> for UseSelfVisitor<'a, 'tcx> {
fn visit_path(&mut self, path: &'tcx Path, _id: HirId) {
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
if self.item_path.res == path.res {
span_use_self_lint(self.cx, path);
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_did) = path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_did) {
span_use_self_lint(self.cx, path);
if path.segments.len() >= 2 {
let last_but_one = &path.segments[path.segments.len() - 2];
if last_but_one.ident.name != kw::SelfUpper {
let enum_def_id = match path.res {
Res::Def(DefKind::Variant, variant_def_id) => self.cx.tcx.parent(variant_def_id),
Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), ctor_def_id) => {
let variant_def_id = self.cx.tcx.parent(ctor_def_id);
variant_def_id.and_then(|def_id| self.cx.tcx.parent(def_id))
},
_ => None,
};
if self.item_path.res.opt_def_id() == enum_def_id {
span_use_self_lint(self.cx, path, Some(last_but_one));
}
}
}
if path.segments.last().expect(SEGMENTS_MSG).ident.name != kw::SelfUpper {
if self.item_path.res == path.res {
span_use_self_lint(self.cx, path, None);
} else if let Res::Def(DefKind::Ctor(def::CtorOf::Struct, CtorKind::Fn), ctor_def_id) = path.res {
if self.item_path.res.opt_def_id() == self.cx.tcx.parent(ctor_def_id) {
span_use_self_lint(self.cx, path, None);
}
}
}
walk_path(self, path);
}

View file

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

View file

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

View file

@ -438,9 +438,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(fun);
self.hash_exprs(args);
},
ExprKind::Cast(ref e, ref _ty) | ExprKind::Type(ref e, ref _ty) => {
ExprKind::Cast(ref e, ref ty) | ExprKind::Type(ref e, ref ty) => {
self.hash_expr(e);
// TODO: _ty
self.hash_ty(ty);
},
ExprKind::Closure(cap, _, eid, _, _) => {
match cap {
@ -512,7 +512,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e);
}
},
ExprKind::Tup(ref v) | ExprKind::Array(ref v) => {
ExprKind::Tup(ref tup) => {
self.hash_exprs(tup);
},
ExprKind::Array(ref v) => {
self.hash_exprs(v);
},
ExprKind::Unary(lop, ref le) => {
@ -574,4 +577,100 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
},
}
}
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
std::mem::discriminant(&lifetime.name).hash(&mut self.s);
if let LifetimeName::Param(ref name) = lifetime.name {
std::mem::discriminant(name).hash(&mut self.s);
match name {
ParamName::Plain(ref ident) => {
ident.name.hash(&mut self.s);
},
ParamName::Fresh(ref size) => {
size.hash(&mut self.s);
},
ParamName::Error => {},
}
}
}
pub fn hash_ty(&mut self, ty: &Ty) {
self.hash_tykind(&ty.node);
}
pub fn hash_tykind(&mut self, ty: &TyKind) {
std::mem::discriminant(ty).hash(&mut self.s);
match ty {
TyKind::Slice(ty) => {
self.hash_ty(ty);
},
TyKind::Array(ty, anon_const) => {
self.hash_ty(ty);
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
},
TyKind::Ptr(mut_ty) => {
self.hash_ty(&mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
},
TyKind::Rptr(lifetime, mut_ty) => {
self.hash_lifetime(lifetime);
self.hash_ty(&mut_ty.ty);
mut_ty.mutbl.hash(&mut self.s);
},
TyKind::BareFn(bfn) => {
bfn.unsafety.hash(&mut self.s);
bfn.abi.hash(&mut self.s);
for arg in &bfn.decl.inputs {
self.hash_ty(&arg);
}
match bfn.decl.output {
FunctionRetTy::DefaultReturn(_) => {
().hash(&mut self.s);
},
FunctionRetTy::Return(ref ty) => {
self.hash_ty(ty);
},
}
bfn.decl.c_variadic.hash(&mut self.s);
},
TyKind::Tup(ty_list) => {
for ty in ty_list {
self.hash_ty(ty);
}
},
TyKind::Path(qpath) => match qpath {
QPath::Resolved(ref maybe_ty, ref path) => {
if let Some(ref ty) = maybe_ty {
self.hash_ty(ty);
}
for segment in &path.segments {
segment.ident.name.hash(&mut self.s);
}
},
QPath::TypeRelative(ref ty, ref segment) => {
self.hash_ty(ty);
segment.ident.name.hash(&mut self.s);
},
},
TyKind::Def(_, arg_list) => {
for arg in arg_list {
match arg {
GenericArg::Lifetime(ref l) => self.hash_lifetime(l),
GenericArg::Type(ref ty) => self.hash_ty(&ty),
GenericArg::Const(ref ca) => {
self.hash_expr(&self.cx.tcx.hir().body(ca.value.body).value);
},
}
}
},
TyKind::TraitObject(_, lifetime) => {
self.hash_lifetime(lifetime);
},
TyKind::Typeof(anon_const) => {
self.hash_expr(&self.cx.tcx.hir().body(anon_const.body).value);
},
TyKind::CVarArgs(lifetime) => self.hash_lifetime(lifetime),
TyKind::Err | TyKind::Infer | TyKind::Never => {},
}
}
}

View file

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

View file

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

View file

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

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 INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
@ -50,8 +49,6 @@ pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
@ -109,7 +106,6 @@ pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_stri
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_FROM_ERROR: [&str; 4] = ["std", "ops", "Try", "from_error"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];

View file

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

View file

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

View file

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

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