Auto merge of #107886 - flip1995:clippyup, r=Manishearth

Update Clippy

r? `@Manishearth`

One day late. Sorry forgot about it yesterday :|
This commit is contained in:
bors 2023-02-10 18:33:22 +00:00
commit 2773383a31
77 changed files with 2215 additions and 581 deletions

View file

@ -449,7 +449,7 @@ dependencies = [
name = "cargo-miri"
version = "0.1.0"
dependencies = [
"cargo_metadata 0.15.0",
"cargo_metadata 0.15.3",
"directories",
"rustc-build-sysroot",
"rustc-workspace-hack",
@ -540,15 +540,16 @@ dependencies = [
[[package]]
name = "cargo_metadata"
version = "0.15.0"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36"
checksum = "08a1ec454bc3eead8719cb56e15dbbfecdbc14e4b3a3ae4936cc6e31f5fc0d07"
dependencies = [
"camino",
"cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"semver",
"serde",
"serde_json",
"thiserror",
]
[[package]]
@ -732,6 +733,7 @@ dependencies = [
name = "clippy"
version = "0.1.69"
dependencies = [
"clap 4.1.4",
"clippy_lints",
"clippy_utils",
"compiletest_rs",
@ -762,7 +764,7 @@ name = "clippy_dev"
version = "0.0.1"
dependencies = [
"aho-corasick",
"clap 3.2.20",
"clap 4.1.4",
"indoc",
"itertools",
"opener",
@ -774,7 +776,7 @@ dependencies = [
name = "clippy_lints"
version = "0.1.69"
dependencies = [
"cargo_metadata 0.14.0",
"cargo_metadata 0.15.3",
"clippy_utils",
"declare_clippy_lint",
"if_chain",
@ -5865,7 +5867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54ddb6f31025943e2f9d59237f433711c461a43d9415974c3eb3a4902edc1c1f"
dependencies = [
"bstr 1.0.1",
"cargo_metadata 0.15.0",
"cargo_metadata 0.15.3",
"color-eyre",
"colored",
"crossbeam-channel",

View file

@ -4383,6 +4383,7 @@ Released 2018-09-13
[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
[`extra_unused_type_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_type_parameters
[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file

View file

@ -42,6 +42,7 @@ filetime = "0.2"
rustc-workspace-hack = "1.0"
# UI test dependencies
clap = { version = "4.1.4", features = ["derive"] }
clippy_utils = { path = "clippy_utils" }
derive-new = "0.5"
if_chain = "1.0"

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 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 600 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.

View file

@ -6,7 +6,7 @@
A collection of lints to catch common mistakes and improve your
[Rust](https://github.com/rust-lang/rust) code.
[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
[There are over 600 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

View file

@ -43,6 +43,7 @@ Please use that command to update the file and do not edit it by hand.
| [allowed-scripts](#allowed-scripts) | `["Latin"]` |
| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` |
| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` |
| [await-holding-invalid-types](#await-holding-invalid-types) | `[]` |
| [max-include-file-size](#max-include-file-size) | `1000000` |
| [allow-expect-in-tests](#allow-expect-in-tests) | `false` |
| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` |
@ -167,6 +168,17 @@ The minimum rust version that the project supports
* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
* [collapsible_str_replace](https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace)
* [seek_from_current](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
* [seek_rewind](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
* [unnecessary_lazy_evaluations](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations)
* [transmute_ptr_to_ref](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
* [almost_complete_range](https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range)
* [needless_borrow](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
* [derivable_impls](https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls)
* [manual_is_ascii_check](https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check)
* [manual_rem_euclid](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid)
* [manual_retain](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain)
### cognitive-complexity-threshold
@ -279,7 +291,7 @@ The minimum size (in bytes) to consider a type for passing by reference instead
**Default Value:** `256` (`u64`)
* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move)
* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
### too-many-lines-threshold
@ -442,6 +454,14 @@ For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
### await-holding-invalid-types
**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
* [await_holding_invalid_type](https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type)
### max-include-file-size
The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
@ -497,7 +517,7 @@ for the generic parameters for determining interior mutability
**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key)
* [mutable_key_type](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type)
### allow-mixed-uninlined-format-args
@ -509,7 +529,7 @@ Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)
### suppress-restriction-lint-in-const
In same
Whether to suppress a restriction lint in constant code. In same
cases the restructured operation might not be unavoidable, as the
suggested counterparts are unavailable in constant code. This
configuration will cause restriction lints to trigger even

View file

@ -5,7 +5,7 @@ edition = "2021"
[dependencies]
aho-corasick = "0.7"
clap = "3.2"
clap = "4.1.4"
indoc = "1.0"
itertools = "0.10.1"
opener = "0.5"

View file

@ -2,7 +2,7 @@
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
use clap::{Arg, ArgAction, ArgMatches, Command};
use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
use indoc::indoc;
@ -11,22 +11,22 @@ fn main() {
match matches.subcommand() {
Some(("bless", matches)) => {
bless::bless(matches.contains_id("ignore-timestamp"));
bless::bless(matches.get_flag("ignore-timestamp"));
},
Some(("dogfood", matches)) => {
dogfood::dogfood(
matches.contains_id("fix"),
matches.contains_id("allow-dirty"),
matches.contains_id("allow-staged"),
matches.get_flag("fix"),
matches.get_flag("allow-dirty"),
matches.get_flag("allow-staged"),
);
},
Some(("fmt", matches)) => {
fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
fmt::run(matches.get_flag("check"), matches.get_flag("verbose"));
},
Some(("update_lints", matches)) => {
if matches.contains_id("print-only") {
if matches.get_flag("print-only") {
update_lints::print_lints();
} else if matches.contains_id("check") {
} else if matches.get_flag("check") {
update_lints::update(update_lints::UpdateMode::Check);
} else {
update_lints::update(update_lints::UpdateMode::Change);
@ -38,7 +38,7 @@ fn main() {
matches.get_one::<String>("name"),
matches.get_one::<String>("category").map(String::as_str),
matches.get_one::<String>("type").map(String::as_str),
matches.contains_id("msrv"),
matches.get_flag("msrv"),
) {
Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
Err(e) => eprintln!("Unable to create lint: {e}"),
@ -46,7 +46,7 @@ fn main() {
},
Some(("setup", sub_command)) => match sub_command.subcommand() {
Some(("intellij", matches)) => {
if matches.contains_id("remove") {
if matches.get_flag("remove") {
setup::intellij::remove_rustc_src();
} else {
setup::intellij::setup_rustc_src(
@ -57,17 +57,17 @@ fn main() {
}
},
Some(("git-hook", matches)) => {
if matches.contains_id("remove") {
if matches.get_flag("remove") {
setup::git_hook::remove_hook();
} else {
setup::git_hook::install_hook(matches.contains_id("force-override"));
setup::git_hook::install_hook(matches.get_flag("force-override"));
}
},
Some(("vscode-tasks", matches)) => {
if matches.contains_id("remove") {
if matches.get_flag("remove") {
setup::vscode::remove_tasks();
} else {
setup::vscode::install_tasks(matches.contains_id("force-override"));
setup::vscode::install_tasks(matches.get_flag("force-override"));
}
},
_ => {},
@ -91,7 +91,7 @@ fn main() {
Some(("rename_lint", matches)) => {
let old_name = matches.get_one::<String>("old_name").unwrap();
let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
let uplift = matches.contains_id("uplift");
let uplift = matches.get_flag("uplift");
update_lints::rename(old_name, new_name, uplift);
},
Some(("deprecate", matches)) => {
@ -110,24 +110,37 @@ fn get_clap_config() -> ArgMatches {
Command::new("bless").about("bless the test output changes").arg(
Arg::new("ignore-timestamp")
.long("ignore-timestamp")
.action(ArgAction::SetTrue)
.help("Include files updated before clippy was built"),
),
Command::new("dogfood").about("Runs the dogfood test").args([
Arg::new("fix").long("fix").help("Apply the suggestions when possible"),
Arg::new("fix")
.long("fix")
.action(ArgAction::SetTrue)
.help("Apply the suggestions when possible"),
Arg::new("allow-dirty")
.long("allow-dirty")
.action(ArgAction::SetTrue)
.help("Fix code even if the working directory has changes")
.requires("fix"),
Arg::new("allow-staged")
.long("allow-staged")
.action(ArgAction::SetTrue)
.help("Fix code even if the working directory has staged changes")
.requires("fix"),
]),
Command::new("fmt")
.about("Run rustfmt on all projects and tests")
.args([
Arg::new("check").long("check").help("Use the rustfmt --check option"),
Arg::new("verbose").short('v').long("verbose").help("Echo commands run"),
Arg::new("check")
.long("check")
.action(ArgAction::SetTrue)
.help("Use the rustfmt --check option"),
Arg::new("verbose")
.short('v')
.long("verbose")
.action(ArgAction::SetTrue)
.help("Echo commands run"),
]),
Command::new("update_lints")
.about("Updates lint registration and information from the source code")
@ -140,13 +153,17 @@ fn get_clap_config() -> ArgMatches {
* all lints are registered in the lint store",
)
.args([
Arg::new("print-only").long("print-only").help(
"Print a table of lints to STDOUT. \
This does not include deprecated and internal lints. \
(Does not modify any files)",
),
Arg::new("print-only")
.long("print-only")
.action(ArgAction::SetTrue)
.help(
"Print a table of lints to STDOUT. \
This does not include deprecated and internal lints. \
(Does not modify any files)",
),
Arg::new("check")
.long("check")
.action(ArgAction::SetTrue)
.help("Checks that `cargo dev update_lints` has been run. Used on CI."),
]),
Command::new("new_lint")
@ -156,15 +173,13 @@ fn get_clap_config() -> ArgMatches {
.short('p')
.long("pass")
.help("Specify whether the lint runs during the early or late pass")
.takes_value(true)
.value_parser([PossibleValue::new("early"), PossibleValue::new("late")])
.value_parser(["early", "late"])
.conflicts_with("type")
.required_unless_present("type"),
Arg::new("name")
.short('n')
.long("name")
.help("Name of the new lint in snake case, ex: fn_too_long")
.takes_value(true)
.required(true),
Arg::new("category")
.short('c')
@ -172,25 +187,23 @@ fn get_clap_config() -> ArgMatches {
.help("What category the lint belongs to")
.default_value("nursery")
.value_parser([
PossibleValue::new("style"),
PossibleValue::new("correctness"),
PossibleValue::new("suspicious"),
PossibleValue::new("complexity"),
PossibleValue::new("perf"),
PossibleValue::new("pedantic"),
PossibleValue::new("restriction"),
PossibleValue::new("cargo"),
PossibleValue::new("nursery"),
PossibleValue::new("internal"),
PossibleValue::new("internal_warn"),
])
.takes_value(true),
Arg::new("type")
.long("type")
.help("What directory the lint belongs in")
.takes_value(true)
.required(false),
Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint"),
"style",
"correctness",
"suspicious",
"complexity",
"perf",
"pedantic",
"restriction",
"cargo",
"nursery",
"internal",
"internal_warn",
]),
Arg::new("type").long("type").help("What directory the lint belongs in"),
Arg::new("msrv")
.long("msrv")
.action(ArgAction::SetTrue)
.help("Add MSRV config code to the lint"),
]),
Command::new("setup")
.about("Support for setting up your personal development environment")
@ -201,13 +214,12 @@ fn get_clap_config() -> ArgMatches {
.args([
Arg::new("remove")
.long("remove")
.help("Remove the dependencies added with 'cargo dev setup intellij'")
.required(false),
.action(ArgAction::SetTrue)
.help("Remove the dependencies added with 'cargo dev setup intellij'"),
Arg::new("rustc-repo-path")
.long("repo-path")
.short('r')
.help("The path to a rustc repo that will be used for setting the dependencies")
.takes_value(true)
.value_name("path")
.conflicts_with("remove")
.required(true),
@ -217,26 +229,26 @@ fn get_clap_config() -> ArgMatches {
.args([
Arg::new("remove")
.long("remove")
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
.required(false),
.action(ArgAction::SetTrue)
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
Arg::new("force-override")
.long("force-override")
.short('f')
.help("Forces the override of an existing git pre-commit hook")
.required(false),
.action(ArgAction::SetTrue)
.help("Forces the override of an existing git pre-commit hook"),
]),
Command::new("vscode-tasks")
.about("Add several tasks to vscode for formatting, validation and testing")
.args([
Arg::new("remove")
.long("remove")
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
.required(false),
.action(ArgAction::SetTrue)
.help("Remove the tasks added with 'cargo dev setup vscode-tasks'"),
Arg::new("force-override")
.long("force-override")
.short('f')
.help("Forces the override of existing vscode tasks")
.required(false),
.action(ArgAction::SetTrue)
.help("Forces the override of existing vscode tasks"),
]),
]),
Command::new("remove")
@ -295,6 +307,7 @@ fn get_clap_config() -> ArgMatches {
.help("The new name of the lint"),
Arg::new("uplift")
.long("uplift")
.action(ArgAction::SetTrue)
.help("This lint will be uplifted into rustc"),
]),
Command::new("deprecate").about("Deprecates the given lint").args([
@ -305,8 +318,6 @@ fn get_clap_config() -> ArgMatches {
Arg::new("reason")
.long("reason")
.short('r')
.required(false)
.takes_value(true)
.help("The reason for deprecation"),
]),
])

View file

@ -9,7 +9,7 @@ keywords = ["clippy", "lint", "plugin"]
edition = "2021"
[dependencies]
cargo_metadata = "0.14"
cargo_metadata = "0.15.3"
clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" }
if_chain = "1.0"

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_copy};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -34,14 +35,16 @@
declare_lint_pass!(BoolAssertComparison => [BOOL_ASSERT_COMPARISON]);
fn is_bool_lit(e: &Expr<'_>) -> bool {
matches!(
e.kind,
ExprKind::Lit(Lit {
node: LitKind::Bool(_),
..
})
) && !e.span.from_expansion()
fn extract_bool_lit(e: &Expr<'_>) -> Option<bool> {
if let ExprKind::Lit(Lit {
node: LitKind::Bool(b), ..
}) = e.kind
&& !e.span.from_expansion()
{
Some(b)
} else {
None
}
}
fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@ -69,24 +72,23 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
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 eq_macro = match macro_name.as_str() {
"assert_eq" | "debug_assert_eq" => true,
"assert_ne" | "debug_assert_ne" => false,
_ => return,
};
let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
let a_span = a.span.source_callsite();
let b_span = b.span.source_callsite();
let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) {
// assert_eq!(true, b)
// ^^^^^^
(true, false) => (a_span.until(b_span), b),
// assert_eq!(a, true)
// ^^^^^^
(false, true) => (b_span.with_lo(a_span.hi()), a),
let (lit_span, bool_value, non_lit_expr) = match (extract_bool_lit(a), extract_bool_lit(b)) {
// assert_eq!(true/false, b)
// ^^^^^^^^^^^^
(Some(bool_value), None) => (a_span.until(b_span), bool_value, b),
// assert_eq!(a, true/false)
// ^^^^^^^^^^^^
(None, Some(bool_value)) => (b_span.with_lo(a_span.hi()), bool_value, a),
// If there are two boolean arguments, we definitely don't understand
// what's going on, so better leave things as is...
//
@ -121,9 +123,16 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// ^^^^^^^^^
let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
if bool_value ^ eq_macro {
let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else { return };
suggestions.push((non_lit_expr.span, (!sugg).to_string()));
}
diag.multipart_suggestion(
format!("replace it with `{non_eq_mac}!(..)`"),
vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())],
suggestions,
Applicability::MachineApplicable,
);
},

View file

@ -156,6 +156,7 @@
crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
crate::exit::EXIT_INFO,
crate::explicit_write::EXPLICIT_WRITE_INFO,
crate::extra_unused_type_parameters::EXTRA_UNUSED_TYPE_PARAMETERS_INFO,
crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
crate::float_literal::EXCESSIVE_PRECISION_INFO,
crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,

View file

@ -4,8 +4,8 @@
use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
declare_clippy_lint! {

View file

@ -0,0 +1,178 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan;
use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor};
use rustc_hir::{
GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{def_id::DefId, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for type parameters in generics that are never used anywhere else.
///
/// ### Why is this bad?
/// Functions cannot infer the value of unused type parameters; therefore, calling them
/// requires using a turbofish, which serves no purpose but to satisfy the compiler.
///
/// ### Example
/// ```rust
/// // unused type parameters
/// fn unused_ty<T>(x: u8) {
/// // ..
/// }
/// ```
/// Use instead:
/// ```rust
/// fn no_unused_ty(x: u8) {
/// // ..
/// }
/// ```
#[clippy::version = "1.69.0"]
pub EXTRA_UNUSED_TYPE_PARAMETERS,
complexity,
"unused type parameters in function definitions"
}
declare_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
/// A visitor struct that walks a given function and gathers generic type parameters, plus any
/// trait bounds those parameters have.
struct TypeWalker<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
/// Collection of all the type parameters and their spans.
ty_params: FxHashMap<DefId, Span>,
/// Collection of any (inline) trait bounds corresponding to each type parameter.
bounds: FxHashMap<DefId, Span>,
/// The entire `Generics` object of the function, useful for querying purposes.
generics: &'tcx Generics<'tcx>,
/// The value of this will remain `true` if *every* parameter:
/// 1. Is a type parameter, and
/// 2. Goes unused in the function.
/// Otherwise, if any type parameters end up being used, or if any lifetime or const-generic
/// parameters are present, this will be set to `false`.
all_params_unused: bool,
}
impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> {
fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'tcx>) -> Self {
let mut all_params_unused = true;
let ty_params = generics
.params
.iter()
.filter_map(|param| {
if let GenericParamKind::Type { .. } = param.kind {
Some((param.def_id.into(), param.span))
} else {
if !param.is_elided_lifetime() {
all_params_unused = false;
}
None
}
})
.collect();
Self {
cx,
ty_params,
bounds: FxHashMap::default(),
generics,
all_params_unused,
}
}
fn emit_lint(&self) {
let (msg, help) = match self.ty_params.len() {
0 => return,
1 => (
"type parameter goes unused in function definition",
"consider removing the parameter",
),
_ => (
"type parameters go unused in function definition",
"consider removing the parameters",
),
};
let source_map = self.cx.tcx.sess.source_map();
let span = if self.all_params_unused {
self.generics.span.into() // Remove the entire list of generics
} else {
MultiSpan::from_spans(
self.ty_params
.iter()
.map(|(def_id, &span)| {
// Extend the span past any trait bounds, and include the comma at the end.
let span_to_extend = self.bounds.get(def_id).copied().map_or(span, Span::shrink_to_hi);
let comma_range = source_map.span_extend_to_next_char(span_to_extend, '>', false);
let comma_span = source_map.span_through_char(comma_range, ',');
span.with_hi(comma_span.hi())
})
.collect(),
)
};
span_lint_and_help(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, span, msg, None, help);
}
}
impl<'cx, 'tcx> Visitor<'tcx> for TypeWalker<'cx, 'tcx> {
type NestedFilter = nested_filter::OnlyBodies;
fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
if self.ty_params.remove(&def_id).is_some() {
self.all_params_unused = false;
}
} else if let TyKind::OpaqueDef(id, _, _) = t.kind {
// Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
// `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
// using `OnlyBodies`, so the check ends up failing and the type isn't fully walked.
let item = self.nested_visit_map().item(id);
walk_item(self, item);
} else {
walk_ty(self, t);
}
}
fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
if let WherePredicate::BoundPredicate(predicate) = predicate {
// Collect spans for bounds that appear in the list of generics (not in a where-clause)
// for use in forming the help message
if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param()
&& let PredicateOrigin::GenericParam = predicate.origin
{
self.bounds.insert(def_id, predicate.span);
}
// Only walk the right-hand side of where-bounds
for bound in predicate.bounds {
walk_param_bound(self, bound);
}
}
}
fn nested_visit_map(&mut self) -> Self::Map {
self.cx.tcx.hir()
}
}
impl<'tcx> LateLintPass<'tcx> for ExtraUnusedTypeParameters {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Fn(_, generics, _) = item.kind {
let mut walker = TypeWalker::new(cx, generics);
walk_item(&mut walker, item);
walker.emit_lint();
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
// Only lint on inherent methods, not trait methods.
if let ImplItemKind::Fn(..) = item.kind && trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
let mut walker = TypeWalker::new(cx, item.generics);
walk_impl_item(&mut walker, item);
walker.emit_lint();
}
}
}

View file

@ -125,7 +125,7 @@
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
#[clippy::version = "1.66.0"]
pub UNINLINED_FORMAT_ARGS,
style,
pedantic,
"using non-inlined variables in `format!` calls"
}

View file

@ -10,13 +10,7 @@
use super::MISNAMED_GETTERS;
pub fn check_fn(
cx: &LateContext<'_>,
kind: FnKind<'_>,
decl: &FnDecl<'_>,
body: &Body<'_>,
span: Span,
) {
pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body: &Body<'_>, span: Span) {
let FnKind::Method(ref ident, sig) = kind else {
return;
};

View file

@ -25,7 +25,7 @@ pub(super) fn check_fn<'tcx>(
intravisit::FnKind::Closure => return,
};
check_raw_ptr(cx, unsafety, decl, body, def_id)
check_raw_ptr(cx, unsafety, decl, body, def_id);
}
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {

View file

@ -66,7 +66,9 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>
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.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output());
let ret_ty = cx
.tcx
.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output());
let ret_ty = cx
.tcx
.try_normalize_erasing_regions(cx.param_env, ret_ty)

View file

@ -135,6 +135,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
if item.ident.name == sym::len;
if let ImplItemKind::Fn(sig, _) = &item.kind;
if sig.decl.implicit_self.has_implicit_self();
if sig.decl.inputs.len() == 1;
if cx.effective_visibilities.is_exported(item.owner_id.def_id);
if matches!(sig.decl.output, FnRetTy::Return(_));
if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
@ -196,7 +197,15 @@ fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
item.ident.name == name
&& if let AssocItemKind::Fn { has_self } = item.kind {
has_self && { cx.tcx.fn_sig(item.id.owner_id).skip_binder().inputs().skip_binder().len() == 1 }
has_self && {
cx.tcx
.fn_sig(item.id.owner_id)
.skip_binder()
.inputs()
.skip_binder()
.len()
== 1
}
} else {
false
}
@ -342,7 +351,11 @@ fn check_for_is_empty<'tcx>(
),
Some(is_empty)
if !(is_empty.fn_has_self_parameter
&& check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output)) =>
&& check_is_empty_sig(
cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(),
self_kind,
output,
)) =>
{
(
format!(

View file

@ -121,6 +121,7 @@
mod exhaustive_items;
mod exit;
mod explicit_write;
mod extra_unused_type_parameters;
mod fallible_impl_from;
mod float_literal;
mod floating_point_arithmetic;
@ -909,6 +910,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
store.register_late_pass(|_| Box::new(extra_unused_type_parameters::ExtraUnusedTypeParameters));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View file

@ -1,20 +1,21 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability;
use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
use rustc_hir::intravisit::{
walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor,
};
use rustc_hir::lang_items;
use rustc_hir::FnRetTy::Return;
use rustc_hir::{
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn,
TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics,
Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
@ -33,8 +34,6 @@
/// ### Known problems
/// - We bail out if the function has a `where` clause where lifetimes
/// are mentioned due to potential false positives.
/// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
/// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
///
/// ### Example
/// ```rust
@ -92,7 +91,7 @@
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let ItemKind::Fn(ref sig, generics, id) = item.kind {
check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
check_fn_inner(cx, sig, Some(id), None, generics, item.span, true);
} else if let ItemKind::Impl(impl_) = item.kind {
if !item.span.from_expansion() {
report_extra_impl_lifetimes(cx, impl_);
@ -105,7 +104,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none();
check_fn_inner(
cx,
sig.decl,
sig,
Some(id),
None,
item.generics,
@ -121,29 +120,21 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
TraitFn::Required(sig) => (None, Some(sig)),
TraitFn::Provided(id) => (Some(id), None),
};
check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true);
}
}
}
/// The lifetime of a &-reference.
#[derive(PartialEq, Eq, Hash, Debug, Clone)]
enum RefLt {
Unnamed,
Static,
Named(LocalDefId),
}
fn check_fn_inner<'tcx>(
cx: &LateContext<'tcx>,
decl: &'tcx FnDecl<'_>,
sig: &'tcx FnSig<'_>,
body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
generics: &'tcx Generics<'_>,
span: Span,
report_extra_lifetimes: bool,
) {
if span.from_expansion() || has_where_lifetimes(cx, generics) {
if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) {
return;
}
@ -162,7 +153,7 @@ fn check_fn_inner<'tcx>(
for bound in pred.bounds {
let mut visitor = RefVisitor::new(cx);
walk_param_bound(&mut visitor, bound);
if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
if visitor.lts.iter().any(|lt| matches!(lt.res, LifetimeName::Param(_))) {
return;
}
if let GenericBound::Trait(ref trait_ref, _) = *bound {
@ -189,12 +180,12 @@ fn check_fn_inner<'tcx>(
}
}
if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) {
if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) {
let lts = elidable_lts
.iter()
// In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
// `Node::GenericParam`.
.filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident())
.filter_map(|&def_id| cx.tcx.hir().get_by_def_id(def_id).ident())
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ");
@ -202,21 +193,99 @@ fn check_fn_inner<'tcx>(
span_lint_and_then(
cx,
NEEDLESS_LIFETIMES,
span.with_hi(decl.output.span().hi()),
span.with_hi(sig.decl.output.span().hi()),
&format!("the following explicit lifetimes could be elided: {lts}"),
|diag| {
if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) {
diag.span_help(span, "replace with `'_` in generic arguments such as here");
if sig.header.is_async() {
// async functions have usages whose spans point at the lifetime declaration which messes up
// suggestions
return;
};
if let Some(suggestions) = elision_suggestions(cx, generics, &elidable_lts, &usages) {
diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable);
}
},
);
}
if report_extra_lifetimes {
self::report_extra_lifetimes(cx, decl, generics);
self::report_extra_lifetimes(cx, sig.decl, generics);
}
}
fn elision_suggestions(
cx: &LateContext<'_>,
generics: &Generics<'_>,
elidable_lts: &[LocalDefId],
usages: &[Lifetime],
) -> Option<Vec<(Span, String)>> {
let explicit_params = generics
.params
.iter()
.filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
.collect::<Vec<_>>();
let mut suggestions = if elidable_lts.len() == explicit_params.len() {
// if all the params are elided remove the whole generic block
//
// fn x<'a>() {}
// ^^^^
vec![(generics.span, String::new())]
} else {
elidable_lts
.iter()
.map(|&id| {
let pos = explicit_params.iter().position(|param| param.def_id == id)?;
let param = explicit_params.get(pos)?;
let span = if let Some(next) = explicit_params.get(pos + 1) {
// fn x<'prev, 'a, 'next>() {}
// ^^^^
param.span.until(next.span)
} else {
// `pos` should be at least 1 here, because the param in position 0 would either have a `next`
// param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
let prev = explicit_params.get(pos - 1)?;
// fn x<'prev, 'a>() {}
// ^^^^
param.span.with_lo(prev.span.hi())
};
Some((span, String::new()))
})
.collect::<Option<Vec<_>>>()?
};
suggestions.extend(
usages
.iter()
.filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
.map(|usage| {
match cx.tcx.hir().get_parent(usage.hir_id) {
Node::Ty(Ty {
kind: TyKind::Ref(..), ..
}) => {
// expand `&'a T` to `&'a T`
// ^^ ^^^
let span = cx
.sess()
.source_map()
.span_extend_while(usage.ident.span, |ch| ch.is_ascii_whitespace())
.unwrap_or(usage.ident.span);
(span, String::new())
},
// `T<'a>` and `impl Foo + 'a` should be replaced by `'_`
_ => (usage.ident.span, String::from("'_")),
}
}),
);
Some(suggestions)
}
// elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if_chain! {
@ -236,13 +305,20 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
}
}
fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {
match lt.res {
LifetimeName::Param(id) if !lt.is_anonymous() => Some(id),
_ => None,
}
}
fn could_use_elision<'tcx>(
cx: &LateContext<'tcx>,
func: &'tcx FnDecl<'_>,
body: Option<BodyId>,
trait_sig: Option<&[Ident]>,
named_generics: &'tcx [GenericParam<'_>],
) -> Option<Vec<(LocalDefId, Option<Span>)>> {
) -> Option<(Vec<LocalDefId>, Vec<Lifetime>)> {
// There are two scenarios where elision works:
// * no output references, all input references have different LT
// * output references, exactly one input reference with same LT
@ -300,32 +376,24 @@ fn could_use_elision<'tcx>(
// check for lifetimes from higher scopes
for lt in input_lts.iter().chain(output_lts.iter()) {
if !allowed_lts.contains(lt) {
if let Some(id) = named_lifetime(lt)
&& !allowed_lts.contains(&id)
{
return None;
}
}
// check for higher-ranked trait bounds
if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() {
let allowed_lts: FxHashSet<_> = allowed_lts
.iter()
.filter_map(|lt| match lt {
RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())),
_ => None,
})
.collect();
let allowed_lts: FxHashSet<_> = allowed_lts.iter().map(|id| cx.tcx.item_name(id.to_def_id())).collect();
for lt in input_visitor.nested_elision_site_lts {
if let RefLt::Named(def_id) = lt {
if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
return None;
}
if allowed_lts.contains(&lt.ident.name) {
return None;
}
}
for lt in output_visitor.nested_elision_site_lts {
if let RefLt::Named(def_id) = lt {
if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
return None;
}
if allowed_lts.contains(&lt.ident.name) {
return None;
}
}
}
@ -337,15 +405,10 @@ fn could_use_elision<'tcx>(
let elidable_lts = named_lifetime_occurrences(&input_lts)
.into_iter()
.filter_map(|(def_id, occurrences)| {
if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) {
Some((
def_id,
input_visitor
.lifetime_generic_arg_spans
.get(&def_id)
.or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id))
.copied(),
))
if occurrences == 1
&& (input_lts.len() == 1 || !output_lts.iter().any(|lt| named_lifetime(lt) == Some(def_id)))
{
Some(def_id)
} else {
None
}
@ -353,31 +416,34 @@ fn could_use_elision<'tcx>(
.collect::<Vec<_>>();
if elidable_lts.is_empty() {
None
} else {
Some(elidable_lts)
return None;
}
let usages = itertools::chain(input_lts, output_lts).collect();
Some((elidable_lts, usages))
}
fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
let mut allowed_lts = FxHashSet::default();
for par in named_generics.iter() {
if let GenericParamKind::Lifetime { .. } = par.kind {
allowed_lts.insert(RefLt::Named(par.def_id));
}
}
allowed_lts.insert(RefLt::Unnamed);
allowed_lts.insert(RefLt::Static);
allowed_lts
fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> {
named_generics
.iter()
.filter_map(|par| {
if let GenericParamKind::Lifetime { .. } = par.kind {
Some(par.def_id)
} else {
None
}
})
.collect()
}
/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
/// relative order.
#[must_use]
fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
fn named_lifetime_occurrences(lts: &[Lifetime]) -> Vec<(LocalDefId, usize)> {
let mut occurrences = Vec::new();
for lt in lts {
if let &RefLt::Named(curr_def_id) = lt {
if let Some(curr_def_id) = named_lifetime(lt) {
if let Some(pair) = occurrences
.iter_mut()
.find(|(prev_def_id, _)| *prev_def_id == curr_def_id)
@ -391,12 +457,10 @@ fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
occurrences
}
/// A visitor usable for `rustc_front::visit::walk_ty()`.
struct RefVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
lts: Vec<RefLt>,
lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>,
nested_elision_site_lts: Vec<RefLt>,
lts: Vec<Lifetime>,
nested_elision_site_lts: Vec<Lifetime>,
unelided_trait_object_lifetime: bool,
}
@ -405,32 +469,16 @@ fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
cx,
lts: Vec::new(),
lifetime_generic_arg_spans: FxHashMap::default(),
nested_elision_site_lts: Vec::new(),
unelided_trait_object_lifetime: false,
}
}
fn record(&mut self, lifetime: &Option<Lifetime>) {
if let Some(ref lt) = *lifetime {
if lt.is_static() {
self.lts.push(RefLt::Static);
} else if lt.is_anonymous() {
// Fresh lifetimes generated should be ignored.
self.lts.push(RefLt::Unnamed);
} else if let LifetimeName::Param(def_id) = lt.res {
self.lts.push(RefLt::Named(def_id));
}
} else {
self.lts.push(RefLt::Unnamed);
}
}
fn all_lts(&self) -> Vec<RefLt> {
fn all_lts(&self) -> Vec<Lifetime> {
self.lts
.iter()
.chain(self.nested_elision_site_lts.iter())
.cloned()
.copied()
.collect::<Vec<_>>()
}
@ -442,7 +490,7 @@ fn abort(&self) -> bool {
impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
// for lifetimes as parameters of generics
fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
self.record(&Some(*lifetime));
self.lts.push(*lifetime);
}
fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
@ -467,11 +515,7 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
walk_item(self, item);
self.lts.truncate(len);
self.lts.extend(bounds.iter().filter_map(|bound| match bound {
GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res {
RefLt::Named(def_id)
} else {
RefLt::Unnamed
}),
GenericArg::Lifetime(&l) => Some(l),
_ => None,
}));
},
@ -491,13 +535,6 @@ fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
_ => walk_ty(self, ty),
}
}
fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'tcx>) {
if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res {
self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span);
}
walk_generic_arg(self, generic_arg);
}
}
/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
@ -521,8 +558,12 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
walk_param_bound(&mut visitor, bound);
}
// and check that all lifetimes are allowed
if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
return true;
for lt in visitor.all_lts() {
if let Some(id) = named_lifetime(&lt)
&& !allowed_lts.contains(&id)
{
return true;
}
}
},
WherePredicate::EqPredicate(ref pred) => {

View file

@ -61,7 +61,8 @@
///
/// ### Why is this bad?
/// Just iterating the collection itself makes the intent
/// more clear and is probably faster.
/// more clear and is probably faster because it eliminates
/// the bounds check that is done when indexing.
///
/// ### Example
/// ```rust

View file

@ -149,7 +149,7 @@ pub(super) fn check<'tcx>(
|diag| {
multispan_sugg(
diag,
"consider using an iterator",
"consider using an iterator and enumerate()",
vec![
(pat.span, format!("({}, <item>)", ident.name)),
(

View file

@ -1,7 +1,6 @@
use crate::rustc_lint::LintContext;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::macros::root_macro_call;
use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, UnOp};
@ -38,57 +37,57 @@
impl<'tcx> LateLintPass<'tcx> for ManualAssert {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if_chain! {
if let ExprKind::If(cond, then, None) = expr.kind;
if !matches!(cond.kind, ExprKind::Let(_));
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);
if let ExprKind::If(cond, then, None) = expr.kind
&& !matches!(cond.kind, ExprKind::Let(_))
&& !expr.span.from_expansion()
&& let then = peel_blocks_with_stmt(then)
&& let Some(macro_call) = root_macro_call(then.span)
&& cx.tcx.item_name(macro_call.def_id) == sym::panic
&& !cx.tcx.sess.source_map().is_multiline(cond.span)
&& let Ok(panic_snippet) = cx.sess().source_map().span_to_snippet(macro_call.span)
&& let Some(panic_snippet) = panic_snippet.strip_suffix(')')
&& let Some((_, format_args_snip)) = panic_snippet.split_once('(')
// Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just
// shuffles the condition around.
// Should this have a config value?
if !is_else_clause(cx.tcx, expr);
then {
let mut applicability = Applicability::MachineApplicable;
let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
let cond = cond.peel_drop_temps();
let mut comments = span_extract_comment(cx.sess().source_map(), expr.span);
if !comments.is_empty() {
comments += "\n";
}
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});");
// we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block
span_lint_and_then(
cx,
MANUAL_ASSERT,
expr.span,
"only a `panic!` in `if`-then statement",
|diag| {
// comments can be noisy, do not show them to the user
if !comments.is_empty() {
diag.tool_only_span_suggestion(
expr.span.shrink_to_lo(),
"add comments back",
comments,
applicability);
}
diag.span_suggestion(
expr.span,
"try instead",
sugg,
applicability);
}
);
&& !is_else_clause(cx.tcx, expr)
{
let mut applicability = Applicability::MachineApplicable;
let cond = cond.peel_drop_temps();
let mut comments = span_extract_comment(cx.sess().source_map(), expr.span);
if !comments.is_empty() {
comments += "\n";
}
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});");
// we show to the user the suggestion without the comments, but when applicating the fix, include the comments in the block
span_lint_and_then(
cx,
MANUAL_ASSERT,
expr.span,
"only a `panic!` in `if`-then statement",
|diag| {
// comments can be noisy, do not show them to the user
if !comments.is_empty() {
diag.tool_only_span_suggestion(
expr.span.shrink_to_lo(),
"add comments back",
comments,
applicability
);
}
diag.span_suggestion(
expr.span,
"try instead",
sugg,
applicability
);
}
);
}
}
}

View file

@ -45,8 +45,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
// Accumulate the variants which should be put in place of the wildcard because they're not
// already covered.
let has_hidden = adt_def.variants().iter().any(|x| is_hidden(cx, x));
let mut missing_variants: Vec<_> = adt_def.variants().iter().filter(|x| !is_hidden(cx, x)).collect();
let is_external = adt_def.did().as_local().is_none();
let has_external_hidden = is_external && adt_def.variants().iter().any(|x| is_hidden(cx, x));
let mut missing_variants: Vec<_> = adt_def
.variants()
.iter()
.filter(|x| !(is_external && is_hidden(cx, x)))
.collect();
let mut path_prefix = CommonPrefixSearcher::None;
for arm in arms {
@ -133,7 +138,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
match missing_variants.as_slice() {
[] => (),
[x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
[x] if !adt_def.is_variant_list_non_exhaustive() && !has_external_hidden => span_lint_and_sugg(
cx,
MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
wildcard_span,
@ -144,7 +149,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
),
variants => {
let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden {
suggestions.push("_".into());
"wildcard matches known variants and will also match future added variants"
} else {

View file

@ -1818,6 +1818,7 @@
/// - `or_else` to `or`
/// - `get_or_insert_with` to `get_or_insert`
/// - `ok_or_else` to `ok_or`
/// - `then` to `then_some` (for msrv >= 1.62.0)
///
/// ### Why is this bad?
/// Using eager evaluation is shorter and simpler in some cases.

View file

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_diag_trait_item;
use clippy_utils::source::snippet_with_context;
use if_chain::if_chain;
@ -17,19 +17,31 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -
let input_type = cx.typeck_results().expr_ty(expr);
if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
then {
let mut app = Applicability::MaybeIncorrect;
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg(
span_lint_and_then(
cx,
SUSPICIOUS_TO_OWNED,
expr.span,
&with_forced_trimmed_paths!(format!(
"this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"
)),
"consider using, depending on intent",
format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"),
app,
|diag| {
diag.span_suggestion(
expr.span,
"depending on intent, either make the Cow an Owned variant",
format!("{recv_snip}.into_owned()"),
app
);
diag.span_suggestion(
expr.span,
"or clone the Cow itself",
format!("{recv_snip}.clone()"),
app
);
}
);
return true;
}

View file

@ -10,6 +10,7 @@
use rustc_ast::Mutability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
@ -66,7 +67,7 @@
impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) || in_external_macro(cx.tcx.sess, block.span) {
return;
}
let mut unsafe_ops = vec![];

View file

@ -25,11 +25,11 @@
/// Using the dedicated functions of the `Option` type is clearer and
/// more concise than an `if let` expression.
///
/// ### Known problems
/// This lint uses a deliberately conservative metric for checking
/// if the inside of either body contains breaks or continues which will
/// cause it to not suggest a fix if either block contains a loop with
/// continues or breaks contained within the loop.
/// ### Notes
/// This lint uses a deliberately conservative metric for checking if the
/// inside of either body contains loop control expressions `break` or
/// `continue` (which cannot be used within closures). If these are found,
/// this lint will not be raised.
///
/// ### Example
/// ```rust

View file

@ -624,7 +624,10 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
return;
};
match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i].peel_refs().kind() {
match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i]
.peel_refs()
.kind()
{
ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
set_skip_flag();
},

View file

@ -1,5 +1,8 @@
use std::fmt::Display;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::source::snippet_opt;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::{LitKind, StrStyle};
@ -77,13 +80,45 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
}
}
#[must_use]
fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span {
let offset = u32::from(offset);
let end = base.lo() + BytePos(u32::try_from(c.end.offset).expect("offset too large") + offset);
let start = base.lo() + BytePos(u32::try_from(c.start.offset).expect("offset too large") + offset);
assert!(start <= end);
Span::new(start, end, base.ctxt(), base.parent())
fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescaped: &str, base: Span, offset: u8) {
let parts: Option<(_, _, &dyn Display)> = match &error {
regex_syntax::Error::Parse(e) => Some((e.span(), e.auxiliary_span(), e.kind())),
regex_syntax::Error::Translate(e) => Some((e.span(), None, e.kind())),
_ => None,
};
let convert_span = |regex_span: &regex_syntax::ast::Span| {
let offset = u32::from(offset);
let start = base.lo() + BytePos(u32::try_from(regex_span.start.offset).expect("offset too large") + offset);
let end = base.lo() + BytePos(u32::try_from(regex_span.end.offset).expect("offset too large") + offset);
Span::new(start, end, base.ctxt(), base.parent())
};
if let Some((primary, auxiliary, kind)) = parts
&& let Some(literal_snippet) = snippet_opt(cx, base)
&& let Some(inner) = literal_snippet.get(offset as usize..)
// Only convert to native rustc spans if the parsed regex matches the
// source snippet exactly, to ensure the span offsets are correct
&& inner.get(..unescaped.len()) == Some(unescaped)
{
let spans = if let Some(auxiliary) = auxiliary {
vec![convert_span(primary), convert_span(auxiliary)]
} else {
vec![convert_span(primary)]
};
span_lint(cx, INVALID_REGEX, spans, &format!("regex syntax error: {kind}"));
} else {
span_lint_and_help(
cx,
INVALID_REGEX,
base,
&error.to_string(),
None,
"consider using a raw string literal: `r\"..\"`",
);
}
}
fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
@ -155,25 +190,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
}
},
Err(regex_syntax::Error::Parse(e)) => {
span_lint(
cx,
INVALID_REGEX,
str_span(expr.span, *e.span(), offset),
&format!("regex syntax error: {}", e.kind()),
);
},
Err(regex_syntax::Error::Translate(e)) => {
span_lint(
cx,
INVALID_REGEX,
str_span(expr.span, *e.span(), offset),
&format!("regex syntax error: {}", e.kind()),
);
},
Err(e) => {
span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}"));
},
Err(e) => lint_syntax_error(cx, &e, r, expr.span, offset),
}
}
} else if let Some(r) = const_str(cx, expr) {
@ -183,25 +200,7 @@ fn check_regex<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, utf8: bool) {
span_lint_and_help(cx, TRIVIAL_REGEX, expr.span, "trivial regex", None, repl);
}
},
Err(regex_syntax::Error::Parse(e)) => {
span_lint(
cx,
INVALID_REGEX,
expr.span,
&format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
);
},
Err(regex_syntax::Error::Translate(e)) => {
span_lint(
cx,
INVALID_REGEX,
expr.span,
&format!("regex syntax error on position {}: {}", e.span().start.offset, e.kind()),
);
},
Err(e) => {
span_lint(cx, INVALID_REGEX, expr.span, &format!("regex syntax error: {e}"));
},
Err(e) => span_lint(cx, INVALID_REGEX, expr.span, &e.to_string()),
}
}
}

View file

@ -78,8 +78,8 @@ fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, spa
// We don't want to emit this lint if the `#[must_use]` attribute is already there.
if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use));
if cx.tcx.visibility(fn_def.to_def_id()).is_public();
let ret_ty = return_ty(cx, owner_id.into());
let self_arg = nth_arg(cx, owner_id.into(), 0);
let ret_ty = return_ty(cx, owner_id);
let self_arg = nth_arg(cx, owner_id, 0);
// If `Self` has the same type as the returned type, then we want to warn.
//
// For this check, we don't want to remove the reference on the returned type because if

View file

@ -30,7 +30,7 @@
/// # let x = 0;
/// unsafe { f(x); }
/// ```
#[clippy::version = "1.66.0"]
#[clippy::version = "1.68.0"]
pub SEMICOLON_INSIDE_BLOCK,
restriction,
"add a semicolon inside the block"
@ -59,7 +59,7 @@
/// # let x = 0;
/// unsafe { f(x) };
/// ```
#[clippy::version = "1.66.0"]
#[clippy::version = "1.68.0"]
pub SEMICOLON_OUTSIDE_BLOCK,
restriction,
"add a semicolon outside the block"

View file

@ -392,9 +392,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
}
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
let is_exported = cx
.effective_visibilities
.is_exported(field.def_id);
let is_exported = cx.effective_visibilities.is_exported(field.def_id);
self.check_ty(
cx,

View file

@ -65,7 +65,7 @@ fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
}
},
hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() {
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" | "is_ok" | "is_err" => {
check_map_error(cx, arg_0, expr);
},
_ => (),

View file

@ -253,7 +253,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION.
/// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN.
///
/// The minimum rust version that the project supports
(msrv: Option<String> = None),
@ -323,7 +323,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
///
/// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
(trivial_copy_size_limit: Option<u64> = None),
/// Lint: LARGE_TYPE_PASS_BY_MOVE.
/// Lint: LARGE_TYPES_PASSED_BY_VALUE.
///
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
(pass_by_value_size_limit: u64 = 256),
@ -411,7 +411,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
/// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
/// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
(max_suggested_slice_pattern_length: u64 = 3),
/// Lint: AWAIT_HOLDING_INVALID_TYPE
/// Lint: AWAIT_HOLDING_INVALID_TYPE.
(await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
/// Lint: LARGE_INCLUDE_FILE.
///
@ -437,7 +437,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
///
/// The maximum size of the `Err`-variant in a `Result` returned from a function
(large_error_threshold: u64 = 128),
/// Lint: MUTABLE_KEY.
/// Lint: MUTABLE_KEY_TYPE.
///
/// A list of paths to types that should be treated like `Arc`, i.e. ignored but
/// for the generic parameters for determining interior mutability
@ -446,7 +446,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
///
/// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
(allow_mixed_uninlined_format_args: bool = true),
/// Lint: INDEXING_SLICING
/// Lint: INDEXING_SLICING.
///
/// Whether to suppress a restriction lint in constant code. In same
/// cases the restructured operation might not be unavoidable, as the

View file

@ -1,4 +1,5 @@
use clippy_utils::get_attr;
use hir::TraitItem;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -47,6 +48,18 @@ fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
println!("{stmt:#?}");
}
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
if has_attr(cx, item.hir_id()) {
println!("{item:#?}");
}
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
if has_attr(cx, item.hir_id()) {
println!("{item:#?}");
}
}
}
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {

View file

@ -215,14 +215,13 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
cx,
};
let body_id = cx.tcx.hir().body_owned_by(
cx.tcx.hir().local_def_id(
impl_item_refs
.iter()
.find(|iiref| iiref.ident.as_str() == "get_lints")
.expect("LintPass needs to implement get_lints")
.id
.hir_id(),
),
impl_item_refs
.iter()
.find(|iiref| iiref.ident.as_str() == "get_lints")
.expect("LintPass needs to implement get_lints")
.id
.owner_id
.def_id,
);
collector.visit_expr(cx.tcx.hir().body(body_id).value);
}

View file

@ -770,10 +770,7 @@ pub fn is_default(&self) -> bool {
/// Has no other formatting specifiers than setting the format trait. returns true for `{}`,
/// `{foo}`, `{:?}`, but false for `{foo:5}`, `{3:.5?}`
pub fn is_default_for_trait(&self) -> bool {
self.width.is_implied()
&& self.precision.is_implied()
&& self.align == Alignment::AlignUnknown
&& self.no_flags
self.width.is_implied() && self.precision.is_implied() && self.align == Alignment::AlignUnknown && self.no_flags
}
}

View file

@ -809,7 +809,10 @@ pub struct DerefClosure {
///
/// note: this only works on single line immutable closures with exactly one input parameter.
pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> {
if let hir::ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind {
if let hir::ExprKind::Closure(&Closure {
fn_decl, def_id, body, ..
}) = closure.kind
{
let closure_body = cx.tcx.hir().body(body);
// is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
// a type annotation is present if param `kind` is different from `TyKind::Infer`

View file

@ -392,12 +392,16 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
.cx
.typeck_results()
.type_dependent_def_id(e.hir_id)
.map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe) =>
.map_or(false, |id| {
self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe
}) =>
{
self.is_unsafe = true;
},
ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => {
self.is_unsafe = true;
},
ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
_ => walk_expr(self, e),
},

View file

@ -10,8 +10,8 @@ edition = "2021"
publish = false
[dependencies]
cargo_metadata = "0.14"
clap = "3.2"
cargo_metadata = "0.15.3"
clap = "4.1.4"
crossbeam-channel = "0.5.6"
flate2 = "1.0"
rayon = "1.5.1"

View file

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-01-27"
channel = "nightly-2023-02-10"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View file

@ -1,12 +1,3 @@
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::Deref`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
@ -14,6 +5,15 @@ LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref",
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::deref_method`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:10:36
|
LL | const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: convert all references to use `sym::Deref`
error: hardcoded path to a language item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40

View file

@ -149,3 +149,22 @@ macro_rules! almost_complete_range {
let _ = '0'..'9';
};
}
#[macro_export]
macro_rules! unsafe_macro {
() => {
unsafe {
*core::ptr::null::<()>();
*core::ptr::null::<()>();
}
};
}
#[macro_export]
macro_rules! needless_lifetime {
() => {
fn needless_lifetime<'a>(x: &'a u8) -> &'a u8 {
unimplemented!()
}
};
}

View file

@ -86,7 +86,7 @@ fn main() {
let b = ImplNotTraitWithBool;
assert_eq!("a".len(), 1);
assert!("a".is_empty());
assert!(!"a".is_empty());
assert!("".is_empty());
assert!("".is_empty());
assert_eq!(a!(), b!());
@ -97,16 +97,16 @@ fn main() {
assert_ne!("a".len(), 1);
assert!("a".is_empty());
assert!("".is_empty());
assert!("".is_empty());
assert!(!"".is_empty());
assert!(!"".is_empty());
assert_ne!(a!(), b!());
assert_ne!(a!(), "".is_empty());
assert_ne!("".is_empty(), b!());
assert_ne!(a, true);
assert!(b);
assert!(!b);
debug_assert_eq!("a".len(), 1);
debug_assert!("a".is_empty());
debug_assert!(!"a".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert_eq!(a!(), b!());
@ -117,27 +117,27 @@ fn main() {
debug_assert_ne!("a".len(), 1);
debug_assert!("a".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert!(!"".is_empty());
debug_assert!(!"".is_empty());
debug_assert_ne!(a!(), b!());
debug_assert_ne!(a!(), "".is_empty());
debug_assert_ne!("".is_empty(), b!());
debug_assert_ne!(a, true);
debug_assert!(b);
debug_assert!(!b);
// assert with error messages
assert_eq!("a".len(), 1, "tadam {}", 1);
assert_eq!("a".len(), 1, "tadam {}", true);
assert!("a".is_empty(), "tadam {}", 1);
assert!("a".is_empty(), "tadam {}", true);
assert!("a".is_empty(), "tadam {}", true);
assert!(!"a".is_empty(), "tadam {}", 1);
assert!(!"a".is_empty(), "tadam {}", true);
assert!(!"a".is_empty(), "tadam {}", true);
assert_eq!(a, true, "tadam {}", false);
debug_assert_eq!("a".len(), 1, "tadam {}", 1);
debug_assert_eq!("a".len(), 1, "tadam {}", true);
debug_assert!("a".is_empty(), "tadam {}", 1);
debug_assert!("a".is_empty(), "tadam {}", true);
debug_assert!("a".is_empty(), "tadam {}", true);
debug_assert!(!"a".is_empty(), "tadam {}", 1);
debug_assert!(!"a".is_empty(), "tadam {}", true);
debug_assert!(!"a".is_empty(), "tadam {}", true);
debug_assert_eq!(a, true, "tadam {}", false);
assert!(a!());
@ -158,4 +158,14 @@ fn main() {
}};
}
in_macro!(a);
assert!("".is_empty());
assert!("".is_empty());
assert!(!"requires negation".is_empty());
assert!(!"requires negation".is_empty());
debug_assert!("".is_empty());
debug_assert!("".is_empty());
debug_assert!(!"requires negation".is_empty());
debug_assert!(!"requires negation".is_empty());
}

View file

@ -158,4 +158,14 @@ macro_rules! in_macro {
}};
}
in_macro!(a);
assert_eq!("".is_empty(), true);
assert_ne!("".is_empty(), false);
assert_ne!("requires negation".is_empty(), true);
assert_eq!("requires negation".is_empty(), false);
debug_assert_eq!("".is_empty(), true);
debug_assert_ne!("".is_empty(), false);
debug_assert_ne!("requires negation".is_empty(), true);
debug_assert_eq!("requires negation".is_empty(), false);
}

View file

@ -8,7 +8,7 @@ LL | assert_eq!("a".is_empty(), false);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false);
LL + assert!("a".is_empty());
LL + assert!(!"a".is_empty());
|
error: used `assert_eq!` with a literal bool
@ -68,7 +68,7 @@ LL | assert_ne!("".is_empty(), true);
help: replace it with `assert!(..)`
|
LL - assert_ne!("".is_empty(), true);
LL + assert!("".is_empty());
LL + assert!(!"".is_empty());
|
error: used `assert_ne!` with a literal bool
@ -80,7 +80,7 @@ LL | assert_ne!(true, "".is_empty());
help: replace it with `assert!(..)`
|
LL - assert_ne!(true, "".is_empty());
LL + assert!("".is_empty());
LL + assert!(!"".is_empty());
|
error: used `assert_ne!` with a literal bool
@ -92,7 +92,7 @@ LL | assert_ne!(b, true);
help: replace it with `assert!(..)`
|
LL - assert_ne!(b, true);
LL + assert!(b);
LL + assert!(!b);
|
error: used `debug_assert_eq!` with a literal bool
@ -104,7 +104,7 @@ LL | debug_assert_eq!("a".is_empty(), false);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false);
LL + debug_assert!("a".is_empty());
LL + debug_assert!(!"a".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
@ -164,7 +164,7 @@ LL | debug_assert_ne!("".is_empty(), true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("".is_empty(), true);
LL + debug_assert!("".is_empty());
LL + debug_assert!(!"".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
@ -176,7 +176,7 @@ LL | debug_assert_ne!(true, "".is_empty());
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!(true, "".is_empty());
LL + debug_assert!("".is_empty());
LL + debug_assert!(!"".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
@ -188,7 +188,7 @@ LL | debug_assert_ne!(b, true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!(b, true);
LL + debug_assert!(b);
LL + debug_assert!(!b);
|
error: used `assert_eq!` with a literal bool
@ -200,7 +200,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false, "tadam {}", 1);
LL + assert!("a".is_empty(), "tadam {}", 1);
LL + assert!(!"a".is_empty(), "tadam {}", 1);
|
error: used `assert_eq!` with a literal bool
@ -212,7 +212,7 @@ LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
help: replace it with `assert!(..)`
|
LL - assert_eq!("a".is_empty(), false, "tadam {}", true);
LL + assert!("a".is_empty(), "tadam {}", true);
LL + assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `assert_eq!` with a literal bool
@ -224,7 +224,7 @@ LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
help: replace it with `assert!(..)`
|
LL - assert_eq!(false, "a".is_empty(), "tadam {}", true);
LL + assert!("a".is_empty(), "tadam {}", true);
LL + assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `debug_assert_eq!` with a literal bool
@ -236,7 +236,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
LL + debug_assert!("a".is_empty(), "tadam {}", 1);
LL + debug_assert!(!"a".is_empty(), "tadam {}", 1);
|
error: used `debug_assert_eq!` with a literal bool
@ -248,7 +248,7 @@ LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
LL + debug_assert!("a".is_empty(), "tadam {}", true);
LL + debug_assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `debug_assert_eq!` with a literal bool
@ -260,7 +260,7 @@ LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
LL + debug_assert!("a".is_empty(), "tadam {}", true);
LL + debug_assert!(!"a".is_empty(), "tadam {}", true);
|
error: used `assert_eq!` with a literal bool
@ -299,5 +299,101 @@ LL - renamed!(b, true);
LL + debug_assert!(b);
|
error: aborting due to 25 previous errors
error: used `assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:162:5
|
LL | assert_eq!("".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!("".is_empty(), true);
LL + assert!("".is_empty());
|
error: used `assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:163:5
|
LL | assert_ne!("".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_ne!("".is_empty(), false);
LL + assert!("".is_empty());
|
error: used `assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:164:5
|
LL | assert_ne!("requires negation".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_ne!("requires negation".is_empty(), true);
LL + assert!(!"requires negation".is_empty());
|
error: used `assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:165:5
|
LL | assert_eq!("requires negation".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `assert!(..)`
|
LL - assert_eq!("requires negation".is_empty(), false);
LL + assert!(!"requires negation".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:167:5
|
LL | debug_assert_eq!("".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("".is_empty(), true);
LL + debug_assert!("".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:168:5
|
LL | debug_assert_ne!("".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("".is_empty(), false);
LL + debug_assert!("".is_empty());
|
error: used `debug_assert_ne!` with a literal bool
--> $DIR/bool_assert_comparison.rs:169:5
|
LL | debug_assert_ne!("requires negation".is_empty(), true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_ne!("requires negation".is_empty(), true);
LL + debug_assert!(!"requires negation".is_empty());
|
error: used `debug_assert_eq!` with a literal bool
--> $DIR/bool_assert_comparison.rs:170:5
|
LL | debug_assert_eq!("requires negation".is_empty(), false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace it with `debug_assert!(..)`
|
LL - debug_assert_eq!("requires negation".is_empty(), false);
LL + debug_assert!(!"requires negation".is_empty());
|
error: aborting due to 33 previous errors

View file

@ -5,6 +5,11 @@ LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
help: elide the lifetimes
|
LL - pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
LL + pub fn add_barfoos_to_foos(bars: &HashSet<&Bar>) {
|
error: aborting due to previous error

View file

@ -9,6 +9,11 @@ note: the lint level is defined here
|
LL | #![deny(clippy::needless_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: elide the lifetimes
|
LL - fn baz<'a>(&'a self) -> impl Foo + 'a {
LL + fn baz(&self) -> impl Foo + '_ {
|
error: aborting due to previous error

View file

@ -0,0 +1,69 @@
#![allow(unused, clippy::needless_lifetimes)]
#![warn(clippy::extra_unused_type_parameters)]
fn unused_ty<T>(x: u8) {}
fn unused_multi<T, U>(x: u8) {}
fn unused_with_lt<'a, T>(x: &'a u8) {}
fn used_ty<T>(x: T, y: u8) {}
fn used_ref<'a, T>(x: &'a T) {}
fn used_ret<T: Default>(x: u8) -> T {
T::default()
}
fn unused_bounded<T: Default, U>(x: U) {}
fn unused_where_clause<T, U>(x: U)
where
T: Default,
{
}
fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
fn used_opaque<A>(iter: impl Iterator<Item = A>) -> usize {
iter.count()
}
fn used_ret_opaque<A>() -> impl Iterator<Item = A> {
std::iter::empty()
}
fn used_vec_box<T>(x: Vec<Box<T>>) {}
fn used_body<T: Default + ToString>() -> String {
T::default().to_string()
}
fn used_closure<T: Default + ToString>() -> impl Fn() {
|| println!("{}", T::default().to_string())
}
struct S;
impl S {
fn unused_ty_impl<T>(&self) {}
}
// Don't lint on trait methods
trait Foo {
fn bar<T>(&self);
}
impl Foo for S {
fn bar<T>(&self) {}
}
fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
where
Iter: Iterator<Item = A>,
{
iter.enumerate()
.filter_map(move |(i, a)| if i == index { None } else { Some(a) })
}
fn main() {}

View file

@ -0,0 +1,59 @@
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:4:13
|
LL | fn unused_ty<T>(x: u8) {}
| ^^^
|
= help: consider removing the parameter
= note: `-D clippy::extra-unused-type-parameters` implied by `-D warnings`
error: type parameters go unused in function definition
--> $DIR/extra_unused_type_parameters.rs:6:16
|
LL | fn unused_multi<T, U>(x: u8) {}
| ^^^^^^
|
= help: consider removing the parameters
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:8:23
|
LL | fn unused_with_lt<'a, T>(x: &'a u8) {}
| ^
|
= help: consider removing the parameter
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:18:19
|
LL | fn unused_bounded<T: Default, U>(x: U) {}
| ^^^^^^^^^^^
|
= help: consider removing the parameter
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:20:24
|
LL | fn unused_where_clause<T, U>(x: U)
| ^^
|
= help: consider removing the parameter
error: type parameters go unused in function definition
--> $DIR/extra_unused_type_parameters.rs:26:16
|
LL | fn some_unused<A, B, C, D: Iterator<Item = (B, C)>, E>(b: B, c: C) {}
| ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^
|
= help: consider removing the parameters
error: type parameter goes unused in function definition
--> $DIR/extra_unused_type_parameters.rs:49:22
|
LL | fn unused_ty_impl<T>(&self) {}
| ^^^
|
= help: consider removing the parameter
error: aborting due to 7 previous errors

View file

@ -282,4 +282,50 @@ pub async fn is_empty(&self) -> bool {
}
}
// issue #9520
pub struct NonStandardLenAndIsEmptySignature;
impl NonStandardLenAndIsEmptySignature {
// don't lint
pub fn len(&self, something: usize) -> usize {
something
}
pub fn is_empty(&self, something: usize) -> bool {
something == 0
}
}
// test case for #9520 with generics in the function signature
pub trait TestResource {
type NonStandardSignatureWithGenerics: Copy;
fn lookup_content(&self, item: Self::NonStandardSignatureWithGenerics) -> Result<Option<&[u8]>, String>;
}
pub struct NonStandardSignatureWithGenerics(u32);
impl NonStandardSignatureWithGenerics {
pub fn is_empty<T, U>(self, resource: &T) -> bool
where
T: TestResource<NonStandardSignatureWithGenerics = U>,
U: Copy + From<NonStandardSignatureWithGenerics>,
{
if let Ok(Some(content)) = resource.lookup_content(self.into()) {
content.is_empty()
} else {
true
}
}
// test case for #9520 with generics in the function signature
pub fn len<T, U>(self, resource: &T) -> usize
where
T: TestResource<NonStandardSignatureWithGenerics = U>,
U: Copy + From<NonStandardSignatureWithGenerics>,
{
if let Ok(Some(content)) = resource.lookup_content(self.into()) {
content.len()
} else {
0_usize
}
}
}
fn main() {}

View file

@ -29,9 +29,7 @@ fn main() {
panic!("qaqaq{:?}", a);
}
assert!(a.is_empty(), "qaqaq{:?}", a);
if !a.is_empty() {
panic!("qwqwq");
}
assert!(a.is_empty(), "qwqwq");
if a.len() == 3 {
println!("qwq");
println!("qwq");
@ -46,21 +44,11 @@ fn main() {
println!("qwq");
}
let b = vec![1, 2, 3];
if b.is_empty() {
panic!("panic1");
}
if b.is_empty() && a.is_empty() {
panic!("panic2");
}
if a.is_empty() && !b.is_empty() {
panic!("panic3");
}
if b.is_empty() || a.is_empty() {
panic!("panic4");
}
if a.is_empty() || !b.is_empty() {
panic!("panic5");
}
assert!(!b.is_empty(), "panic1");
assert!(!(b.is_empty() && a.is_empty()), "panic2");
assert!(!(a.is_empty() && !b.is_empty()), "panic3");
assert!(!(b.is_empty() || a.is_empty()), "panic4");
assert!(!(a.is_empty() || !b.is_empty()), "panic5");
assert!(!a.is_empty(), "with expansion {}", one!());
if a.is_empty() {
let _ = 0;
@ -71,12 +59,11 @@ fn main() {
fn issue7730(a: u8) {
// Suggestion should preserve comment
if a > 2 {
// comment
/* this is a
// comment
/* this is a
multiline
comment */
/// Doc comment
panic!("panic with comment") // comment after `panic!`
}
/// Doc comment
// comment after `panic!`
assert!(!(a > 2), "panic with comment");
}

View file

@ -8,6 +8,54 @@ LL | | }
|
= note: `-D clippy::manual-assert` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:34:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
LL | | }
| |_____^ help: try instead: `assert!(a.is_empty(), "qwqwq");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:51:5
|
LL | / if b.is_empty() {
LL | | panic!("panic1");
LL | | }
| |_____^ help: try instead: `assert!(!b.is_empty(), "panic1");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:54:5
|
LL | / if b.is_empty() && a.is_empty() {
LL | | panic!("panic2");
LL | | }
| |_____^ help: try instead: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:57:5
|
LL | / if a.is_empty() && !b.is_empty() {
LL | | panic!("panic3");
LL | | }
| |_____^ help: try instead: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:60:5
|
LL | / if b.is_empty() || a.is_empty() {
LL | | panic!("panic4");
LL | | }
| |_____^ help: try instead: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:63:5
|
LL | / if a.is_empty() || !b.is_empty() {
LL | | panic!("panic5");
LL | | }
| |_____^ help: try instead: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:66:5
|
@ -16,5 +64,22 @@ LL | | panic!("with expansion {}", one!())
LL | | }
| |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());`
error: aborting due to 2 previous errors
error: only a `panic!` in `if`-then statement
--> $DIR/manual_assert.rs:78:5
|
LL | / if a > 2 {
LL | | // comment
LL | | /* this is a
LL | | multiline
... |
LL | | panic!("panic with comment") // comment after `panic!`
LL | | }
| |_____^
|
help: try instead
|
LL | assert!(!(a > 2), "panic with comment");
|
error: aborting due to 9 previous errors

View file

@ -123,7 +123,7 @@ fn main() {
Enum::A => (),
Enum::B => (),
Enum::C => (),
_ => (),
Enum::__Private => (),
}
match Enum::A {
Enum::A => (),

View file

@ -48,11 +48,17 @@ error: wildcard matches only a single variant and will also match any future add
LL | _ => (),
| ^ help: try this: `Color::Blue`
error: wildcard matches only a single variant and will also match any future added variants
--> $DIR/match_wildcard_for_single_variants.rs:126:13
|
LL | _ => (),
| ^ help: try this: `Enum::__Private`
error: wildcard matches only a single variant and will also match any future added variants
--> $DIR/match_wildcard_for_single_variants.rs:153:13
|
LL | _ => 2,
| ^ help: try this: `Foo::B`
error: aborting due to 9 previous errors
error: aborting due to 10 previous errors

View file

@ -1,9 +1,13 @@
// aux-build:macro_rules.rs
#![allow(unused)]
#![allow(deref_nullptr)]
#![allow(clippy::unnecessary_operation)]
#![allow(clippy::drop_copy)]
#![warn(clippy::multiple_unsafe_ops_per_block)]
#[macro_use]
extern crate macro_rules;
use core::arch::asm;
fn raw_ptr() -> *const () {
@ -107,4 +111,9 @@ unsafe fn read_char_good(ptr: *const u8) -> char {
unsafe { core::char::from_u32_unchecked(int_value) }
}
// no lint
fn issue10259() {
unsafe_macro!();
}
fn main() {}

View file

@ -1,5 +1,5 @@
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:32:5
--> $DIR/multiple_unsafe_ops_per_block.rs:36:5
|
LL | / unsafe {
LL | | STATIC += 1;
@ -8,19 +8,19 @@ LL | | }
| |_____^
|
note: modification of a mutable static occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:33:9
--> $DIR/multiple_unsafe_ops_per_block.rs:37:9
|
LL | STATIC += 1;
| ^^^^^^^^^^^
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:34:9
--> $DIR/multiple_unsafe_ops_per_block.rs:38:9
|
LL | not_very_safe();
| ^^^^^^^^^^^^^^^
= note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings`
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:41:5
--> $DIR/multiple_unsafe_ops_per_block.rs:45:5
|
LL | / unsafe {
LL | | drop(u.u);
@ -29,18 +29,18 @@ LL | | }
| |_____^
|
note: union field access occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:42:14
--> $DIR/multiple_unsafe_ops_per_block.rs:46:14
|
LL | drop(u.u);
| ^^^
note: raw pointer dereference occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:43:9
--> $DIR/multiple_unsafe_ops_per_block.rs:47:9
|
LL | *raw_ptr();
| ^^^^^^^^^^
error: this `unsafe` block contains 3 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:48:5
--> $DIR/multiple_unsafe_ops_per_block.rs:52:5
|
LL | / unsafe {
LL | | asm!("nop");
@ -50,23 +50,23 @@ LL | | }
| |_____^
|
note: inline assembly used here
--> $DIR/multiple_unsafe_ops_per_block.rs:49:9
--> $DIR/multiple_unsafe_ops_per_block.rs:53:9
|
LL | asm!("nop");
| ^^^^^^^^^^^
note: unsafe method call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:50:9
--> $DIR/multiple_unsafe_ops_per_block.rs:54:9
|
LL | sample.not_very_safe();
| ^^^^^^^^^^^^^^^^^^^^^^
note: modification of a mutable static occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:51:9
--> $DIR/multiple_unsafe_ops_per_block.rs:55:9
|
LL | STATIC = 0;
| ^^^^^^^^^^
error: this `unsafe` block contains 6 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:57:5
--> $DIR/multiple_unsafe_ops_per_block.rs:61:5
|
LL | / unsafe {
LL | | drop(u.u);
@ -78,49 +78,49 @@ LL | | }
| |_____^
|
note: union field access occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:58:14
--> $DIR/multiple_unsafe_ops_per_block.rs:62:14
|
LL | drop(u.u);
| ^^^
note: access of a mutable static occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:59:14
--> $DIR/multiple_unsafe_ops_per_block.rs:63:14
|
LL | drop(STATIC);
| ^^^^^^
note: unsafe method call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:60:9
--> $DIR/multiple_unsafe_ops_per_block.rs:64:9
|
LL | sample.not_very_safe();
| ^^^^^^^^^^^^^^^^^^^^^^
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:61:9
--> $DIR/multiple_unsafe_ops_per_block.rs:65:9
|
LL | not_very_safe();
| ^^^^^^^^^^^^^^^
note: raw pointer dereference occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:62:9
--> $DIR/multiple_unsafe_ops_per_block.rs:66:9
|
LL | *raw_ptr();
| ^^^^^^^^^^
note: inline assembly used here
--> $DIR/multiple_unsafe_ops_per_block.rs:63:9
--> $DIR/multiple_unsafe_ops_per_block.rs:67:9
|
LL | asm!("nop");
| ^^^^^^^^^^^
error: this `unsafe` block contains 2 unsafe operations, expected only one
--> $DIR/multiple_unsafe_ops_per_block.rs:101:5
--> $DIR/multiple_unsafe_ops_per_block.rs:105:5
|
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: unsafe function call occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:101:14
--> $DIR/multiple_unsafe_ops_per_block.rs:105:14
|
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: raw pointer dereference occurs here
--> $DIR/multiple_unsafe_ops_per_block.rs:101:39
--> $DIR/multiple_unsafe_ops_per_block.rs:105:39
|
LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
| ^^^^^^^^^^^^^^^^^^

View file

@ -0,0 +1,538 @@
// run-rustfix
// aux-build:macro_rules.rs
#![warn(clippy::needless_lifetimes)]
#![allow(
unused,
clippy::boxed_local,
clippy::extra_unused_type_parameters,
clippy::needless_pass_by_value,
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first
)]
#[macro_use]
extern crate macro_rules;
fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {}
// No error; same lifetime on two params.
fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
// No error; static involved.
fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
fn in_and_out(x: &u8, _y: u8) -> &u8 {
x
}
// No error; multiple input refs.
fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
x
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8
// ^^^
fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 {
x
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8
// ^^^
fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 {
y
}
// No error; multiple input refs
async fn func<'a>(args: &[&'a str]) -> Option<&'a str> {
args.get(0).cloned()
}
// No error; static involved.
fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
x
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()>
// ^^^
fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> {
Ok(x)
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()>
// ^^^
fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> {
Ok(y)
}
// No error; two input refs.
fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
x.unwrap()
}
fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> {
Ok(x)
}
// Where-clause, but without lifetimes.
fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()>
where
T: Copy,
{
Ok(x)
}
type Ref<'r> = &'r u8;
// No error; same lifetime on two params.
fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {}
// No error; bounded lifetime.
fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
// No error; bounded lifetime.
fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
where
'b: 'a,
{
}
struct Lt<'a, I: 'static> {
x: &'a I,
}
// No error; fn bound references `'a`.
fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
where
F: Fn(Lt<'a, I>) -> Lt<'a, I>,
{
unreachable!()
}
fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I>
where
for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
{
unreachable!()
}
// No error; see below.
fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
f(x);
}
fn fn_bound_3_cannot_elide() {
let x = 42;
let p = &x;
let mut q = &x;
// This will fail if we elide lifetimes of `fn_bound_3`.
fn_bound_3(p, |y| q = y);
}
// No error; multiple input refs.
fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
if cond { x } else { f() }
}
struct X {
x: u8,
}
impl X {
fn self_and_out(&self) -> &u8 {
&self.x
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8
// ^^^
fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 {
&self.x
}
// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
// fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8
// ^^^^^
fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 {
x
}
fn distinct_self_and_in(&self, _x: &u8) {}
// No error; same lifetimes on two params.
fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
}
struct Foo<'a>(&'a u8);
impl<'a> Foo<'a> {
// No error; lifetime `'a` not defined in method.
fn self_shared_lifetime(&self, _: &'a u8) {}
// No error; bounds exist.
fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
}
fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
unimplemented!()
}
fn struct_with_lt(_foo: Foo<'_>) -> &str {
unimplemented!()
}
// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
unimplemented!()
}
// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
unimplemented!()
}
// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
// valid:
// fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str
// ^^
fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str {
unimplemented!()
}
// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
// valid:
// fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str
// ^^^^
fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str {
unimplemented!()
}
trait WithLifetime<'a> {}
type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
// Should not warn because it won't build without the lifetime.
fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
unimplemented!()
}
// Should warn because there is no lifetime on `Drop`, so this would be
// unambiguous if we elided the lifetime.
fn trait_obj_elided2(_arg: &dyn Drop) -> &str {
unimplemented!()
}
type FooAlias<'a> = Foo<'a>;
fn alias_with_lt(_foo: FooAlias<'_>) -> &str {
unimplemented!()
}
// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
unimplemented!()
}
// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
unimplemented!()
}
// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
// valid:
// fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str
// ^^
fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str {
unimplemented!()
}
// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
// valid:
// fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str
// ^^^^^^^^^
fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str {
unimplemented!()
}
fn named_input_elided_output(_arg: &str) -> &str {
unimplemented!()
}
fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
unimplemented!()
}
fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) {
unimplemented!()
}
fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
unimplemented!()
}
// Don't warn on these; see issue #292.
fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
unimplemented!()
}
// See issue #740.
struct Test {
vec: Vec<usize>,
}
impl Test {
fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
unimplemented!()
}
}
trait LintContext<'a> {}
fn f<'a, T: LintContext<'a>>(_: &T) {}
fn test<'a>(x: &'a [u8]) -> u8 {
let y: &'a u8 = &x[5];
*y
}
// Issue #3284: give hint regarding lifetime in return type.
struct Cow<'a> {
x: &'a str,
}
fn out_return_type_lts(e: &str) -> Cow<'_> {
unimplemented!()
}
// Make sure we still warn on implementations
mod issue4291 {
trait BadTrait {
fn needless_lt(x: &u8) {}
}
impl BadTrait for () {
fn needless_lt(_x: &u8) {}
}
}
mod issue2944 {
trait Foo {}
struct Bar;
struct Baz<'a> {
bar: &'a Bar,
}
impl<'a> Foo for Baz<'a> {}
impl Bar {
fn baz(&self) -> impl Foo + '_ {
Baz { bar: self }
}
}
}
mod nested_elision_sites {
// issue #issue2944
// closure trait bounds subject to nested elision
// don't lint because they refer to outer lifetimes
fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
move || i
}
fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
move || i
}
// don't lint
fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
f()
}
fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
move || i
}
// lint
fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
f(i)
}
fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 {
f(i)
}
// don't lint
fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
f()
}
// lint
fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 {
f(i)
}
// don't lint
fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
where
T: Fn() -> &'a i32,
{
f()
}
// lint
fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32
where
T: Fn(&i32) -> &i32,
{
f(i)
}
// don't lint
fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
f(i)
}
fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
|i| i
}
// lint
fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 {
f(i)
}
// don't lint
fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
|f| ()
}
// lint
fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 {
|f| 42
}
fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) {
|f| ()
}
}
mod issue6159 {
use std::ops::Deref;
pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
where
T: Deref,
F: FnOnce(&'a T::Target) -> R,
{
f(x.deref())
}
}
mod issue7296 {
use std::rc::Rc;
use std::sync::Arc;
struct Foo;
impl Foo {
fn implicit(&self) -> &() {
&()
}
fn implicit_mut(&mut self) -> &() {
&()
}
fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
&()
}
fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() {
&()
}
}
trait Bar {
fn implicit(&self) -> &();
fn implicit_provided(&self) -> &() {
&()
}
fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
&()
}
fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &();
fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() {
&()
}
}
}
mod pr_9743_false_negative_fix {
#![allow(unused)]
fn foo(x: &u8, y: &'_ u8) {}
fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {}
}
mod pr_9743_output_lifetime_checks {
#![allow(unused)]
// lint: only one input
fn one_input(x: &u8) -> &u8 {
unimplemented!()
}
// lint: multiple inputs, output would not be elided
fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 {
unimplemented!()
}
// don't lint: multiple inputs, output would be elided (which would create an ambiguity)
fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 {
unimplemented!()
}
}
mod in_macro {
macro_rules! local_one_input_macro {
() => {
fn one_input(x: &u8) -> &u8 {
unimplemented!()
}
};
}
// lint local macro expands to function with needless lifetimes
local_one_input_macro!();
// no lint on external macro
macro_rules::needless_lifetime!();
}
mod issue5787 {
use std::sync::MutexGuard;
struct Foo;
impl Foo {
// doesn't get linted without async
pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
guard
}
}
async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str {
y
}
}
fn main() {}

View file

@ -1,13 +1,20 @@
// run-rustfix
// aux-build:macro_rules.rs
#![warn(clippy::needless_lifetimes)]
#![allow(
dead_code,
unused,
clippy::boxed_local,
clippy::extra_unused_type_parameters,
clippy::needless_pass_by_value,
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first
)]
#[macro_use]
extern crate macro_rules;
fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
@ -495,4 +502,37 @@ fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u
}
}
mod in_macro {
macro_rules! local_one_input_macro {
() => {
fn one_input<'a>(x: &'a u8) -> &'a u8 {
unimplemented!()
}
};
}
// lint local macro expands to function with needless lifetimes
local_one_input_macro!();
// no lint on external macro
macro_rules::needless_lifetime!();
}
mod issue5787 {
use std::sync::MutexGuard;
struct Foo;
impl Foo {
// doesn't get linted without async
pub async fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
guard
}
}
async fn foo<'a>(_x: &i32, y: &'a str) -> &'a str {
y
}
}
fn main() {}

View file

@ -1,316 +1,559 @@
error: the following explicit lifetimes could be elided: 'a, 'b
--> $DIR/needless_lifetimes.rs:11:1
--> $DIR/needless_lifetimes.rs:18:1
|
LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::needless-lifetimes` implied by `-D warnings`
help: elide the lifetimes
|
LL - fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {}
|
error: the following explicit lifetimes could be elided: 'a, 'b
--> $DIR/needless_lifetimes.rs:13:1
--> $DIR/needless_lifetimes.rs:20:1
|
LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:23:1
--> $DIR/needless_lifetimes.rs:30:1
|
LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
LL + fn in_and_out(x: &u8, _y: u8) -> &u8 {
|
error: the following explicit lifetimes could be elided: 'b
--> $DIR/needless_lifetimes.rs:35:1
--> $DIR/needless_lifetimes.rs:42:1
|
LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:42:1
--> $DIR/needless_lifetimes.rs:49:1
|
LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 {
|
error: the following explicit lifetimes could be elided: 'b
--> $DIR/needless_lifetimes.rs:59:1
--> $DIR/needless_lifetimes.rs:66:1
|
LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:66:1
--> $DIR/needless_lifetimes.rs:73:1
|
LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:75:1
--> $DIR/needless_lifetimes.rs:82:1
|
LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:80:1
--> $DIR/needless_lifetimes.rs:87:1
|
LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
LL + fn where_clause_without_lt<T>(x: &u8, _y: u8) -> Result<&u8, ()>
|
error: the following explicit lifetimes could be elided: 'a, 'b
--> $DIR/needless_lifetimes.rs:92:1
--> $DIR/needless_lifetimes.rs:99:1
|
LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:92:37
help: elide the lifetimes
|
LL - fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {}
|
LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
| ^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:116:1
--> $DIR/needless_lifetimes.rs:123:1
|
LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:116:32
help: elide the lifetimes
|
LL - fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
LL + fn fn_bound_2<F, I>(_m: Lt<'_, I>, _f: F) -> Lt<'_, I>
|
LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
| ^^
error: the following explicit lifetimes could be elided: 's
--> $DIR/needless_lifetimes.rs:146:5
--> $DIR/needless_lifetimes.rs:153:5
|
LL | fn self_and_out<'s>(&'s self) -> &'s u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn self_and_out<'s>(&'s self) -> &'s u8 {
LL + fn self_and_out(&self) -> &u8 {
|
error: the following explicit lifetimes could be elided: 't
--> $DIR/needless_lifetimes.rs:153:5
--> $DIR/needless_lifetimes.rs:160:5
|
LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 {
|
error: the following explicit lifetimes could be elided: 's
--> $DIR/needless_lifetimes.rs:160:5
--> $DIR/needless_lifetimes.rs:167:5
|
LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 {
|
error: the following explicit lifetimes could be elided: 's, 't
--> $DIR/needless_lifetimes.rs:164:5
--> $DIR/needless_lifetimes.rs:171:5
|
LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
LL + fn distinct_self_and_in(&self, _x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:183:1
--> $DIR/needless_lifetimes.rs:190:1
|
LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:183:33
help: elide the lifetimes
|
LL - fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
LL + fn struct_with_lt(_foo: Foo<'_>) -> &str {
|
LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
| ^^
error: the following explicit lifetimes could be elided: 'b
--> $DIR/needless_lifetimes.rs:201:1
--> $DIR/needless_lifetimes.rs:208:1
|
LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:201:43
help: elide the lifetimes
|
LL - fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str {
|
LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
| ^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:209:1
--> $DIR/needless_lifetimes.rs:216:1
|
LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:224:1
--> $DIR/needless_lifetimes.rs:231:1
|
LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:230:1
--> $DIR/needless_lifetimes.rs:237:1
|
LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:230:37
help: elide the lifetimes
|
LL - fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str {
|
LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
| ^^
error: the following explicit lifetimes could be elided: 'b
--> $DIR/needless_lifetimes.rs:248:1
--> $DIR/needless_lifetimes.rs:255:1
|
LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:248:47
help: elide the lifetimes
|
LL - fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str {
|
LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
| ^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:256:1
--> $DIR/needless_lifetimes.rs:263:1
|
LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:260:1
--> $DIR/needless_lifetimes.rs:267:1
|
LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
LL + fn named_input_elided_output(_arg: &str) -> &str {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:268:1
--> $DIR/needless_lifetimes.rs:275:1
|
LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
LL + fn trait_bound_ok<T: WithLifetime<'static>>(_: &u8, _: T) {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:304:1
--> $DIR/needless_lifetimes.rs:311:1
|
LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace with `'_` in generic arguments such as here
--> $DIR/needless_lifetimes.rs:304:47
help: elide the lifetimes
|
LL - fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
LL + fn out_return_type_lts(e: &str) -> Cow<'_> {
|
LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
| ^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:311:9
--> $DIR/needless_lifetimes.rs:318:9
|
LL | fn needless_lt<'a>(x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn needless_lt<'a>(x: &'a u8) {}
LL + fn needless_lt(x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:315:9
--> $DIR/needless_lifetimes.rs:322:9
|
LL | fn needless_lt<'a>(_x: &'a u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn needless_lt<'a>(_x: &'a u8) {}
LL + fn needless_lt(_x: &u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:328:9
--> $DIR/needless_lifetimes.rs:335:9
|
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn baz<'a>(&'a self) -> impl Foo + 'a {
LL + fn baz(&self) -> impl Foo + '_ {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:360:5
--> $DIR/needless_lifetimes.rs:367:5
|
LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(&i32) -> &i32) -> &i32 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:369:5
--> $DIR/needless_lifetimes.rs:376:5
|
LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
LL + fn generics_elidable<T: Fn(&i32) -> &i32>(i: &i32, f: T) -> &i32 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:381:5
--> $DIR/needless_lifetimes.rs:388:5
|
LL | fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
LL + fn where_clause_elidadable<T>(i: &i32, f: T) -> &i32
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:396:5
--> $DIR/needless_lifetimes.rs:403:5
|
LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:409:5
--> $DIR/needless_lifetimes.rs:416:5
|
LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:412:5
--> $DIR/needless_lifetimes.rs:419:5
|
LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:434:9
--> $DIR/needless_lifetimes.rs:441:9
|
LL | fn implicit<'a>(&'a self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn implicit<'a>(&'a self) -> &'a () {
LL + fn implicit(&self) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:437:9
--> $DIR/needless_lifetimes.rs:444:9
|
LL | fn implicit_mut<'a>(&'a mut self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:448:9
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:454:9
help: elide the lifetimes
|
LL - fn implicit_mut<'a>(&'a mut self) -> &'a () {
LL + fn implicit_mut(&mut self) -> &() {
|
LL | fn implicit<'a>(&'a self) -> &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:455:9
|
LL | fn implicit_provided<'a>(&'a self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:464:9
--> $DIR/needless_lifetimes.rs:461:9
|
LL | fn implicit<'a>(&'a self) -> &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn implicit<'a>(&'a self) -> &'a ();
LL + fn implicit(&self) -> &();
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:462:9
|
LL | fn implicit_provided<'a>(&'a self) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn implicit_provided<'a>(&'a self) -> &'a () {
LL + fn implicit_provided(&self) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:471:9
|
LL | fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
LL + fn lifetime_elsewhere(self: Box<Self>, here: &()) -> &();
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:465:9
--> $DIR/needless_lifetimes.rs:472:9
|
LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
LL + fn lifetime_elsewhere_provided(self: Box<Self>, here: &()) -> &() {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:474:5
--> $DIR/needless_lifetimes.rs:481:5
|
LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:476:5
|
LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: elide the lifetimes
|
LL - fn foo<'a>(x: &'a u8, y: &'_ u8) {}
LL + fn foo(x: &u8, y: &'_ u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:483:5
|
LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {}
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:488:5
--> $DIR/needless_lifetimes.rs:490:5
|
LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn one_input<'a>(x: &'a u8) -> &'a u8 {
LL + fn one_input(x: &u8) -> &u8 {
|
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:495:5
|
LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: elide the lifetimes
|
LL - fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) -> &'b u8 {
|
error: aborting due to 45 previous errors
error: the following explicit lifetimes could be elided: 'a
--> $DIR/needless_lifetimes.rs:508:13
|
LL | fn one_input<'a>(x: &'a u8) -> &'a u8 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
LL | local_one_input_macro!();
| ------------------------ in this macro invocation
|
= note: this error originates in the macro `local_one_input_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
help: elide the lifetimes
|
LL - fn one_input<'a>(x: &'a u8) -> &'a u8 {
LL + fn one_input(x: &u8) -> &u8 {
|
error: aborting due to 46 previous errors

View file

@ -49,7 +49,7 @@ error: the loop variable `i` is used to index `vec`
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
|
help: consider using an iterator
help: consider using an iterator and enumerate()
|
LL | for (i, <item>) in vec.iter().enumerate() {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -126,7 +126,7 @@ error: the loop variable `i` is used to index `vec`
LL | for i in 5..vec.len() {
| ^^^^^^^^^^^^
|
help: consider using an iterator
help: consider using an iterator and enumerate()
|
LL | for (i, <item>) in vec.iter().enumerate().skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -137,7 +137,7 @@ error: the loop variable `i` is used to index `vec`
LL | for i in 5..10 {
| ^^^^^
|
help: consider using an iterator
help: consider using an iterator and enumerate()
|
LL | for (i, <item>) in vec.iter().enumerate().take(10).skip(5) {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -148,7 +148,7 @@ error: the loop variable `i` is used to index `vec`
LL | for i in 0..vec.len() {
| ^^^^^^^^^^^^
|
help: consider using an iterator
help: consider using an iterator and enumerate()
|
LL | for (i, <item>) in vec.iter_mut().enumerate() {
| ~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -1,4 +1,9 @@
#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)]
#![allow(
dead_code,
clippy::missing_safety_doc,
clippy::extra_unused_lifetimes,
clippy::extra_unused_type_parameters
)]
#![warn(clippy::new_without_default)]
pub struct Foo;

View file

@ -1,5 +1,5 @@
error: you should consider adding a `Default` implementation for `Foo`
--> $DIR/new_without_default.rs:7:5
--> $DIR/new_without_default.rs:12:5
|
LL | / pub fn new() -> Foo {
LL | | Foo
@ -17,7 +17,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `Bar`
--> $DIR/new_without_default.rs:15:5
--> $DIR/new_without_default.rs:20:5
|
LL | / pub fn new() -> Self {
LL | | Bar
@ -34,7 +34,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `LtKo<'c>`
--> $DIR/new_without_default.rs:79:5
--> $DIR/new_without_default.rs:84:5
|
LL | / pub fn new() -> LtKo<'c> {
LL | | unimplemented!()
@ -51,7 +51,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
--> $DIR/new_without_default.rs:172:5
--> $DIR/new_without_default.rs:177:5
|
LL | / pub fn new() -> Self {
LL | | NewNotEqualToDerive { foo: 1 }
@ -68,7 +68,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `FooGenerics<T>`
--> $DIR/new_without_default.rs:180:5
--> $DIR/new_without_default.rs:185:5
|
LL | / pub fn new() -> Self {
LL | | Self(Default::default())
@ -85,7 +85,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `BarGenerics<T>`
--> $DIR/new_without_default.rs:187:5
--> $DIR/new_without_default.rs:192:5
|
LL | / pub fn new() -> Self {
LL | | Self(Default::default())
@ -102,7 +102,7 @@ LL + }
|
error: you should consider adding a `Default` implementation for `Foo<T>`
--> $DIR/new_without_default.rs:198:9
--> $DIR/new_without_default.rs:203:9
|
LL | / pub fn new() -> Self {
LL | | todo!()

View file

@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::redundant_field_names)]
#![allow(clippy::no_effect, dead_code, unused_variables)]
#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)]
#[macro_use]
extern crate derive_new;

View file

@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::redundant_field_names)]
#![allow(clippy::no_effect, dead_code, unused_variables)]
#![allow(clippy::extra_unused_type_parameters, clippy::no_effect, dead_code, unused_variables)]
#[macro_use]
extern crate derive_new;

View file

@ -36,6 +36,10 @@ fn syntax_error() {
let raw_string_error = Regex::new(r"[...\/...]");
let raw_string_error = Regex::new(r#"[...\/...]"#);
let escaped_string_span = Regex::new("\\b\\c");
let aux_span = Regex::new("(?ixi)");
}
fn trivial_regex() {

View file

@ -29,7 +29,10 @@ error: regex syntax error: invalid character class range, the start must be <= t
LL | let some_unicode = Regex::new("[é-è]");
| ^^^
error: regex syntax error on position 0: unclosed group
error: regex parse error:
(
^
error: unclosed group
--> $DIR/regex.rs:18:33
|
LL | let some_regex = Regex::new(OPENING_PAREN);
@ -43,25 +46,37 @@ LL | let binary_pipe_in_wrong_position = BRegex::new("|");
|
= help: the regex is unlikely to be useful as it is
error: regex syntax error on position 0: unclosed group
error: regex parse error:
(
^
error: unclosed group
--> $DIR/regex.rs:21:41
|
LL | let some_binary_regex = BRegex::new(OPENING_PAREN);
| ^^^^^^^^^^^^^
error: regex syntax error on position 0: unclosed group
error: regex parse error:
(
^
error: unclosed group
--> $DIR/regex.rs:22:56
|
LL | let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
| ^^^^^^^^^^^^^
error: regex syntax error on position 0: unclosed group
error: regex parse error:
(
^
error: unclosed group
--> $DIR/regex.rs:34:37
|
LL | let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
| ^^^^^^^^^^^^^
error: regex syntax error on position 0: unclosed group
error: regex parse error:
(
^
error: unclosed group
--> $DIR/regex.rs:35:39
|
LL | let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+/.(com|org|net)"]);
@ -79,8 +94,25 @@ error: regex syntax error: unrecognized escape sequence
LL | let raw_string_error = Regex::new(r#"[...//...]"#);
| ^^
error: regex parse error:
/b/c
^^
error: unrecognized escape sequence
--> $DIR/regex.rs:40:42
|
LL | let escaped_string_span = Regex::new("/b/c");
| ^^^^^^^^
|
= help: consider using a raw string literal: `r".."`
error: regex syntax error: duplicate flag
--> $DIR/regex.rs:42:34
|
LL | let aux_span = Regex::new("(?ixi)");
| ^ ^
error: trivial regex
--> $DIR/regex.rs:42:33
--> $DIR/regex.rs:46:33
|
LL | let trivial_eq = Regex::new("^foobar$");
| ^^^^^^^^^^
@ -88,7 +120,7 @@ LL | let trivial_eq = Regex::new("^foobar$");
= help: consider using `==` on `str`s
error: trivial regex
--> $DIR/regex.rs:44:48
--> $DIR/regex.rs:48:48
|
LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
| ^^^^^^^^^^
@ -96,7 +128,7 @@ LL | let trivial_eq_builder = RegexBuilder::new("^foobar$");
= help: consider using `==` on `str`s
error: trivial regex
--> $DIR/regex.rs:46:42
--> $DIR/regex.rs:50:42
|
LL | let trivial_starts_with = Regex::new("^foobar");
| ^^^^^^^^^
@ -104,7 +136,7 @@ LL | let trivial_starts_with = Regex::new("^foobar");
= help: consider using `str::starts_with`
error: trivial regex
--> $DIR/regex.rs:48:40
--> $DIR/regex.rs:52:40
|
LL | let trivial_ends_with = Regex::new("foobar$");
| ^^^^^^^^^
@ -112,7 +144,7 @@ LL | let trivial_ends_with = Regex::new("foobar$");
= help: consider using `str::ends_with`
error: trivial regex
--> $DIR/regex.rs:50:39
--> $DIR/regex.rs:54:39
|
LL | let trivial_contains = Regex::new("foobar");
| ^^^^^^^^
@ -120,7 +152,7 @@ LL | let trivial_contains = Regex::new("foobar");
= help: consider using `str::contains`
error: trivial regex
--> $DIR/regex.rs:52:39
--> $DIR/regex.rs:56:39
|
LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
| ^^^^^^^^^^^^^^^^
@ -128,7 +160,7 @@ LL | let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
= help: consider using `str::contains`
error: trivial regex
--> $DIR/regex.rs:54:40
--> $DIR/regex.rs:58:40
|
LL | let trivial_backslash = Regex::new("a/.b");
| ^^^^^^^
@ -136,7 +168,7 @@ LL | let trivial_backslash = Regex::new("a/.b");
= help: consider using `str::contains`
error: trivial regex
--> $DIR/regex.rs:57:36
--> $DIR/regex.rs:61:36
|
LL | let trivial_empty = Regex::new("");
| ^^
@ -144,7 +176,7 @@ LL | let trivial_empty = Regex::new("");
= help: the regex is unlikely to be useful as it is
error: trivial regex
--> $DIR/regex.rs:59:36
--> $DIR/regex.rs:63:36
|
LL | let trivial_empty = Regex::new("^");
| ^^^
@ -152,7 +184,7 @@ LL | let trivial_empty = Regex::new("^");
= help: the regex is unlikely to be useful as it is
error: trivial regex
--> $DIR/regex.rs:61:36
--> $DIR/regex.rs:65:36
|
LL | let trivial_empty = Regex::new("^$");
| ^^^^
@ -160,12 +192,12 @@ LL | let trivial_empty = Regex::new("^$");
= help: consider using `str::is_empty`
error: trivial regex
--> $DIR/regex.rs:63:44
--> $DIR/regex.rs:67:44
|
LL | let binary_trivial_empty = BRegex::new("^$");
| ^^^^
|
= help: consider using `str::is_empty`
error: aborting due to 23 previous errors
error: aborting due to 25 previous errors

View file

@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) {
// This should NOT trigger clippy warning because
// StructWithSeekMethod does not implement std::io::Seek;
fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) {
fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) {
t.seek(SeekFrom::Start(0));
}
@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) {
// This should NOT trigger clippy warning because
// StructWithSeekMethod does not implement std::io::Seek;
fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) {
fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) {
t.seek(SeekFrom::Start(0));
}

View file

@ -26,7 +26,7 @@ fn seek_to_start_false_method(t: &mut StructWithSeekMethod) {
// This should NOT trigger clippy warning because
// StructWithSeekMethod does not implement std::io::Seek;
fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) {
fn seek_to_start_method_owned_false(mut t: StructWithSeekMethod) {
t.seek(SeekFrom::Start(0));
}
@ -38,7 +38,7 @@ fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) {
// This should NOT trigger clippy warning because
// StructWithSeekMethod does not implement std::io::Seek;
fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) {
fn seek_to_start_false_trait_owned(mut t: StructWithSeekTrait) {
t.seek(SeekFrom::Start(0));
}

View file

@ -2,27 +2,62 @@ error: this `to_owned` call clones the Cow<'_, str> itself and does not cause th
--> $DIR/suspicious_to_owned.rs:16:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
| ^^^^^^^^^^^^^^
|
= note: `-D clippy::suspicious-to-owned` implied by `-D warnings`
help: depending on intent, either make the Cow an Owned variant
|
LL | let _ = cow.into_owned();
| ~~~~~~~~~~~~~~~~
help: or clone the Cow itself
|
LL | let _ = cow.clone();
| ~~~~~~~~~~~
error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned
--> $DIR/suspicious_to_owned.rs:26:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
| ^^^^^^^^^^^^^^
|
help: depending on intent, either make the Cow an Owned variant
|
LL | let _ = cow.into_owned();
| ~~~~~~~~~~~~~~~~
help: or clone the Cow itself
|
LL | let _ = cow.clone();
| ~~~~~~~~~~~
error: this `to_owned` call clones the Cow<'_, Vec<char>> itself and does not cause the Cow<'_, Vec<char>> contents to become owned
--> $DIR/suspicious_to_owned.rs:36:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
| ^^^^^^^^^^^^^^
|
help: depending on intent, either make the Cow an Owned variant
|
LL | let _ = cow.into_owned();
| ~~~~~~~~~~~~~~~~
help: or clone the Cow itself
|
LL | let _ = cow.clone();
| ~~~~~~~~~~~
error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned
--> $DIR/suspicious_to_owned.rs:46:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
| ^^^^^^^^^^^^^^
|
help: depending on intent, either make the Cow an Owned variant
|
LL | let _ = cow.into_owned();
| ~~~~~~~~~~~~~~~~
help: or clone the Cow itself
|
LL | let _ = cow.clone();
| ~~~~~~~~~~~
error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
--> $DIR/suspicious_to_owned.rs:60:13

View file

@ -1,4 +1,5 @@
#![deny(clippy::type_repetition_in_bounds)]
#![allow(clippy::extra_unused_type_parameters)]
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};

View file

@ -1,5 +1,5 @@
error: this type has already been used as a bound predicate
--> $DIR/type_repetition_in_bounds.rs:8:5
--> $DIR/type_repetition_in_bounds.rs:9:5
|
LL | T: Clone,
| ^^^^^^^^
@ -12,7 +12,7 @@ LL | #![deny(clippy::type_repetition_in_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this type has already been used as a bound predicate
--> $DIR/type_repetition_in_bounds.rs:25:5
--> $DIR/type_repetition_in_bounds.rs:26:5
|
LL | Self: Copy + Default + Ord,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -20,7 +20,7 @@ LL | Self: Copy + Default + Ord,
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
error: this type has already been used as a bound predicate
--> $DIR/type_repetition_in_bounds.rs:85:5
--> $DIR/type_repetition_in_bounds.rs:86:5
|
LL | T: Clone,
| ^^^^^^^^
@ -28,7 +28,7 @@ LL | T: Clone,
= help: consider combining the bounds: `T: ?Sized + Clone`
error: this type has already been used as a bound predicate
--> $DIR/type_repetition_in_bounds.rs:90:5
--> $DIR/type_repetition_in_bounds.rs:91:5
|
LL | T: ?Sized,
| ^^^^^^^^^

View file

@ -63,6 +63,14 @@ fn combine_or(file: &str) -> Result<(), Error> {
Ok(())
}
fn is_ok_err<T: io::Read + io::Write>(s: &mut T) {
s.write(b"ok").is_ok();
s.write(b"err").is_err();
let mut buf = [0u8; 0];
s.read(&mut buf).is_ok();
s.read(&mut buf).is_err();
}
async fn bad_async_write<W: AsyncWrite + Unpin>(w: &mut W) {
w.write(b"hello world").await.unwrap();
}

View file

@ -82,13 +82,45 @@ LL | | .expect("error");
error: written amount is not handled
--> $DIR/unused_io_amount.rs:67:5
|
LL | s.write(b"ok").is_ok();
| ^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
error: written amount is not handled
--> $DIR/unused_io_amount.rs:68:5
|
LL | s.write(b"err").is_err();
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Write::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:70:5
|
LL | s.read(&mut buf).is_ok();
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Read::read_exact` instead, or handle partial reads
error: read amount is not handled
--> $DIR/unused_io_amount.rs:71:5
|
LL | s.read(&mut buf).is_err();
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `Read::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:75:5
|
LL | w.write(b"hello world").await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:72:5
--> $DIR/unused_io_amount.rs:80:5
|
LL | r.read(&mut buf[..]).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -96,7 +128,7 @@ LL | r.read(&mut buf[..]).await.unwrap();
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:85:9
--> $DIR/unused_io_amount.rs:93:9
|
LL | w.write(b"hello world").await?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -104,7 +136,7 @@ LL | w.write(b"hello world").await?;
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:93:9
--> $DIR/unused_io_amount.rs:101:9
|
LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -112,7 +144,7 @@ LL | r.read(&mut buf[..]).await.or(Err(Error::Kind))?;
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: written amount is not handled
--> $DIR/unused_io_amount.rs:101:5
--> $DIR/unused_io_amount.rs:109:5
|
LL | w.write(b"hello world").await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -120,12 +152,12 @@ LL | w.write(b"hello world").await.unwrap();
= help: use `AsyncWriteExt::write_all` instead, or handle partial writes
error: read amount is not handled
--> $DIR/unused_io_amount.rs:106:5
--> $DIR/unused_io_amount.rs:114:5
|
LL | r.read(&mut buf[..]).await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: use `AsyncReadExt::read_exact` instead, or handle partial reads
error: aborting due to 16 previous errors
error: aborting due to 20 previous errors

View file

@ -96,7 +96,7 @@ fn main() {
}
match Enum::A {
Enum::A => (),
Enum::B | _ => (),
Enum::B | Enum::__Private => (),
}
}
}

View file

@ -34,11 +34,11 @@ error: wildcard matches known variants and will also match future added variants
LL | _ => {},
| ^ help: try this: `ErrorKind::PermissionDenied | _`
error: wildcard matches known variants and will also match future added variants
error: wildcard match will also match any future added variants
--> $DIR/wildcard_enum_match_arm.rs:99:13
|
LL | _ => (),
| ^ help: try this: `Enum::B | _`
| ^ help: try this: `Enum::B | Enum::__Private`
error: aborting due to 6 previous errors