Merge commit '97a5daa65908e59744e2bc625b14849352231c75' into clippyup

This commit is contained in:
flip1995 2022-01-13 13:18:19 +01:00
parent dda2aef64f
commit fb0142ae41
223 changed files with 3261 additions and 1687 deletions

View file

@ -2,9 +2,12 @@
uitest = "test --test compile-test" uitest = "test --test compile-test"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" 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 -- " 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] [build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"] rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
target-dir = "target" target-dir = "target"
[unstable]
binary-dep-depinfo = true

View file

@ -49,17 +49,17 @@ jobs:
echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
- name: Build - name: Build
run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint run: cargo build --features deny-warnings,internal
- name: Test - name: Test
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint run: cargo test --features deny-warnings,internal
- name: Test clippy_lints - 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 working-directory: clippy_lints
- name: Test clippy_utils - 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 working-directory: clippy_utils
- name: Test rustc_tools_util - name: Test rustc_tools_util
@ -70,14 +70,6 @@ jobs:
run: cargo test --features deny-warnings run: cargo test --features deny-warnings
working-directory: clippy_dev 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 - name: Test clippy-driver
run: bash .github/driver.sh run: bash .github/driver.sh
env: env:

View file

@ -112,17 +112,22 @@ jobs:
echo "$SYSROOT/bin" >> $GITHUB_PATH echo "$SYSROOT/bin" >> $GITHUB_PATH
- name: Build - name: Build
run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint run: cargo build --features deny-warnings,internal
- name: Test - 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 - 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 working-directory: clippy_lints
- name: Test clippy_utils - 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 working-directory: clippy_utils
- name: Test rustc_tools_util - name: Test rustc_tools_util
@ -133,14 +138,6 @@ jobs:
run: cargo test --features deny-warnings run: cargo test --features deny-warnings
working-directory: clippy_dev 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 - name: Test clippy-driver
run: bash .github/driver.sh run: bash .github/driver.sh
env: env:

1
.gitignore vendored
View file

@ -19,7 +19,6 @@ out
/target /target
/clippy_lints/target /clippy_lints/target
/clippy_utils/target /clippy_utils/target
/clippy_workspace_tests/target
/clippy_dev/target /clippy_dev/target
/lintcheck/target /lintcheck/target
/rustc_tools_util/target /rustc_tools_util/target

View file

@ -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 [`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_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 [`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 [`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 [`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 [`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 [`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_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_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_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_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 [`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 [`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 [`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_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_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_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 [`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.59" version = "0.1.60"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"
@ -47,7 +47,9 @@ itertools = "0.10"
quote = "1.0" quote = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
syn = { version = "1.0", features = ["full"] } syn = { version = "1.0", features = ["full"] }
futures = "0.3"
parking_lot = "0.11.2" parking_lot = "0.11.2"
tokio = { version = "1", features = ["io-util"] }
[build-dependencies] [build-dependencies]
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" } 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] [features]
deny-warnings = ["clippy_lints/deny-warnings"] deny-warnings = ["clippy_lints/deny-warnings"]
integration = ["tempfile"] integration = ["tempfile"]
internal-lints = ["clippy_lints/internal-lints"] internal = ["clippy_lints/internal"]
metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
[package.metadata.rust-analyzer] [package.metadata.rust-analyzer]
# This package uses #[feature(rustc_private)] # This package uses #[feature(rustc_private)]

View file

@ -5,7 +5,7 @@
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
[There are 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). 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. 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 ## Usage
Below are instructions on how to use Clippy as a subcommand, compiled from source Below are instructions on how to use Clippy as a cargo subcommand,
or in Travis CI. in projects that do not use cargo, or in Travis CI.
### As a cargo subcommand (`cargo clippy`) ### 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 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 Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
your `rustc` compilation commands with `clippy-driver`. For example, if your project runs: with the same arguments you use for `rustc`. For example:
```terminal
rustc --edition 2018 -Cpanic=abort foo.rs
```
Then, to enable Clippy, you will need to call:
```terminal ```terminal
clippy-driver --edition 2018 -Cpanic=abort foo.rs 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 ### Travis CI

View file

@ -3,7 +3,7 @@ use itertools::Itertools;
use shell_escape::escape; use shell_escape::escape;
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
use std::path::Path; use std::path::Path;
use std::process::{self, Command}; use std::process::{self, Command, Stdio};
use std::{fs, io}; use std::{fs, io};
use walkdir::WalkDir; use walkdir::WalkDir;
@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError {
struct FmtContext { struct FmtContext {
check: bool, check: bool,
verbose: bool, verbose: bool,
rustfmt_path: String,
} }
// the "main" function of cargo dev fmt // 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 result = try_run(&context);
let code = match result { let code = match result {
Ok(true) => 0, Ok(true) => 0,
@ -141,8 +158,12 @@ fn exec(
println!("{}", format_command(&program, &dir, args)); println!("{}", format_command(&program, &dir, args));
} }
let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?; let output = Command::new(&program)
let output = child.wait_with_output()?; .env("RUSTFMT", &context.rustfmt_path)
.current_dir(&dir)
.args(args.iter())
.output()
.unwrap();
let success = output.status.success(); let success = output.status.success();
if !context.check && !success { if !context.check && !success {
@ -159,7 +180,6 @@ fn exec(
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> { fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
let mut args = vec!["fmt", "--all"]; let mut args = vec!["fmt", "--all"];
if context.check { if context.check {
args.push("--");
args.push("--check"); args.push("--check");
} }
let success = exec(context, "cargo", path, &args)?; let success = exec(context, "cargo", path, &args)?;
@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul
} }
args.extend(paths); 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) Ok(success)
} }

View file

@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>(
for (is_public, module_name, lint_name) in details { for (is_public, module_name, lint_name) in details {
if !is_public { 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)); output.push_str(&format!(" {}::{},\n", module_name, lint_name));
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.59" version = "0.1.60"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"
@ -30,8 +30,7 @@ url = { version = "2.2", features = ["serde"] }
[features] [features]
deny-warnings = ["clippy_utils/deny-warnings"] deny-warnings = ["clippy_utils/deny-warnings"]
# build clippy with internal lints enabled, off by default # build clippy with internal lints enabled, off by default
internal-lints = ["clippy_utils/internal-lints"] internal = ["clippy_utils/internal", "serde_json"]
metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
[package.metadata.rust-analyzer] [package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)] # This crate uses #[feature(rustc_private)]

View file

@ -1,12 +1,10 @@
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::higher; use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
use clippy_utils::source::snippet_opt; use rustc_hir::Expr;
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 rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { 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( span_lint_and_help(
cx, cx,
ASSERTIONS_ON_CONSTANTS, ASSERTIONS_ON_CONSTANTS,
e.span, macro_call.span,
if is_debug { &format!(
"`debug_assert!(true)` will be optimized out by the compiler" "`{}!(true)` will be optimized out by the compiler",
} else { cx.tcx.item_name(macro_call.def_id)
"`assert!(true)` will be optimized out by the compiler" ),
},
None, None,
"remove it", "remove it",
); );
}; } else if !is_debug {
let lint_false_without_message = || { let (assert_arg, panic_arg) = match panic_expn {
span_lint_and_help( PanicExpn::Empty => ("", ""),
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 let Some(assert_span) = is_direct_expn_of(e.span, "assert") { span_lint_and_help(
if assert_span.from_expansion() { cx,
return; ASSERTIONS_ON_CONSTANTS,
} macro_call.span,
if let Some(assert_match) = match_assert_with_message(cx, e) { &format!("`assert!(false{})` should probably be replaced", assert_arg),
match assert_match { None,
// matched assert but not message &format!("use `panic!({})` or `unreachable!({0})`", panic_arg),
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),
};
}
} }
} }
} }
/// 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
}

View file

@ -1,9 +1,10 @@
//! checks for attributes //! checks for attributes
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; 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::msrvs;
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; 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 if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability; 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 { 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 { match &expr.kind {
ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
ExprKind::Ret(None) | ExprKind::Break(_, None) => false, 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, _ => true,
} }
} }

View file

@ -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_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Lit}; use rustc_hir::{Expr, ExprKind, Lit};
@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool {
) && !e.span.from_expansion() ) && !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); let ty = cx.typeck_results().expr_ty(e);
cx.tcx 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 { impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let macros = ["assert_eq", "debug_assert_eq"]; let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
let inverted_macros = ["assert_ne", "debug_assert_ne"]; let macro_name = cx.tcx.item_name(macro_call.def_id);
if !matches!(
for mac in macros.iter().chain(inverted_macros.iter()) { macro_name.as_str(),
if let Some(span) = is_direct_expn_of(expr.span, mac) { "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
if let Some(args) = higher::extract_assert_macro_args(expr) { ) {
if let [a, b, ..] = args[..] { return;
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 ((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 = &macro_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,
);
} }
} }

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

View file

@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &
None None
} }
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
span_lint_and_help( span_lint_and_help(

View file

@ -9,7 +9,7 @@ use rustc_span::symbol::sym;
use super::CAST_PTR_ALIGNMENT; 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 let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
if is_hir_ty_cfg_dependant(cx, cast_to) { if is_hir_ty_cfg_dependant(cx, cast_to) {
return; return;

View file

@ -6,7 +6,7 @@ use rustc_middle::ty;
use super::CAST_REF_TO_MUT; 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_chain! {
if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind;
if let ExprKind::Cast(e, t) = &e.kind; if let ExprKind::Cast(e, t) = &e.kind;

View file

@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy};
use super::CHAR_LIT_AS_U8; 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_chain! {
if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Cast(e, _) = &expr.kind;
if let ExprKind::Lit(l) = &e.kind; if let ExprKind::Lit(l) = &e.kind;

View file

@ -12,7 +12,7 @@ use rustc_semver::RustcVersion;
use super::PTR_AS_PTR; 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) { if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
return; return;
} }

View file

@ -316,7 +316,7 @@ struct BlockEqual {
/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// 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. /// 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 start_eq = usize::MAX;
let mut end_eq = usize::MAX; let mut end_eq = usize::MAX;
let mut expr_eq = true; 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( fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
cx: &LateContext<'tcx>,
symbols: &FxHashSet<Symbol>,
if_expr: &'tcx Expr<'_>,
) -> bool {
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
let ignore_span = block.span.shrink_to_lo().to(if_expr.span); 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( fn emit_branches_sharing_code_lint(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
start_stmts: usize, start_stmts: usize,
end_stmts: usize, end_stmts: usize,
lint_end: bool, lint_end: bool,
warn_about_moved_symbol: bool, warn_about_moved_symbol: bool,
blocks: &[&Block<'tcx>], blocks: &[&Block<'_>],
if_expr: &'tcx Expr<'_>, if_expr: &Expr<'_>,
) { ) {
if start_stmts == 0 && !lint_end { if start_stmts == 0 && !lint_end {
return; return;

View file

@ -77,7 +77,7 @@ pub struct Default {
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if_chain! {
if !expr.span.from_expansion(); if !expr.span.from_expansion();
@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default {
} }
#[allow(clippy::too_many_lines)] #[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 // start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding // statements, see if they re-assign the fields of the binding
let stmts_head = match block.stmts { let stmts_head = match block.stmts {

View file

@ -54,7 +54,7 @@ declare_clippy_lint! {
declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); 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<'_>) { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let mut visitor = NumericFallbackVisitor::new(cx); let mut visitor = NumericFallbackVisitor::new(cx);
visitor.visit_body(body); visitor.visit_body(body);

View file

@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
} }
} }
fn try_parse_ref_op( fn try_parse_ref_op<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
typeck: &'tcx TypeckResults<'_>, typeck: &'tcx TypeckResults<'_>,
expr: &'tcx Expr<'_>, 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 // Checks whether the type for a deref call actually changed the type, not just the mutability of
// the reference. // 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()) { match (result_ty.kind(), arg_ty.kind()) {
(ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), (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. /// Adjustments are sometimes made in the parent block rather than the expression itself.
fn find_adjustments( fn find_adjustments<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
typeck: &'tcx TypeckResults<'_>, typeck: &'tcx TypeckResults<'_>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
@ -499,7 +499,7 @@ fn find_adjustments(
} }
#[allow(clippy::needless_pass_by_value)] #[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 { match state {
State::DerefMethod { State::DerefMethod {
ty_changed_count, ty_changed_count,
@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat
} }
impl Dereferencing { 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(outer_pat) = self.ref_locals.get_mut(&local) {
if let Some(pat) = outer_pat { if let Some(pat) = outer_pat {
// Check for auto-deref // Check for auto-deref

View file

@ -11,6 +11,9 @@ declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Denies the configured methods and functions in clippy.toml /// 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? /// ### Why is this bad?
/// Some methods are undesirable in certain contexts, and it's beneficial to /// Some methods are undesirable in certain contexts, and it's beneficial to
/// lint for them as needed. /// lint for them as needed.
@ -49,14 +52,14 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "1.49.0"] #[clippy::version = "1.49.0"]
pub DISALLOWED_METHODS, pub DISALLOWED_METHODS,
nursery, style,
"use of a disallowed method call" "use of a disallowed method call"
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DisallowedMethods { pub struct DisallowedMethods {
conf_disallowed: Vec<conf::DisallowedMethod>, conf_disallowed: Vec<conf::DisallowedMethod>,
disallowed: DefIdMap<Option<String>>, disallowed: DefIdMap<usize>,
} }
impl DisallowedMethods { impl DisallowedMethods {
@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
fn check_crate(&mut self, cx: &LateContext<'_>) { fn check_crate(&mut self, cx: &LateContext<'_>) {
for conf in &self.conf_disallowed { for (index, conf) in self.conf_disallowed.iter().enumerate() {
let (path, reason) = match conf { let segs: Vec<_> = conf.path().split("::").collect();
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();
if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { 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, Some(def_id) => def_id,
None => return, None => return,
}; };
let reason = match self.disallowed.get(&def_id) { let conf = match self.disallowed.get(&def_id) {
Some(reason) => reason, Some(&index) => &self.conf_disallowed[index],
None => return, None => return,
}; };
let func_path = cx.tcx.def_path_str(def_id); let msg = format!("use of a disallowed method `{}`", conf.path());
let msg = format!("use of a disallowed method `{}`", func_path);
span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
if let Some(reason) = reason { if let conf::DisallowedMethod::WithReason {
diag.note(reason); reason: Some(reason), ..
} = conf
{
diag.note(&format!("{} (from clippy.toml)", reason));
} }
}); });
} }

View file

@ -14,6 +14,9 @@ declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Denies the configured types in clippy.toml. /// 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? /// ### Why is this bad?
/// Some types are undesirable in certain contexts. /// Some types are undesirable in certain contexts.
/// ///
@ -44,7 +47,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "1.55.0"] #[clippy::version = "1.55.0"]
pub DISALLOWED_TYPES, pub DISALLOWED_TYPES,
nursery, style,
"use of disallowed types" "use of disallowed types"
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

View file

@ -1,8 +1,9 @@
use clippy_utils::attrs::is_doc_hidden; 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::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::source::{first_line_of_span, snippet_with_applicability};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; 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 if_chain::if_chain;
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; 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_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; 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_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map; use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
return; return;
} }
// check for `begin_panic` if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
if_chain! { if is_panic(self.cx, macro_call.def_id)
if let ExprKind::Call(func_expr, _) = expr.kind; || matches!(
if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; self.cx.tcx.item_name(macro_call.def_id).as_str(),
if let Some(path_def_id) = path.res.opt_def_id(); "assert" | "assert_eq" | "assert_ne" | "todo"
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); self.panic_span = Some(macro_call.span);
then {
self.panic_span = Some(expr.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` // check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); 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()) 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())
}

View file

@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> {
key: &'tcx Expr<'tcx>, key: &'tcx Expr<'tcx>,
call_ctxt: SyntaxContext, 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 mut negated = false;
let expr = peel_hir_expr_while(expr, |e| match e.kind { let expr = peel_hir_expr_while(expr, |e| match e.kind {
ExprKind::Unary(UnOp::Not, e) => { ExprKind::Unary(UnOp::Not, e) => {
@ -280,7 +280,7 @@ struct InsertExpr<'tcx> {
key: &'tcx Expr<'tcx>, key: &'tcx Expr<'tcx>,
value: &'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 { if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; 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) { 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. /// An insertion into the map.
Insertion(Insertion<'tcx>), Insertion(Insertion<'tcx>),
} }
impl Edit<'tcx> { impl<'tcx> Edit<'tcx> {
fn as_insertion(self) -> Option<Insertion<'tcx>> { fn as_insertion(self) -> Option<Insertion<'tcx>> {
if let Self::Insertion(i) = self { Some(i) } else { None } if let Self::Insertion(i) = self { Some(i) } else { None }
} }
@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> {
allow_insert_closure: bool, allow_insert_closure: bool,
is_single_insert: bool, is_single_insert: bool,
} }
impl InsertSearchResults<'tcx> { impl<'tcx> InsertSearchResults<'tcx> {
fn as_single_insertion(&self) -> Option<Insertion<'tcx>> { fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) 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>, cx: &LateContext<'tcx>,
contains_expr: &ContainsExpr<'tcx>, contains_expr: &ContainsExpr<'tcx>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,

View file

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; 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::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy}; 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 if_chain::if_chain;
use rustc_errors::Applicability; 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_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -68,32 +69,26 @@ declare_clippy_lint! {
declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); 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 { impl<'tcx> LateLintPass<'tcx> for EqOp {
#[allow(clippy::similar_names, clippy::too_many_lines)] #[allow(clippy::similar_names, clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Block(block, _) = e.kind { if_chain! {
for stmt in block.stmts { if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
for amn in &ASSERT_MACRO_NAMES { let name = cx.tcx.item_name(macro_call.def_id);
if_chain! { matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
if is_expn_of(stmt.span, amn).is_some(); .then(|| (macro_call, name))
if let StmtKind::Semi(matchexpr) = stmt.kind; });
if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
if macro_args.len() == 2; if eq_expr_value(cx, lhs, rhs);
let (lhs, rhs) = (macro_args[0], macro_args[1]); if macro_call.is_local();
if eq_expr_value(cx, lhs, rhs); if !is_in_test_function(cx.tcx, e.hir_id);
if !is_in_test_function(cx.tcx, e.hir_id); then {
then { span_lint(
span_lint( cx,
cx, EQ_OP,
EQ_OP, lhs.span.to(rhs.span),
lhs.span.to(rhs.span), &format!("identical args used in this `{}!` macro call", macro_name),
&format!("identical args used in this `{}!` macro call", amn), );
);
}
}
}
} }
} }
if let ExprKind::Binary(op, left, right) = e.kind { if let ExprKind::Binary(op, left, right) = e.kind {

View file

@ -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() { if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
implements_trait(cx, ty, def_id, &[other.into()]) implements_trait(cx, ty, def_id, &[other.into()])
} else { } else {

View file

@ -1,9 +1,11 @@
use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::same_type_and_consts;
use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TypeckResults;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp {
return; return;
} }
if let ExprKind::Binary(ref cmp, left, right) = e.kind { if let ExprKind::Binary(ref cmp, left, right) = e.kind {
let tck = cx.typeck_results();
match cmp.node { match cmp.node {
BinOpKind::Mul | BinOpKind::BitAnd => { BinOpKind::Mul | BinOpKind::BitAnd => {
check(cx, left, e.span); check(cx, tck, left, right, e);
check(cx, right, e.span); 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) { fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { 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( span_lint(
cx, cx,
ERASING_OP, ERASING_OP,
span, parent.span,
"this operation will always return zero. This is likely not the intended outcome", "this operation will always return zero. This is likely not the intended outcome",
); );
} }

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt; 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::usage::local_used_after_expr;
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
use if_chain::if_chain; 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::subst::Subst;
use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### 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 // 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. // 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(_))); 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 { then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(mut snippet) = snippet_opt(cx, callee.span) {

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; 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 clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
} else { } else {
None None
}; };
if let Some(format_args) = FormatArgsExpn::parse(write_arg); if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
then { then {
let calling_macro = let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally // 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); 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(); let mut write_output = write_output.to_string();
if write_output.ends_with('\n') { if write_output.ends_with('\n') {
write_output.pop(); write_output.pop();

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then; 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::ty::is_type_diagnostic_item;
use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; 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]) { 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::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; use rustc_hir::{Expr, ImplItemKind};
struct FindPanicUnwrap<'a, 'tcx> { struct FindPanicUnwrap<'a, 'tcx> {
lcx: &'a LateContext<'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>; type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `begin_panic` if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
if_chain! { if is_panic(self.lcx, macro_call.def_id) {
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 {
self.result.push(expr.span); self.result.push(expr.span);
} }
} }

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use if_chain::if_chain; use if_chain::if_chain;
@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
impl<'tcx> LateLintPass<'tcx> for UselessFormat { impl<'tcx> LateLintPass<'tcx> for UselessFormat {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) { let (format_args, call_site) = if_chain! {
Some(e) if !e.call_site.from_expansion() => e, if let Some(macro_call) = root_macro_call_first_node(cx, expr);
_ => return, 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; let mut applicability = Applicability::MachineApplicable;
if format_args.value_args.is_empty() { if format_args.value_args.is_empty() {
if format_args.format_string_parts.is_empty() { match *format_args.format_string_parts {
span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability); [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
} else { [_] => {
if_chain! { if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
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 {
// Simulate macro expansion, converting {{ and }} to { and }. // Simulate macro expansion, converting {{ and }} to { and }.
let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let s_expand = s_src.replace("{{", "{").replace("}}", "}");
let sugg = format!("{}.to_string()", s_expand); let sugg = format!("{}.to_string()", s_expand);
span_useless_format(cx, call_site, sugg, applicability); span_useless_format(cx, call_site, sugg, applicability);
} }
} },
[..] => {},
} }
} else if let [value] = *format_args.value_args { } else if let [value] = *format_args.value_args {
if_chain! { 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() { if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did), ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
ty::Str => true, ty::Str => true,
_ => false, _ => false,
}; };
if let Some(args) = format_args.args(); 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 { then {
let is_new_string = match value.kind { let is_new_string = match value.kind {
ExprKind::Binary(..) => true, ExprKind::Binary(..) => true,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; 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::source::snippet_opt;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::{is_diag_trait_item, match_def_path, paths}; 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 { impl<'tcx> LateLintPass<'tcx> for FormatArgs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! { 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 expr_expn_data = expr.span.ctxt().outer_expn_data();
let outermost_expn_data = outermost_expn_data(expr_expn_data); let outermost_expn_data = outermost_expn_data(expr_expn_data);
if let Some(macro_def_id) = outermost_expn_data.macro_def_id; 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(); if let Some(args) = format_args.args();
then { then {
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
if !arg.is_display() { if arg.format_trait != sym::Display {
continue; continue;
} }
if arg.has_string_formatting() { if arg.has_string_formatting() {
@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if is_aliased(&args, i) { if is_aliased(&args, i) {
continue; continue;
} }
check_format_in_format_args(cx, outermost_expn_data.call_site, 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); 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<'_>) { fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
if_chain! { let expn_data = arg.span.ctxt().outer_expn_data();
if FormatExpn::parse(arg.value).is_some(); if expn_data.call_site.from_expansion() {
if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); return;
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!`");
},
);
}
} }
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>) { fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
let value = arg.value;
if_chain! { if_chain! {
if !value.span.from_expansion(); if !value.span.from_expansion();
if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind;

View file

@ -53,7 +53,7 @@ impl FromOverInto {
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); 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<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) {
return; return;

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); 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>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
if_chain! { if_chain! {
if let ExprKind::Call(maybe_path, arguments) = &exp.kind; if let ExprKind::Call(maybe_path, arguments) = &exp.kind;

View file

@ -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}; 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 attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = must_use_attr(attrs); let attr = must_use_attr(attrs);
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { 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 { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id); let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); 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 { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id); let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());

View file

@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local};
use super::NOT_UNSAFE_PTR_ARG_DEREF; use super::NOT_UNSAFE_PTR_ARG_DEREF;
pub(super) fn check_fn( pub(super) fn check_fn<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
kind: intravisit::FnKind<'tcx>, kind: intravisit::FnKind<'tcx>,
decl: &'tcx hir::FnDecl<'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)); 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 { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind {
let body = cx.tcx.hir().body(eid); let body = cx.tcx.hir().body(eid);
check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id); 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>, cx: &LateContext<'tcx>,
unsafety: hir::Unsafety, unsafety: hir::Unsafety,
decl: &'tcx hir::FnDecl<'tcx>, decl: &'tcx hir::FnDecl<'tcx>,

View file

@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
use super::RESULT_UNIT_ERR; 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 { if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id); let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); 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 { if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id); let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); 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 { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id); let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());

View file

@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item;
use super::TOO_MANY_ARGUMENTS; use super::TOO_MANY_ARGUMENTS;
pub(super) fn check_fn( pub(super) fn check_fn(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
kind: intravisit::FnKind<'tcx>, kind: intravisit::FnKind<'_>,
decl: &'tcx hir::FnDecl<'_>, decl: &hir::FnDecl<'_>,
span: Span, span: Span,
hir_id: hir::HirId, hir_id: hir::HirId,
too_many_arguments_threshold: u64, too_many_arguments_threshold: u64,
@ -39,11 +39,7 @@ pub(super) fn check_fn(
} }
} }
pub(super) fn check_trait_item( pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) {
cx: &LateContext<'tcx>,
item: &'tcx hir::TraitItem<'_>,
too_many_arguments_threshold: u64,
) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
// don't lint extern functions decls, it's not their fault // don't lint extern functions decls, it's not their fault
if sig.header.abi == Abi::Rust { if sig.header.abi == Abi::Rust {

View file

@ -11,9 +11,9 @@ use super::TOO_MANY_LINES;
pub(super) fn check_fn( pub(super) fn check_fn(
cx: &LateContext<'_>, cx: &LateContext<'_>,
kind: FnKind<'tcx>, kind: FnKind<'_>,
span: Span, span: Span,
body: &'tcx hir::Body<'_>, body: &hir::Body<'_>,
too_many_lines_threshold: u64, too_many_lines_threshold: u64,
) { ) {
// Closures must be contained in a parent body, which will be checked for `too_many_lines`. // Closures must be contained in a parent body, which will be checked for `too_many_lines`.

View file

@ -55,7 +55,7 @@ impl IfThenSomeElseNone {
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) {
return; return;

View file

@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> {
} }
fn lint_implicit_returns( fn lint_implicit_returns(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &'tcx Expr<'_>, expr: &Expr<'_>,
// The context of the function body. // The context of the function body.
ctxt: SyntaxContext, ctxt: SyntaxContext,
// Whether the expression is from a macro expansion. // Whether the expression is from a macro expansion.

View file

@ -63,7 +63,7 @@ declare_clippy_lint! {
declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! { if_chain! {
if !expr.span.from_expansion(); if !expr.span.from_expansion();

View file

@ -69,7 +69,7 @@ impl IndexRefutableSlice {
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! { if_chain! {
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();

View file

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if let ExprKind::Struct(path, fields, None) = e.kind { if let ExprKind::Struct(path, fields, None) = e.kind {
if !fields.is_empty() if !fields.is_empty()
&& !in_macro(e.span) && !e.span.from_expansion()
&& fields && fields
.iter() .iter()
.all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))

View file

@ -1,8 +1,7 @@
use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
use rustc_hir::{ImplItem, ImplItemKind}; use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
declare_clippy_lint! { declare_clippy_lint! {
@ -40,26 +39,52 @@ declare_clippy_lint! {
declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
impl LateLintPass<'_> for IterNotReturningIterator { impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
let name = impl_item.ident.name.as_str(); let name = item.ident.name.as_str();
if_chain! { if matches!(name, "iter" | "iter_mut") {
if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
let ret_ty = return_ty(cx, impl_item.hir_id()); check_sig(cx, name, fn_sig, item.def_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, &[]);
then { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
span_lint( let name = item.ident.name.as_str();
cx, if matches!(name, "iter" | "iter_mut")
ITER_NOT_RETURNING_ITERATOR, && !matches!(
fn_sig.span, get_parent_node(cx.tcx, item.hir_id()),
&format!("this method is named `{}` but its return type does not implement `Iterator`", name), 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
),
);
}
}
}

View file

@ -245,7 +245,7 @@ enum LenOutput<'tcx> {
Option(DefId), Option(DefId),
Result(DefId, Ty<'tcx>), 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() { match *sig.output().kind() {
ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => { ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => {

View file

@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), 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::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(double_comparison::DOUBLE_COMPARISONS), 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(loops::WHILE_LET_ON_ITERATOR),
LintId::of(main_recursion::MAIN_RECURSION), LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_map::MANUAL_MAP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(manual_strip::MANUAL_STRIP), 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_key::MUTABLE_KEY_TYPE),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), 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_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL), LintId::of(needless_bool::NEEDLESS_BOOL),

View file

@ -3,33 +3,33 @@
// Manual edits will be overwritten. // Manual edits will be overwritten.
store.register_lints(&[ store.register_lints(&[
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::CLIPPY_LINTS_INTERNAL, utils::internal_lints::CLIPPY_LINTS_INTERNAL,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::COMPILER_LINT_FUNCTIONS, utils::internal_lints::COMPILER_LINT_FUNCTIONS,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::DEFAULT_LINT, utils::internal_lints::DEFAULT_LINT,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::IF_CHAIN_STYLE, utils::internal_lints::IF_CHAIN_STYLE,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::INTERNING_DEFINED_SYMBOL, utils::internal_lints::INTERNING_DEFINED_SYMBOL,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::INVALID_PATHS, utils::internal_lints::INVALID_PATHS,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::LINT_WITHOUT_LINT_PASS, utils::internal_lints::LINT_WITHOUT_LINT_PASS,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::OUTER_EXPN_EXPN_DATA, utils::internal_lints::OUTER_EXPN_EXPN_DATA,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::PRODUCE_ICE, utils::internal_lints::PRODUCE_ICE,
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
utils::internal_lints::UNNECESSARY_SYMBOL_STR, utils::internal_lints::UNNECESSARY_SYMBOL_STR,
absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
approx_const::APPROX_CONSTANT, approx_const::APPROX_CONSTANT,
@ -59,6 +59,7 @@ store.register_lints(&[
bool_assert_comparison::BOOL_ASSERT_COMPARISON, bool_assert_comparison::BOOL_ASSERT_COMPARISON,
booleans::LOGIC_BUG, booleans::LOGIC_BUG,
booleans::NONMINIMAL_BOOL, booleans::NONMINIMAL_BOOL,
borrow_as_ptr::BORROW_AS_PTR,
bytecount::NAIVE_BYTECOUNT, bytecount::NAIVE_BYTECOUNT,
cargo_common_metadata::CARGO_COMMON_METADATA, cargo_common_metadata::CARGO_COMMON_METADATA,
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
@ -225,6 +226,7 @@ store.register_lints(&[
main_recursion::MAIN_RECURSION, main_recursion::MAIN_RECURSION,
manual_assert::MANUAL_ASSERT, manual_assert::MANUAL_ASSERT,
manual_async_fn::MANUAL_ASYNC_FN, manual_async_fn::MANUAL_ASYNC_FN,
manual_bits::MANUAL_BITS,
manual_map::MANUAL_MAP, manual_map::MANUAL_MAP,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
manual_ok_or::MANUAL_OK_OR, manual_ok_or::MANUAL_OK_OR,
@ -435,6 +437,7 @@ store.register_lints(&[
shadow::SHADOW_REUSE, shadow::SHADOW_REUSE,
shadow::SHADOW_SAME, shadow::SHADOW_SAME,
shadow::SHADOW_UNRELATED, shadow::SHADOW_UNRELATED,
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,

View file

@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(copies::BRANCHES_SHARING_CODE), 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(equatable_if_let::EQUATABLE_IF_LET),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), 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(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),

View file

@ -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_LOCK),
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(bit_mask::VERBOSE_BIT_MASK), LintId::of(bit_mask::VERBOSE_BIT_MASK),
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
LintId::of(bytecount::NAIVE_BYTECOUNT), LintId::of(bytecount::NAIVE_BYTECOUNT),
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(casts::CAST_LOSSLESS), LintId::of(casts::CAST_LOSSLESS),

View file

@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::SINGLE_CHAR_PATTERN),
LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(misc::CMP_OWNED), LintId::of(misc::CMP_OWNED),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),

View file

@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_REUSE),
LintId::of(shadow::SHADOW_SAME), LintId::of(shadow::SHADOW_SAME),
LintId::of(shadow::SHADOW_UNRELATED), LintId::of(shadow::SHADOW_UNRELATED),
LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
LintId::of(strings::STRING_ADD), LintId::of(strings::STRING_ADD),
LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_SLICE),
LintId::of(strings::STRING_TO_STRING), LintId::of(strings::STRING_TO_STRING),

View file

@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(comparison_chain::COMPARISON_CHAIN),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
LintId::of(dereference::NEEDLESS_BORROW), 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::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(enum_variants::ENUM_VARIANT_NAMES), 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(loops::WHILE_LET_ON_ITERATOR),
LintId::of(main_recursion::MAIN_RECURSION), LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN), LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_map::MANUAL_MAP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(map_clone::MAP_CLONE), LintId::of(map_clone::MAP_CLONE),

View file

@ -4,7 +4,6 @@
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(control_flow_enum)] #![feature(control_flow_enum)]
#![feature(drain_filter)] #![feature(drain_filter)]
#![feature(in_band_lifetimes)]
#![feature(iter_intersperse)] #![feature(iter_intersperse)]
#![feature(let_else)] #![feature(let_else)]
#![feature(once_cell)] #![feature(once_cell)]
@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint {
}; };
} }
#[cfg(feature = "metadata-collector-lint")] #[cfg(feature = "internal")]
mod deprecated_lints; mod deprecated_lints;
#[cfg_attr( #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
any(feature = "internal-lints", feature = "metadata-collector-lint"),
allow(clippy::missing_clippy_version_attribute)
)]
mod utils; mod utils;
// begin lints modules, do not remove this comment, its used in `update_lints` // begin lints modules, do not remove this comment, its used in `update_lints`
@ -177,6 +173,7 @@ mod blacklisted_name;
mod blocks_in_if_conditions; mod blocks_in_if_conditions;
mod bool_assert_comparison; mod bool_assert_comparison;
mod booleans; mod booleans;
mod borrow_as_ptr;
mod bytecount; mod bytecount;
mod cargo_common_metadata; mod cargo_common_metadata;
mod case_sensitive_file_extension_comparisons; mod case_sensitive_file_extension_comparisons;
@ -264,6 +261,7 @@ mod macro_use;
mod main_recursion; mod main_recursion;
mod manual_assert; mod manual_assert;
mod manual_async_fn; mod manual_async_fn;
mod manual_bits;
mod manual_map; mod manual_map;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_ok_or; mod manual_ok_or;
@ -351,6 +349,7 @@ mod self_named_constructors;
mod semicolon_if_nothing_returned; mod semicolon_if_nothing_returned;
mod serde_api; mod serde_api;
mod shadow; mod shadow;
mod single_char_lifetime_names;
mod single_component_path_imports; mod single_component_path_imports;
mod size_of_in_element_count; mod size_of_in_element_count;
mod slow_vector_initialization; 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_restriction.rs");
include!("lib.register_pedantic.rs"); include!("lib.register_pedantic.rs");
#[cfg(feature = "internal-lints")] #[cfg(feature = "internal")]
include!("lib.register_internal.rs"); include!("lib.register_internal.rs");
include!("lib.register_all.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_cargo.rs");
include!("lib.register_nursery.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())) { 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())); 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 // 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::ClippyLintsInternal));
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); 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(needless_late_init::NeedlessLateInit));
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); 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` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate};
use rustc_hir::{Block, Expr}; use rustc_hir::{Block, Expr};
use rustc_lint::LateContext; 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) { 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 msg = "empty `loop {}` wastes CPU cycles";
let help = if is_no_std_crate(cx) { let help = if is_no_std_crate(cx) {

View file

@ -8,7 +8,7 @@ use rustc_lint::LateContext;
use rustc_middle::ty::TyS; use rustc_middle::ty::TyS;
use rustc_span::symbol::sym; 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 = cx.typeck_results().expr_ty(self_arg);
let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(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)) { if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) {

View file

@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg; 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 clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
@ -62,15 +62,15 @@ pub(super) fn check<'tcx>(
if_chain! { if_chain! {
if let ExprKind::Index(base_left, idx_left) = lhs.kind; if let ExprKind::Index(base_left, idx_left) = lhs.kind;
if let ExprKind::Index(base_right, idx_right) = rhs.kind; if let ExprKind::Index(base_right, idx_right) = rhs.kind;
if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left));
if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); 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_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); if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
// Source and destination must be different // Source and destination must be different
if path_to_local(base_left) != path_to_local(base_right); if path_to_local(base_left) != path_to_local(base_right);
then { 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 })) IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right }))
} else { } else {
None 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<_>>>() .collect::<Option<Vec<_>>>()
.filter(|v| !v.is_empty()) .filter(|v| !v.is_empty())
.map(|v| v.join("\n ")); .map(|v| v.join("\n "));
@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
start: &Expr<'_>, start: &Expr<'_>,
end: &Expr<'_>, end: &Expr<'_>,
limits: ast::RangeLimits, limits: ast::RangeLimits,
elem_ty: Ty<'tcx>,
dst: &IndexExpr<'_>, dst: &IndexExpr<'_>,
src: &IndexExpr<'_>, src: &IndexExpr<'_>,
) -> String { ) -> String {
@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>(
.into() .into()
}; };
let method_str = if is_copy(cx, elem_ty) {
"copy_from_slice"
} else {
"clone_from_slice"
};
format!( format!(
"{}.clone_from_slice(&{}[{}..{}]);", "{}.{}(&{}[{}..{}]);",
dst, dst,
method_str,
src_base_str, src_base_str,
src_offset.maybe_par(), src_offset.maybe_par(),
src_limit.maybe_par() src_limit.maybe_par()
@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>(
#[derive(Clone)] #[derive(Clone)]
struct MinifyingSugg<'a>(Sugg<'a>); 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 { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) self.0.fmt(f)
} }
@ -324,14 +332,13 @@ struct Start<'hir> {
kind: StartKind<'hir>, kind: StartKind<'hir>,
} }
fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
let is_slice = match ty.kind() { match ty.kind() {
ty::Ref(_, subty, _) => is_slice_like(cx, subty), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)),
ty::Slice(..) | ty::Array(..) => true, ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty),
_ => false, ty::Slice(ty) | ty::Array(ty, _) => Some(ty),
}; _ => None,
}
is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque)
} }
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {

View file

@ -147,7 +147,7 @@ impl BreakAfterExprVisitor {
} }
} }
impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
type Map = Map<'tcx>; type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {

View file

@ -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(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet {
fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
match ty.kind() { match ty.kind() {
ty::Adt(_, generics) => { ty::Adt(_, generics) => {
for generic in *generics { for generic in *generics {

View file

@ -10,8 +10,8 @@ use rustc_span::Span;
use std::iter::{once, Iterator}; use std::iter::{once, Iterator};
pub(super) fn check( pub(super) fn check(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
block: &'tcx Block<'_>, block: &Block<'_>,
loop_id: HirId, loop_id: HirId,
span: Span, span: Span,
for_loop: Option<&ForLoop<'_>>, for_loop: Option<&ForLoop<'_>>,

View file

@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; 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 // extract the expression from the first statement (if any) in a block
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
// or extract the first expression (if any) from the block // or extract the first expression (if any) from the block

View file

@ -8,12 +8,13 @@ use clippy_utils::{
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; 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_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<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { 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); if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
// check for `Some(..)` pattern // check for `Some(..)` pattern
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; 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 // get the loop containing the match expression
if !uses_iter(cx, &iter_expr_struct, if_then); if !uses_iter(cx, &iter_expr_struct, if_then);
then { then {
(let_expr, iter_expr_struct, some_pat, expr) (let_expr, iter_expr_struct, iter_expr, some_pat, expr)
} else { } else {
return; 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 // 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 // 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. // 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()" ".by_ref()"
} else { } else {
"" ""
@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
#[derive(Debug)] #[derive(Debug)]
struct IterExpr { 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. /// The fields used, in order of child to parent.
fields: Vec<Symbol>, fields: Vec<Symbol>,
/// The path being used. /// The path being used.
path: Res, 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 /// Parses any expression to find out which field of which variable is used. Will return `None` if
/// the expression might have side effects. /// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> { fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
let span = e.span;
let mut fields = Vec::new(); let mut fields = Vec::new();
let mut can_move = true;
loop { 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 { match e.kind {
ExprKind::Path(ref path) => { ExprKind::Path(ref path) => {
break Some(IterExpr { break Some(IterExpr {
span,
fields, fields,
path: cx.qpath_res(path, e.hir_id), path: cx.qpath_res(path, e.hir_id),
can_move,
}); });
}, },
ExprKind::Field(base, name) => { 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 // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
// already been seen. // already been seen.
ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
can_move = false;
fields.clear(); fields.clear();
e = base; e = base;
}, },
ExprKind::Unary(UnOp::Deref, base) => { ExprKind::Unary(UnOp::Deref, base) => {
can_move = false;
fields.clear(); fields.clear();
e = base; 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 /// 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. /// 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 mut e = expr;
let e = loop { let e = loop {
match e.kind { 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. /// 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> { struct V<'a, 'b, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
iter_expr: &'b IterExpr, iter_expr: &'b IterExpr,
uses_iter: bool, uses_iter: bool,
} }
impl Visitor<'tcx> for V<'_, '_, 'tcx> { impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
type Map = ErasedMap<'tcx>; type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None NestedVisitorMap::None
@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr
} }
#[allow(clippy::too_many_lines)] #[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> { struct AfterLoopVisitor<'a, 'b, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
iter_expr: &'b IterExpr, iter_expr: &'b IterExpr,
@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
after_loop: bool, after_loop: bool,
used_iter: bool, used_iter: bool,
} }
impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
type Map = ErasedMap<'tcx>; type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None NestedVisitorMap::None
@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
found_local: bool, found_local: bool,
used_after: 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>; type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {

View file

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::in_macro;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use hir::def::{DefKind, Res}; use hir::def::{DefKind, Res};
use if_chain::if_chain; use if_chain::if_chain;
@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
} }
} }
} else { } else {
if in_macro(item.span) { if item.span.from_expansion() {
self.push_unique_macro_pat_ty(cx, item.span); self.push_unique_macro_pat_ty(cx, item.span);
} }
} }
} }
} }
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { 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); self.push_unique_macro(cx, attr.span);
} }
} }
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { 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); self.push_unique_macro(cx, expr.span);
} }
} }
fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { 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); self.push_unique_macro(cx, stmt.span);
} }
} }
fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { 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); self.push_unique_macro_pat_ty(cx, pat.span);
} }
} }
fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { 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); self.push_unique_macro_pat_ty(cx, ty.span);
} }
} }

View file

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::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_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -34,65 +35,34 @@ declare_clippy_lint! {
declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
impl LateLintPass<'_> for ManualAssert { impl<'tcx> LateLintPass<'tcx> for ManualAssert {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if_chain! { if_chain! {
if let Expr { if let ExprKind::If(cond, then, None) = expr.kind;
kind: ExprKind:: If(cond, Expr {
kind: ExprKind::Block(
Block {
stmts: [stmt],
..
},
_),
..
}, None),
..
} = &expr;
if is_expn_of(stmt.span, "panic").is_some();
if !matches!(cond.kind, ExprKind::Let(_)); 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 !cx.tcx.sess.source_map().is_multiline(cond.span);
if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn);
then { 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 mut applicability = Applicability::MachineApplicable;
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { let cond = cond.peel_drop_temps();
if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { let (cond, not) = match cond.kind {
sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() ExprKind::Unary(UnOp::Not, e) => (e, ""),
} else { _ => (cond, "!"),
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 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( span_lint_and_sugg(
cx, cx,
MANUAL_ASSERT, MANUAL_ASSERT,
expr.span, expr.span,
"only a `panic!` in `if`-then statement", "only a `panic!` in `if`-then statement",
"try", "try",
format!("assert!({}, {});", cond_sugg, sugg), sugg,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

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

View file

@ -45,7 +45,7 @@ declare_clippy_lint! {
declare_lint_pass!(ManualMap => [MANUAL_MAP]); declare_lint_pass!(ManualMap => [MANUAL_MAP]);
impl LateLintPass<'_> for ManualMap { impl<'tcx> LateLintPass<'tcx> for ManualMap {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { 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) { 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. // 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. // 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 { match expr.kind {
ExprKind::Call(func, [arg]) ExprKind::Call(func, [arg])
if path_to_local_id(arg, binding) if path_to_local_id(arg, binding)
@ -251,8 +251,13 @@ struct SomeExpr<'tcx> {
// Try to parse into a recognized `Option` pattern. // Try to parse into a recognized `Option` pattern.
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those. // 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 try_parse_pattern<'tcx>(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 f<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
ref_count: usize,
ctxt: SyntaxContext,
) -> Option<OptionPat<'tcx>> {
match pat.kind { match pat.kind {
PatKind::Wild => Some(OptionPat::Wild), PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), 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. // 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>, cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
needs_unsafe_block: bool, needs_unsafe_block: bool,
@ -306,6 +311,6 @@ fn get_some_expr(
} }
// Checks for the `None` value. // 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)) matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
} }

View file

@ -40,7 +40,7 @@ declare_clippy_lint! {
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); 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>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
if in_external_macro(cx.sess(), scrutinee.span) { if in_external_macro(cx.sess(), scrutinee.span) {
return; return;

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); 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>) { 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) { if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
return; return;

View file

@ -55,7 +55,7 @@ enum CaseMethod {
AsciiUppercase, AsciiUppercase,
} }
impl LateLintPass<'_> for MatchStrCaseMismatch { impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if_chain! {
if !in_external_macro(cx.tcx.sess, expr.span); if !in_external_macro(cx.tcx.sess, expr.span);

View file

@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
use clippy_utils::diagnostics::{ use clippy_utils::diagnostics::{
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, 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::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; 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::visitors::is_local_used;
use clippy_utils::{ 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, path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
strip_pat_refs, strip_pat_refs,
}; };
use clippy_utils::{higher, peel_blocks_with_stmt};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use core::iter::{once, ExactSizeIterator}; use core::iter::{once, ExactSizeIterator};
use if_chain::if_chain; 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_chain! {
if matching_wild; 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 { then {
// `Err(_)` or `Err(_e)` arm with `panic!` found // `Err(_)` or `Err(_e)` arm with `panic!` found
span_lint_and_note(cx, span_lint_and_note(cx,
@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> {
Path(&'a [PathSegment<'a>]), Path(&'a [PathSegment<'a>]),
Mixed, Mixed,
} }
impl CommonPrefixSearcher<'a> { impl<'a> CommonPrefixSearcher<'a> {
fn with_path(&mut self, path: &'a [PathSegment<'a>]) { fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
match path { match path {
[path @ .., _] => self.with_prefix(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<'_>) fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
where where
'b: 'a, '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 /// 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 /// deallocate memory. For these types, and composites containing them, changing the drop order
/// won't result in any observable side effects. /// 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()) 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) { if !seen.insert(ty) {
return false; 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 // Checks if there are any temporaries created in the given expression for which drop order
// matters. // 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> { struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
res: bool, res: bool,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL;
/// Checks for the `EXPECT_FUN_CALL` lint. /// Checks for the `EXPECT_FUN_CALL` lint.
#[allow(clippy::too_many_lines)] #[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 // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
// `&str` // `&str`
fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { 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; let mut applicability = Applicability::MachineApplicable;
//Special handling for `format!` as arg_root //Special handling for `format!` as arg_root
if let Some(format_expn) = FormatExpn::parse(arg_root) { if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
let span = match *format_expn.format_args.value_args { if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) {
[] => format_expn.format_args.format_string_span, return;
[.., last] => format_expn.format_args.format_string_span.to(last.span), }
}; 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); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View file

@ -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> { fn strip_angle_brackets(s: &str) -> Option<&str> {
s.strip_prefix('<')?.strip_suffix('>') s.strip_prefix('<')?.strip_suffix('>')
} }

View file

@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_middle::ty::{self, TraitRef, Ty, TyS};
use rustc_semver::RustcVersion; use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span}; use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty; use rustc_typeck::hir_ty_to_ty;
@ -1895,6 +1894,11 @@ declare_clippy_lint! {
/// ### Why is this bad? /// ### Why is this bad?
/// The unnecessary calls result in useless allocations. /// 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 /// ### Example
/// ```rust /// ```rust
/// let path = std::path::Path::new("x"); /// 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. /// 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 let ExprKind::MethodCall(path, span, args, _) = recv.kind {
if !args.iter().any(|e| e.span.from_expansion()) { 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 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 { impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if expr.span.from_expansion() { if expr.span.from_expansion() {
@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { 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) { match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv); 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), ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), ("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], _)) => { Some((name @ ("cloned" | "copied"), [recv2], _)) => {
iter_cloned_collect::check(cx, name, expr, 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], _)) => { Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
iter_count::check(cx, expr, recv2, name); iter_count::check(cx, expr, recv2, name);
}, },
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), 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), Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::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); flat_map_option::check(cx, expr, arg, span);
}, },
("flatten", []) => { ("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); map_flatten::check(cx, expr, recv, map_arg);
} }
}, },
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => { ("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); 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_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("map", [m_arg]) => { ("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) { match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("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), ("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), ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
("next", []) => { ("next", []) => {
if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { if let Some((name, [recv, args @ ..], _)) = method_call(recv) {
match (name, args) { match (name, args) {
("filter", [arg]) => filter_next::check(cx, expr, recv, arg), ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), ("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(("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", [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), 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", []) => { ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv, span); 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", [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), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
_ => unwrap_used::check(cx, expr, recv), _ => 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], _)) => { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); 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) => {}, 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); 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) { 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); 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()]) 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 { match self {
Self::Value => matches_value(cx, parent_ty, ty), 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::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::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
Self::No => ty != parent_ty, Self::No => matches_none(cx, parent_ty, ty),
} }
} }

View file

@ -124,7 +124,7 @@ struct IterUsage {
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn parse_iter_usage( fn parse_iter_usage<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
ctxt: SyntaxContext, ctxt: SyntaxContext,
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, 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>, cx: &LateContext<'tcx>,
ctxt: SyntaxContext, ctxt: SyntaxContext,
mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,

View file

@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol};
use super::UNNECESSARY_TO_OWNED; 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_chain! {
if let Some(parent) = get_parent_expr(cx, expr); if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent); if let Some(callee_def_id) = fn_def_id(cx, parent);
if is_into_iter(cx, callee_def_id); if is_into_iter(cx, callee_def_id);
then { then {
check_for_loop_iter(cx, parent, method_name, receiver) check_for_loop_iter(cx, parent, method_name, receiver, false)
} else { } else {
false 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 /// include this code directly is so that it can be called from
/// `unnecessary_into_owned::check_into_iter_call_arg`. /// `unnecessary_into_owned::check_into_iter_call_arg`.
pub fn check_for_loop_iter( pub fn check_for_loop_iter(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &'tcx Expr<'tcx>, expr: &Expr<'_>,
method_name: Symbol, method_name: Symbol,
receiver: &'tcx Expr<'tcx>, receiver: &Expr<'_>,
cloned_before_iter: bool,
) -> bool { ) -> bool {
if_chain! { if_chain! {
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); 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, expr.span,
&format!("unnecessary use of `{}`", method_name), &format!("unnecessary use of `{}`", method_name),
|diag| { |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 { for addr_of_expr in addr_of_exprs {
match addr_of_expr.kind { match addr_of_expr.kind {
ExprKind::AddrOf(_, _, referent) => { ExprKind::AddrOf(_, _, referent) => {
let span = addr_of_expr.span.with_hi(referent.span.lo()); 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!(), _ => 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 /// The core logic of `check_for_loop_iter` above, this function wraps a use of
/// `CloneOrCopyVisitor`. /// `CloneOrCopyVisitor`.
fn clone_or_copy_needed( fn clone_or_copy_needed<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
pat: &Pat<'tcx>, pat: &Pat<'tcx>,
body: &'tcx Expr<'tcx>, body: &'tcx Expr<'tcx>,

View file

@ -16,7 +16,7 @@ use std::cmp::max;
use super::UNNECESSARY_TO_OWNED; 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_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let [receiver] = args; 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. /// call of a `to_owned`-like function is unnecessary.
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn check_addr_of_expr( fn check_addr_of_expr(
cx: &LateContext<'tcx>, cx: &LateContext<'_>,
expr: &'tcx Expr<'tcx>, expr: &Expr<'_>,
method_name: Symbol, method_name: Symbol,
method_def_id: DefId, method_def_id: DefId,
receiver: &'tcx Expr<'tcx>, receiver: &Expr<'_>,
) -> bool { ) -> bool {
if_chain! { if_chain! {
if let Some(parent) = get_parent_expr(cx, expr); 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 /// 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. /// call of a `to_owned`-like function is unnecessary.
fn check_into_iter_call_arg( fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
method_name: Symbol,
receiver: &'tcx Expr<'tcx>,
) -> bool {
if_chain! { if_chain! {
if let Some(parent) = get_parent_expr(cx, expr); if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent); 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(item_ty) = get_iterator_item_ty(cx, parent_ty);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then { 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; return true;
} }
let cloned_or_copied = if is_copy(cx, item_ty) { let cloned_or_copied = if is_copy(cx, item_ty) {
@ -195,6 +196,9 @@ fn check_into_iter_call_arg(
} else { } else {
"cloned" "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( span_lint_and_sugg(
cx, cx,
UNNECESSARY_TO_OWNED, UNNECESSARY_TO_OWNED,
@ -202,7 +206,7 @@ fn check_into_iter_call_arg(
&format!("unnecessary use of `{}`", method_name), &format!("unnecessary use of `{}`", method_name),
"use", "use",
format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
Applicability::MachineApplicable, Applicability::MaybeIncorrect,
); );
return true; 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 /// 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. /// of a `to_owned`-like function is unnecessary.
fn check_other_call_arg( fn check_other_call_arg<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>,
method_name: Symbol, 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 /// 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. /// expression found (if any) along with the immediately prior expression.
fn skip_addr_of_ancestors( fn skip_addr_of_ancestors<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
mut expr: &'tcx Expr<'tcx>, mut expr: &'tcx Expr<'tcx>,
) -> Option<(&'tcx Expr<'tcx>, &'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`, /// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
/// `Substs`, and arguments. /// `Substs`, and arguments.
fn get_callee_substs_and_args( fn get_callee_substs_and_args<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>,
) -> Option<(DefId, SubstsRef<'tcx>, &'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. /// 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>, cx: &LateContext<'tcx>,
callee_def_id: DefId, callee_def_id: DefId,
input: Ty<'tcx>, 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. /// 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() left.iter()
.map(|arg| { .map(|arg| {
if let GenericArgKind::Type(arg_ty) = arg.unpack() { if let GenericArgKind::Type(arg_ty) = arg.unpack() {

View file

@ -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>, cx: &LateContext<'a>,
expr: &Expr<'_>, expr: &Expr<'_>,
cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>, cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint; 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::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -34,26 +34,30 @@ declare_clippy_lint! {
declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); 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 { impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
for dmn in &DEBUG_MACRO_NAMES { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
if is_direct_expn_of(e.span, dmn).is_some() { let macro_name = cx.tcx.item_name(macro_call.def_id);
if let Some(macro_args) = higher::extract_assert_macro_args(e) { if !matches!(
for arg in macro_args { macro_name.as_str(),
let mut visitor = MutArgVisitor::new(cx); "debug_assert" | "debug_assert_eq" | "debug_assert_ne"
visitor.visit_expr(arg); ) {
if let Some(span) = visitor.expr_span() { return;
span_lint( }
cx, let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return };
DEBUG_ASSERT_WITH_MUT_CALL, for arg in [lhs, rhs] {
span, let mut visitor = MutArgVisitor::new(cx);
&format!("do not call a function with mutable arguments inside of `{}!`", dmn), 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
),
);
} }
} }
} }

View file

@ -38,7 +38,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub MUTEX_ATOMIC, pub MUTEX_ATOMIC,
perf, nursery,
"using a mutex where an atomic value could be used instead" "using a mutex where an atomic value could be used instead"
} }

View file

@ -48,7 +48,7 @@ declare_clippy_lint! {
declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); 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<'_>) { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
let expr = match stmt.kind { let expr = match stmt.kind {
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,

View file

@ -330,7 +330,7 @@ fn check<'tcx>(
Some(()) Some(())
} }
impl LateLintPass<'tcx> for NeedlessLateInit { impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
let mut parents = cx.tcx.hir().parent_iter(local.hir_id); let mut parents = cx.tcx.hir().parent_iter(local.hir_id);

View file

@ -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 let StmtKind::Semi(expr) = stmt.kind {
if has_no_effect(cx, expr) { if has_no_effect(cx, expr) {
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); 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_chain! {
if let StmtKind::Semi(expr) = stmt.kind; if let StmtKind::Semi(expr) = stmt.kind;
if let Some(reduced) = reduce_expression(cx, expr); if let Some(reduced) = reduce_expression(cx, expr);

View file

@ -40,7 +40,7 @@ declare_clippy_lint! {
declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); 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>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
match &expr.kind { match &expr.kind {
ExprKind::MethodCall(path, _, [func, param], _) => { ExprKind::MethodCall(path, _, [func, param], _) => {

View file

@ -50,7 +50,7 @@ declare_clippy_lint! {
declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
impl EarlyLintPass for OctalEscapes { 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) { if in_external_macro(cx.sess, expr.span) {
return; 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 contents = lit.symbol.as_str();
let mut iter = contents.char_indices().peekable(); let mut iter = contents.char_indices().peekable();
let mut found = vec![]; let mut found = vec![];

View file

@ -1,8 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_then; 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::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 as hir;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span}; 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>) { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
let mut panics = find_macro_calls( let mut panics = Vec::new();
&[ expr_visitor_no_bodies(|expr| {
"unimplemented", let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
"unreachable", if matches!(
"panic", &*cx.tcx.item_name(macro_call.def_id).as_str(),
"todo", "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
"assert", ) {
"assert_eq", panics.push(macro_call.span);
"assert_ne", return false;
], }
body, true
); })
panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); .visit_expr(&body.value);
if !panics.is_empty() { if !panics.is_empty() {
span_lint_and_then( span_lint_and_then(
cx, cx,

View file

@ -1,10 +1,8 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::{is_expn_of, match_panic_call}; use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use if_chain::if_chain;
use rustc_hir::Expr; use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI
impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if match_panic_call(cx, expr).is_some() let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
&& (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none()) if is_panic(cx, macro_call.def_id) {
{ span_lint(
let span = get_outer_span(expr); cx,
if is_expn_of(expr.span, "unimplemented").is_some() { 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( span_lint(
cx, cx,
UNIMPLEMENTED, UNIMPLEMENTED,
span, macro_call.span,
"`unimplemented` should not be present in production code", "`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"); "unreachable" => {
} else if is_expn_of(expr.span, "unreachable").is_some() { span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
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
} }
} }
} }

View file

@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]);
static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if expr.span.from_expansion() { if expr.span.from_expansion() {
return; return;

View file

@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
continue; continue;
} else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
// cloned value is mutated, and the clone is alive. // 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; continue;
} }
} }
@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> {
self.bitset.0 == self.bitset.1 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.seek_after_primary_effect(at);
self.maybe_live.contains(local) self.maybe_live.contains(local)
} }

View file

@ -42,7 +42,7 @@ declare_clippy_lint! {
declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if expr.span.from_expansion() { if expr.span.from_expansion() {
return; return;

View file

@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
if_chain! { if_chain! {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; 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 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(); if !addrof_target.span.from_expansion();
then { then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_must_use_ty; 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::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; 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. /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
/// ///
/// ### Why is this bad? /// ### 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 /// ### Limitations
/// This lint is only applied on methods taking a `self` argument. It would be mostly noise /// This lint is only applied on methods taking a `self` argument. It would be mostly noise
/// if it was added on constructors for example. /// if it was added on constructors for example.
/// ///
/// ### Example /// ### Example
/// Missing attribute
/// ```rust /// ```rust
/// pub struct Bar; /// pub struct Bar;
///
/// impl Bar { /// impl Bar {
/// // Bad /// // Bad
/// pub fn bar(&self) -> Self { /// pub fn bar(&self) -> 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] /// #[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 /// Self
/// } /// }
/// } /// }
@ -44,7 +66,7 @@ declare_clippy_lint! {
declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); 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_chain! {
// If it comes from an external macro, better ignore it. // If it comes from an external macro, better ignore it.
if !in_external_macro(cx.sess(), span); 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); if !is_must_use_ty(cx, ret_ty);
then { then {
span_lint( span_lint_and_help(
cx, cx,
RETURN_SELF_NOT_MUST_USE, RETURN_SELF_NOT_MUST_USE,
span, span,
"missing `#[must_use]` attribute on a method returning `Self`", "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"
); );
} }
} }

View file

@ -37,7 +37,7 @@ declare_clippy_lint! {
declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); 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>) { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
if_chain! { if_chain! {
if !block.span.from_expansion(); if !block.span.from_expansion();

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

View file

@ -37,7 +37,7 @@ declare_clippy_lint! {
declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); 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 { match expr.kind {
ExprKind::Call(count_func, _func_args) => { ExprKind::Call(count_func, _func_args) => {
if_chain! { 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] = [ const FUNCTIONS: [&[&str]; 8] = [
&paths::PTR_COPY_NONOVERLAPPING, &paths::PTR_COPY_NONOVERLAPPING,
&paths::PTR_COPY, &paths::PTR_COPY,

View file

@ -381,7 +381,7 @@ declare_clippy_lint! {
declare_lint_pass!(StrToString => [STR_TO_STRING]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! { if_chain! {
if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;
@ -431,7 +431,7 @@ declare_clippy_lint! {
declare_lint_pass!(StringToString => [STRING_TO_STRING]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! { if_chain! {
if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind;

View file

@ -39,7 +39,7 @@ declare_clippy_lint! {
declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); 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<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if_chain! {
if !expr.span.from_expansion(); if !expr.span.from_expansion();

View file

@ -355,7 +355,7 @@ struct BinaryOp<'exprs> {
right: &'exprs Expr, right: &'exprs Expr,
} }
impl BinaryOp<'exprs> { impl<'exprs> BinaryOp<'exprs> {
fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
Self { op, span, left, right } 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) { match (&left_outer.kind, &right_outer.kind) {
( (
ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), 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