mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-22 04:33:15 +00:00
Merge commit '97a5daa65908e59744e2bc625b14849352231c75' into clippyup
This commit is contained in:
parent
dda2aef64f
commit
fb0142ae41
223 changed files with 3261 additions and 1687 deletions
|
@ -2,9 +2,12 @@
|
|||
uitest = "test --test compile-test"
|
||||
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
|
||||
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
|
||||
collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
|
||||
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
|
||||
|
||||
[build]
|
||||
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
|
||||
rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
|
||||
target-dir = "target"
|
||||
|
||||
[unstable]
|
||||
binary-dep-depinfo = true
|
||||
|
|
16
.github/workflows/clippy.yml
vendored
16
.github/workflows/clippy.yml
vendored
|
@ -49,17 +49,17 @@ jobs:
|
|||
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo build --features deny-warnings,internal
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo test --features deny-warnings,internal
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo test --features deny-warnings,internal
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test clippy_utils
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo test --features deny-warnings,internal
|
||||
working-directory: clippy_utils
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
|
@ -70,14 +70,6 @@ jobs:
|
|||
run: cargo test --features deny-warnings
|
||||
working-directory: clippy_dev
|
||||
|
||||
- name: Test cargo-clippy
|
||||
run: ../target/debug/cargo-clippy
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test cargo-clippy --fix
|
||||
run: ../target/debug/cargo-clippy clippy --fix
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test clippy-driver
|
||||
run: bash .github/driver.sh
|
||||
env:
|
||||
|
|
21
.github/workflows/clippy_bors.yml
vendored
21
.github/workflows/clippy_bors.yml
vendored
|
@ -112,17 +112,22 @@ jobs:
|
|||
echo "$SYSROOT/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build
|
||||
run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo build --features deny-warnings,internal
|
||||
|
||||
- name: Test
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
if: runner.os == 'Linux'
|
||||
run: cargo test --features deny-warnings,internal
|
||||
|
||||
- name: Test
|
||||
if: runner.os != 'Linux'
|
||||
run: cargo test --features deny-warnings,internal -- --skip dogfood
|
||||
|
||||
- name: Test clippy_lints
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo test --features deny-warnings,internal
|
||||
working-directory: clippy_lints
|
||||
|
||||
- name: Test clippy_utils
|
||||
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
|
||||
run: cargo test --features deny-warnings,internal
|
||||
working-directory: clippy_utils
|
||||
|
||||
- name: Test rustc_tools_util
|
||||
|
@ -133,14 +138,6 @@ jobs:
|
|||
run: cargo test --features deny-warnings
|
||||
working-directory: clippy_dev
|
||||
|
||||
- name: Test cargo-clippy
|
||||
run: ../target/debug/cargo-clippy
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test cargo-clippy --fix
|
||||
run: ../target/debug/cargo-clippy clippy --fix
|
||||
working-directory: clippy_workspace_tests
|
||||
|
||||
- name: Test clippy-driver
|
||||
run: bash .github/driver.sh
|
||||
env:
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -19,7 +19,6 @@ out
|
|||
/target
|
||||
/clippy_lints/target
|
||||
/clippy_utils/target
|
||||
/clippy_workspace_tests/target
|
||||
/clippy_dev/target
|
||||
/lintcheck/target
|
||||
/rustc_tools_util/target
|
||||
|
|
|
@ -2887,6 +2887,7 @@ Released 2018-09-13
|
|||
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
|
||||
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
|
||||
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
|
||||
[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
|
||||
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
|
||||
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
|
||||
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
|
||||
|
@ -3070,6 +3071,7 @@ Released 2018-09-13
|
|||
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
|
||||
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
|
||||
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
|
||||
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
|
||||
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
|
||||
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
|
||||
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
|
||||
|
@ -3253,6 +3255,7 @@ Released 2018-09-13
|
|||
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
|
||||
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
|
||||
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
|
||||
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
|
||||
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
|
||||
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
|
||||
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.59"
|
||||
version = "0.1.60"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -47,7 +47,9 @@ itertools = "0.10"
|
|||
quote = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
syn = { version = "1.0", features = ["full"] }
|
||||
futures = "0.3"
|
||||
parking_lot = "0.11.2"
|
||||
tokio = { version = "1", features = ["io-util"] }
|
||||
|
||||
[build-dependencies]
|
||||
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
|
||||
|
@ -55,8 +57,7 @@ rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
|
|||
[features]
|
||||
deny-warnings = ["clippy_lints/deny-warnings"]
|
||||
integration = ["tempfile"]
|
||||
internal-lints = ["clippy_lints/internal-lints"]
|
||||
metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
|
||||
internal = ["clippy_lints/internal"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This package uses #[feature(rustc_private)]
|
||||
|
|
22
README.md
22
README.md
|
@ -5,7 +5,7 @@
|
|||
|
||||
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
||||
|
||||
[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
||||
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
||||
|
@ -37,8 +37,8 @@ Table of contents:
|
|||
|
||||
## Usage
|
||||
|
||||
Below are instructions on how to use Clippy as a subcommand, compiled from source
|
||||
or in Travis CI.
|
||||
Below are instructions on how to use Clippy as a cargo subcommand,
|
||||
in projects that do not use cargo, or in Travis CI.
|
||||
|
||||
### As a cargo subcommand (`cargo clippy`)
|
||||
|
||||
|
@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
|
|||
cargo clippy -p example -- --no-deps
|
||||
```
|
||||
|
||||
### As a rustc replacement (`clippy-driver`)
|
||||
### Using `clippy-driver`
|
||||
|
||||
Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
|
||||
your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
|
||||
|
||||
```terminal
|
||||
rustc --edition 2018 -Cpanic=abort foo.rs
|
||||
```
|
||||
|
||||
Then, to enable Clippy, you will need to call:
|
||||
Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
|
||||
with the same arguments you use for `rustc`. For example:
|
||||
|
||||
```terminal
|
||||
clippy-driver --edition 2018 -Cpanic=abort foo.rs
|
||||
```
|
||||
|
||||
Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
|
||||
Note that `clippy-driver` is designed for running Clippy only and should not be used as a general
|
||||
replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected,
|
||||
for example.
|
||||
|
||||
### Travis CI
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use itertools::Itertools;
|
|||
use shell_escape::escape;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::path::Path;
|
||||
use std::process::{self, Command};
|
||||
use std::process::{self, Command, Stdio};
|
||||
use std::{fs, io};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
|
@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError {
|
|||
struct FmtContext {
|
||||
check: bool,
|
||||
verbose: bool,
|
||||
rustfmt_path: String,
|
||||
}
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
|
@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first."
|
|||
}
|
||||
}
|
||||
|
||||
let context = FmtContext { check, verbose };
|
||||
let output = Command::new("rustup")
|
||||
.args(["which", "rustfmt"])
|
||||
.stderr(Stdio::inherit())
|
||||
.output()
|
||||
.expect("error running `rustup which rustfmt`");
|
||||
if !output.status.success() {
|
||||
eprintln!("`rustup which rustfmt` did not execute successfully");
|
||||
process::exit(1);
|
||||
}
|
||||
let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path");
|
||||
rustfmt_path.truncate(rustfmt_path.trim_end().len());
|
||||
|
||||
let context = FmtContext {
|
||||
check,
|
||||
verbose,
|
||||
rustfmt_path,
|
||||
};
|
||||
let result = try_run(&context);
|
||||
let code = match result {
|
||||
Ok(true) => 0,
|
||||
|
@ -141,8 +158,12 @@ fn exec(
|
|||
println!("{}", format_command(&program, &dir, args));
|
||||
}
|
||||
|
||||
let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
|
||||
let output = child.wait_with_output()?;
|
||||
let output = Command::new(&program)
|
||||
.env("RUSTFMT", &context.rustfmt_path)
|
||||
.current_dir(&dir)
|
||||
.args(args.iter())
|
||||
.output()
|
||||
.unwrap();
|
||||
let success = output.status.success();
|
||||
|
||||
if !context.check && !success {
|
||||
|
@ -159,7 +180,6 @@ fn exec(
|
|||
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
|
||||
let mut args = vec!["fmt", "--all"];
|
||||
if context.check {
|
||||
args.push("--");
|
||||
args.push("--check");
|
||||
}
|
||||
let success = exec(context, "cargo", path, &args)?;
|
||||
|
@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul
|
|||
}
|
||||
args.extend(paths);
|
||||
|
||||
let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
|
||||
let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
|
||||
|
||||
Ok(success)
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>(
|
|||
|
||||
for (is_public, module_name, lint_name) in details {
|
||||
if !is_public {
|
||||
output.push_str(" #[cfg(feature = \"internal-lints\")]\n");
|
||||
output.push_str(" #[cfg(feature = \"internal\")]\n");
|
||||
}
|
||||
output.push_str(&format!(" {}::{},\n", module_name, lint_name));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.59"
|
||||
version = "0.1.60"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] }
|
|||
[features]
|
||||
deny-warnings = ["clippy_utils/deny-warnings"]
|
||||
# build clippy with internal lints enabled, off by default
|
||||
internal-lints = ["clippy_utils/internal-lints"]
|
||||
metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
|
||||
internal = ["clippy_utils/internal", "serde_json"]
|
||||
|
||||
[package.metadata.rust-analyzer]
|
||||
# This crate uses #[feature(rustc_private)]
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
use clippy_utils::consts::{constant, Constant};
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
let lint_true = |is_debug: bool| {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
|
||||
let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
|
||||
Some(sym::debug_assert_macro) => true,
|
||||
Some(sym::assert_macro) => false,
|
||||
_ => return,
|
||||
};
|
||||
let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return };
|
||||
let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return };
|
||||
if val {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ASSERTIONS_ON_CONSTANTS,
|
||||
e.span,
|
||||
if is_debug {
|
||||
"`debug_assert!(true)` will be optimized out by the compiler"
|
||||
} else {
|
||||
"`assert!(true)` will be optimized out by the compiler"
|
||||
},
|
||||
macro_call.span,
|
||||
&format!(
|
||||
"`{}!(true)` will be optimized out by the compiler",
|
||||
cx.tcx.item_name(macro_call.def_id)
|
||||
),
|
||||
None,
|
||||
"remove it",
|
||||
);
|
||||
};
|
||||
let lint_false_without_message = || {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ASSERTIONS_ON_CONSTANTS,
|
||||
e.span,
|
||||
"`assert!(false)` should probably be replaced",
|
||||
None,
|
||||
"use `panic!()` or `unreachable!()`",
|
||||
);
|
||||
};
|
||||
let lint_false_with_message = |panic_message: String| {
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ASSERTIONS_ON_CONSTANTS,
|
||||
e.span,
|
||||
&format!("`assert!(false, {})` should probably be replaced", panic_message),
|
||||
None,
|
||||
&format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
|
||||
);
|
||||
};
|
||||
|
||||
if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
|
||||
if debug_assert_span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if_chain! {
|
||||
if let ExprKind::Unary(_, lit) = e.kind;
|
||||
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
|
||||
if is_true;
|
||||
then {
|
||||
lint_true(true);
|
||||
}
|
||||
} else if !is_debug {
|
||||
let (assert_arg, panic_arg) = match panic_expn {
|
||||
PanicExpn::Empty => ("", ""),
|
||||
_ => (", ..", ".."),
|
||||
};
|
||||
} else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
|
||||
if assert_span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let Some(assert_match) = match_assert_with_message(cx, e) {
|
||||
match assert_match {
|
||||
// matched assert but not message
|
||||
AssertKind::WithoutMessage(false) => lint_false_without_message(),
|
||||
AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
|
||||
AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
|
||||
};
|
||||
}
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
ASSERTIONS_ON_CONSTANTS,
|
||||
macro_call.span,
|
||||
&format!("`assert!(false{})` should probably be replaced", assert_arg),
|
||||
None,
|
||||
&format!("use `panic!({})` or `unreachable!({0})`", panic_arg),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of calling `match_assert_with_message`.
|
||||
enum AssertKind {
|
||||
WithMessage(String, bool),
|
||||
WithoutMessage(bool),
|
||||
}
|
||||
|
||||
/// Check if the expression matches
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// if !c {
|
||||
/// {
|
||||
/// ::std::rt::begin_panic(message, _)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// where `message` is any expression and `c` is a constant bool.
|
||||
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
|
||||
if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
|
||||
// bind the first argument of the `assert!` macro
|
||||
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
|
||||
let begin_panic_call = peel_blocks(then);
|
||||
// function call
|
||||
if let Some(arg) = match_panic_call(cx, begin_panic_call);
|
||||
// bind the second argument of the `assert!` macro if it exists
|
||||
if let panic_message = snippet_opt(cx, arg.span);
|
||||
// second argument of begin_panic is irrelevant
|
||||
// as is the second match arm
|
||||
then {
|
||||
// an empty message occurs when it was generated by the macro
|
||||
// (and not passed by the user)
|
||||
return panic_message
|
||||
.filter(|msg| !msg.is_empty())
|
||||
.map(|msg| AssertKind::WithMessage(msg, is_true))
|
||||
.or(Some(AssertKind::WithoutMessage(is_true)));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! checks for attributes
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, macro_backtrace};
|
||||
use clippy_utils::msrvs;
|
||||
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
|
||||
use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
|
||||
use clippy_utils::{extract_msrv_attr, meets_msrv};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
|||
}
|
||||
|
||||
fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
|
||||
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
|
||||
is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
|
||||
}) {
|
||||
return false;
|
||||
}
|
||||
match &expr.kind {
|
||||
ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
|
||||
ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
|
||||
ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
|
||||
ExprKind::Call(path_expr, _) => {
|
||||
if let ExprKind::Path(qpath) = &path_expr.kind {
|
||||
typeck_results
|
||||
.qpath_res(qpath, path_expr.hir_id)
|
||||
.opt_def_id()
|
||||
.map_or(true, |fun_id| !match_panic_def_id(cx, fun_id))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait};
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Lit};
|
||||
|
@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool {
|
|||
) && !e.span.from_expansion()
|
||||
}
|
||||
|
||||
fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
||||
fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
|
||||
let ty = cx.typeck_results().expr_ty(e);
|
||||
|
||||
cx.tcx
|
||||
|
@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let macros = ["assert_eq", "debug_assert_eq"];
|
||||
let inverted_macros = ["assert_ne", "debug_assert_ne"];
|
||||
|
||||
for mac in macros.iter().chain(inverted_macros.iter()) {
|
||||
if let Some(span) = is_direct_expn_of(expr.span, mac) {
|
||||
if let Some(args) = higher::extract_assert_macro_args(expr) {
|
||||
if let [a, b, ..] = args[..] {
|
||||
let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
|
||||
|
||||
if nb_bool_args != 1 {
|
||||
// If there are two boolean arguments, we definitely don't understand
|
||||
// what's going on, so better leave things as is...
|
||||
//
|
||||
// Or there is simply no boolean and then we can leave things as is!
|
||||
return;
|
||||
}
|
||||
|
||||
if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
|
||||
// At this point the expression which is not a boolean
|
||||
// literal does not implement Not trait with a bool output,
|
||||
// so we cannot suggest to rewrite our code
|
||||
return;
|
||||
}
|
||||
|
||||
let non_eq_mac = &mac[..mac.len() - 3];
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_ASSERT_COMPARISON,
|
||||
span,
|
||||
&format!("used `{}!` with a literal bool", mac),
|
||||
"replace it with",
|
||||
format!("{}!(..)", non_eq_mac),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
if !matches!(
|
||||
macro_name.as_str(),
|
||||
"assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
|
||||
if !(is_bool_lit(a) ^ is_bool_lit(b)) {
|
||||
// If there are two boolean arguments, we definitely don't understand
|
||||
// what's going on, so better leave things as is...
|
||||
//
|
||||
// Or there is simply no boolean and then we can leave things as is!
|
||||
return;
|
||||
}
|
||||
|
||||
if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
|
||||
// At this point the expression which is not a boolean
|
||||
// literal does not implement Not trait with a bool output,
|
||||
// so we cannot suggest to rewrite our code
|
||||
return;
|
||||
}
|
||||
|
||||
let macro_name = macro_name.as_str();
|
||||
let non_eq_mac = ¯o_name[..macro_name.len() - 3];
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BOOL_ASSERT_COMPARISON,
|
||||
macro_call.span,
|
||||
&format!("used `{}!` with a literal bool", macro_name),
|
||||
"replace it with",
|
||||
format!("{}!(..)", non_eq_mac),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
97
clippy_lints/src/borrow_as_ptr.rs
Normal file
97
clippy_lints/src/borrow_as_ptr.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_no_std_crate;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{meets_msrv, msrvs};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
|
||||
|
||||
pub struct BorrowAsPtr {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl BorrowAsPtr {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) {
|
||||
return;
|
||||
}
|
||||
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(left_expr, ty) = &expr.kind;
|
||||
if let TyKind::Ptr(_) = ty.kind;
|
||||
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
|
||||
|
||||
then {
|
||||
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
|
||||
let macro_name = match mutability {
|
||||
Mutability::Not => "addr_of",
|
||||
Mutability::Mut => "addr_of_mut",
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
BORROW_AS_PTR,
|
||||
expr.span,
|
||||
"borrow as raw pointer",
|
||||
"try",
|
||||
format!(
|
||||
"{}::ptr::{}!({})",
|
||||
core_or_std,
|
||||
macro_name,
|
||||
snippet_opt(cx, e.span).unwrap()
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
|
|||
None
|
||||
}
|
||||
|
||||
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
|
||||
impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
|
||||
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
|
||||
span_lint_and_help(
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_span::symbol::sym;
|
|||
|
||||
use super::CAST_PTR_ALIGNMENT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to) {
|
||||
return;
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_middle::ty;
|
|||
|
||||
use super::CAST_REF_TO_MUT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind;
|
||||
if let ExprKind::Cast(e, t) = &e.kind;
|
||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy};
|
|||
|
||||
use super::CHAR_LIT_AS_U8;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Cast(e, _) = &expr.kind;
|
||||
if let ExprKind::Lit(l) = &e.kind;
|
||||
|
|
|
@ -12,7 +12,7 @@ use rustc_semver::RustcVersion;
|
|||
|
||||
use super::PTR_AS_PTR;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
|
||||
if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -316,7 +316,7 @@ struct BlockEqual {
|
|||
|
||||
/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
|
||||
/// abort any further processing and avoid duplicate lint triggers.
|
||||
fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
|
||||
fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> {
|
||||
let mut start_eq = usize::MAX;
|
||||
let mut end_eq = usize::MAX;
|
||||
let mut expr_eq = true;
|
||||
|
@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
|
|||
})
|
||||
}
|
||||
|
||||
fn check_for_warn_of_moved_symbol(
|
||||
cx: &LateContext<'tcx>,
|
||||
symbols: &FxHashSet<Symbol>,
|
||||
if_expr: &'tcx Expr<'_>,
|
||||
) -> bool {
|
||||
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
|
||||
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
|
||||
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
|
||||
|
||||
|
@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol(
|
|||
}
|
||||
|
||||
fn emit_branches_sharing_code_lint(
|
||||
cx: &LateContext<'tcx>,
|
||||
cx: &LateContext<'_>,
|
||||
start_stmts: usize,
|
||||
end_stmts: usize,
|
||||
lint_end: bool,
|
||||
warn_about_moved_symbol: bool,
|
||||
blocks: &[&Block<'tcx>],
|
||||
if_expr: &'tcx Expr<'_>,
|
||||
blocks: &[&Block<'_>],
|
||||
if_expr: &Expr<'_>,
|
||||
) {
|
||||
if start_stmts == 0 && !lint_end {
|
||||
return;
|
||||
|
|
|
@ -77,7 +77,7 @@ pub struct Default {
|
|||
|
||||
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
||||
|
||||
impl LateLintPass<'_> for Default {
|
||||
impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
|
@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
|
||||
// start from the `let mut _ = _::default();` and look at all the following
|
||||
// statements, see if they re-assign the fields of the binding
|
||||
let stmts_head = match block.stmts {
|
||||
|
|
|
@ -54,7 +54,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
|
||||
|
||||
impl LateLintPass<'_> for DefaultNumericFallback {
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
|
||||
let mut visitor = NumericFallbackVisitor::new(cx);
|
||||
visitor.visit_body(body);
|
||||
|
|
|
@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_parse_ref_op(
|
||||
fn try_parse_ref_op<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
|
@ -387,7 +387,7 @@ fn try_parse_ref_op(
|
|||
|
||||
// Checks whether the type for a deref call actually changed the type, not just the mutability of
|
||||
// the reference.
|
||||
fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
|
||||
fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool {
|
||||
match (result_ty.kind(), arg_ty.kind()) {
|
||||
(ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
|
||||
|
||||
|
@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
|
|||
}
|
||||
|
||||
/// Adjustments are sometimes made in the parent block rather than the expression itself.
|
||||
fn find_adjustments(
|
||||
fn find_adjustments<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'_>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
|
@ -499,7 +499,7 @@ fn find_adjustments(
|
|||
}
|
||||
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
|
||||
fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) {
|
||||
match state {
|
||||
State::DerefMethod {
|
||||
ty_changed_count,
|
||||
|
@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
|
|||
}
|
||||
|
||||
impl Dereferencing {
|
||||
fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
|
||||
fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) {
|
||||
if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
|
||||
if let Some(pat) = outer_pat {
|
||||
// Check for auto-deref
|
||||
|
|
|
@ -11,6 +11,9 @@ declare_clippy_lint! {
|
|||
/// ### What it does
|
||||
/// Denies the configured methods and functions in clippy.toml
|
||||
///
|
||||
/// Note: Even though this lint is warn-by-default, it will only trigger if
|
||||
/// methods are defined in the clippy.toml file.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Some methods are undesirable in certain contexts, and it's beneficial to
|
||||
/// lint for them as needed.
|
||||
|
@ -49,14 +52,14 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub DISALLOWED_METHODS,
|
||||
nursery,
|
||||
style,
|
||||
"use of a disallowed method call"
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisallowedMethods {
|
||||
conf_disallowed: Vec<conf::DisallowedMethod>,
|
||||
disallowed: DefIdMap<Option<String>>,
|
||||
disallowed: DefIdMap<usize>,
|
||||
}
|
||||
|
||||
impl DisallowedMethods {
|
||||
|
@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
for conf in &self.conf_disallowed {
|
||||
let (path, reason) = match conf {
|
||||
conf::DisallowedMethod::Simple(path) => (path, None),
|
||||
conf::DisallowedMethod::WithReason { path, reason } => (
|
||||
path,
|
||||
reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
|
||||
),
|
||||
};
|
||||
let segs: Vec<_> = path.split("::").collect();
|
||||
for (index, conf) in self.conf_disallowed.iter().enumerate() {
|
||||
let segs: Vec<_> = conf.path().split("::").collect();
|
||||
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
|
||||
self.disallowed.insert(id, reason);
|
||||
self.disallowed.insert(id, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
|||
Some(def_id) => def_id,
|
||||
None => return,
|
||||
};
|
||||
let reason = match self.disallowed.get(&def_id) {
|
||||
Some(reason) => reason,
|
||||
let conf = match self.disallowed.get(&def_id) {
|
||||
Some(&index) => &self.conf_disallowed[index],
|
||||
None => return,
|
||||
};
|
||||
let func_path = cx.tcx.def_path_str(def_id);
|
||||
let msg = format!("use of a disallowed method `{}`", func_path);
|
||||
let msg = format!("use of a disallowed method `{}`", conf.path());
|
||||
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
|
||||
if let Some(reason) = reason {
|
||||
diag.note(reason);
|
||||
if let conf::DisallowedMethod::WithReason {
|
||||
reason: Some(reason), ..
|
||||
} = conf
|
||||
{
|
||||
diag.note(&format!("{} (from clippy.toml)", reason));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@ declare_clippy_lint! {
|
|||
/// ### What it does
|
||||
/// Denies the configured types in clippy.toml.
|
||||
///
|
||||
/// Note: Even though this lint is warn-by-default, it will only trigger if
|
||||
/// types are defined in the clippy.toml file.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Some types are undesirable in certain contexts.
|
||||
///
|
||||
|
@ -44,7 +47,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "1.55.0"]
|
||||
pub DISALLOWED_TYPES,
|
||||
nursery,
|
||||
style,
|
||||
"use of disallowed types"
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use clippy_utils::attrs::is_doc_hidden;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
|
||||
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
|
||||
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
|
||||
|
@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter;
|
|||
use rustc_errors::{Applicability, Handler, SuggestionStyle};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
|
||||
use rustc_hir::{AnonConst, Expr};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::map::Map;
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
|||
return;
|
||||
}
|
||||
|
||||
// check for `begin_panic`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func_expr, _) = expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
|
||||
if let Some(path_def_id) = path.res.opt_def_id();
|
||||
if match_panic_def_id(self.cx, path_def_id);
|
||||
if is_expn_of(expr.span, "unreachable").is_none();
|
||||
if !is_expn_of_debug_assertions(expr.span);
|
||||
then {
|
||||
self.panic_span = Some(expr.span);
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
|
||||
if is_panic(self.cx, macro_call.def_id)
|
||||
|| matches!(
|
||||
self.cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"assert" | "assert_eq" | "assert_ne" | "todo"
|
||||
)
|
||||
{
|
||||
self.panic_span = Some(macro_call.span);
|
||||
}
|
||||
}
|
||||
|
||||
// check for `assert_eq` or `assert_ne`
|
||||
if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
|
||||
self.panic_span = Some(expr.span);
|
||||
}
|
||||
|
||||
// check for `unwrap`
|
||||
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
|
||||
let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
|
||||
|
@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
|
|||
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_expn_of_debug_assertions(span: Span) -> bool {
|
||||
const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
|
||||
MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
|
||||
}
|
||||
|
|
|
@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> {
|
|||
key: &'tcx Expr<'tcx>,
|
||||
call_ctxt: SyntaxContext,
|
||||
}
|
||||
fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
|
||||
fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
|
||||
let mut negated = false;
|
||||
let expr = peel_hir_expr_while(expr, |e| match e.kind {
|
||||
ExprKind::Unary(UnOp::Not, e) => {
|
||||
|
@ -280,7 +280,7 @@ struct InsertExpr<'tcx> {
|
|||
key: &'tcx Expr<'tcx>,
|
||||
value: &'tcx Expr<'tcx>,
|
||||
}
|
||||
fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
|
||||
fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
|
||||
if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
|
||||
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
|
||||
if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
|
||||
|
@ -301,7 +301,7 @@ enum Edit<'tcx> {
|
|||
/// An insertion into the map.
|
||||
Insertion(Insertion<'tcx>),
|
||||
}
|
||||
impl Edit<'tcx> {
|
||||
impl<'tcx> Edit<'tcx> {
|
||||
fn as_insertion(self) -> Option<Insertion<'tcx>> {
|
||||
if let Self::Insertion(i) = self { Some(i) } else { None }
|
||||
}
|
||||
|
@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> {
|
|||
allow_insert_closure: bool,
|
||||
is_single_insert: bool,
|
||||
}
|
||||
impl InsertSearchResults<'tcx> {
|
||||
impl<'tcx> InsertSearchResults<'tcx> {
|
||||
fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
|
||||
self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
|
||||
}
|
||||
|
@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_insert_calls(
|
||||
fn find_insert_calls<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
contains_expr: &ContainsExpr<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function};
|
||||
use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
|
@ -68,32 +69,26 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
|
||||
|
||||
const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"];
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for EqOp {
|
||||
#[allow(clippy::similar_names, clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Block(block, _) = e.kind {
|
||||
for stmt in block.stmts {
|
||||
for amn in &ASSERT_MACRO_NAMES {
|
||||
if_chain! {
|
||||
if is_expn_of(stmt.span, amn).is_some();
|
||||
if let StmtKind::Semi(matchexpr) = stmt.kind;
|
||||
if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr);
|
||||
if macro_args.len() == 2;
|
||||
let (lhs, rhs) = (macro_args[0], macro_args[1]);
|
||||
if eq_expr_value(cx, lhs, rhs);
|
||||
if !is_in_test_function(cx.tcx, e.hir_id);
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
lhs.span.to(rhs.span),
|
||||
&format!("identical args used in this `{}!` macro call", amn),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if_chain! {
|
||||
if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
||||
let name = cx.tcx.item_name(macro_call.def_id);
|
||||
matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
|
||||
.then(|| (macro_call, name))
|
||||
});
|
||||
if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
|
||||
if eq_expr_value(cx, lhs, rhs);
|
||||
if macro_call.is_local();
|
||||
if !is_in_test_function(cx.tcx, e.hir_id);
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
EQ_OP,
|
||||
lhs.span.to(rhs.span),
|
||||
&format!("identical args used in this `{}!` macro call", macro_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
if let ExprKind::Binary(op, left, right) = e.kind {
|
||||
|
|
|
@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
|
||||
fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
|
||||
if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
|
||||
implements_trait(cx, ty, def_id, &[other.into()])
|
||||
} else {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use clippy_utils::consts::{constant_simple, Constant};
|
||||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::ty::same_type_and_consts;
|
||||
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TypeckResults;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::source_map::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp {
|
|||
return;
|
||||
}
|
||||
if let ExprKind::Binary(ref cmp, left, right) = e.kind {
|
||||
let tck = cx.typeck_results();
|
||||
match cmp.node {
|
||||
BinOpKind::Mul | BinOpKind::BitAnd => {
|
||||
check(cx, left, e.span);
|
||||
check(cx, right, e.span);
|
||||
check(cx, tck, left, right, e);
|
||||
check(cx, tck, right, left, e);
|
||||
},
|
||||
BinOpKind::Div => check(cx, left, e.span),
|
||||
BinOpKind::Div => check(cx, tck, left, right, e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) {
|
||||
if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) {
|
||||
fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
|
||||
let input_ty = tck.expr_ty(input).peel_refs();
|
||||
let output_ty = tck.expr_ty(output).peel_refs();
|
||||
!same_type_and_consts(input_ty, output_ty)
|
||||
}
|
||||
|
||||
fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
tck: &TypeckResults<'tcx>,
|
||||
op: &Expr<'tcx>,
|
||||
other: &Expr<'tcx>,
|
||||
parent: &Expr<'tcx>,
|
||||
) {
|
||||
if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
|
||||
if different_types(tck, other, parent) {
|
||||
return;
|
||||
}
|
||||
span_lint(
|
||||
cx,
|
||||
ERASING_OP,
|
||||
span,
|
||||
parent.span,
|
||||
"this operation will always return zero. This is likely not the intended outcome",
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher::VecArgs;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
|
||||
use if_chain::if_chain;
|
||||
|
@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
|||
use rustc_middle::ty::subst::Subst;
|
||||
use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||
// A type param function ref like `T::f` is not 'static, however
|
||||
// it is if cast like `T::f as fn()`. This seems like a rustc bug.
|
||||
if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
|
||||
let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
|
||||
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
|
||||
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
|
||||
then {
|
||||
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
|
||||
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
|
||||
use clippy_utils::higher::FormatArgsExpn;
|
||||
use clippy_utils::macros::FormatArgsExpn;
|
||||
use clippy_utils::{is_expn_of, match_function_call, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(format_args) = FormatArgsExpn::parse(write_arg);
|
||||
if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
|
||||
then {
|
||||
let calling_macro =
|
||||
// ordering is important here, since `writeln!` uses `write!` internally
|
||||
|
@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
|||
)
|
||||
};
|
||||
let msg = format!("use of `{}.unwrap()`", used);
|
||||
if let [write_output] = *format_args.format_string_symbols {
|
||||
if let [write_output] = *format_args.format_string_parts {
|
||||
let mut write_output = write_output.to_string();
|
||||
if write_output.ends_with('\n') {
|
||||
write_output.pop();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::method_chain_args;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
|
|||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
|
||||
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
|
||||
use rustc_hir::{Expr, ImplItemKind};
|
||||
|
||||
struct FindPanicUnwrap<'a, 'tcx> {
|
||||
lcx: &'a LateContext<'tcx>,
|
||||
|
@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
|
|||
type Map = Map<'tcx>;
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
// check for `begin_panic`
|
||||
if_chain! {
|
||||
if let ExprKind::Call(func_expr, _) = expr.kind;
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
|
||||
if let Some(path_def_id) = path.res.opt_def_id();
|
||||
if match_panic_def_id(self.lcx, path_def_id);
|
||||
if is_expn_of(expr.span, "unreachable").is_none();
|
||||
then {
|
||||
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
|
||||
if is_panic(self.lcx, macro_call.def_id) {
|
||||
self.result.push(expr.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::FormatExpn;
|
||||
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
|
@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
|
||||
Some(e) if !e.call_site.from_expansion() => e,
|
||||
_ => return,
|
||||
let (format_args, call_site) = if_chain! {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr);
|
||||
if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
|
||||
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
|
||||
then {
|
||||
(format_args, macro_call.span)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if format_args.value_args.is_empty() {
|
||||
if format_args.format_string_parts.is_empty() {
|
||||
span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability);
|
||||
} else {
|
||||
if_chain! {
|
||||
if let [e] = &*format_args.format_string_parts;
|
||||
if let ExprKind::Lit(lit) = &e.kind;
|
||||
if let Some(s_src) = snippet_opt(cx, lit.span);
|
||||
then {
|
||||
match *format_args.format_string_parts {
|
||||
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
|
||||
[_] => {
|
||||
if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
|
||||
let sugg = format!("{}.to_string()", s_expand);
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
}
|
||||
}
|
||||
},
|
||||
[..] => {},
|
||||
}
|
||||
} else if let [value] = *format_args.value_args {
|
||||
if_chain! {
|
||||
if format_args.format_string_symbols == [kw::Empty];
|
||||
if format_args.format_string_parts == [kw::Empty];
|
||||
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
|
||||
ty::Str => true,
|
||||
_ => false,
|
||||
};
|
||||
if let Some(args) = format_args.args();
|
||||
if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting());
|
||||
if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
|
||||
then {
|
||||
let is_new_string = match value.kind {
|
||||
ExprKind::Binary(..) => true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn};
|
||||
use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{is_diag_trait_item, match_def_path, paths};
|
||||
|
@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m
|
|||
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let Some(format_args) = FormatArgsExpn::parse(expr);
|
||||
if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
|
||||
let expr_expn_data = expr.span.ctxt().outer_expn_data();
|
||||
let outermost_expn_data = outermost_expn_data(expr_expn_data);
|
||||
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
|
||||
|
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||
if let Some(args) = format_args.args();
|
||||
then {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if !arg.is_display() {
|
||||
if arg.format_trait != sym::Display {
|
||||
continue;
|
||||
}
|
||||
if arg.has_string_formatting() {
|
||||
|
@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
|||
if is_aliased(&args, i) {
|
||||
continue;
|
||||
}
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg);
|
||||
check_to_string_in_format_args(cx, name, arg);
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
|
||||
check_to_string_in_format_args(cx, name, arg.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) {
|
||||
if_chain! {
|
||||
if FormatExpn::parse(arg.value).is_some();
|
||||
if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion();
|
||||
then {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
call_site,
|
||||
&format!("`format!` in `{}!` args", name),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
"combine the `format!(..)` arguments with the outer `{}!(..)` call",
|
||||
name
|
||||
));
|
||||
diag.help("or consider changing `format!` to `format_args!`");
|
||||
},
|
||||
);
|
||||
}
|
||||
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
|
||||
let expn_data = arg.span.ctxt().outer_expn_data();
|
||||
if expn_data.call_site.from_expansion() {
|
||||
return;
|
||||
}
|
||||
let Some(mac_id) = expn_data.macro_def_id else { return };
|
||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
|
||||
return;
|
||||
}
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
call_site,
|
||||
&format!("`format!` in `{}!` args", name),
|
||||
|diag| {
|
||||
diag.help(&format!(
|
||||
"combine the `format!(..)` arguments with the outer `{}!(..)` call",
|
||||
name
|
||||
));
|
||||
diag.help("or consider changing `format!` to `format_args!`");
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) {
|
||||
let value = arg.value;
|
||||
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if !value.span.from_expansion();
|
||||
if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind;
|
||||
|
|
|
@ -53,7 +53,7 @@ impl FromOverInto {
|
|||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
impl LateLintPass<'_> for FromOverInto {
|
||||
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
|
||||
return;
|
||||
|
|
|
@ -43,7 +43,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
|
||||
|
||||
impl LateLintPass<'tcx> for FromStrRadix10 {
|
||||
impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(maybe_path, arguments) = &exp.kind;
|
||||
|
|
|
@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method
|
|||
|
||||
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
||||
|
||||
pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||
let attr = must_use_attr(attrs);
|
||||
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
|
||||
|
@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
|
|
@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local};
|
|||
|
||||
use super::NOT_UNSAFE_PTR_ARG_DEREF;
|
||||
|
||||
pub(super) fn check_fn(
|
||||
pub(super) fn check_fn<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
kind: intravisit::FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl<'tcx>,
|
||||
|
@ -25,14 +25,14 @@ pub(super) fn check_fn(
|
|||
check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id));
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
|
||||
let body = cx.tcx.hir().body(eid);
|
||||
check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_raw_ptr(
|
||||
fn check_raw_ptr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
unsafety: hir::Unsafety,
|
||||
decl: &'tcx hir::FnDecl<'tcx>,
|
||||
|
|
|
@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
|
||||
use super::RESULT_UNIT_ERR;
|
||||
|
||||
pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
|
||||
if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
|
||||
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
|
||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||
|
|
|
@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item;
|
|||
use super::TOO_MANY_ARGUMENTS;
|
||||
|
||||
pub(super) fn check_fn(
|
||||
cx: &LateContext<'tcx>,
|
||||
kind: intravisit::FnKind<'tcx>,
|
||||
decl: &'tcx hir::FnDecl<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
kind: intravisit::FnKind<'_>,
|
||||
decl: &hir::FnDecl<'_>,
|
||||
span: Span,
|
||||
hir_id: hir::HirId,
|
||||
too_many_arguments_threshold: u64,
|
||||
|
@ -39,11 +39,7 @@ pub(super) fn check_fn(
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn check_trait_item(
|
||||
cx: &LateContext<'tcx>,
|
||||
item: &'tcx hir::TraitItem<'_>,
|
||||
too_many_arguments_threshold: u64,
|
||||
) {
|
||||
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) {
|
||||
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
|
||||
// don't lint extern functions decls, it's not their fault
|
||||
if sig.header.abi == Abi::Rust {
|
||||
|
|
|
@ -11,9 +11,9 @@ use super::TOO_MANY_LINES;
|
|||
|
||||
pub(super) fn check_fn(
|
||||
cx: &LateContext<'_>,
|
||||
kind: FnKind<'tcx>,
|
||||
kind: FnKind<'_>,
|
||||
span: Span,
|
||||
body: &'tcx hir::Body<'_>,
|
||||
body: &hir::Body<'_>,
|
||||
too_many_lines_threshold: u64,
|
||||
) {
|
||||
// Closures must be contained in a parent body, which will be checked for `too_many_lines`.
|
||||
|
|
|
@ -55,7 +55,7 @@ impl IfThenSomeElseNone {
|
|||
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
impl LateLintPass<'_> for IfThenSomeElseNone {
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
|
||||
return;
|
||||
|
|
|
@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
|
|||
}
|
||||
|
||||
fn lint_implicit_returns(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
// The context of the function body.
|
||||
ctxt: SyntaxContext,
|
||||
// Whether the expression is from a macro expansion.
|
||||
|
|
|
@ -63,7 +63,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
|
||||
|
||||
impl LateLintPass<'_> for InconsistentStructConstructor {
|
||||
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
|
|
|
@ -69,7 +69,7 @@ impl IndexRefutableSlice {
|
|||
|
||||
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
||||
|
||||
impl LateLintPass<'_> for IndexRefutableSlice {
|
||||
impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::in_macro;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
|
|||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Struct(path, fields, None) = e.kind {
|
||||
if !fields.is_empty()
|
||||
&& !in_macro(e.span)
|
||||
&& !e.span.from_expansion()
|
||||
&& fields
|
||||
.iter()
|
||||
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
|
||||
use rustc_hir::{ImplItem, ImplItemKind};
|
||||
use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
|
||||
use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::symbol::kw;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
|
@ -40,26 +39,52 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
|
||||
|
||||
impl LateLintPass<'_> for IterNotReturningIterator {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
|
||||
let name = impl_item.ident.name.as_str();
|
||||
if_chain! {
|
||||
if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
|
||||
let ret_ty = return_ty(cx, impl_item.hir_id());
|
||||
if matches!(name, "iter" | "iter_mut");
|
||||
if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
|
||||
if param.name == kw::SelfLower;
|
||||
if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
|
||||
if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
|
||||
impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
let name = item.ident.name.as_str();
|
||||
if matches!(name, "iter" | "iter_mut") {
|
||||
if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
|
||||
check_sig(cx, name, fn_sig, item.def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
then {
|
||||
span_lint(
|
||||
cx,
|
||||
ITER_NOT_RETURNING_ITERATOR,
|
||||
fn_sig.span,
|
||||
&format!("this method is named `{}` but its return type does not implement `Iterator`", name),
|
||||
);
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
|
||||
let name = item.ident.name.as_str();
|
||||
if matches!(name, "iter" | "iter_mut")
|
||||
&& !matches!(
|
||||
get_parent_node(cx.tcx, item.hir_id()),
|
||||
Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some()
|
||||
)
|
||||
{
|
||||
if let ImplItemKind::Fn(fn_sig, _) = &item.kind {
|
||||
check_sig(cx, name, fn_sig, item.def_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
|
||||
if sig.decl.implicit_self.has_implicit_self() {
|
||||
let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output();
|
||||
let ret_ty = cx
|
||||
.tcx
|
||||
.try_normalize_erasing_regions(cx.param_env, ret_ty)
|
||||
.unwrap_or(ret_ty);
|
||||
if cx
|
||||
.tcx
|
||||
.get_diagnostic_item(sym::Iterator)
|
||||
.map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
ITER_NOT_RETURNING_ITERATOR,
|
||||
sig.span,
|
||||
&format!(
|
||||
"this method is named `{}` but its return type does not implement `Iterator`",
|
||||
name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -245,7 +245,7 @@ enum LenOutput<'tcx> {
|
|||
Option(DefId),
|
||||
Result(DefId, Ty<'tcx>),
|
||||
}
|
||||
fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
|
||||
fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
|
||||
match *sig.output().kind() {
|
||||
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
|
||||
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => {
|
||||
|
|
|
@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(derivable_impls::DERIVABLE_IMPLS),
|
||||
LintId::of(derive::DERIVE_HASH_XOR_EQ),
|
||||
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
|
||||
LintId::of(disallowed_methods::DISALLOWED_METHODS),
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(double_comparison::DOUBLE_COMPARISONS),
|
||||
|
@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(loops::WHILE_LET_ON_ITERATOR),
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_map::MANUAL_MAP),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
|
@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
|||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(needless_bool::BOOL_COMPARISON),
|
||||
LintId::of(needless_bool::NEEDLESS_BOOL),
|
||||
|
|
|
@ -3,33 +3,33 @@
|
|||
// Manual edits will be overwritten.
|
||||
|
||||
store.register_lints(&[
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::CLIPPY_LINTS_INTERNAL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::COMPILER_LINT_FUNCTIONS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::DEFAULT_LINT,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::IF_CHAIN_STYLE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INTERNING_DEFINED_SYMBOL,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::INVALID_PATHS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::OUTER_EXPN_EXPN_DATA,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::PRODUCE_ICE,
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
utils::internal_lints::UNNECESSARY_SYMBOL_STR,
|
||||
absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
|
@ -59,6 +59,7 @@ store.register_lints(&[
|
|||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::LOGIC_BUG,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
cargo_common_metadata::CARGO_COMMON_METADATA,
|
||||
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
|
@ -225,6 +226,7 @@ store.register_lints(&[
|
|||
main_recursion::MAIN_RECURSION,
|
||||
manual_assert::MANUAL_ASSERT,
|
||||
manual_async_fn::MANUAL_ASYNC_FN,
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_map::MANUAL_MAP,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
|
@ -435,6 +437,7 @@ store.register_lints(&[
|
|||
shadow::SHADOW_REUSE,
|
||||
shadow::SHADOW_SAME,
|
||||
shadow::SHADOW_UNRELATED,
|
||||
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
|
||||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
|
|
|
@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
|
||||
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
|
||||
LintId::of(copies::BRANCHES_SHARING_CODE),
|
||||
LintId::of(disallowed_methods::DISALLOWED_METHODS),
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
|
||||
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
|
||||
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
|
||||
|
@ -17,6 +15,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
|||
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(mutex_atomic::MUTEX_INTEGER),
|
||||
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
|
|
|
@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
|||
LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
|
||||
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
|
||||
LintId::of(bit_mask::VERBOSE_BIT_MASK),
|
||||
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
|
||||
LintId::of(bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(casts::CAST_LOSSLESS),
|
||||
|
|
|
@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
|||
LintId::of(methods::SINGLE_CHAR_PATTERN),
|
||||
LintId::of(methods::UNNECESSARY_TO_OWNED),
|
||||
LintId::of(misc::CMP_OWNED),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(redundant_clone::REDUNDANT_CLONE),
|
||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
|
|
|
@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
|||
LintId::of(shadow::SHADOW_REUSE),
|
||||
LintId::of(shadow::SHADOW_SAME),
|
||||
LintId::of(shadow::SHADOW_UNRELATED),
|
||||
LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
|
||||
LintId::of(strings::STRING_ADD),
|
||||
LintId::of(strings::STRING_SLICE),
|
||||
LintId::of(strings::STRING_TO_STRING),
|
||||
|
|
|
@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(comparison_chain::COMPARISON_CHAIN),
|
||||
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
|
||||
LintId::of(dereference::NEEDLESS_BORROW),
|
||||
LintId::of(disallowed_methods::DISALLOWED_METHODS),
|
||||
LintId::of(disallowed_types::DISALLOWED_TYPES),
|
||||
LintId::of(doc::MISSING_SAFETY_DOC),
|
||||
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
|
||||
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
|
||||
|
@ -41,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
|||
LintId::of(loops::WHILE_LET_ON_ITERATOR),
|
||||
LintId::of(main_recursion::MAIN_RECURSION),
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_map::MANUAL_MAP),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#![feature(box_patterns)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(drain_filter)]
|
||||
#![feature(in_band_lifetimes)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(let_else)]
|
||||
#![feature(once_cell)]
|
||||
|
@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint {
|
|||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "metadata-collector-lint")]
|
||||
#[cfg(feature = "internal")]
|
||||
mod deprecated_lints;
|
||||
#[cfg_attr(
|
||||
any(feature = "internal-lints", feature = "metadata-collector-lint"),
|
||||
allow(clippy::missing_clippy_version_attribute)
|
||||
)]
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
mod utils;
|
||||
|
||||
// begin lints modules, do not remove this comment, it’s used in `update_lints`
|
||||
|
@ -177,6 +173,7 @@ mod blacklisted_name;
|
|||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod bytecount;
|
||||
mod cargo_common_metadata;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
|
@ -264,6 +261,7 @@ mod macro_use;
|
|||
mod main_recursion;
|
||||
mod manual_assert;
|
||||
mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_map;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
|
@ -351,6 +349,7 @@ mod self_named_constructors;
|
|||
mod semicolon_if_nothing_returned;
|
||||
mod serde_api;
|
||||
mod shadow;
|
||||
mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
|
@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
include!("lib.register_restriction.rs");
|
||||
include!("lib.register_pedantic.rs");
|
||||
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
include!("lib.register_internal.rs");
|
||||
|
||||
include!("lib.register_all.rs");
|
||||
|
@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
include!("lib.register_cargo.rs");
|
||||
include!("lib.register_nursery.rs");
|
||||
|
||||
#[cfg(feature = "metadata-collector-lint")]
|
||||
#[cfg(feature = "internal")]
|
||||
{
|
||||
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
|
||||
store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
|
||||
|
@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
}
|
||||
|
||||
// all the internal lints
|
||||
#[cfg(feature = "internal-lints")]
|
||||
#[cfg(feature = "internal")]
|
||||
{
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
|
||||
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
|
||||
|
@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
|
||||
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate};
|
|||
use rustc_hir::{Block, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) {
|
||||
if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) {
|
||||
let msg = "empty `loop {}` wastes CPU cycles";
|
||||
let help = if is_no_std_crate(cx) {
|
||||
|
|
|
@ -8,7 +8,7 @@ use rustc_lint::LateContext;
|
|||
use rustc_middle::ty::TyS;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
|
||||
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) {
|
||||
let self_ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
|
||||
if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) {
|
||||
|
|
|
@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::ty::is_copy;
|
||||
use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
|
||||
use if_chain::if_chain;
|
||||
use rustc_ast::ast;
|
||||
|
@ -62,15 +62,15 @@ pub(super) fn check<'tcx>(
|
|||
if_chain! {
|
||||
if let ExprKind::Index(base_left, idx_left) = lhs.kind;
|
||||
if let ExprKind::Index(base_right, idx_right) = rhs.kind;
|
||||
if is_slice_like(cx, cx.typeck_results().expr_ty(base_left));
|
||||
if is_slice_like(cx, cx.typeck_results().expr_ty(base_right));
|
||||
if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left));
|
||||
if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some();
|
||||
if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts);
|
||||
if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
|
||||
|
||||
// Source and destination must be different
|
||||
if path_to_local(base_left) != path_to_local(base_right);
|
||||
then {
|
||||
Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
|
||||
Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left },
|
||||
IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
|
||||
} else {
|
||||
None
|
||||
|
@ -78,7 +78,7 @@ pub(super) fn check<'tcx>(
|
|||
}
|
||||
})
|
||||
})
|
||||
.map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src)))
|
||||
.map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.join("\n "));
|
||||
|
@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
start: &Expr<'_>,
|
||||
end: &Expr<'_>,
|
||||
limits: ast::RangeLimits,
|
||||
elem_ty: Ty<'tcx>,
|
||||
dst: &IndexExpr<'_>,
|
||||
src: &IndexExpr<'_>,
|
||||
) -> String {
|
||||
|
@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
.into()
|
||||
};
|
||||
|
||||
let method_str = if is_copy(cx, elem_ty) {
|
||||
"copy_from_slice"
|
||||
} else {
|
||||
"clone_from_slice"
|
||||
};
|
||||
|
||||
format!(
|
||||
"{}.clone_from_slice(&{}[{}..{}]);",
|
||||
"{}.{}(&{}[{}..{}]);",
|
||||
dst,
|
||||
method_str,
|
||||
src_base_str,
|
||||
src_offset.maybe_par(),
|
||||
src_limit.maybe_par()
|
||||
|
@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
|
|||
#[derive(Clone)]
|
||||
struct MinifyingSugg<'a>(Sugg<'a>);
|
||||
|
||||
impl Display for MinifyingSugg<'a> {
|
||||
impl<'a> Display for MinifyingSugg<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
|
@ -324,14 +332,13 @@ struct Start<'hir> {
|
|||
kind: StartKind<'hir>,
|
||||
}
|
||||
|
||||
fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool {
|
||||
let is_slice = match ty.kind() {
|
||||
ty::Ref(_, subty, _) => is_slice_like(cx, subty),
|
||||
ty::Slice(..) | ty::Array(..) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque)
|
||||
fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
match ty.kind() {
|
||||
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)),
|
||||
ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty),
|
||||
ty::Slice(ty) | ty::Array(ty, _) => Some(ty),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
|
||||
|
|
|
@ -147,7 +147,7 @@ impl BreakAfterExprVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
|
||||
impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
|
||||
type Map = Map<'tcx>;
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
|
|
|
@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet {
|
||||
fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
|
||||
fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet {
|
||||
fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
|
||||
match ty.kind() {
|
||||
ty::Adt(_, generics) => {
|
||||
for generic in *generics {
|
||||
|
|
|
@ -10,8 +10,8 @@ use rustc_span::Span;
|
|||
use std::iter::{once, Iterator};
|
||||
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'tcx>,
|
||||
block: &'tcx Block<'_>,
|
||||
cx: &LateContext<'_>,
|
||||
block: &Block<'_>,
|
||||
loop_id: HirId,
|
||||
span: Span,
|
||||
for_loop: Option<&ForLoop<'_>>,
|
||||
|
|
|
@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
|
|||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
// extract the expression from the first statement (if any) in a block
|
||||
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
|
||||
// or extract the first expression (if any) from the block
|
||||
|
|
|
@ -8,12 +8,13 @@ use clippy_utils::{
|
|||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{symbol::sym, Span, Symbol};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::{symbol::sym, Symbol};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
|
||||
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
|
||||
// check for `Some(..)` pattern
|
||||
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
|
||||
|
@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
// get the loop containing the match expression
|
||||
if !uses_iter(cx, &iter_expr_struct, if_then);
|
||||
then {
|
||||
(let_expr, iter_expr_struct, some_pat, expr)
|
||||
(let_expr, iter_expr_struct, iter_expr, some_pat, expr)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
|
||||
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
|
||||
// afterwards a mutable borrow of a field isn't necessary.
|
||||
let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
|
||||
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|
||||
|| !iter_expr_struct.can_move
|
||||
|| !iter_expr_struct.fields.is_empty()
|
||||
|| needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
|
||||
{
|
||||
".by_ref()"
|
||||
} else {
|
||||
""
|
||||
|
@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
|||
|
||||
#[derive(Debug)]
|
||||
struct IterExpr {
|
||||
/// The span of the whole expression, not just the path and fields stored here.
|
||||
span: Span,
|
||||
/// The fields used, in order of child to parent.
|
||||
fields: Vec<Symbol>,
|
||||
/// The path being used.
|
||||
path: Res,
|
||||
/// Whether or not the iterator can be moved.
|
||||
can_move: bool,
|
||||
}
|
||||
|
||||
/// Parses any expression to find out which field of which variable is used. Will return `None` if
|
||||
/// the expression might have side effects.
|
||||
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
|
||||
let span = e.span;
|
||||
let mut fields = Vec::new();
|
||||
let mut can_move = true;
|
||||
loop {
|
||||
if cx
|
||||
.typeck_results()
|
||||
.expr_adjustments(e)
|
||||
.iter()
|
||||
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
|
||||
{
|
||||
// Custom deref impls need to borrow the whole value as it's captured by reference
|
||||
can_move = false;
|
||||
fields.clear();
|
||||
}
|
||||
match e.kind {
|
||||
ExprKind::Path(ref path) => {
|
||||
break Some(IterExpr {
|
||||
span,
|
||||
fields,
|
||||
path: cx.qpath_res(path, e.hir_id),
|
||||
can_move,
|
||||
});
|
||||
},
|
||||
ExprKind::Field(base, name) => {
|
||||
|
@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
|
|||
// Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
|
||||
// already been seen.
|
||||
ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
|
||||
can_move = false;
|
||||
fields.clear();
|
||||
e = base;
|
||||
},
|
||||
ExprKind::Unary(UnOp::Deref, base) => {
|
||||
can_move = false;
|
||||
fields.clear();
|
||||
e = base;
|
||||
},
|
||||
|
@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
|
|||
|
||||
/// Strips off all field and path expressions. This will return true if a field or path has been
|
||||
/// skipped. Used to skip them after failing to check for equality.
|
||||
fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
|
||||
fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
|
||||
let mut e = expr;
|
||||
let e = loop {
|
||||
match e.kind {
|
||||
|
@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool
|
|||
}
|
||||
|
||||
/// Checks if the given expression uses the iterator.
|
||||
fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
|
||||
fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
|
||||
struct V<'a, 'b, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
iter_expr: &'b IterExpr,
|
||||
uses_iter: bool,
|
||||
}
|
||||
impl Visitor<'tcx> for V<'_, '_, 'tcx> {
|
||||
impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
|
@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
|
||||
fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool {
|
||||
struct AfterLoopVisitor<'a, 'b, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
iter_expr: &'b IterExpr,
|
||||
|
@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
|
|||
after_loop: bool,
|
||||
used_iter: bool,
|
||||
}
|
||||
impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
|
||||
impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
NestedVisitorMap::None
|
||||
|
@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
|
|||
found_local: bool,
|
||||
used_after: bool,
|
||||
}
|
||||
impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||
type Map = ErasedMap<'tcx>;
|
||||
|
||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::in_macro;
|
||||
use clippy_utils::source::snippet;
|
||||
use hir::def::{DefKind, Res};
|
||||
use if_chain::if_chain;
|
||||
|
@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if in_macro(item.span) {
|
||||
if item.span.from_expansion() {
|
||||
self.push_unique_macro_pat_ty(cx, item.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
|
||||
if in_macro(attr.span) {
|
||||
if attr.span.from_expansion() {
|
||||
self.push_unique_macro(cx, attr.span);
|
||||
}
|
||||
}
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
|
||||
if in_macro(expr.span) {
|
||||
if expr.span.from_expansion() {
|
||||
self.push_unique_macro(cx, expr.span);
|
||||
}
|
||||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
|
||||
if in_macro(stmt.span) {
|
||||
if stmt.span.from_expansion() {
|
||||
self.push_unique_macro(cx, stmt.span);
|
||||
}
|
||||
}
|
||||
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) {
|
||||
if in_macro(pat.span) {
|
||||
if pat.span.from_expansion() {
|
||||
self.push_unique_macro_pat_ty(cx, pat.span);
|
||||
}
|
||||
}
|
||||
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) {
|
||||
if in_macro(ty.span) {
|
||||
if ty.span.from_expansion() {
|
||||
self.push_unique_macro_pat_ty(cx, ty.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::PanicExpn;
|
||||
use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_expn_of, sugg};
|
||||
use clippy_utils::{peel_blocks_with_stmt, sugg};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -34,65 +35,34 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
|
||||
|
||||
impl LateLintPass<'_> for ManualAssert {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualAssert {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if_chain! {
|
||||
if let Expr {
|
||||
kind: ExprKind:: If(cond, Expr {
|
||||
kind: ExprKind::Block(
|
||||
Block {
|
||||
stmts: [stmt],
|
||||
..
|
||||
},
|
||||
_),
|
||||
..
|
||||
}, None),
|
||||
..
|
||||
} = &expr;
|
||||
if is_expn_of(stmt.span, "panic").is_some();
|
||||
if let ExprKind::If(cond, then, None) = expr.kind;
|
||||
if !matches!(cond.kind, ExprKind::Let(_));
|
||||
if let StmtKind::Semi(semi) = stmt.kind;
|
||||
if !expr.span.from_expansion();
|
||||
let then = peel_blocks_with_stmt(then);
|
||||
if let Some(macro_call) = root_macro_call(then.span);
|
||||
if cx.tcx.item_name(macro_call.def_id) == sym::panic;
|
||||
if !cx.tcx.sess.source_map().is_multiline(cond.span);
|
||||
|
||||
if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn);
|
||||
then {
|
||||
let call = if_chain! {
|
||||
if let ExprKind::Block(block, _) = semi.kind;
|
||||
if let Some(init) = block.expr;
|
||||
then {
|
||||
init
|
||||
} else {
|
||||
semi
|
||||
}
|
||||
};
|
||||
let span = if let Some(panic_expn) = PanicExpn::parse(call) {
|
||||
match *panic_expn.format_args.value_args {
|
||||
[] => panic_expn.format_args.format_string_span,
|
||||
[.., last] => panic_expn.format_args.format_string_span.to(last.span),
|
||||
}
|
||||
} else if let ExprKind::Call(_, [format_args]) = call.kind {
|
||||
format_args.span
|
||||
} else {
|
||||
return
|
||||
};
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||
let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind {
|
||||
if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
|
||||
sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
|
||||
} else {
|
||||
format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par())
|
||||
}
|
||||
} else {
|
||||
format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par())
|
||||
let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
|
||||
let cond = cond.peel_drop_temps();
|
||||
let (cond, not) = match cond.kind {
|
||||
ExprKind::Unary(UnOp::Not, e) => (e, ""),
|
||||
_ => (cond, "!"),
|
||||
};
|
||||
|
||||
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
|
||||
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_ASSERT,
|
||||
expr.span,
|
||||
"only a `panic!` in `if`-then statement",
|
||||
"try",
|
||||
format!("assert!({}, {});", cond_sugg, sugg),
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
|
107
clippy_lints/src/manual_bits.rs
Normal file
107
clippy_lints/src/manual_bits.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for uses of `std::mem::size_of::<T>() * 8` when
|
||||
/// `T::BITS` is available.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Can be written as the shorter `T::BITS`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// std::mem::size_of::<usize>() * 8;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// usize::BITS;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub MANUAL_BITS,
|
||||
style,
|
||||
"manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`"
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ManualBits {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl ManualBits {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self { msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ManualBits => [MANUAL_BITS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualBits {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
|
||||
if let BinOpKind::Mul = &bin_op.node;
|
||||
if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
|
||||
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
|
||||
if let ExprKind::Lit(lit) = &other_expr.kind;
|
||||
if let LitKind::Int(8, _) = lit.node;
|
||||
|
||||
then {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
MANUAL_BITS,
|
||||
expr.span,
|
||||
"usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
|
||||
"consider using",
|
||||
format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_one_size_of_ty<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr1: &'tcx Expr<'_>,
|
||||
expr2: &'tcx Expr<'_>,
|
||||
) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
|
||||
(Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
|
||||
(None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
|
||||
if_chain! {
|
||||
if let ExprKind::Call(count_func, _func_args) = expr.kind;
|
||||
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
|
||||
|
||||
if let QPath::Resolved(_, count_func_path) = count_func_qpath;
|
||||
if let Some(segment_zero) = count_func_path.segments.get(0);
|
||||
if let Some(args) = segment_zero.args;
|
||||
if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
|
||||
|
||||
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
|
||||
if match_def_path(cx, def_id, &paths::MEM_SIZE_OF);
|
||||
then {
|
||||
cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ManualMap => [MANUAL_MAP]);
|
||||
|
||||
impl LateLintPass<'_> for ManualMap {
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualMap {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
|
||||
|
@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap {
|
|||
|
||||
// Checks whether the expression could be passed as a function, or whether a closure is needed.
|
||||
// Returns the function to be passed to `map` if it exists.
|
||||
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(func, [arg])
|
||||
if path_to_local_id(arg, binding)
|
||||
|
@ -251,8 +251,13 @@ struct SomeExpr<'tcx> {
|
|||
|
||||
// Try to parse into a recognized `Option` pattern.
|
||||
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
|
||||
fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
|
||||
fn f<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &'tcx Pat<'_>,
|
||||
ref_count: usize,
|
||||
ctxt: SyntaxContext,
|
||||
) -> Option<OptionPat<'tcx>> {
|
||||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
|
@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
|
|||
}
|
||||
|
||||
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
|
||||
fn get_some_expr(
|
||||
fn get_some_expr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
needs_unsafe_block: bool,
|
||||
|
@ -306,6 +311,6 @@ fn get_some_expr(
|
|||
}
|
||||
|
||||
// Checks for the `None` value.
|
||||
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
|
||||
|
||||
impl LateLintPass<'_> for ManualOkOr {
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), scrutinee.span) {
|
||||
return;
|
||||
|
|
|
@ -43,7 +43,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
|
||||
|
||||
impl LateLintPass<'_> for ManualUnwrapOr {
|
||||
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
|
|
|
@ -55,7 +55,7 @@ enum CaseMethod {
|
|||
AsciiUppercase,
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for MatchStrCaseMismatch {
|
||||
impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, expr.span);
|
||||
|
|
|
@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
|
|||
use clippy_utils::diagnostics::{
|
||||
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call};
|
||||
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
|
||||
get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
|
||||
path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
|
||||
strip_pat_refs,
|
||||
};
|
||||
use clippy_utils::{higher, peel_blocks_with_stmt};
|
||||
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
|
||||
use core::iter::{once, ExactSizeIterator};
|
||||
use if_chain::if_chain;
|
||||
|
@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
|
|||
}
|
||||
if_chain! {
|
||||
if matching_wild;
|
||||
if is_panic_call(arm.body);
|
||||
if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
|
||||
if is_panic(cx, macro_call.def_id);
|
||||
then {
|
||||
// `Err(_)` or `Err(_e)` arm with `panic!` found
|
||||
span_lint_and_note(cx,
|
||||
|
@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> {
|
|||
Path(&'a [PathSegment<'a>]),
|
||||
Mixed,
|
||||
}
|
||||
impl CommonPrefixSearcher<'a> {
|
||||
impl<'a> CommonPrefixSearcher<'a> {
|
||||
fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
|
||||
match path {
|
||||
[path @ .., _] => self.with_prefix(path),
|
||||
|
@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
|
|||
};
|
||||
}
|
||||
|
||||
// If the block contains only a `panic!` macro (as expression or statement)
|
||||
fn is_panic_call(expr: &Expr<'_>) -> bool {
|
||||
// Unwrap any wrapping blocks
|
||||
let span = if let ExprKind::Block(block, _) = expr.kind {
|
||||
match (&block.expr, block.stmts.len(), block.stmts.first()) {
|
||||
(&Some(exp), 0, _) => exp.span,
|
||||
(&None, 1, Some(stmt)) => stmt.span,
|
||||
_ => return false,
|
||||
}
|
||||
} else {
|
||||
expr.span
|
||||
};
|
||||
|
||||
is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none()
|
||||
}
|
||||
|
||||
fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
|
||||
where
|
||||
'b: 'a,
|
||||
|
@ -1818,11 +1804,15 @@ mod redundant_pattern_match {
|
|||
/// Checks if the drop order for a type matters. Some std types implement drop solely to
|
||||
/// deallocate memory. For these types, and composites containing them, changing the drop order
|
||||
/// won't result in any observable side effects.
|
||||
fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
|
||||
}
|
||||
|
||||
fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
|
||||
fn type_needs_ordered_drop_inner<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
seen: &mut FxHashSet<Ty<'tcx>>,
|
||||
) -> bool {
|
||||
if !seen.insert(ty) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1884,7 +1874,7 @@ mod redundant_pattern_match {
|
|||
|
||||
// Checks if there are any temporaries created in the given expression for which drop order
|
||||
// matters.
|
||||
fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
struct V<'a, 'tcx> {
|
||||
cx: &'a LateContext<'tcx>,
|
||||
res: bool,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::higher::FormatExpn;
|
||||
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_errors::Applicability;
|
||||
|
@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL;
|
|||
|
||||
/// Checks for the `EXPECT_FUN_CALL` lint.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) {
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'_>,
|
||||
method_span: Span,
|
||||
name: &str,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
) {
|
||||
// Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
|
||||
// `&str`
|
||||
fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
|
@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa
|
|||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
||||
//Special handling for `format!` as arg_root
|
||||
if let Some(format_expn) = FormatExpn::parse(arg_root) {
|
||||
let span = match *format_expn.format_args.value_args {
|
||||
[] => format_expn.format_args.format_string_span,
|
||||
[.., last] => format_expn.format_args.format_string_span.to(last.span),
|
||||
};
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
|
||||
if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
|
||||
return;
|
||||
}
|
||||
let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return };
|
||||
let span = format_args.inputs_span();
|
||||
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp
|
|||
}
|
||||
}
|
||||
|
||||
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String {
|
||||
fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String {
|
||||
fn strip_angle_brackets(s: &str) -> Option<&str> {
|
||||
s.strip_prefix('<')?.strip_suffix('>')
|
||||
}
|
||||
|
|
|
@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::ty::{self, TraitRef, Ty, TyS};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
|
||||
|
@ -1895,6 +1894,11 @@ declare_clippy_lint! {
|
|||
/// ### Why is this bad?
|
||||
/// The unnecessary calls result in useless allocations.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
|
||||
/// owned copy of a resource and the resource is later used mutably. See
|
||||
/// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let path = std::path::Path::new("x");
|
||||
|
@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [
|
|||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> {
|
||||
fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
|
||||
if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
|
||||
if !args.iter().any(|e| e.span.from_expansion()) {
|
||||
return Some((path.ident.name, args, span));
|
||||
let name = path.ident.name.as_str();
|
||||
return Some((name, args, span));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str`
|
||||
macro_rules! method_call {
|
||||
($expr:expr) => {
|
||||
method_call($expr)
|
||||
.as_ref()
|
||||
.map(|&(ref name, args, span)| (name.as_str(), args, span))
|
||||
};
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Methods {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
|
@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
|
||||
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
|
||||
if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
|
||||
match (name, args) {
|
||||
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
|
||||
zst_offset::check(cx, expr, recv);
|
||||
|
@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
|
||||
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
|
||||
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
|
||||
("collect", []) => match method_call!(recv) {
|
||||
("collect", []) => match method_call(recv) {
|
||||
Some((name @ ("cloned" | "copied"), [recv2], _)) => {
|
||||
iter_cloned_collect::check(cx, name, expr, recv2);
|
||||
},
|
||||
|
@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
},
|
||||
_ => {},
|
||||
},
|
||||
("count", []) => match method_call!(recv) {
|
||||
("count", []) => match method_call(recv) {
|
||||
Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
|
||||
iter_count::check(cx, expr, recv2, name);
|
||||
},
|
||||
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
|
||||
_ => {},
|
||||
},
|
||||
("expect", [_]) => match method_call!(recv) {
|
||||
("expect", [_]) => match method_call(recv) {
|
||||
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
|
||||
_ => expect_used::check(cx, expr, recv),
|
||||
},
|
||||
|
@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
flat_map_option::check(cx, expr, arg, span);
|
||||
},
|
||||
("flatten", []) => {
|
||||
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
|
||||
if let Some(("map", [recv, map_arg], _)) = method_call(recv) {
|
||||
map_flatten::check(cx, expr, recv, map_arg);
|
||||
}
|
||||
},
|
||||
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
|
||||
("for_each", [_]) => {
|
||||
if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
|
||||
if let Some(("inspect", [_, _], span2)) = method_call(recv) {
|
||||
inspect_for_each::check(cx, expr, span2);
|
||||
}
|
||||
},
|
||||
|
@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
|
||||
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
|
||||
("map", [m_arg]) => {
|
||||
if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
|
||||
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
|
||||
match (name, args) {
|
||||
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
|
||||
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
|
||||
|
@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
},
|
||||
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
|
||||
("next", []) => {
|
||||
if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
|
||||
if let Some((name, [recv, args @ ..], _)) = method_call(recv) {
|
||||
match (name, args) {
|
||||
("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
|
||||
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
|
||||
|
@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
}
|
||||
}
|
||||
},
|
||||
("nth", [n_arg]) => match method_call!(recv) {
|
||||
("nth", [n_arg]) => match method_call(recv) {
|
||||
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
|
||||
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
|
||||
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
|
||||
|
@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
|
||||
implicit_clone::check(cx, name, expr, recv, span);
|
||||
},
|
||||
("unwrap", []) => match method_call!(recv) {
|
||||
("unwrap", []) => match method_call(recv) {
|
||||
Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
|
||||
Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
|
||||
_ => unwrap_used::check(cx, expr, recv),
|
||||
},
|
||||
("unwrap_or", [u_arg]) => match method_call!(recv) {
|
||||
("unwrap_or", [u_arg]) => match method_call(recv) {
|
||||
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
|
||||
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
|
||||
},
|
||||
|
@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
},
|
||||
_ => {},
|
||||
},
|
||||
("unwrap_or_else", [u_arg]) => match method_call!(recv) {
|
||||
("unwrap_or_else", [u_arg]) => match method_call(recv) {
|
||||
Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
|
||||
_ => {
|
||||
unwrap_or_else_default::check(cx, expr, recv, u_arg);
|
||||
|
@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
|
|||
}
|
||||
|
||||
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
|
||||
if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
|
||||
if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
|
||||
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
|
||||
}
|
||||
}
|
||||
|
@ -2535,11 +2531,17 @@ impl SelfKind {
|
|||
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
|
||||
}
|
||||
|
||||
fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
|
||||
!matches_value(cx, parent_ty, ty)
|
||||
&& !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
|
||||
&& !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
|
||||
}
|
||||
|
||||
match self {
|
||||
Self::Value => matches_value(cx, parent_ty, ty),
|
||||
Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
|
||||
Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
|
||||
Self::No => ty != parent_ty,
|
||||
Self::No => matches_none(cx, parent_ty, ty),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -124,7 +124,7 @@ struct IterUsage {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn parse_iter_usage(
|
||||
fn parse_iter_usage<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
|
||||
|
@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn(
|
|||
}
|
||||
}
|
||||
|
||||
fn check_iter(
|
||||
fn check_iter<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
|
||||
|
|
|
@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol};
|
|||
|
||||
use super::UNNECESSARY_TO_OWNED;
|
||||
|
||||
pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
|
||||
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
if let Some(callee_def_id) = fn_def_id(cx, parent);
|
||||
if is_into_iter(cx, callee_def_id);
|
||||
then {
|
||||
check_for_loop_iter(cx, parent, method_name, receiver)
|
||||
check_for_loop_iter(cx, parent, method_name, receiver, false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
|
|||
/// include this code directly is so that it can be called from
|
||||
/// `unnecessary_into_owned::check_into_iter_call_arg`.
|
||||
pub fn check_for_loop_iter(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
method_name: Symbol,
|
||||
receiver: &'tcx Expr<'tcx>,
|
||||
receiver: &Expr<'_>,
|
||||
cloned_before_iter: bool,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
|
||||
|
@ -70,12 +71,22 @@ pub fn check_for_loop_iter(
|
|||
expr.span,
|
||||
&format!("unnecessary use of `{}`", method_name),
|
||||
|diag| {
|
||||
diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
|
||||
// If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to
|
||||
// a `to_owned`-like function was removed, then the next suggestion may be
|
||||
// incorrect. This is because the iterator that results from the call's removal
|
||||
// could hold a reference to a resource that is used mutably. See
|
||||
// https://github.com/rust-lang/rust-clippy/issues/8148.
|
||||
let applicability = if cloned_before_iter {
|
||||
Applicability::MaybeIncorrect
|
||||
} else {
|
||||
Applicability::MachineApplicable
|
||||
};
|
||||
diag.span_suggestion(expr.span, "use", snippet, applicability);
|
||||
for addr_of_expr in addr_of_exprs {
|
||||
match addr_of_expr.kind {
|
||||
ExprKind::AddrOf(_, _, referent) => {
|
||||
let span = addr_of_expr.span.with_hi(referent.span.lo());
|
||||
diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
|
||||
diag.span_suggestion(span, "remove this `&`", String::new(), applicability);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -90,7 +101,7 @@ pub fn check_for_loop_iter(
|
|||
|
||||
/// The core logic of `check_for_loop_iter` above, this function wraps a use of
|
||||
/// `CloneOrCopyVisitor`.
|
||||
fn clone_or_copy_needed(
|
||||
fn clone_or_copy_needed<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
body: &'tcx Expr<'tcx>,
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::cmp::max;
|
|||
|
||||
use super::UNNECESSARY_TO_OWNED;
|
||||
|
||||
pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
|
||||
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
|
||||
if_chain! {
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
|
||||
if let [receiver] = args;
|
||||
|
@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
|
|||
/// call of a `to_owned`-like function is unnecessary.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_addr_of_expr(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
cx: &LateContext<'_>,
|
||||
expr: &Expr<'_>,
|
||||
method_name: Symbol,
|
||||
method_def_id: DefId,
|
||||
receiver: &'tcx Expr<'tcx>,
|
||||
receiver: &Expr<'_>,
|
||||
) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
|
@ -171,12 +171,7 @@ fn check_addr_of_expr(
|
|||
|
||||
/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
|
||||
/// call of a `to_owned`-like function is unnecessary.
|
||||
fn check_into_iter_call_arg(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
method_name: Symbol,
|
||||
receiver: &'tcx Expr<'tcx>,
|
||||
) -> bool {
|
||||
fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
|
||||
if_chain! {
|
||||
if let Some(parent) = get_parent_expr(cx, expr);
|
||||
if let Some(callee_def_id) = fn_def_id(cx, parent);
|
||||
|
@ -187,7 +182,13 @@ fn check_into_iter_call_arg(
|
|||
if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) {
|
||||
if unnecessary_iter_cloned::check_for_loop_iter(
|
||||
cx,
|
||||
parent,
|
||||
method_name,
|
||||
receiver,
|
||||
true,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
let cloned_or_copied = if is_copy(cx, item_ty) {
|
||||
|
@ -195,6 +196,9 @@ fn check_into_iter_call_arg(
|
|||
} else {
|
||||
"cloned"
|
||||
};
|
||||
// The next suggestion may be incorrect because the removal of the `to_owned`-like
|
||||
// function could cause the iterator to hold a reference to a resource that is used
|
||||
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_TO_OWNED,
|
||||
|
@ -202,7 +206,7 @@ fn check_into_iter_call_arg(
|
|||
&format!("unnecessary use of `{}`", method_name),
|
||||
"use",
|
||||
format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
|
||||
Applicability::MachineApplicable,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
@ -212,7 +216,7 @@ fn check_into_iter_call_arg(
|
|||
|
||||
/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
|
||||
/// of a `to_owned`-like function is unnecessary.
|
||||
fn check_other_call_arg(
|
||||
fn check_other_call_arg<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
method_name: Symbol,
|
||||
|
@ -278,7 +282,7 @@ fn check_other_call_arg(
|
|||
|
||||
/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
|
||||
/// expression found (if any) along with the immediately prior expression.
|
||||
fn skip_addr_of_ancestors(
|
||||
fn skip_addr_of_ancestors<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
mut expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
|
@ -294,7 +298,7 @@ fn skip_addr_of_ancestors(
|
|||
|
||||
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
|
||||
/// `Substs`, and arguments.
|
||||
fn get_callee_substs_and_args(
|
||||
fn get_callee_substs_and_args<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
|
||||
|
@ -319,7 +323,7 @@ fn get_callee_substs_and_args(
|
|||
}
|
||||
|
||||
/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
|
||||
fn get_input_traits_and_projections(
|
||||
fn get_input_traits_and_projections<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
callee_def_id: DefId,
|
||||
input: Ty<'tcx>,
|
||||
|
@ -359,7 +363,11 @@ fn get_input_traits_and_projections(
|
|||
}
|
||||
|
||||
/// Composes two substitutions by applying the latter to the types of the former.
|
||||
fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> {
|
||||
fn compose_substs<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
left: &[GenericArg<'tcx>],
|
||||
right: SubstsRef<'tcx>,
|
||||
) -> Vec<GenericArg<'tcx>> {
|
||||
left.iter()
|
||||
.map(|arg| {
|
||||
if let GenericArgKind::Type(arg_ty) = arg.unpack() {
|
||||
|
|
|
@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
|
|||
}
|
||||
}
|
||||
|
||||
fn check_binary(
|
||||
fn check_binary<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
expr: &Expr<'_>,
|
||||
cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{higher, is_direct_expn_of};
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
@ -34,26 +34,30 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
|
||||
|
||||
const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"];
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
for dmn in &DEBUG_MACRO_NAMES {
|
||||
if is_direct_expn_of(e.span, dmn).is_some() {
|
||||
if let Some(macro_args) = higher::extract_assert_macro_args(e) {
|
||||
for arg in macro_args {
|
||||
let mut visitor = MutArgVisitor::new(cx);
|
||||
visitor.visit_expr(arg);
|
||||
if let Some(span) = visitor.expr_span() {
|
||||
span_lint(
|
||||
cx,
|
||||
DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
span,
|
||||
&format!("do not call a function with mutable arguments inside of `{}!`", dmn),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
if !matches!(
|
||||
macro_name.as_str(),
|
||||
"debug_assert" | "debug_assert_eq" | "debug_assert_ne"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return };
|
||||
for arg in [lhs, rhs] {
|
||||
let mut visitor = MutArgVisitor::new(cx);
|
||||
visitor.visit_expr(arg);
|
||||
if let Some(span) = visitor.expr_span() {
|
||||
span_lint(
|
||||
cx,
|
||||
DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
span,
|
||||
&format!(
|
||||
"do not call a function with mutable arguments inside of `{}!`",
|
||||
macro_name
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ declare_clippy_lint! {
|
|||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MUTEX_ATOMIC,
|
||||
perf,
|
||||
nursery,
|
||||
"using a mutex where an atomic value could be used instead"
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
|
||||
|
||||
impl LateLintPass<'_> for NeedlessForEach {
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
let expr = match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
|
||||
|
|
|
@ -330,7 +330,7 @@ fn check<'tcx>(
|
|||
Some(())
|
||||
}
|
||||
|
||||
impl LateLintPass<'tcx> for NeedlessLateInit {
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
|
||||
let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool {
|
||||
fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
if has_no_effect(cx, expr) {
|
||||
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
|
||||
|
@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if_chain! {
|
||||
if let StmtKind::Semi(expr) = stmt.kind;
|
||||
if let Some(reduced) = reduce_expression(cx, expr);
|
||||
|
|
|
@ -40,7 +40,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]);
|
||||
|
||||
impl LateLintPass<'_> for NonOctalUnixPermissions {
|
||||
impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(path, _, [func, param], _) => {
|
||||
|
|
|
@ -50,7 +50,7 @@ declare_clippy_lint! {
|
|||
declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
|
||||
|
||||
impl EarlyLintPass for OctalEscapes {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if in_external_macro(cx.sess, expr.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
|
||||
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
|
||||
let contents = lit.symbol.as_str();
|
||||
let mut iter = contents.char_indices().peekable();
|
||||
let mut found = vec![];
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::return_ty;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{find_macro_calls, is_expn_of, return_ty};
|
||||
use clippy_utils::visitors::expr_visitor_no_bodies;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::{sym, Span};
|
||||
|
@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
|
|||
}
|
||||
|
||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||
let mut panics = find_macro_calls(
|
||||
&[
|
||||
"unimplemented",
|
||||
"unreachable",
|
||||
"panic",
|
||||
"todo",
|
||||
"assert",
|
||||
"assert_eq",
|
||||
"assert_ne",
|
||||
],
|
||||
body,
|
||||
);
|
||||
panics.retain(|span| is_expn_of(*span, "debug_assert").is_none());
|
||||
let mut panics = Vec::new();
|
||||
expr_visitor_no_bodies(|expr| {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
|
||||
if matches!(
|
||||
&*cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
|
||||
) {
|
||||
panics.push(macro_call.span);
|
||||
return false;
|
||||
}
|
||||
true
|
||||
})
|
||||
.visit_expr(&body.value);
|
||||
if !panics.is_empty() {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{is_expn_of, match_panic_call};
|
||||
use if_chain::if_chain;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if match_panic_call(cx, expr).is_some()
|
||||
&& (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none())
|
||||
{
|
||||
let span = get_outer_span(expr);
|
||||
if is_expn_of(expr.span, "unimplemented").is_some() {
|
||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
|
||||
if is_panic(cx, macro_call.def_id) {
|
||||
span_lint(
|
||||
cx,
|
||||
PANIC,
|
||||
macro_call.span,
|
||||
"`panic` should not be present in production code",
|
||||
);
|
||||
return;
|
||||
}
|
||||
match cx.tcx.item_name(macro_call.def_id).as_str() {
|
||||
"todo" => {
|
||||
span_lint(
|
||||
cx,
|
||||
TODO,
|
||||
macro_call.span,
|
||||
"`todo` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unimplemented" => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIMPLEMENTED,
|
||||
span,
|
||||
macro_call.span,
|
||||
"`unimplemented` should not be present in production code",
|
||||
);
|
||||
} else if is_expn_of(expr.span, "todo").is_some() {
|
||||
span_lint(cx, TODO, span, "`todo` should not be present in production code");
|
||||
} else if is_expn_of(expr.span, "unreachable").is_some() {
|
||||
span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro");
|
||||
} else if is_expn_of(expr.span, "panic").is_some() {
|
||||
span_lint(cx, PANIC, span, "`panic` should not be present in production code");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_outer_span(expr: &Expr<'_>) -> Span {
|
||||
if_chain! {
|
||||
if expr.span.from_expansion();
|
||||
let first = expr.span.ctxt().outer_expn_data().call_site;
|
||||
if first.from_expansion();
|
||||
then {
|
||||
first.ctxt().outer_expn_data().call_site
|
||||
} else {
|
||||
expr.span
|
||||
},
|
||||
"unreachable" => {
|
||||
span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]);
|
|||
|
||||
static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
|
||||
|
||||
impl LateLintPass<'_> for PtrEq {
|
||||
impl<'tcx> LateLintPass<'tcx> for PtrEq {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
|
|
|
@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
|||
continue;
|
||||
} else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
|
||||
// cloned value is mutated, and the clone is alive.
|
||||
if possible_borrower.is_alive_at(ret_local, loc) {
|
||||
if possible_borrower.local_is_alive_at(ret_local, loc) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> {
|
|||
self.bitset.0 == self.bitset.1
|
||||
}
|
||||
|
||||
fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
|
||||
fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
|
||||
self.maybe_live.seek_after_primary_effect(at);
|
||||
self.maybe_live.contains(local)
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
|
||||
|
||||
impl LateLintPass<'_> for RedundantSlicing {
|
||||
impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
|
|
|
@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
|
|||
if_chain! {
|
||||
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
|
||||
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
|
||||
if deref_target.span.ctxt() == e.span.ctxt();
|
||||
if !addrof_target.span.from_expansion();
|
||||
then {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::is_must_use_ty;
|
||||
use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty};
|
||||
use clippy_utils::{nth_arg, return_ty};
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
|
||||
|
@ -13,25 +14,46 @@ declare_clippy_lint! {
|
|||
/// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It prevents to "forget" to use the newly created value.
|
||||
/// Methods returning `Self` often create new values, having the `#[must_use]` attribute
|
||||
/// prevents users from "forgetting" to use the newly created value.
|
||||
///
|
||||
/// The `#[must_use]` attribute can be added to the type itself to ensure that instances
|
||||
/// are never forgotten. Functions returning a type marked with `#[must_use]` will not be
|
||||
/// linted, as the usage is already enforced by the type attribute.
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint is only applied on methods taking a `self` argument. It would be mostly noise
|
||||
/// if it was added on constructors for example.
|
||||
///
|
||||
/// ### Example
|
||||
/// Missing attribute
|
||||
/// ```rust
|
||||
/// pub struct Bar;
|
||||
///
|
||||
/// impl Bar {
|
||||
/// // Bad
|
||||
/// pub fn bar(&self) -> Self {
|
||||
/// Self
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// // Good
|
||||
/// It's better to have the `#[must_use]` attribute on the method like this:
|
||||
/// ```rust
|
||||
/// pub struct Bar;
|
||||
/// impl Bar {
|
||||
/// #[must_use]
|
||||
/// pub fn foo(&self) -> Self {
|
||||
/// pub fn bar(&self) -> Self {
|
||||
/// Self
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or on the type definition like this:
|
||||
/// ```rust
|
||||
/// #[must_use]
|
||||
/// pub struct Bar;
|
||||
/// impl Bar {
|
||||
/// pub fn bar(&self) -> Self {
|
||||
/// Self
|
||||
/// }
|
||||
/// }
|
||||
|
@ -44,7 +66,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
|
||||
|
||||
fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
|
||||
fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
|
||||
if_chain! {
|
||||
// If it comes from an external macro, better ignore it.
|
||||
if !in_external_macro(cx.sess(), span);
|
||||
|
@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD
|
|||
if !is_must_use_ty(cx, ret_ty);
|
||||
|
||||
then {
|
||||
span_lint(
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
RETURN_SELF_NOT_MUST_USE,
|
||||
span,
|
||||
"missing `#[must_use]` attribute on a method returning `Self`",
|
||||
None,
|
||||
"consider adding the `#[must_use]` attribute to the method or directly to the `Self` type"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]);
|
||||
|
||||
impl LateLintPass<'_> for SemicolonIfNothingReturned {
|
||||
impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned {
|
||||
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
|
||||
if_chain! {
|
||||
if !block.span.from_expansion();
|
||||
|
|
63
clippy_lints/src/single_char_lifetime_names.rs
Normal file
63
clippy_lints/src/single_char_lifetime_names.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use rustc_ast::ast::{GenericParam, GenericParamKind};
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lifetimes with names which are one character
|
||||
/// long.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A single character is likely not enough to express the
|
||||
/// purpose of a lifetime. Using a longer name can make code
|
||||
/// easier to understand, especially for those who are new to
|
||||
/// Rust.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Rust programmers and learning resources tend to use single
|
||||
/// character lifetimes, so this lint is at odds with the
|
||||
/// ecosystem at large. In addition, the lifetime's purpose may
|
||||
/// be obvious or, rarely, expressible in one character.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct DiagnosticCtx<'a> {
|
||||
/// source: &'a str,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct DiagnosticCtx<'src> {
|
||||
/// source: &'src str,
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
pub SINGLE_CHAR_LIFETIME_NAMES,
|
||||
restriction,
|
||||
"warns against single-character lifetime names"
|
||||
}
|
||||
|
||||
declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]);
|
||||
|
||||
impl EarlyLintPass for SingleCharLifetimeNames {
|
||||
fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) {
|
||||
if in_external_macro(ctx.sess, param.ident.span) {
|
||||
return;
|
||||
}
|
||||
|
||||
if let GenericParamKind::Lifetime = param.kind {
|
||||
if !param.is_placeholder && param.ident.as_str().len() <= 2 {
|
||||
span_lint_and_help(
|
||||
ctx,
|
||||
SINGLE_CHAR_LIFETIME_NAMES,
|
||||
param.ident.span,
|
||||
"single-character lifetime names are likely uninformative",
|
||||
None,
|
||||
"use a more informative name",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
|
||||
|
||||
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
|
||||
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
|
||||
match expr.kind {
|
||||
ExprKind::Call(count_func, _func_args) => {
|
||||
if_chain! {
|
||||
|
@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
|
|||
}
|
||||
}
|
||||
|
||||
fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
fn get_pointee_ty_and_count_expr<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &'tcx Expr<'_>,
|
||||
) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
const FUNCTIONS: [&[&str]; 8] = [
|
||||
&paths::PTR_COPY_NONOVERLAPPING,
|
||||
&paths::PTR_COPY,
|
||||
|
|
|
@ -381,7 +381,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(StrToString => [STR_TO_STRING]);
|
||||
|
||||
impl LateLintPass<'_> for StrToString {
|
||||
impl<'tcx> LateLintPass<'tcx> for StrToString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
|
||||
|
@ -431,7 +431,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(StringToString => [STRING_TO_STRING]);
|
||||
|
||||
impl LateLintPass<'_> for StringToString {
|
||||
impl<'tcx> LateLintPass<'tcx> for StringToString {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
|
||||
|
|
|
@ -39,7 +39,7 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
|
||||
|
||||
impl LateLintPass<'tcx> for StrlenOnCStrings {
|
||||
impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if_chain! {
|
||||
if !expr.span.from_expansion();
|
||||
|
|
|
@ -355,7 +355,7 @@ struct BinaryOp<'exprs> {
|
|||
right: &'exprs Expr,
|
||||
}
|
||||
|
||||
impl BinaryOp<'exprs> {
|
||||
impl<'exprs> BinaryOp<'exprs> {
|
||||
fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
|
||||
Self { op, span, left, right }
|
||||
}
|
||||
|
@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
|
||||
fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
|
||||
match (&left_outer.kind, &right_outer.kind) {
|
||||
(
|
||||
ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue