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"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
collect-metadata = "test --test dogfood --features metadata-collector-lint -- run_metadata_collection_lint --ignored"
collect-metadata = "test --test dogfood --features internal -- run_metadata_collection_lint --ignored"
[build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
rustflags = ["-Zunstable-options", "-Zbinary-dep-depinfo"]
target-dir = "target"
[unstable]
binary-dep-depinfo = true

View file

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

View file

@ -112,17 +112,22 @@ jobs:
echo "$SYSROOT/bin" >> $GITHUB_PATH
- name: Build
run: cargo build --features deny-warnings,internal-lints,metadata-collector-lint
run: cargo build --features deny-warnings,internal
- name: Test
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
if: runner.os == 'Linux'
run: cargo test --features deny-warnings,internal
- name: Test
if: runner.os != 'Linux'
run: cargo test --features deny-warnings,internal -- --skip dogfood
- name: Test clippy_lints
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
run: cargo test --features deny-warnings,internal
working-directory: clippy_lints
- name: Test clippy_utils
run: cargo test --features deny-warnings,internal-lints,metadata-collector-lint
run: cargo test --features deny-warnings,internal
working-directory: clippy_utils
- name: Test rustc_tools_util
@ -133,14 +138,6 @@ jobs:
run: cargo test --features deny-warnings
working-directory: clippy_dev
- name: Test cargo-clippy
run: ../target/debug/cargo-clippy
working-directory: clippy_workspace_tests
- name: Test cargo-clippy --fix
run: ../target/debug/cargo-clippy clippy --fix
working-directory: clippy_workspace_tests
- name: Test clippy-driver
run: bash .github/driver.sh
env:

1
.gitignore vendored
View file

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

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
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
@ -3070,6 +3071,7 @@ Released 2018-09-13
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
@ -3253,6 +3255,7 @@ Released 2018-09-13
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop

View file

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

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.
[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
@ -37,8 +37,8 @@ Table of contents:
## Usage
Below are instructions on how to use Clippy as a subcommand, compiled from source
or in Travis CI.
Below are instructions on how to use Clippy as a cargo subcommand,
in projects that do not use cargo, or in Travis CI.
### As a cargo subcommand (`cargo clippy`)
@ -98,22 +98,18 @@ If you want to run Clippy **only** on the given crate, use the `--no-deps` optio
cargo clippy -p example -- --no-deps
```
### As a rustc replacement (`clippy-driver`)
### Using `clippy-driver`
Clippy can also be used in projects that do not use cargo. To do so, you will need to replace
your `rustc` compilation commands with `clippy-driver`. For example, if your project runs:
```terminal
rustc --edition 2018 -Cpanic=abort foo.rs
```
Then, to enable Clippy, you will need to call:
Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
with the same arguments you use for `rustc`. For example:
```terminal
clippy-driver --edition 2018 -Cpanic=abort foo.rs
```
Note that `rustc` will still run, i.e. it will still emit the output files it normally does.
Note that `clippy-driver` is designed for running Clippy only and should not be used as a general
replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected,
for example.
### Travis CI

View file

@ -3,7 +3,7 @@ use itertools::Itertools;
use shell_escape::escape;
use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::process::{self, Command};
use std::process::{self, Command, Stdio};
use std::{fs, io};
use walkdir::WalkDir;
@ -31,6 +31,7 @@ impl From<walkdir::Error> for CliError {
struct FmtContext {
check: bool,
verbose: bool,
rustfmt_path: String,
}
// the "main" function of cargo dev fmt
@ -102,7 +103,23 @@ Please revert the changes to Cargo.tomls first."
}
}
let context = FmtContext { check, verbose };
let output = Command::new("rustup")
.args(["which", "rustfmt"])
.stderr(Stdio::inherit())
.output()
.expect("error running `rustup which rustfmt`");
if !output.status.success() {
eprintln!("`rustup which rustfmt` did not execute successfully");
process::exit(1);
}
let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path");
rustfmt_path.truncate(rustfmt_path.trim_end().len());
let context = FmtContext {
check,
verbose,
rustfmt_path,
};
let result = try_run(&context);
let code = match result {
Ok(true) => 0,
@ -141,8 +158,12 @@ fn exec(
println!("{}", format_command(&program, &dir, args));
}
let child = Command::new(&program).current_dir(&dir).args(args.iter()).spawn()?;
let output = child.wait_with_output()?;
let output = Command::new(&program)
.env("RUSTFMT", &context.rustfmt_path)
.current_dir(&dir)
.args(args.iter())
.output()
.unwrap();
let success = output.status.success();
if !context.check && !success {
@ -159,7 +180,6 @@ fn exec(
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
let mut args = vec!["fmt", "--all"];
if context.check {
args.push("--");
args.push("--check");
}
let success = exec(context, "cargo", path, &args)?;
@ -200,7 +220,7 @@ fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Resul
}
args.extend(paths);
let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
Ok(success)
}

View file

@ -321,7 +321,7 @@ fn gen_register_lint_list<'a>(
for (is_public, module_name, lint_name) in details {
if !is_public {
output.push_str(" #[cfg(feature = \"internal-lints\")]\n");
output.push_str(" #[cfg(feature = \"internal\")]\n");
}
output.push_str(&format!(" {}::{},\n", module_name, lint_name));
}

View file

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

View file

@ -1,12 +1,10 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::higher;
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, UnOp};
use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
let lint_true = |is_debug: bool| {
let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
Some(sym::debug_assert_macro) => true,
Some(sym::assert_macro) => false,
_ => return,
};
let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return };
let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return };
if val {
span_lint_and_help(
cx,
ASSERTIONS_ON_CONSTANTS,
e.span,
if is_debug {
"`debug_assert!(true)` will be optimized out by the compiler"
} else {
"`assert!(true)` will be optimized out by the compiler"
},
macro_call.span,
&format!(
"`{}!(true)` will be optimized out by the compiler",
cx.tcx.item_name(macro_call.def_id)
),
None,
"remove it",
);
};
let lint_false_without_message = || {
span_lint_and_help(
cx,
ASSERTIONS_ON_CONSTANTS,
e.span,
"`assert!(false)` should probably be replaced",
None,
"use `panic!()` or `unreachable!()`",
);
};
let lint_false_with_message = |panic_message: String| {
span_lint_and_help(
cx,
ASSERTIONS_ON_CONSTANTS,
e.span,
&format!("`assert!(false, {})` should probably be replaced", panic_message),
None,
&format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message),
);
};
if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") {
if debug_assert_span.from_expansion() {
return;
}
if_chain! {
if let ExprKind::Unary(_, lit) = e.kind;
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit);
if is_true;
then {
lint_true(true);
}
} else if !is_debug {
let (assert_arg, panic_arg) = match panic_expn {
PanicExpn::Empty => ("", ""),
_ => (", ..", ".."),
};
} else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
if assert_span.from_expansion() {
return;
}
if let Some(assert_match) = match_assert_with_message(cx, e) {
match assert_match {
// matched assert but not message
AssertKind::WithoutMessage(false) => lint_false_without_message(),
AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false),
AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message),
};
}
span_lint_and_help(
cx,
ASSERTIONS_ON_CONSTANTS,
macro_call.span,
&format!("`assert!(false{})` should probably be replaced", assert_arg),
None,
&format!("use `panic!({})` or `unreachable!({0})`", panic_arg),
);
}
}
}
/// Result of calling `match_assert_with_message`.
enum AssertKind {
WithMessage(String, bool),
WithoutMessage(bool),
}
/// Check if the expression matches
///
/// ```rust,ignore
/// if !c {
/// {
/// ::std::rt::begin_panic(message, _)
/// }
/// }
/// ```
///
/// where `message` is any expression and `c` is a constant bool.
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
if_chain! {
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
// bind the first argument of the `assert!` macro
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
let begin_panic_call = peel_blocks(then);
// function call
if let Some(arg) = match_panic_call(cx, begin_panic_call);
// bind the second argument of the `assert!` macro if it exists
if let panic_message = snippet_opt(cx, arg.span);
// second argument of begin_panic is irrelevant
// as is the second match arm
then {
// an empty message occurs when it was generated by the macro
// (and not passed by the user)
return panic_message
.filter(|msg| !msg.is_empty())
.map(|msg| AssertKind::WithMessage(msg, is_true))
.or(Some(AssertKind::WithoutMessage(is_true)));
}
}
None
}

View file

@ -1,9 +1,10 @@
//! checks for attributes
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::macros::{is_panic, macro_backtrace};
use clippy_utils::msrvs;
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
use clippy_utils::{extract_msrv_attr, meets_msrv};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
}
fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
}) {
return false;
}
match &expr.kind {
ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
ExprKind::Call(path_expr, _) => {
if let ExprKind::Path(qpath) = &path_expr.kind {
typeck_results
.qpath_res(qpath, path_expr.hir_id)
.opt_def_id()
.map_or(true, |fun_id| !match_panic_def_id(cx, fun_id))
} else {
true
}
},
_ => true,
}
}

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_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Lit};
@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool {
) && !e.span.from_expansion()
}
fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(e);
cx.tcx
@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let macros = ["assert_eq", "debug_assert_eq"];
let inverted_macros = ["assert_ne", "debug_assert_ne"];
for mac in macros.iter().chain(inverted_macros.iter()) {
if let Some(span) = is_direct_expn_of(expr.span, mac) {
if let Some(args) = higher::extract_assert_macro_args(expr) {
if let [a, b, ..] = args[..] {
let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
if nb_bool_args != 1 {
// If there are two boolean arguments, we definitely don't understand
// what's going on, so better leave things as is...
//
// Or there is simply no boolean and then we can leave things as is!
return;
}
if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
// At this point the expression which is not a boolean
// literal does not implement Not trait with a bool output,
// so we cannot suggest to rewrite our code
return;
}
let non_eq_mac = &mac[..mac.len() - 3];
span_lint_and_sugg(
cx,
BOOL_ASSERT_COMPARISON,
span,
&format!("used `{}!` with a literal bool", mac),
"replace it with",
format!("{}!(..)", non_eq_mac),
Applicability::MaybeIncorrect,
);
return;
}
}
}
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
let macro_name = cx.tcx.item_name(macro_call.def_id);
if !matches!(
macro_name.as_str(),
"assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne"
) {
return;
}
let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
if !(is_bool_lit(a) ^ is_bool_lit(b)) {
// If there are two boolean arguments, we definitely don't understand
// what's going on, so better leave things as is...
//
// Or there is simply no boolean and then we can leave things as is!
return;
}
if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
// At this point the expression which is not a boolean
// literal does not implement Not trait with a bool output,
// so we cannot suggest to rewrite our code
return;
}
let macro_name = macro_name.as_str();
let non_eq_mac = &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
}
impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
span_lint_and_help(

View file

@ -9,7 +9,7 @@ use rustc_span::symbol::sym;
use super::CAST_PTR_ALIGNMENT;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
if is_hir_ty_cfg_dependant(cx, cast_to) {
return;

View file

@ -6,7 +6,7 @@ use rustc_middle::ty;
use super::CAST_REF_TO_MUT;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind;
if let ExprKind::Cast(e, t) = &e.kind;

View file

@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy};
use super::CHAR_LIT_AS_U8;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Cast(e, _) = &expr.kind;
if let ExprKind::Lit(l) = &e.kind;

View file

@ -12,7 +12,7 @@ use rustc_semver::RustcVersion;
use super::PTR_AS_PTR;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) {
return;
}

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
/// abort any further processing and avoid duplicate lint triggers.
fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> {
let mut start_eq = usize::MAX;
let mut end_eq = usize::MAX;
let mut expr_eq = true;
@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
})
}
fn check_for_warn_of_moved_symbol(
cx: &LateContext<'tcx>,
symbols: &FxHashSet<Symbol>,
if_expr: &'tcx Expr<'_>,
) -> bool {
fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol(
}
fn emit_branches_sharing_code_lint(
cx: &LateContext<'tcx>,
cx: &LateContext<'_>,
start_stmts: usize,
end_stmts: usize,
lint_end: bool,
warn_about_moved_symbol: bool,
blocks: &[&Block<'tcx>],
if_expr: &'tcx Expr<'_>,
blocks: &[&Block<'_>],
if_expr: &Expr<'_>,
) {
if start_stmts == 0 && !lint_end {
return;

View file

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

View file

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

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

View file

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

View file

@ -14,6 +14,9 @@ declare_clippy_lint! {
/// ### What it does
/// Denies the configured types in clippy.toml.
///
/// Note: Even though this lint is warn-by-default, it will only trigger if
/// types are defined in the clippy.toml file.
///
/// ### Why is this bad?
/// Some types are undesirable in certain contexts.
///
@ -44,7 +47,7 @@ declare_clippy_lint! {
/// ```
#[clippy::version = "1.55.0"]
pub DISALLOWED_TYPES,
nursery,
style,
"use of disallowed types"
}
#[derive(Clone, Debug)]

View file

@ -1,8 +1,9 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter;
use rustc_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
use rustc_hir::{AnonConst, Expr};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_middle::lint::in_external_macro;
@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
return;
}
// check for `begin_panic`
if_chain! {
if let ExprKind::Call(func_expr, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
if let Some(path_def_id) = path.res.opt_def_id();
if match_panic_def_id(self.cx, path_def_id);
if is_expn_of(expr.span, "unreachable").is_none();
if !is_expn_of_debug_assertions(expr.span);
then {
self.panic_span = Some(expr.span);
if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
if is_panic(self.cx, macro_call.def_id)
|| matches!(
self.cx.tcx.item_name(macro_call.def_id).as_str(),
"assert" | "assert_eq" | "assert_ne" | "todo"
)
{
self.panic_span = Some(macro_call.span);
}
}
// check for `assert_eq` or `assert_ne`
if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
self.panic_span = Some(expr.span);
}
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
}
}
fn is_expn_of_debug_assertions(span: Span) -> bool {
const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
}

View file

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

View file

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

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() {
implements_trait(cx, ty, def_id, &[other.into()])
} else {

View file

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

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::local_used_after_expr;
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
use if_chain::if_chain;
@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
// A type param function ref like `T::f` is not 'static, however
// it is if cast like `T::f as fn()`. This seems like a rustc bug.
if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
then {
span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::higher::FormatArgsExpn;
use clippy_utils::macros::FormatArgsExpn;
use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
} else {
None
};
if let Some(format_args) = FormatArgsExpn::parse(write_arg);
if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
then {
let calling_macro =
// ordering is important here, since `writeln!` uses `write!` internally
@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
)
};
let msg = format!("use of `{}.unwrap()`", used);
if let [write_output] = *format_args.format_string_symbols {
if let [write_output] = *format_args.format_string_parts {
let mut write_output = write_output.to_string();
if write_output.ends_with('\n') {
write_output.pop();

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::method_chain_args;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath};
use rustc_hir::{Expr, ImplItemKind};
struct FindPanicUnwrap<'a, 'tcx> {
lcx: &'a LateContext<'tcx>,
@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
type Map = Map<'tcx>;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `begin_panic`
if_chain! {
if let ExprKind::Call(func_expr, _) = expr.kind;
if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
if let Some(path_def_id) = path.res.opt_def_id();
if match_panic_def_id(self.lcx, path_def_id);
if is_expn_of(expr.span, "unreachable").is_none();
then {
if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
if is_panic(self.lcx, macro_call.def_id) {
self.result.push(expr.span);
}
}

View file

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

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn};
use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_diag_trait_item, match_def_path, paths};
@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m
impl<'tcx> LateLintPass<'tcx> for FormatArgs {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! {
if let Some(format_args) = FormatArgsExpn::parse(expr);
if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
let expr_expn_data = expr.span.ctxt().outer_expn_data();
let outermost_expn_data = outermost_expn_data(expr_expn_data);
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if let Some(args) = format_args.args();
then {
for (i, arg) in args.iter().enumerate() {
if !arg.is_display() {
if arg.format_trait != sym::Display {
continue;
}
if arg.has_string_formatting() {
@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if is_aliased(&args, i) {
continue;
}
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg);
check_to_string_in_format_args(cx, name, arg);
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
check_to_string_in_format_args(cx, name, arg.value);
}
}
}
@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
}
}
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) {
if_chain! {
if FormatExpn::parse(arg.value).is_some();
if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion();
then {
span_lint_and_then(
cx,
FORMAT_IN_FORMAT_ARGS,
call_site,
&format!("`format!` in `{}!` args", name),
|diag| {
diag.help(&format!(
"combine the `format!(..)` arguments with the outer `{}!(..)` call",
name
));
diag.help("or consider changing `format!` to `format_args!`");
},
);
}
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
}
let Some(mac_id) = expn_data.macro_def_id else { return };
if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
return;
}
span_lint_and_then(
cx,
FORMAT_IN_FORMAT_ARGS,
call_site,
&format!("`format!` in `{}!` args", name),
|diag| {
diag.help(&format!(
"combine the `format!(..)` arguments with the outer `{}!(..)` call",
name
));
diag.help("or consider changing `format!` to `format_args!`");
},
);
}
fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) {
let value = arg.value;
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
if_chain! {
if !value.span.from_expansion();
if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind;

View file

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

View file

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

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};
pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = must_use_attr(attrs);
if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind {
@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
}
}
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
}
}
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());

View file

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

View file

@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
use super::RESULT_UNIT_ERR;
pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
}
}
pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<
}
}
pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,8 +1,7 @@
use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait};
use rustc_hir::{ImplItem, ImplItemKind};
use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait};
use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::kw;
use rustc_span::symbol::sym;
declare_clippy_lint! {
@ -40,26 +39,52 @@ declare_clippy_lint! {
declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
impl LateLintPass<'_> for IterNotReturningIterator {
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
let name = impl_item.ident.name.as_str();
if_chain! {
if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind;
let ret_ty = return_ty(cx, impl_item.hir_id());
if matches!(name, "iter" | "iter_mut");
if let [param] = cx.tcx.fn_arg_names(impl_item.def_id);
if param.name == kw::SelfLower;
if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
if !implements_trait(cx, ret_ty, iter_trait_id, &[]);
impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
let name = item.ident.name.as_str();
if matches!(name, "iter" | "iter_mut") {
if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
check_sig(cx, name, fn_sig, item.def_id);
}
}
}
then {
span_lint(
cx,
ITER_NOT_RETURNING_ITERATOR,
fn_sig.span,
&format!("this method is named `{}` but its return type does not implement `Iterator`", name),
);
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
let name = item.ident.name.as_str();
if matches!(name, "iter" | "iter_mut")
&& !matches!(
get_parent_node(cx.tcx, item.hir_id()),
Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some()
)
{
if let ImplItemKind::Fn(fn_sig, _) = &item.kind {
check_sig(cx, name, fn_sig, item.def_id);
}
}
}
}
fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
if sig.decl.implicit_self.has_implicit_self() {
let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output();
let ret_ty = cx
.tcx
.try_normalize_erasing_regions(cx.param_env, ret_ty)
.unwrap_or(ret_ty);
if cx
.tcx
.get_diagnostic_item(sym::Iterator)
.map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
{
span_lint(
cx,
ITER_NOT_RETURNING_ITERATOR,
sig.span,
&format!(
"this method is named `{}` but its return type does not implement `Iterator`",
name
),
);
}
}
}

View file

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

View file

@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(disallowed_methods::DISALLOWED_METHODS),
LintId::of(disallowed_types::DISALLOWED_TYPES),
LintId::of(doc::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(double_comparison::DOUBLE_COMPARISONS),
@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(loops::WHILE_LET_ON_ITERATOR),
LintId::of(main_recursion::MAIN_RECURSION),
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_map::MANUAL_MAP),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(manual_strip::MANUAL_STRIP),
@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL),

View file

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

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

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

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::UNNECESSARY_TO_OWNED),
LintId::of(misc::CMP_OWNED),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(redundant_clone::REDUNDANT_CLONE),
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),

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_SAME),
LintId::of(shadow::SHADOW_UNRELATED),
LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
LintId::of(strings::STRING_ADD),
LintId::of(strings::STRING_SLICE),
LintId::of(strings::STRING_TO_STRING),

View file

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

View file

@ -4,7 +4,6 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(in_band_lifetimes)]
#![feature(iter_intersperse)]
#![feature(let_else)]
#![feature(once_cell)]
@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint {
};
}
#[cfg(feature = "metadata-collector-lint")]
#[cfg(feature = "internal")]
mod deprecated_lints;
#[cfg_attr(
any(feature = "internal-lints", feature = "metadata-collector-lint"),
allow(clippy::missing_clippy_version_attribute)
)]
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
// begin lints modules, do not remove this comment, its used in `update_lints`
@ -177,6 +173,7 @@ mod blacklisted_name;
mod blocks_in_if_conditions;
mod bool_assert_comparison;
mod booleans;
mod borrow_as_ptr;
mod bytecount;
mod cargo_common_metadata;
mod case_sensitive_file_extension_comparisons;
@ -264,6 +261,7 @@ mod macro_use;
mod main_recursion;
mod manual_assert;
mod manual_async_fn;
mod manual_bits;
mod manual_map;
mod manual_non_exhaustive;
mod manual_ok_or;
@ -351,6 +349,7 @@ mod self_named_constructors;
mod semicolon_if_nothing_returned;
mod serde_api;
mod shadow;
mod single_char_lifetime_names;
mod single_component_path_imports;
mod size_of_in_element_count;
mod slow_vector_initialization;
@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
include!("lib.register_restriction.rs");
include!("lib.register_pedantic.rs");
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
include!("lib.register_internal.rs");
include!("lib.register_all.rs");
@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
include!("lib.register_cargo.rs");
include!("lib.register_nursery.rs");
#[cfg(feature = "metadata-collector-lint")]
#[cfg(feature = "internal")]
{
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}
// all the internal lints
#[cfg(feature = "internal-lints")]
#[cfg(feature = "internal")]
{
store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate};
use rustc_hir::{Block, Expr};
use rustc_lint::LateContext;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) {
if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) {
let msg = "empty `loop {}` wastes CPU cycles";
let help = if is_no_std_crate(cx) {

View file

@ -8,7 +8,7 @@ use rustc_lint::LateContext;
use rustc_middle::ty::TyS;
use rustc_span::symbol::sym;
pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) {
pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) {
let self_ty = cx.typeck_results().expr_ty(self_arg);
let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg);
if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) {

View file

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

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

View file

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

View file

@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
// extract the expression from the first statement (if any) in a block
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
// or extract the first expression (if any) from the block

View file

@ -8,12 +8,13 @@ use clippy_utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::{symbol::sym, Span, Symbol};
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::{symbol::sym, Symbol};
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
// check for `Some(..)` pattern
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// get the loop containing the match expression
if !uses_iter(cx, &iter_expr_struct, if_then);
then {
(let_expr, iter_expr_struct, some_pat, expr)
(let_expr, iter_expr_struct, iter_expr, some_pat, expr)
} else {
return;
}
@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
// afterwards a mutable borrow of a field isn't necessary.
let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) {
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|| !iter_expr_struct.can_move
|| !iter_expr_struct.fields.is_empty()
|| needs_mutable_borrow(cx, &iter_expr_struct, loop_expr)
{
".by_ref()"
} else {
""
@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
#[derive(Debug)]
struct IterExpr {
/// The span of the whole expression, not just the path and fields stored here.
span: Span,
/// The fields used, in order of child to parent.
fields: Vec<Symbol>,
/// The path being used.
path: Res,
/// Whether or not the iterator can be moved.
can_move: bool,
}
/// Parses any expression to find out which field of which variable is used. Will return `None` if
/// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
let span = e.span;
let mut fields = Vec::new();
let mut can_move = true;
loop {
if cx
.typeck_results()
.expr_adjustments(e)
.iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
{
// Custom deref impls need to borrow the whole value as it's captured by reference
can_move = false;
fields.clear();
}
match e.kind {
ExprKind::Path(ref path) => {
break Some(IterExpr {
span,
fields,
path: cx.qpath_res(path, e.hir_id),
can_move,
});
},
ExprKind::Field(base, name) => {
@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp
// Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have
// already been seen.
ExprKind::Index(base, idx) if !idx.can_have_side_effects() => {
can_move = false;
fields.clear();
e = base;
},
ExprKind::Unary(UnOp::Deref, base) => {
can_move = false;
fields.clear();
e = base;
},
@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie
/// Strips off all field and path expressions. This will return true if a field or path has been
/// skipped. Used to skip them after failing to check for equality.
fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) {
let mut e = expr;
let e = loop {
match e.kind {
@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool
}
/// Checks if the given expression uses the iterator.
fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool {
struct V<'a, 'b, 'tcx> {
cx: &'a LateContext<'tcx>,
iter_expr: &'b IterExpr,
uses_iter: bool,
}
impl Visitor<'tcx> for V<'_, '_, 'tcx> {
impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr
}
#[allow(clippy::too_many_lines)]
fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool {
fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool {
struct AfterLoopVisitor<'a, 'b, 'tcx> {
cx: &'a LateContext<'tcx>,
iter_expr: &'b IterExpr,
@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
after_loop: bool,
used_iter: bool,
}
impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
found_local: bool,
used_after: bool,
}
impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {

View file

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

View file

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::PanicExpn;
use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_expn_of, sugg};
use clippy_utils::{peel_blocks_with_stmt, sugg};
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
@ -34,65 +35,34 @@ declare_clippy_lint! {
declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]);
impl LateLintPass<'_> for ManualAssert {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
impl<'tcx> LateLintPass<'tcx> for ManualAssert {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if_chain! {
if let Expr {
kind: ExprKind:: If(cond, Expr {
kind: ExprKind::Block(
Block {
stmts: [stmt],
..
},
_),
..
}, None),
..
} = &expr;
if is_expn_of(stmt.span, "panic").is_some();
if let ExprKind::If(cond, then, None) = expr.kind;
if !matches!(cond.kind, ExprKind::Let(_));
if let StmtKind::Semi(semi) = stmt.kind;
if !expr.span.from_expansion();
let then = peel_blocks_with_stmt(then);
if let Some(macro_call) = root_macro_call(then.span);
if cx.tcx.item_name(macro_call.def_id) == sym::panic;
if !cx.tcx.sess.source_map().is_multiline(cond.span);
if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn);
then {
let call = if_chain! {
if let ExprKind::Block(block, _) = semi.kind;
if let Some(init) = block.expr;
then {
init
} else {
semi
}
};
let span = if let Some(panic_expn) = PanicExpn::parse(call) {
match *panic_expn.format_args.value_args {
[] => panic_expn.format_args.format_string_span,
[.., last] => panic_expn.format_args.format_string_span.to(last.span),
}
} else if let ExprKind::Call(_, [format_args]) = call.kind {
format_args.span
} else {
return
};
let mut applicability = Applicability::MachineApplicable;
let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind {
if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
} else {
format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par())
}
} else {
format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par())
let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
let cond = cond.peel_drop_temps();
let (cond, not) = match cond.kind {
ExprKind::Unary(UnOp::Not, e) => (e, ""),
_ => (cond, "!"),
};
let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par();
let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});");
span_lint_and_sugg(
cx,
MANUAL_ASSERT,
expr.span,
"only a `panic!` in `if`-then statement",
"try",
format!("assert!({}, {});", cond_sugg, sugg),
sugg,
Applicability::MachineApplicable,
);
}

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]);
impl LateLintPass<'_> for ManualMap {
impl<'tcx> LateLintPass<'tcx> for ManualMap {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) {
@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap {
// Checks whether the expression could be passed as a function, or whether a closure is needed.
// Returns the function to be passed to `map` if it exists.
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
if path_to_local_id(arg, binding)
@ -251,8 +251,13 @@ struct SomeExpr<'tcx> {
// Try to parse into a recognized `Option` pattern.
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
fn f<'tcx>(
cx: &LateContext<'tcx>,
pat: &'tcx Pat<'_>,
ref_count: usize,
ctxt: SyntaxContext,
) -> Option<OptionPat<'tcx>> {
match pat.kind {
PatKind::Wild => Some(OptionPat::Wild),
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon
}
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
fn get_some_expr(
fn get_some_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
needs_unsafe_block: bool,
@ -306,6 +311,6 @@ fn get_some_expr(
}
// Checks for the `None` value.
fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
}

View file

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

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
impl LateLintPass<'_> for ManualUnwrapOr {
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
return;

View file

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

View file

@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
use clippy_utils::diagnostics::{
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
use clippy_utils::higher;
use clippy_utils::macros::{is_panic, root_macro_call};
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::is_local_used;
use clippy_utils::{
get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
strip_pat_refs,
};
use clippy_utils::{higher, peel_blocks_with_stmt};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use core::iter::{once, ExactSizeIterator};
use if_chain::if_chain;
@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
}
if_chain! {
if matching_wild;
if is_panic_call(arm.body);
if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
if is_panic(cx, macro_call.def_id);
then {
// `Err(_)` or `Err(_e)` arm with `panic!` found
span_lint_and_note(cx,
@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> {
Path(&'a [PathSegment<'a>]),
Mixed,
}
impl CommonPrefixSearcher<'a> {
impl<'a> CommonPrefixSearcher<'a> {
fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
match path {
[path @ .., _] => self.with_prefix(path),
@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
};
}
// If the block contains only a `panic!` macro (as expression or statement)
fn is_panic_call(expr: &Expr<'_>) -> bool {
// Unwrap any wrapping blocks
let span = if let ExprKind::Block(block, _) = expr.kind {
match (&block.expr, block.stmts.len(), block.stmts.first()) {
(&Some(exp), 0, _) => exp.span,
(&None, 1, Some(stmt)) => stmt.span,
_ => return false,
}
} else {
expr.span
};
is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none()
}
fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
where
'b: 'a,
@ -1818,11 +1804,15 @@ mod redundant_pattern_match {
/// Checks if the drop order for a type matters. Some std types implement drop solely to
/// deallocate memory. For these types, and composites containing them, changing the drop order
/// won't result in any observable side effects.
fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
}
fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
fn type_needs_ordered_drop_inner<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
seen: &mut FxHashSet<Ty<'tcx>>,
) -> bool {
if !seen.insert(ty) {
return false;
}
@ -1884,7 +1874,7 @@ mod redundant_pattern_match {
// Checks if there are any temporaries created in the given expression for which drop order
// matters.
fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
res: bool,

View file

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

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> {
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_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol;
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
@ -1895,6 +1894,11 @@ declare_clippy_lint! {
/// ### Why is this bad?
/// The unnecessary calls result in useless allocations.
///
/// ### Known problems
/// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
/// owned copy of a resource and the resource is later used mutably. See
/// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
///
/// ### Example
/// ```rust
/// let path = std::path::Path::new("x");
@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [
]);
/// Extracts a method call name, args, and `Span` of the method name.
fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> {
fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
if !args.iter().any(|e| e.span.from_expansion()) {
return Some((path.ident.name, args, span));
let name = path.ident.name.as_str();
return Some((name, args, span));
}
}
None
}
/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str`
macro_rules! method_call {
($expr:expr) => {
method_call($expr)
.as_ref()
.map(|&(ref name, args, span)| (name.as_str(), args, span))
};
}
impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if expr.span.from_expansion() {
@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
#[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv);
@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
("collect", []) => match method_call!(recv) {
("collect", []) => match method_call(recv) {
Some((name @ ("cloned" | "copied"), [recv2], _)) => {
iter_cloned_collect::check(cx, name, expr, recv2);
},
@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
_ => {},
},
("count", []) => match method_call!(recv) {
("count", []) => match method_call(recv) {
Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
iter_count::check(cx, expr, recv2, name);
},
Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
_ => {},
},
("expect", [_]) => match method_call!(recv) {
("expect", [_]) => match method_call(recv) {
Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
_ => expect_used::check(cx, expr, recv),
},
@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
flat_map_option::check(cx, expr, arg, span);
},
("flatten", []) => {
if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
if let Some(("map", [recv, map_arg], _)) = method_call(recv) {
map_flatten::check(cx, expr, recv, map_arg);
}
},
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => {
if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
if let Some(("inspect", [_, _], span2)) = method_call(recv) {
inspect_for_each::check(cx, expr, span2);
}
},
@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
("map", [m_arg]) => {
if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
("next", []) => {
if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
if let Some((name, [recv, args @ ..], _)) = method_call(recv) {
match (name, args) {
("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
}
}
},
("nth", [n_arg]) => match method_call!(recv) {
("nth", [n_arg]) => match method_call(recv) {
Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv, span);
},
("unwrap", []) => match method_call!(recv) {
("unwrap", []) => match method_call(recv) {
Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
_ => unwrap_used::check(cx, expr, recv),
},
("unwrap_or", [u_arg]) => match method_call!(recv) {
("unwrap_or", [u_arg]) => match method_call(recv) {
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
},
@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
},
_ => {},
},
("unwrap_or_else", [u_arg]) => match method_call!(recv) {
("unwrap_or_else", [u_arg]) => match method_call(recv) {
Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
_ => {
unwrap_or_else_default::check(cx, expr, recv, u_arg);
@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
}
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
}
}
@ -2535,11 +2531,17 @@ impl SelfKind {
implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
}
fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
!matches_value(cx, parent_ty, ty)
&& !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
&& !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
}
match self {
Self::Value => matches_value(cx, parent_ty, ty),
Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
Self::No => ty != parent_ty,
Self::No => matches_none(cx, parent_ty, ty),
}
}

View file

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

View file

@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol};
use super::UNNECESSARY_TO_OWNED;
pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool {
pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent);
if is_into_iter(cx, callee_def_id);
then {
check_for_loop_iter(cx, parent, method_name, receiver)
check_for_loop_iter(cx, parent, method_name, receiver, false)
} else {
false
}
@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
/// include this code directly is so that it can be called from
/// `unnecessary_into_owned::check_into_iter_call_arg`.
pub fn check_for_loop_iter(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
cx: &LateContext<'_>,
expr: &Expr<'_>,
method_name: Symbol,
receiver: &'tcx Expr<'tcx>,
receiver: &Expr<'_>,
cloned_before_iter: bool,
) -> bool {
if_chain! {
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent));
@ -70,12 +71,22 @@ pub fn check_for_loop_iter(
expr.span,
&format!("unnecessary use of `{}`", method_name),
|diag| {
diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable);
// If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to
// a `to_owned`-like function was removed, then the next suggestion may be
// incorrect. This is because the iterator that results from the call's removal
// could hold a reference to a resource that is used mutably. See
// https://github.com/rust-lang/rust-clippy/issues/8148.
let applicability = if cloned_before_iter {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
diag.span_suggestion(expr.span, "use", snippet, applicability);
for addr_of_expr in addr_of_exprs {
match addr_of_expr.kind {
ExprKind::AddrOf(_, _, referent) => {
let span = addr_of_expr.span.with_hi(referent.span.lo());
diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable);
diag.span_suggestion(span, "remove this `&`", String::new(), applicability);
}
_ => unreachable!(),
}
@ -90,7 +101,7 @@ pub fn check_for_loop_iter(
/// The core logic of `check_for_loop_iter` above, this function wraps a use of
/// `CloneOrCopyVisitor`.
fn clone_or_copy_needed(
fn clone_or_copy_needed<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'tcx>,
body: &'tcx Expr<'tcx>,

View file

@ -16,7 +16,7 @@ use std::cmp::max;
use super::UNNECESSARY_TO_OWNED;
pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) {
if_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let [receiver] = args;
@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol
/// call of a `to_owned`-like function is unnecessary.
#[allow(clippy::too_many_lines)]
fn check_addr_of_expr(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
cx: &LateContext<'_>,
expr: &Expr<'_>,
method_name: Symbol,
method_def_id: DefId,
receiver: &'tcx Expr<'tcx>,
receiver: &Expr<'_>,
) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
@ -171,12 +171,7 @@ fn check_addr_of_expr(
/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
/// call of a `to_owned`-like function is unnecessary.
fn check_into_iter_call_arg(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
method_name: Symbol,
receiver: &'tcx Expr<'tcx>,
) -> bool {
fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
if_chain! {
if let Some(parent) = get_parent_expr(cx, expr);
if let Some(callee_def_id) = fn_def_id(cx, parent);
@ -187,7 +182,13 @@ fn check_into_iter_call_arg(
if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) {
if unnecessary_iter_cloned::check_for_loop_iter(
cx,
parent,
method_name,
receiver,
true,
) {
return true;
}
let cloned_or_copied = if is_copy(cx, item_ty) {
@ -195,6 +196,9 @@ fn check_into_iter_call_arg(
} else {
"cloned"
};
// The next suggestion may be incorrect because the removal of the `to_owned`-like
// function could cause the iterator to hold a reference to a resource that is used
// mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
span_lint_and_sugg(
cx,
UNNECESSARY_TO_OWNED,
@ -202,7 +206,7 @@ fn check_into_iter_call_arg(
&format!("unnecessary use of `{}`", method_name),
"use",
format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
Applicability::MachineApplicable,
Applicability::MaybeIncorrect,
);
return true;
}
@ -212,7 +216,7 @@ fn check_into_iter_call_arg(
/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
/// of a `to_owned`-like function is unnecessary.
fn check_other_call_arg(
fn check_other_call_arg<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
method_name: Symbol,
@ -278,7 +282,7 @@ fn check_other_call_arg(
/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
/// expression found (if any) along with the immediately prior expression.
fn skip_addr_of_ancestors(
fn skip_addr_of_ancestors<'tcx>(
cx: &LateContext<'tcx>,
mut expr: &'tcx Expr<'tcx>,
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
@ -294,7 +298,7 @@ fn skip_addr_of_ancestors(
/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
/// `Substs`, and arguments.
fn get_callee_substs_and_args(
fn get_callee_substs_and_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
@ -319,7 +323,7 @@ fn get_callee_substs_and_args(
}
/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
fn get_input_traits_and_projections(
fn get_input_traits_and_projections<'tcx>(
cx: &LateContext<'tcx>,
callee_def_id: DefId,
input: Ty<'tcx>,
@ -359,7 +363,11 @@ fn get_input_traits_and_projections(
}
/// Composes two substitutions by applying the latter to the types of the former.
fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> {
fn compose_substs<'tcx>(
cx: &LateContext<'tcx>,
left: &[GenericArg<'tcx>],
right: SubstsRef<'tcx>,
) -> Vec<GenericArg<'tcx>> {
left.iter()
.map(|arg| {
if let GenericArgKind::Type(arg_ty) = arg.unpack() {

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

View file

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

View file

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

View file

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

View file

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

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 has_no_effect(cx, expr) {
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
}
}
fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
if_chain! {
if let StmtKind::Semi(expr) = stmt.kind;
if let Some(reduced) = reduce_expression(cx, expr);

View file

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

View file

@ -50,7 +50,7 @@ declare_clippy_lint! {
declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
impl EarlyLintPass for OctalEscapes {
fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
if in_external_macro(cx.sess, expr.span) {
return;
}
@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes {
}
}
fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
let contents = lit.symbol.as_str();
let mut iter = contents.char_indices().peekable();
let mut found = vec![];

View file

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

View file

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

View file

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

View file

@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
continue;
} else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
// cloned value is mutated, and the clone is alive.
if possible_borrower.is_alive_at(ret_local, loc) {
if possible_borrower.local_is_alive_at(ret_local, loc) {
continue;
}
}
@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> {
self.bitset.0 == self.bitset.1
}
fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
self.maybe_live.seek_after_primary_effect(at);
self.maybe_live.contains(local)
}

View file

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

View file

@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf {
if_chain! {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
if deref_target.span.ctxt() == e.span.ctxt();
if !addrof_target.span.from_expansion();
then {
let mut applicability = Applicability::MachineApplicable;

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::{diagnostics::span_lint, nth_arg, return_ty};
use clippy_utils::{nth_arg, return_ty};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
@ -13,25 +14,46 @@ declare_clippy_lint! {
/// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute.
///
/// ### Why is this bad?
/// It prevents to "forget" to use the newly created value.
/// Methods returning `Self` often create new values, having the `#[must_use]` attribute
/// prevents users from "forgetting" to use the newly created value.
///
/// The `#[must_use]` attribute can be added to the type itself to ensure that instances
/// are never forgotten. Functions returning a type marked with `#[must_use]` will not be
/// linted, as the usage is already enforced by the type attribute.
///
/// ### Limitations
/// This lint is only applied on methods taking a `self` argument. It would be mostly noise
/// if it was added on constructors for example.
///
/// ### Example
/// Missing attribute
/// ```rust
/// pub struct Bar;
///
/// impl Bar {
/// // Bad
/// pub fn bar(&self) -> Self {
/// Self
/// }
/// }
/// ```
///
/// // Good
/// It's better to have the `#[must_use]` attribute on the method like this:
/// ```rust
/// pub struct Bar;
/// impl Bar {
/// #[must_use]
/// pub fn foo(&self) -> Self {
/// pub fn bar(&self) -> Self {
/// Self
/// }
/// }
/// ```
///
/// Or on the type definition like this:
/// ```rust
/// #[must_use]
/// pub struct Bar;
/// impl Bar {
/// pub fn bar(&self) -> Self {
/// Self
/// }
/// }
@ -44,7 +66,7 @@ declare_clippy_lint! {
declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
if_chain! {
// If it comes from an external macro, better ignore it.
if !in_external_macro(cx.sess(), span);
@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD
if !is_must_use_ty(cx, ret_ty);
then {
span_lint(
span_lint_and_help(
cx,
RETURN_SELF_NOT_MUST_USE,
span,
"missing `#[must_use]` attribute on a method returning `Self`",
None,
"consider adding the `#[must_use]` attribute to the method or directly to the `Self` type"
);
}
}

View file

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

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]);
fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
match expr.kind {
ExprKind::Call(count_func, _func_args) => {
if_chain! {
@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool)
}
}
fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
fn get_pointee_ty_and_count_expr<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
const FUNCTIONS: [&[&str]; 8] = [
&paths::PTR_COPY_NONOVERLAPPING,
&paths::PTR_COPY,

View file

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

View file

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

View file

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

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