diff options
| author | bors <bors@rust-lang.org> | 2022-11-22 17:09:06 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2022-11-22 17:09:06 +0000 |
| commit | b33afd61edfb690ace17d0672b367e44758c1bef (patch) | |
| tree | 4b806322d7fb1463672319546e1aadc3e2a0b5cc /clippy_lints/src | |
| parent | 9e093072457d9cafe8a7259992d1a05d179e4757 (diff) | |
| parent | e95d40980b3044a6a9cad1b5e72eb57390790d18 (diff) | |
Auto merge of #104688 - flip1995:clippyup, r=Manishearth,flip1995
Update Clippy r? `@Manishearth` Sorry for taking so long. There were so many blockers and so little time. This situation should be mitigated with #104007 in the future.
Diffstat (limited to 'clippy_lints/src')
122 files changed, 3324 insertions, 3154 deletions
diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0bd1f8b784e..ecf8e83375d 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -11,14 +11,14 @@ use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -use rustc_span::sym; use rustc_span::symbol::Symbol; +use rustc_span::{sym, DUMMY_SP}; use semver::Version; static UNIX_SYSTEMS: &[&str] = &[ @@ -303,6 +303,26 @@ declare_lint_pass!(Attributes => [ ]); impl<'tcx> LateLintPass<'tcx> for Attributes { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + for (name, level) in &cx.sess().opts.lint_opts { + if name == "clippy::restriction" && *level > Level::Allow { + span_lint_and_then( + cx, + BLANKET_CLIPPY_RESTRICTION_LINTS, + DUMMY_SP, + "`clippy::restriction` is not meant to be enabled as a group", + |diag| { + diag.note(format!( + "because of the command line `--{} clippy::restriction`", + level.as_str() + )); + diag.help("enable the restriction lints you need individually"); + }, + ); + } + } + } + fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { if let Some(items) = &attr.meta_item_list() { if let Some(ident) = attr.ident() { @@ -358,7 +378,9 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { | "enum_glob_use" | "redundant_pub_crate" | "macro_use_imports" - | "unsafe_removed_from_name", + | "unsafe_removed_from_name" + | "module_name_repetitions" + | "single_component_path_imports" ) }) { @@ -441,9 +463,9 @@ fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMe cx, BLANKET_CLIPPY_RESTRICTION_LINTS, lint.span(), - "restriction lints are not meant to be all enabled", + "`clippy::restriction` is not meant to be enabled as a group", None, - "try enabling only the lints you really need", + "enable the restriction lints you need individually", ); } } @@ -464,6 +486,11 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem return; } + // Check if the attribute is in an external macro and therefore out of the developer's control + if in_external_macro(cx.sess(), attr.span) { + return; + } + span_lint_and_help( cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 34717811866..d40a385435a 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -189,7 +188,7 @@ impl LateLintPass<'_> for AwaitHolding { fn check_crate(&mut self, cx: &LateContext<'_>) { for conf in &self.conf_invalid_types { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.def_ids.insert(id, conf.clone()); } } diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs index 001d74c2605..bdb3a011602 100644 --- a/clippy_lints/src/bool_to_int_with_if.rs +++ b/clippy_lints/src/bool_to_int_with_if.rs @@ -1,9 +1,10 @@ +use clippy_utils::higher::If; use rustc_ast::LitKind; use rustc_hir::{Block, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg}; +use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg}; use rustc_errors::Applicability; declare_clippy_lint! { @@ -12,7 +13,7 @@ declare_clippy_lint! { /// this lint suggests using a `from()` function or an `as` coercion. /// /// ### Why is this bad? - /// Coercion or `from()` is idiomatic way to convert bool to a number. + /// Coercion or `from()` is another way to convert bool to a number. /// Both methods are guaranteed to return 1 for true, and 0 for false. /// /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E @@ -38,23 +39,23 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.65.0"] pub BOOL_TO_INT_WITH_IF, - style, + pedantic, "using if to convert bool to int" } declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]); impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf { - fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if !expr.span.from_expansion() { - check_if_else(ctx, expr); + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) { + check_if_else(cx, expr); } } } -fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { - if let ExprKind::If(check, then, Some(else_)) = expr.kind +fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) { + if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr) && let Some(then_lit) = int_literal(then) - && let Some(else_lit) = int_literal(else_) + && let Some(else_lit) = int_literal(r#else) { let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) { false @@ -66,17 +67,17 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx }; let mut applicability = Applicability::MachineApplicable; let snippet = { - let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability); + let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); if inverted { sugg = !sugg; } sugg }; - let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type + let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type let suggestion = { - let wrap_in_curly = is_else_clause(ctx.tcx, expr); + let wrap_in_curly = is_else_clause(cx.tcx, expr); let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into()); if wrap_in_curly { s = s.blockify(); @@ -87,7 +88,7 @@ fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx let into_snippet = snippet.clone().maybe_par(); let as_snippet = snippet.as_ty(ty); - span_lint_and_then(ctx, + span_lint_and_then(cx, BOOL_TO_INT_WITH_IF, expr.span, "boolean to int conversion using if", diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 08164c0b654..939bdbcdc7c 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> { } } -fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(expr); cx.tcx .get_diagnostic_item(sym::Ord) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b72c4c772f1..7148b5e6ebf 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -593,7 +593,7 @@ declare_clippy_lint! { /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len); /// ``` /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub CAST_SLICE_FROM_RAW_PARTS, suspicious, "casting a slice created from a pointer and length to a slice pointer" diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 77af3b53d63..1c3a89a9782 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -4,11 +4,11 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::LimitStack; +use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; -use rustc_hir::{Body, ExprKind, FnDecl, HirId}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -56,15 +56,13 @@ impl CognitiveComplexity { cx: &LateContext<'tcx>, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, - body: &'tcx Body<'_>, + expr: &'tcx Expr<'_>, body_span: Span, ) { if body_span.from_expansion() { return; } - let expr = body.value; - let mut cc = 1u64; let mut returns = 0u64; let _: Option<!> = for_each_expr(expr, |e| { @@ -146,7 +144,18 @@ impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity { ) { let def_id = cx.tcx.hir().local_def_id(hir_id); if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) { - self.check(cx, kind, decl, body, span); + let expr = if is_async_fn(kind) { + match get_async_fn_body(cx.tcx, body) { + Some(b) => b, + None => { + return; + }, + } + } else { + body.value + }; + + self.check(cx, kind, decl, expr, span); } } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs new file mode 100644 index 00000000000..0d3fc43a644 --- /dev/null +++ b/clippy_lints/src/declared_lints.rs @@ -0,0 +1,628 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + +pub(crate) static LINTS: &[&crate::LintInfo] = &[ + #[cfg(feature = "internal")] + crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO, + #[cfg(feature = "internal")] + crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO, + crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO, + crate::approx_const::APPROX_CONSTANT_INFO, + crate::as_conversions::AS_CONVERSIONS_INFO, + crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO, + crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO, + crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO, + crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO, + crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO, + crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO, + crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO, + crate::attrs::DEPRECATED_CFG_ATTR_INFO, + crate::attrs::DEPRECATED_SEMVER_INFO, + crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, + crate::attrs::INLINE_ALWAYS_INFO, + crate::attrs::MISMATCHED_TARGET_OS_INFO, + crate::attrs::USELESS_ATTRIBUTE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO, + crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO, + crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO, + crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO, + crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO, + crate::booleans::NONMINIMAL_BOOL_INFO, + crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, + crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, + crate::box_default::BOX_DEFAULT_INFO, + crate::cargo::CARGO_COMMON_METADATA_INFO, + crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO, + crate::cargo::NEGATIVE_FEATURE_NAMES_INFO, + crate::cargo::REDUNDANT_FEATURE_NAMES_INFO, + crate::cargo::WILDCARD_DEPENDENCIES_INFO, + crate::casts::AS_PTR_CAST_MUT_INFO, + crate::casts::AS_UNDERSCORE_INFO, + crate::casts::BORROW_AS_PTR_INFO, + crate::casts::CAST_ABS_TO_UNSIGNED_INFO, + crate::casts::CAST_ENUM_CONSTRUCTOR_INFO, + crate::casts::CAST_ENUM_TRUNCATION_INFO, + crate::casts::CAST_LOSSLESS_INFO, + crate::casts::CAST_NAN_TO_INT_INFO, + crate::casts::CAST_POSSIBLE_TRUNCATION_INFO, + crate::casts::CAST_POSSIBLE_WRAP_INFO, + crate::casts::CAST_PRECISION_LOSS_INFO, + crate::casts::CAST_PTR_ALIGNMENT_INFO, + crate::casts::CAST_REF_TO_MUT_INFO, + crate::casts::CAST_SIGN_LOSS_INFO, + crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO, + crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO, + crate::casts::CHAR_LIT_AS_U8_INFO, + crate::casts::FN_TO_NUMERIC_CAST_INFO, + crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO, + crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO, + crate::casts::PTR_AS_PTR_INFO, + crate::casts::UNNECESSARY_CAST_INFO, + crate::checked_conversions::CHECKED_CONVERSIONS_INFO, + crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO, + crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO, + crate::collapsible_if::COLLAPSIBLE_IF_INFO, + crate::comparison_chain::COMPARISON_CHAIN_INFO, + crate::copies::BRANCHES_SHARING_CODE_INFO, + crate::copies::IFS_SAME_COND_INFO, + crate::copies::IF_SAME_THEN_ELSE_INFO, + crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO, + crate::copy_iterator::COPY_ITERATOR_INFO, + crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO, + crate::create_dir::CREATE_DIR_INFO, + crate::dbg_macro::DBG_MACRO_INFO, + crate::default::DEFAULT_TRAIT_ACCESS_INFO, + crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO, + crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO, + crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO, + crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO, + crate::dereference::EXPLICIT_AUTO_DEREF_INFO, + crate::dereference::EXPLICIT_DEREF_METHODS_INFO, + crate::dereference::NEEDLESS_BORROW_INFO, + crate::dereference::REF_BINDING_TO_REFERENCE_INFO, + crate::derivable_impls::DERIVABLE_IMPLS_INFO, + crate::derive::DERIVE_HASH_XOR_EQ_INFO, + crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO, + crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO, + crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO, + crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO, + crate::disallowed_macros::DISALLOWED_MACROS_INFO, + crate::disallowed_methods::DISALLOWED_METHODS_INFO, + crate::disallowed_names::DISALLOWED_NAMES_INFO, + crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, + crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::doc::DOC_LINK_WITH_QUOTES_INFO, + crate::doc::DOC_MARKDOWN_INFO, + crate::doc::MISSING_ERRORS_DOC_INFO, + crate::doc::MISSING_PANICS_DOC_INFO, + crate::doc::MISSING_SAFETY_DOC_INFO, + crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, + crate::doc::UNNECESSARY_SAFETY_DOC_INFO, + crate::double_parens::DOUBLE_PARENS_INFO, + crate::drop_forget_ref::DROP_COPY_INFO, + crate::drop_forget_ref::DROP_NON_DROP_INFO, + crate::drop_forget_ref::DROP_REF_INFO, + crate::drop_forget_ref::FORGET_COPY_INFO, + crate::drop_forget_ref::FORGET_NON_DROP_INFO, + crate::drop_forget_ref::FORGET_REF_INFO, + crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO, + crate::duplicate_mod::DUPLICATE_MOD_INFO, + crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO, + crate::empty_drop::EMPTY_DROP_INFO, + crate::empty_enum::EMPTY_ENUM_INFO, + crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO, + crate::entry::MAP_ENTRY_INFO, + crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO, + crate::enum_variants::ENUM_VARIANT_NAMES_INFO, + crate::enum_variants::MODULE_INCEPTION_INFO, + crate::enum_variants::MODULE_NAME_REPETITIONS_INFO, + crate::equatable_if_let::EQUATABLE_IF_LET_INFO, + crate::escape::BOXED_LOCAL_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_INFO, + crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO, + crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO, + crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO, + crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO, + crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO, + crate::exit::EXIT_INFO, + crate::explicit_write::EXPLICIT_WRITE_INFO, + crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO, + crate::float_literal::EXCESSIVE_PRECISION_INFO, + crate::float_literal::LOSSY_FLOAT_LITERAL_INFO, + crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO, + crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO, + crate::format::USELESS_FORMAT_INFO, + crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO, + crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO, + crate::format_args::UNINLINED_FORMAT_ARGS_INFO, + crate::format_args::UNUSED_FORMAT_SPECS_INFO, + crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO, + crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO, + crate::format_push_string::FORMAT_PUSH_STRING_INFO, + crate::formatting::POSSIBLE_MISSING_COMMA_INFO, + crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO, + crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO, + crate::from_over_into::FROM_OVER_INTO_INFO, + crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, + crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, + crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MUST_USE_CANDIDATE_INFO, + crate::functions::MUST_USE_UNIT_INFO, + crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, + crate::functions::RESULT_LARGE_ERR_INFO, + crate::functions::RESULT_UNIT_ERR_INFO, + crate::functions::TOO_MANY_ARGUMENTS_INFO, + crate::functions::TOO_MANY_LINES_INFO, + crate::future_not_send::FUTURE_NOT_SEND_INFO, + crate::if_let_mutex::IF_LET_MUTEX_INFO, + crate::if_not_else::IF_NOT_ELSE_INFO, + crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::implicit_hasher::IMPLICIT_HASHER_INFO, + crate::implicit_return::IMPLICIT_RETURN_INFO, + crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, + crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, + crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO, + crate::indexing_slicing::INDEXING_SLICING_INFO, + crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO, + crate::infinite_iter::INFINITE_ITER_INFO, + crate::infinite_iter::MAYBE_INFINITE_ITER_INFO, + crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_INFO, + crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO, + crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO, + crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO, + crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO, + crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO, + crate::int_plus_one::INT_PLUS_ONE_INFO, + crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO, + crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO, + crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, + crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, + crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, + crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO, + crate::large_include_file::LARGE_INCLUDE_FILE_INFO, + crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO, + crate::len_zero::COMPARISON_TO_EMPTY_INFO, + crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO, + crate::len_zero::LEN_ZERO_INFO, + crate::let_if_seq::USELESS_LET_IF_SEQ_INFO, + crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO, + crate::let_underscore::LET_UNDERSCORE_LOCK_INFO, + crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO, + crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, + crate::lifetimes::NEEDLESS_LIFETIMES_INFO, + crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, + crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, + crate::literal_representation::LARGE_DIGIT_GROUPS_INFO, + crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO, + crate::literal_representation::UNREADABLE_LITERAL_INFO, + crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO, + crate::loops::EMPTY_LOOP_INFO, + crate::loops::EXPLICIT_COUNTER_LOOP_INFO, + crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO, + crate::loops::EXPLICIT_ITER_LOOP_INFO, + crate::loops::FOR_KV_MAP_INFO, + crate::loops::ITER_NEXT_LOOP_INFO, + crate::loops::MANUAL_FIND_INFO, + crate::loops::MANUAL_FLATTEN_INFO, + crate::loops::MANUAL_MEMCPY_INFO, + crate::loops::MISSING_SPIN_LOOP_INFO, + crate::loops::MUT_RANGE_BOUND_INFO, + crate::loops::NEEDLESS_RANGE_LOOP_INFO, + crate::loops::NEVER_LOOP_INFO, + crate::loops::SAME_ITEM_PUSH_INFO, + crate::loops::SINGLE_ELEMENT_LOOP_INFO, + crate::loops::WHILE_IMMUTABLE_CONDITION_INFO, + crate::loops::WHILE_LET_LOOP_INFO, + crate::loops::WHILE_LET_ON_ITERATOR_INFO, + crate::macro_use::MACRO_USE_IMPORTS_INFO, + crate::main_recursion::MAIN_RECURSION_INFO, + crate::manual_assert::MANUAL_ASSERT_INFO, + crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, + crate::manual_bits::MANUAL_BITS_INFO, + crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_let_else::MANUAL_LET_ELSE_INFO, + crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, + crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO, + crate::manual_retain::MANUAL_RETAIN_INFO, + crate::manual_string_new::MANUAL_STRING_NEW_INFO, + crate::manual_strip::MANUAL_STRIP_INFO, + crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO, + crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO, + crate::match_result_ok::MATCH_RESULT_OK_INFO, + crate::matches::COLLAPSIBLE_MATCH_INFO, + crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, + crate::matches::MANUAL_FILTER_INFO, + crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_UNWRAP_OR_INFO, + crate::matches::MATCH_AS_REF_INFO, + crate::matches::MATCH_BOOL_INFO, + crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO, + crate::matches::MATCH_ON_VEC_ITEMS_INFO, + crate::matches::MATCH_OVERLAPPING_ARM_INFO, + crate::matches::MATCH_REF_PATS_INFO, + crate::matches::MATCH_SAME_ARMS_INFO, + crate::matches::MATCH_SINGLE_BINDING_INFO, + crate::matches::MATCH_STR_CASE_MISMATCH_INFO, + crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO, + crate::matches::MATCH_WILD_ERR_ARM_INFO, + crate::matches::NEEDLESS_MATCH_INFO, + crate::matches::REDUNDANT_PATTERN_MATCHING_INFO, + crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO, + crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO, + crate::matches::SINGLE_MATCH_INFO, + crate::matches::SINGLE_MATCH_ELSE_INFO, + crate::matches::TRY_ERR_INFO, + crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO, + crate::matches::WILDCARD_IN_OR_PATTERNS_INFO, + crate::mem_forget::MEM_FORGET_INFO, + crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO, + crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, + crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, + crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BYTES_COUNT_TO_LEN_INFO, + crate::methods::BYTES_NTH_INFO, + crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, + crate::methods::CHARS_LAST_CMP_INFO, + crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CLONED_INSTEAD_OF_COPIED_INFO, + crate::methods::CLONE_DOUBLE_REF_INFO, + crate::methods::CLONE_ON_COPY_INFO, + crate::methods::CLONE_ON_REF_PTR_INFO, + crate::methods::COLLAPSIBLE_STR_REPLACE_INFO, + crate::methods::ERR_EXPECT_INFO, + crate::methods::EXPECT_FUN_CALL_INFO, + crate::methods::EXPECT_USED_INFO, + crate::methods::EXTEND_WITH_DRAIN_INFO, + crate::methods::FILETYPE_IS_FILE_INFO, + crate::methods::FILTER_MAP_IDENTITY_INFO, + crate::methods::FILTER_MAP_NEXT_INFO, + crate::methods::FILTER_NEXT_INFO, + crate::methods::FLAT_MAP_IDENTITY_INFO, + crate::methods::FLAT_MAP_OPTION_INFO, + crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO, + crate::methods::GET_FIRST_INFO, + crate::methods::GET_LAST_WITH_LEN_INFO, + crate::methods::GET_UNWRAP_INFO, + crate::methods::IMPLICIT_CLONE_INFO, + crate::methods::INEFFICIENT_TO_STRING_INFO, + crate::methods::INSPECT_FOR_EACH_INFO, + crate::methods::INTO_ITER_ON_REF_INFO, + crate::methods::IS_DIGIT_ASCII_RADIX_INFO, + crate::methods::ITERATOR_STEP_BY_ZERO_INFO, + crate::methods::ITER_CLONED_COLLECT_INFO, + crate::methods::ITER_COUNT_INFO, + crate::methods::ITER_KV_MAP_INFO, + crate::methods::ITER_NEXT_SLICE_INFO, + crate::methods::ITER_NTH_INFO, + crate::methods::ITER_NTH_ZERO_INFO, + crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO, + crate::methods::ITER_ON_SINGLE_ITEMS_INFO, + crate::methods::ITER_OVEREAGER_CLONED_INFO, + crate::methods::ITER_SKIP_NEXT_INFO, + crate::methods::ITER_WITH_DRAIN_INFO, + crate::methods::MANUAL_FILTER_MAP_INFO, + crate::methods::MANUAL_FIND_MAP_INFO, + crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, + crate::methods::MANUAL_SPLIT_ONCE_INFO, + crate::methods::MANUAL_STR_REPEAT_INFO, + crate::methods::MAP_CLONE_INFO, + crate::methods::MAP_COLLECT_RESULT_UNIT_INFO, + crate::methods::MAP_ERR_IGNORE_INFO, + crate::methods::MAP_FLATTEN_INFO, + crate::methods::MAP_IDENTITY_INFO, + crate::methods::MAP_UNWRAP_OR_INFO, + crate::methods::MUT_MUTEX_LOCK_INFO, + crate::methods::NAIVE_BYTECOUNT_INFO, + crate::methods::NEEDLESS_COLLECT_INFO, + crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO, + crate::methods::NEEDLESS_OPTION_TAKE_INFO, + crate::methods::NEEDLESS_SPLITN_INFO, + crate::methods::NEW_RET_NO_SELF_INFO, + crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO, + crate::methods::NO_EFFECT_REPLACE_INFO, + crate::methods::OBFUSCATED_IF_ELSE_INFO, + crate::methods::OK_EXPECT_INFO, + crate::methods::OPTION_AS_REF_DEREF_INFO, + crate::methods::OPTION_FILTER_MAP_INFO, + crate::methods::OPTION_MAP_OR_NONE_INFO, + crate::methods::OR_FUN_CALL_INFO, + crate::methods::OR_THEN_UNWRAP_INFO, + crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO, + crate::methods::RANGE_ZIP_WITH_LEN_INFO, + crate::methods::REPEAT_ONCE_INFO, + crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO, + crate::methods::SEARCH_IS_SOME_INFO, + crate::methods::SEEK_FROM_CURRENT_INFO, + crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO, + crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, + crate::methods::SINGLE_CHAR_ADD_STR_INFO, + crate::methods::SINGLE_CHAR_PATTERN_INFO, + crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::STABLE_SORT_PRIMITIVE_INFO, + crate::methods::STRING_EXTEND_CHARS_INFO, + crate::methods::SUSPICIOUS_MAP_INFO, + crate::methods::SUSPICIOUS_SPLITN_INFO, + crate::methods::SUSPICIOUS_TO_OWNED_INFO, + crate::methods::UNINIT_ASSUMED_INIT_INFO, + crate::methods::UNIT_HASH_INFO, + crate::methods::UNNECESSARY_FILTER_MAP_INFO, + crate::methods::UNNECESSARY_FIND_MAP_INFO, + crate::methods::UNNECESSARY_FOLD_INFO, + crate::methods::UNNECESSARY_JOIN_INFO, + crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO, + crate::methods::UNNECESSARY_SORT_BY_INFO, + crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO, + crate::methods::UNWRAP_USED_INFO, + crate::methods::USELESS_ASREF_INFO, + crate::methods::VEC_RESIZE_TO_ZERO_INFO, + crate::methods::VERBOSE_FILE_READS_INFO, + crate::methods::WRONG_SELF_CONVENTION_INFO, + crate::methods::ZST_OFFSET_INFO, + crate::minmax::MIN_MAX_INFO, + crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, + crate::misc::TOPLEVEL_REF_ARG_INFO, + crate::misc::USED_UNDERSCORE_BINDING_INFO, + crate::misc::ZERO_PTR_INFO, + crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, + crate::misc_early::DOUBLE_NEG_INFO, + crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, + crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO, + crate::misc_early::REDUNDANT_PATTERN_INFO, + crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO, + crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO, + crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO, + crate::misc_early::ZERO_PREFIXED_LITERAL_INFO, + crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO, + crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO, + crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO, + crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO, + crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO, + crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO, + crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO, + crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO, + crate::module_style::MOD_MODULE_FILES_INFO, + crate::module_style::SELF_NAMED_MODULE_FILES_INFO, + crate::multi_assignments::MULTI_ASSIGNMENTS_INFO, + crate::mut_key::MUTABLE_KEY_TYPE_INFO, + crate::mut_mut::MUT_MUT_INFO, + crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO, + crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO, + crate::mutex_atomic::MUTEX_ATOMIC_INFO, + crate::mutex_atomic::MUTEX_INTEGER_INFO, + crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO, + crate::needless_bool::BOOL_COMPARISON_INFO, + crate::needless_bool::NEEDLESS_BOOL_INFO, + crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO, + crate::needless_continue::NEEDLESS_CONTINUE_INFO, + crate::needless_for_each::NEEDLESS_FOR_EACH_INFO, + crate::needless_late_init::NEEDLESS_LATE_INIT_INFO, + crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO, + crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO, + crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO, + crate::needless_update::NEEDLESS_UPDATE_INFO, + crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO, + crate::neg_multiply::NEG_MULTIPLY_INFO, + crate::new_without_default::NEW_WITHOUT_DEFAULT_INFO, + crate::no_effect::NO_EFFECT_INFO, + crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO, + crate::no_effect::UNNECESSARY_OPERATION_INFO, + crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO, + crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO, + crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO, + crate::non_expressive_names::MANY_SINGLE_CHAR_NAMES_INFO, + crate::non_expressive_names::SIMILAR_NAMES_INFO, + crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, + crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, + crate::octal_escapes::OCTAL_ESCAPES_INFO, + crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, + crate::operators::ABSURD_EXTREME_COMPARISONS_INFO, + crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO, + crate::operators::ASSIGN_OP_PATTERN_INFO, + crate::operators::BAD_BIT_MASK_INFO, + crate::operators::CMP_NAN_INFO, + crate::operators::CMP_OWNED_INFO, + crate::operators::DOUBLE_COMPARISONS_INFO, + crate::operators::DURATION_SUBSEC_INFO, + crate::operators::EQ_OP_INFO, + crate::operators::ERASING_OP_INFO, + crate::operators::FLOAT_ARITHMETIC_INFO, + crate::operators::FLOAT_CMP_INFO, + crate::operators::FLOAT_CMP_CONST_INFO, + crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, + crate::operators::IDENTITY_OP_INFO, + crate::operators::INEFFECTIVE_BIT_MASK_INFO, + crate::operators::INTEGER_ARITHMETIC_INFO, + crate::operators::INTEGER_DIVISION_INFO, + crate::operators::MISREFACTORED_ASSIGN_OP_INFO, + crate::operators::MODULO_ARITHMETIC_INFO, + crate::operators::MODULO_ONE_INFO, + crate::operators::NEEDLESS_BITWISE_BOOL_INFO, + crate::operators::OP_REF_INFO, + crate::operators::PTR_EQ_INFO, + crate::operators::SELF_ASSIGNMENT_INFO, + crate::operators::VERBOSE_BIT_MASK_INFO, + crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO, + crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO, + crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO, + crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO, + crate::panic_unimplemented::PANIC_INFO, + crate::panic_unimplemented::TODO_INFO, + crate::panic_unimplemented::UNIMPLEMENTED_INFO, + crate::panic_unimplemented::UNREACHABLE_INFO, + crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO, + crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO, + crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO, + crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO, + crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO, + crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, + crate::precedence::PRECEDENCE_INFO, + crate::ptr::CMP_NULL_INFO, + crate::ptr::INVALID_NULL_PTR_USAGE_INFO, + crate::ptr::MUT_FROM_REF_INFO, + crate::ptr::PTR_ARG_INFO, + crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO, + crate::pub_use::PUB_USE_INFO, + crate::question_mark::QUESTION_MARK_INFO, + crate::ranges::MANUAL_RANGE_CONTAINS_INFO, + crate::ranges::RANGE_MINUS_ONE_INFO, + crate::ranges::RANGE_PLUS_ONE_INFO, + crate::ranges::REVERSED_EMPTY_RANGES_INFO, + crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO, + crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO, + crate::redundant_clone::REDUNDANT_CLONE_INFO, + crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO, + crate::redundant_else::REDUNDANT_ELSE_INFO, + crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO, + crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO, + crate::redundant_slicing::DEREF_BY_SLICING_INFO, + crate::redundant_slicing::REDUNDANT_SLICING_INFO, + crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO, + crate::ref_option_ref::REF_OPTION_REF_INFO, + crate::reference::DEREF_ADDROF_INFO, + crate::regex::INVALID_REGEX_INFO, + crate::regex::TRIVIAL_REGEX_INFO, + crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO, + crate::returns::LET_AND_RETURN_INFO, + crate::returns::NEEDLESS_RETURN_INFO, + crate::same_name_method::SAME_NAME_METHOD_INFO, + crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO, + crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO, + crate::serde_api::SERDE_API_MISUSE_INFO, + crate::shadow::SHADOW_REUSE_INFO, + crate::shadow::SHADOW_SAME_INFO, + crate::shadow::SHADOW_UNRELATED_INFO, + crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO, + crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO, + crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO, + crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO, + crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO, + crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO, + crate::strings::STRING_ADD_INFO, + crate::strings::STRING_ADD_ASSIGN_INFO, + crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO, + crate::strings::STRING_LIT_AS_BYTES_INFO, + crate::strings::STRING_SLICE_INFO, + crate::strings::STRING_TO_STRING_INFO, + crate::strings::STR_TO_STRING_INFO, + crate::strings::TRIM_SPLIT_WHITESPACE_INFO, + crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, + crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO, + crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO, + crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO, + crate::swap::ALMOST_SWAPPED_INFO, + crate::swap::MANUAL_SWAP_INFO, + crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO, + crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO, + crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO, + crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO, + crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO, + crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO, + crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO, + crate::transmute::CROSSPOINTER_TRANSMUTE_INFO, + crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO, + crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO, + crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO, + crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO, + crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO, + crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO, + crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO, + crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO, + crate::transmute::TRANSMUTE_PTR_TO_REF_INFO, + crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO, + crate::transmute::TRANSMUTING_NULL_INFO, + crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO, + crate::transmute::USELESS_TRANSMUTE_INFO, + crate::transmute::WRONG_TRANSMUTE_INFO, + crate::types::BORROWED_BOX_INFO, + crate::types::BOX_COLLECTION_INFO, + crate::types::LINKEDLIST_INFO, + crate::types::OPTION_OPTION_INFO, + crate::types::RC_BUFFER_INFO, + crate::types::RC_MUTEX_INFO, + crate::types::REDUNDANT_ALLOCATION_INFO, + crate::types::TYPE_COMPLEXITY_INFO, + crate::types::VEC_BOX_INFO, + crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::unicode::INVISIBLE_CHARACTERS_INFO, + crate::unicode::NON_ASCII_LITERAL_INFO, + crate::unicode::UNICODE_NOT_NFC_INFO, + crate::uninit_vec::UNINIT_VEC_INFO, + crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO, + crate::unit_types::LET_UNIT_VALUE_INFO, + crate::unit_types::UNIT_ARG_INFO, + crate::unit_types::UNIT_CMP_INFO, + crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO, + crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO, + crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, + crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, + crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, + crate::unused_async::UNUSED_ASYNC_INFO, + crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, + crate::unused_peekable::UNUSED_PEEKABLE_INFO, + crate::unused_rounding::UNUSED_ROUNDING_INFO, + crate::unused_self::UNUSED_SELF_INFO, + crate::unused_unit::UNUSED_UNIT_INFO, + crate::unwrap::PANICKING_UNWRAP_INFO, + crate::unwrap::UNNECESSARY_UNWRAP_INFO, + crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO, + crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO, + crate::use_self::USE_SELF_INFO, + crate::useless_conversion::USELESS_CONVERSION_INFO, + crate::vec::USELESS_VEC_INFO, + crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO, + crate::wildcard_imports::ENUM_GLOB_USE_INFO, + crate::wildcard_imports::WILDCARD_IMPORTS_INFO, + crate::write::PRINTLN_EMPTY_STRING_INFO, + crate::write::PRINT_LITERAL_INFO, + crate::write::PRINT_STDERR_INFO, + crate::write::PRINT_STDOUT_INFO, + crate::write::PRINT_WITH_NEWLINE_INFO, + crate::write::USE_DEBUG_INFO, + crate::write::WRITELN_EMPTY_STRING_INFO, + crate::write::WRITE_LITERAL_INFO, + crate::write::WRITE_WITH_NEWLINE_INFO, + crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, + crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, +]; diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index c4e7f8bfe1e..18e742a85b3 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -9,6 +9,7 @@ use clippy_utils::{ }; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; +use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ @@ -274,9 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } let typeck = cx.typeck_results(); - let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { - x - } else { + let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { // The whole chain of reference operations has been seen if let Some((state, data)) = self.state.take() { report(cx, expr, state, data); @@ -806,30 +805,39 @@ fn walk_parents<'tcx>( .position(|arg| arg.hir_id == child_id) .zip(expr_sig(cx, func)) .and_then(|(i, sig)| { - sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty { - // Type inference for closures can depend on how they're called. Only go by the explicit - // types here. - Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), - None => { - if let ty::Param(param_ty) = ty.skip_binder().kind() { - needless_borrow_impl_arg_position( - cx, - possible_borrowers, - parent, - i, - *param_ty, - e, - precedence, - msrv, - ) - } else { - ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) - .position_for_arg() - } - }, + sig.input_with_hir(i).map(|(hir_ty, ty)| { + match hir_ty { + // Type inference for closures can depend on how they're called. Only go by the explicit + // types here. + Some(hir_ty) => { + binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()) + }, + None => { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id + && !call_is_qualified(func) + && let ty::Param(param_ty) = ty.skip_binder().kind() + { + needless_borrow_impl_arg_position( + cx, + possible_borrowers, + parent, + i, + *param_ty, + e, + precedence, + msrv, + ) + } else { + ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence) + .position_for_arg() + } + }, + } }) }), - ExprKind::MethodCall(_, receiver, args, _) => { + ExprKind::MethodCall(method, receiver, args, _) => { let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); if receiver.hir_id == child_id { // Check for calls to trait methods where the trait is implemented on a reference. @@ -863,7 +871,9 @@ fn walk_parents<'tcx>( } args.iter().position(|arg| arg.hir_id == child_id).map(|i| { let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1]; - if let ty::Param(param_ty) = ty.kind() { + // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739 + // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782 + if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() { needless_borrow_impl_arg_position( cx, possible_borrowers, @@ -1041,13 +1051,25 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool { v.0 } +fn call_is_qualified(expr: &Expr<'_>) -> bool { + if let ExprKind::Path(path) = &expr.kind { + match path { + QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()), + QPath::TypeRelative(_, segment) => segment.args.is_some(), + QPath::LangItem(..) => false, + } + } else { + false + } +} + // Checks whether: // * child is an expression of the form `&e` in an argument position requiring an `impl Trait` // * `e`'s type implements `Trait` and is copyable // If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`. // The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to // be moved, but it cannot be. -#[expect(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments, clippy::too_many_lines)] fn needless_borrow_impl_arg_position<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -1109,6 +1131,16 @@ fn needless_borrow_impl_arg_position<'tcx>( return Position::Other(precedence); } + // See: + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201 + // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232 + if projection_predicates + .iter() + .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate)) + { + return Position::Other(precedence); + } + // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same // elements are modified each time `check_referent` is called. let mut substs_with_referent_ty = substs_with_expr_ty.to_vec(); @@ -1188,6 +1220,37 @@ fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool { }) } +fn is_mixed_projection_predicate<'tcx>( + cx: &LateContext<'tcx>, + callee_def_id: DefId, + projection_predicate: &ProjectionPredicate<'tcx>, +) -> bool { + let generics = cx.tcx.generics_of(callee_def_id); + // The predicate requires the projected type to equal a type parameter from the parent context. + if let Some(term_ty) = projection_predicate.term.ty() + && let ty::Param(term_param_ty) = term_ty.kind() + && (term_param_ty.index as usize) < generics.parent_count + { + // The inner-most self type is a type parameter from the current function. + let mut projection_ty = projection_predicate.projection_ty; + loop { + match projection_ty.self_ty().kind() { + ty::Projection(inner_projection_ty) => { + projection_ty = *inner_projection_ty; + } + ty::Param(param_ty) => { + return (param_ty.index as usize) >= generics.parent_count; + } + _ => { + return false; + } + } + } + } else { + false + } +} + fn referent_used_exactly_once<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, @@ -1199,6 +1262,8 @@ fn referent_used_exactly_once<'tcx>( && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index) && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind && !place.has_deref() + // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710) + && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none() { let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id); if possible_borrowers @@ -1316,6 +1381,7 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc continue; }, ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty), + ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty), ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => { Position::ReborrowStable(precedence).into() }, @@ -1342,11 +1408,9 @@ fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedenc | ty::Closure(..) | ty::Never | ty::Tuple(_) - | ty::Projection(_) => Position::DerefStable( - precedence, - ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds()), - ) - .into(), + | ty::Projection(_) => { + Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into() + }, }; } } diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index 5ab7144e290..68122b4cef5 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::macro_backtrace; use rustc_data_structures::fx::FxHashSet; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty}; use rustc_lint::{LateContext, LateLintPass}; @@ -89,7 +88,7 @@ impl DisallowedMacros { &format!("use of a disallowed macro `{}`", conf.path()), |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }, ); @@ -104,7 +103,7 @@ impl LateLintPass<'_> for DisallowedMacros { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6ac85606d9c..ca8671c8f1a 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, get_parent_expr, path_def_id}; -use rustc_hir::def::{Namespace, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -79,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.disallowed.insert(id, index); } } @@ -104,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { if let Some(reason) = conf.reason() { - diag.note(&format!("{reason} (from clippy.toml)")); + diag.note(reason); } }); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index c7131fc164d..aee3d8c4f08 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def::{Namespace, Res}; +use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -53,8 +53,8 @@ declare_clippy_lint! { #[derive(Clone, Debug)] pub struct DisallowedTypes { conf_disallowed: Vec<conf::DisallowedPath>, - def_ids: FxHashMap<DefId, Option<String>>, - prim_tys: FxHashMap<PrimTy, Option<String>>, + def_ids: FxHashMap<DefId, usize>, + prim_tys: FxHashMap<PrimTy, usize>, } impl DisallowedTypes { @@ -69,13 +69,13 @@ impl DisallowedTypes { fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { match res { Res::Def(_, did) => { - if let Some(reason) = self.def_ids.get(did) { - emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref()); + if let Some(&index) = self.def_ids.get(did) { + emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]); } }, Res::PrimTy(prim) => { - if let Some(reason) = self.prim_tys.get(prim) { - emit(cx, prim.name_str(), span, reason.as_deref()); + if let Some(&index) = self.prim_tys.get(prim) { + emit(cx, prim.name_str(), span, &self.conf_disallowed[index]); } }, _ => {}, @@ -87,17 +87,19 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { + for (index, conf) in self.conf_disallowed.iter().enumerate() { let segs: Vec<_> = conf.path().split("::").collect(); - let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)")); - match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) { - Res::Def(_, id) => { - self.def_ids.insert(id, reason); - }, - Res::PrimTy(ty) => { - self.prim_tys.insert(ty, reason); - }, - _ => {}, + + for res in clippy_utils::def_path_res(cx, &segs) { + match res { + Res::Def(_, id) => { + self.def_ids.insert(id, index); + }, + Res::PrimTy(ty) => { + self.prim_tys.insert(ty, index); + }, + _ => {}, + } } } } @@ -119,14 +121,14 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { } } -fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) { +fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) { span_lint_and_then( cx, DISALLOWED_TYPES, span, &format!("`{name}` is not allowed according to config"), |diag| { - if let Some(reason) = reason { + if let Some(reason) = conf.reason() { diag.note(reason); } }, diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index daaab79fef9..4557e432885 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -11,7 +11,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle}; +use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -221,6 +221,42 @@ declare_clippy_lint! { "possible typo for an intra-doc link" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the doc comments of publicly visible + /// safe functions and traits and warns if there is a `# Safety` section. + /// + /// ### Why is this bad? + /// Safe functions and traits are safe to implement and therefore do not + /// need to describe safety preconditions that users are required to uphold. + /// + /// ### Examples + /// ```rust + ///# type Universe = (); + /// /// # Safety + /// /// + /// /// This function should not be called before the horsemen are ready. + /// pub fn start_apocalypse_but_safely(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + /// + /// The function is safe, so there shouldn't be any preconditions + /// that have to be explained for safety reasons. + /// + /// ```rust + ///# type Universe = (); + /// /// This function should really be documented + /// pub fn start_apocalypse(u: &mut Universe) { + /// unimplemented!(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub UNNECESSARY_SAFETY_DOC, + style, + "`pub fn` or `pub trait` with `# Safety` docs" +} + #[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { @@ -243,7 +279,8 @@ impl_lint_pass!(DocMarkdown => [ MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, - NEEDLESS_DOCTEST_MAIN + NEEDLESS_DOCTEST_MAIN, + UNNECESSARY_SAFETY_DOC, ]); impl<'tcx> LateLintPass<'tcx> for DocMarkdown { @@ -254,7 +291,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; match item.kind { hir::ItemKind::Fn(ref sig, _, body_id) => { if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { @@ -265,29 +302,26 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.owner_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } }, hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, - hir::ItemKind::Trait(_, unsafety, ..) => { - if !headers.safety && unsafety == hir::Unsafety::Unsafe { - span_lint( - cx, - MISSING_SAFETY_DOC, - item.span, - "docs for unsafe trait missing `# Safety` section", - ); - } + hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), }, _ => (), } @@ -301,17 +335,17 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { if !in_external_macro(cx.tcx.sess, item.span) { - lint_for_missing_headers(cx, item.owner_id.def_id, item.span, sig, headers, None, None); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None); } } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let headers = check_attrs(cx, &self.valid_idents, attrs); + let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return }; if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) { return; } @@ -323,23 +357,14 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { panic_span: None, }; fpu.visit_expr(body.value); - lint_for_missing_headers( - cx, - item.owner_id.def_id, - item.span, - sig, - headers, - Some(body_id), - fpu.panic_span, - ); + lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span); } } } -fn lint_for_missing_headers<'tcx>( - cx: &LateContext<'tcx>, +fn lint_for_missing_headers( + cx: &LateContext<'_>, def_id: LocalDefId, - span: impl Into<MultiSpan> + Copy, sig: &hir::FnSig<'_>, headers: DocHeaders, body_id: Option<hir::BodyId>, @@ -359,13 +384,21 @@ fn lint_for_missing_headers<'tcx>( return; } - if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe { - span_lint( + let span = cx.tcx.def_span(def_id); + match (headers.safety, sig.header.unsafety) { + (false, hir::Unsafety::Unsafe) => span_lint( cx, MISSING_SAFETY_DOC, span, "unsafe function's docs miss `# Safety` section", - ); + ), + (true, hir::Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + span, + "safe function's docs have unnecessary `# Safety` section", + ), + _ => (), } if !headers.panics && panic_span.is_some() { span_lint_and_note( @@ -467,7 +500,7 @@ struct DocHeaders { panics: bool, } -fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders { +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> { use pulldown_cmark::{BrokenLink, CowStr, Options}; /// We don't want the parser to choke on intra doc links. Since we don't /// actually care about rendering them, just pretend that all broken links are @@ -488,11 +521,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs } else if attr.has_name(sym::doc) { // ignore mix of sugared and non-sugared doc // don't trigger the safety or errors check - return DocHeaders { - safety: true, - errors: true, - panics: true, - }; + return None; } } @@ -504,7 +533,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs } if doc.is_empty() { - return DocHeaders::default(); + return Some(DocHeaders::default()); } let mut cb = fake_broken_link_callback; @@ -527,7 +556,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs (previous, current) => Err(((previous, previous_range), (current, current_range))), } }); - check_doc(cx, valid_idents, events, &spans) + Some(check_doc(cx, valid_idents, events, &spans)) } const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 223545fa798..b77b5621b4c 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -250,7 +250,7 @@ impl LateLintPass<'_> for EnumVariantNames { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); if !item.span.from_expansion() && is_present_in_source(cx, item.span) { - if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() { + if let Some((mod_name, mod_camel)) = self.modules.last() { // constants don't have surrounding modules if !mod_camel.is_empty() { if mod_name == &item.ident.name { diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index b40cb7cddaf..c691e6c5402 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -1,7 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::implements_trait; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -67,16 +66,14 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T impl<'tcx> LateLintPass<'tcx> for PatternEquality { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Let(let_expr) = expr.kind; - if unary_pattern(let_expr.pat); + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Let(let_expr) = expr.kind + && unary_pattern(let_expr.pat) { let exp_ty = cx.typeck_results().expr_ty(let_expr.init); let pat_ty = cx.typeck_results().pat_ty(let_expr.pat); - if is_structural_partial_eq(cx, exp_ty, pat_ty); - then { + let mut applicability = Applicability::MachineApplicable; - let mut applicability = Applicability::MachineApplicable; + if is_structural_partial_eq(cx, exp_ty, pat_ty) { let pat_str = match let_expr.pat.kind { PatKind::Struct(..) => format!( "({})", @@ -96,6 +93,20 @@ impl<'tcx> LateLintPass<'tcx> for PatternEquality { ), applicability, ); + } else { + span_lint_and_sugg( + cx, + EQUATABLE_IF_LET, + expr.span, + "this pattern matching can be expressed using `matches!`", + "try", + format!( + "matches!({}, {})", + snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0, + snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0, + ), + applicability, + ); } } } diff --git a/clippy_lints/src/escape.rs b/clippy_lints/src/escape.rs index 7f1a4c4beb1..1d09adec12f 100644 --- a/clippy_lints/src/escape.rs +++ b/clippy_lints/src/escape.rs @@ -176,13 +176,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> { diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 453471c8cdd..fc2912f696e 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -1,8 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool}; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::{sym, Span}; +use rustc_span::Span; +use rustc_target::spec::abi::Abi; declare_clippy_lint! { /// ### What it does @@ -83,6 +86,12 @@ pub struct ExcessiveBools { max_fn_params_bools: u64, } +#[derive(Eq, PartialEq, Debug, Copy, Clone)] +enum Kind { + Struct, + Fn, +} + impl ExcessiveBools { #[must_use] pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self { @@ -92,21 +101,20 @@ impl ExcessiveBools { } } - fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) { - match fn_sig.header.ext { - Extern::Implicit(_) | Extern::Explicit(_, _) => return, - Extern::None => (), + fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool { + if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() { + (if Kind::Fn == kind { + self.max_fn_params_bools + } else { + self.max_struct_bools + }) < bools + } else { + false } + } - let fn_sig_bools = fn_sig - .decl - .inputs - .iter() - .filter(|param| is_bool_ty(¶m.ty)) - .count() - .try_into() - .unwrap(); - if self.max_fn_params_bools < fn_sig_bools { + fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) { + if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) { span_lint_and_help( cx, FN_PARAMS_EXCESSIVE_BOOLS, @@ -121,56 +129,55 @@ impl ExcessiveBools { impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]); -fn is_bool_ty(ty: &Ty) -> bool { - if let TyKind::Path(None, path) = &ty.kind { - if let [name] = path.segments.as_slice() { - return name.ident.name == sym::bool; +impl<'tcx> LateLintPass<'tcx> for ExcessiveBools { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if item.span.from_expansion() { + return; + } + if let ItemKind::Struct(variant_data, _) = &item.kind { + if has_repr_attr(cx, item.hir_id()) { + return; + } + + if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) { + span_lint_and_help( + cx, + STRUCT_EXCESSIVE_BOOLS, + item.span, + &format!("more than {} bools in a struct", self.max_struct_bools), + None, + "consider using a state machine or refactoring bools into two-variant enums", + ); + } } } - false -} -impl EarlyLintPass for ExcessiveBools { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if item.span.from_expansion() { - return; + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) { + // functions with a body are already checked by `check_fn` + if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind + && fn_sig.header.abi == Abi::Rust + { + self.check_fn_sig(cx, fn_sig.decl, fn_sig.span); } - match &item.kind { - ItemKind::Struct(variant_data, _) => { - if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) { - return; - } + } - let struct_bools = variant_data - .fields() - .iter() - .filter(|field| is_bool_ty(&field.ty)) - .count() - .try_into() - .unwrap(); - if self.max_struct_bools < struct_bools { - span_lint_and_help( - cx, - STRUCT_EXCESSIVE_BOOLS, - item.span, - &format!("more than {} bools in a struct", self.max_struct_bools), - None, - "consider using a state machine or refactoring bools into two-variant enums", - ); - } - }, - ItemKind::Impl(box Impl { - of_trait: None, items, .. - }) - | ItemKind::Trait(box Trait { items, .. }) => { - for item in items { - if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind { - self.check_fn_sig(cx, sig, item.span); - } - } - }, - ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span), - _ => (), + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let Some(fn_header) = fn_kind.header() + && fn_header.abi == Abi::Rust + && get_parent_as_impl(cx.tcx, hir_id) + .map_or(true, + |impl_item| impl_item.of_trait.is_none() + ) + { + self.check_fn_sig(cx, fn_decl, span); } } } diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 0a633f242a5..9a1058470e1 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { } } -fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { +fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{Expr, ImplItemKind}; diff --git a/clippy_lints/src/from_raw_with_void_ptr.rs b/clippy_lints/src/from_raw_with_void_ptr.rs new file mode 100644 index 00000000000..00f5ba56496 --- /dev/null +++ b/clippy_lints/src/from_raw_with_void_ptr.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_c_void; +use clippy_utils::{match_def_path, path_def_id, paths}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::RawPtr; +use rustc_middle::ty::TypeAndMut; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)` + /// + /// ### Why is this bad? + /// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer. + /// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box<T>`, so if you pass a `*mut c_void` you will get a `Box<c_void>` (and similarly for `Rc`, `Arc` and `Weak`). + /// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case. + /// + /// ### Example + /// ```rust + /// # use std::ffi::c_void; + /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr) }; + /// ``` + /// Use instead: + /// ```rust + /// # use std::ffi::c_void; + /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void; + /// let _ = unsafe { Box::from_raw(ptr as *mut usize) }; + /// ``` + /// + #[clippy::version = "1.66.0"] + pub FROM_RAW_WITH_VOID_PTR, + suspicious, + "creating a `Box` from a void raw pointer" +} +declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]); + +impl LateLintPass<'_> for FromRawWithVoidPtr { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Call(box_from_raw, [arg]) = expr.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind + && seg.ident.name == sym!(from_raw) + && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id)) + && let arg_kind = cx.typeck_results().expr_ty(arg).kind() + && let RawPtr(TypeAndMut { ty, .. }) = arg_kind + && is_c_void(cx, *ty) { + let msg = format!("creating a `{type_str}` from a void raw pointer"); + span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type"); + } + } +} + +/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types. +/// Returns a static string slice with the name of the type, if one was found. +fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { + // Box + if Some(def_id) == cx.tcx.lang_items().owned_box() { + return Some("Box"); + } + + if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) { + if symbol == sym::Arc { + return Some("Arc"); + } else if symbol == sym::Rc { + return Some("Rc"); + } + } + + if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) { + Some("Weak") + } else { + None + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 90911e0bf25..ae0e0833446 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -254,7 +254,7 @@ declare_clippy_lint! { /// Ok(()) /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub RESULT_LARGE_ERR, perf, "function returning `Result` with large `Err` type" diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index bff69f91518..d22bede36b4 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -50,7 +50,9 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); - } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none() + } else if is_public + && !is_proc_macro(cx.sess(), attrs) + && trait_ref_of_method(cx, item.owner_id.def_id).is_none() { check_must_use_candidate( cx, @@ -175,7 +177,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) return false; // ignore `_` patterns } if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) { - is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), pat.span, tys) + is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys) } else { false } @@ -183,7 +185,7 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc]; -fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool { +fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool { match *ty.kind() { // primitive types are never mutable ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, @@ -192,12 +194,12 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &m || KNOWN_WRAPPER_TYS .iter() .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did())) - && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys)) + && substs.types().any(|ty| is_mutable_ty(cx, ty, tys)) }, - ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)), - ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys), + ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)), + ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys), ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys) + mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys) }, // calling something constitutes a side effect, so return true on all callables // also never calls need not be used, so return true for them, too @@ -225,12 +227,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in args { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); @@ -243,12 +240,7 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo let mut tys = DefIdSet::default(); for arg in std::iter::once(receiver).chain(args.iter()) { if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id()) - && is_mutable_ty( - cx, - cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), - arg.span, - &mut tys, - ) + && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys) && is_mutated_static(arg) { return ControlFlow::Break(()); diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index 5c63fb2acb1..f7e30b051a6 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -2,12 +2,12 @@ use rustc_errors::Diagnostic; use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, Adt, Ty}; use rustc_span::{sym, Span}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item}; +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; @@ -84,17 +84,57 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S } fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { - let ty_size = approx_ty_size(cx, err_ty); - if ty_size >= large_err_threshold { - span_lint_and_then( - cx, - RESULT_LARGE_ERR, - hir_ty_span, - "the `Err`-variant returned from this function is very large", - |diag: &mut Diagnostic| { - diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); - diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); - }, - ); + if_chain! { + if let Adt(adt, subst) = err_ty.kind(); + if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); + if let Some(hir::Node::Item(item)) = cx + .tcx + .hir() + .find_by_def_id(local_def_id); + if let hir::ItemKind::Enum(ref def, _) = item.kind; + then { + let variants_size = AdtVariantInfo::new(cx, *adt, subst); + if variants_size[0].size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag| { + diag.span_label( + def.variants[variants_size[0].ind].span, + format!("the largest variant contains at least {} bytes", variants_size[0].size), + ); + + for variant in &variants_size[1..] { + if variant.size >= large_err_threshold { + let variant_def = &def.variants[variant.ind]; + diag.span_label( + variant_def.span, + format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), + ); + } + } + + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + } + ); + } + } + else { + let ty_size = approx_ty_size(cx, err_ty); + if ty_size >= large_err_threshold { + span_lint_and_then( + cx, + RESULT_LARGE_ERR, + hir_ty_span, + "the `Err`-variant returned from this function is very large", + |diag: &mut Diagnostic| { + diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes")); + diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); + }, + ); + } + } } } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index 94e06cf704b..64a4a3fa741 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -66,8 +66,8 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; - fn suggestion<'tcx>( - cx: &LateContext<'tcx>, + fn suggestion( + cx: &LateContext<'_>, diag: &mut Diagnostic, generics_span: Span, generics_suggestion_span: Span, diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index c7b5badaae5..0d5099bde6d 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -207,8 +207,8 @@ impl SliceLintInformation { } } -fn filter_lintable_slices<'a, 'tcx>( - cx: &'a LateContext<'tcx>, +fn filter_lintable_slices<'tcx>( + cx: &LateContext<'tcx>, slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>, max_suggested_slice: u64, scope: &'tcx hir::Expr<'tcx>, diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs index af40a5a8187..4cd7dff4cfd 100644 --- a/clippy_lints/src/indexing_slicing.rs +++ b/clippy_lints/src/indexing_slicing.rs @@ -171,11 +171,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { /// Returns a tuple of options with the start and end (exclusive) values of /// the range. If the start or end is not constant, None is returned. -fn to_const_range<'tcx>( - cx: &LateContext<'tcx>, - range: higher::Range<'_>, - array_size: u128, -) -> (Option<u128>, Option<u128>) { +fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) { let s = range .start .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c)); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs new file mode 100644 index 00000000000..60754b224fc --- /dev/null +++ b/clippy_lints/src/instant_subtraction.rs @@ -0,0 +1,184 @@ +use clippy_utils::{ + diagnostics::{self, span_lint_and_sugg}, + meets_msrv, msrvs, source, + sugg::Sugg, + ty, +}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{source_map::Spanned, sym}; + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between `Instant::now()` and another `Instant`. + /// + /// ### Why is this bad? + /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns + /// as `Instant` subtraction saturates. + /// + /// `prev_instant.elapsed()` also more clearly signals intention. + /// + /// ### Example + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = Instant::now() - prev_instant; + /// ``` + /// Use instead: + /// ```rust + /// use std::time::Instant; + /// let prev_instant = Instant::now(); + /// let duration = prev_instant.elapsed(); + /// ``` + #[clippy::version = "1.65.0"] + pub MANUAL_INSTANT_ELAPSED, + pedantic, + "subtraction between `Instant::now()` and previous `Instant`" +} + +declare_clippy_lint! { + /// ### What it does + /// Lints subtraction between an [`Instant`] and a [`Duration`]. + /// + /// ### Why is this bad? + /// Unchecked subtraction could cause underflow on certain platforms, leading to + /// unintentional panics. + /// + /// ### Example + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now() - Duration::from_secs(5); + /// ``` + /// + /// Use instead: + /// ```rust + /// # use std::time::{Instant, Duration}; + /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5)); + /// ``` + /// + /// [`Duration`]: std::time::Duration + /// [`Instant::now()`]: std::time::Instant::now; + #[clippy::version = "1.65.0"] + pub UNCHECKED_DURATION_SUBTRACTION, + suspicious, + "finds unchecked subtraction of a 'Duration' from an 'Instant'" +} + +pub struct InstantSubtraction { + msrv: Option<RustcVersion>, +} + +impl InstantSubtraction { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]); + +impl LateLintPass<'_> for InstantSubtraction { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if let ExprKind::Binary( + Spanned { + node: BinOpKind::Sub, .. + }, + lhs, + rhs, + ) = expr.kind + { + if_chain! { + if is_instant_now_call(cx, lhs); + + if is_an_instant(cx, rhs); + if let Some(sugg) = Sugg::hir_opt(cx, rhs); + + then { + print_manual_instant_elapsed_sugg(cx, expr, sugg) + } else { + if_chain! { + if !expr.span.from_expansion(); + if meets_msrv(self.msrv, msrvs::TRY_FROM); + + if is_an_instant(cx, lhs); + if is_a_duration(cx, rhs); + + then { + print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr) + } + } + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { + if let ExprKind::Call(fn_expr, []) = expr_block.kind + && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) + && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) + { + true + } else { + false + } +} + +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + + match expr_ty.kind() { + rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT), + _ => false, + } +} + +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration) +} + +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { + span_lint_and_sugg( + cx, + MANUAL_INSTANT_ELAPSED, + expr.span, + "manual implementation of `Instant::elapsed`", + "try", + format!("{}.elapsed()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); +} + +fn print_unchecked_duration_subtraction_sugg( + cx: &LateContext<'_>, + left_expr: &Expr<'_>, + right_expr: &Expr<'_>, + expr: &Expr<'_>, +) { + let mut applicability = Applicability::MachineApplicable; + + let left_expr = + source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability); + let right_expr = source::snippet_with_applicability( + cx, + right_expr.span, + "std::time::Duration::from_secs(1)", + &mut applicability, + ); + + diagnostics::span_lint_and_sugg( + cx, + UNCHECKED_DURATION_SUBTRACTION, + expr.span, + "unchecked subtraction of a 'Duration' from an 'Instant'", + "try", + format!("{left_expr}.checked_sub({right_expr}).unwrap()"), + applicability, + ); +} diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index f793abdfda3..1b14e525d9a 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -63,58 +63,54 @@ impl IntPlusOne { fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> { match (binop, &lhs.kind, &rhs.kind) { // case where `x - 1 >= ...` or `-1 + x >= ...` - (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => { + (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => { match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) { // `-1 + x` - (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, // `x - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y + 1` or `... >= 1 + y` - (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) - if rhskind.node == BinOpKind::Add => - { + (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => { match (&rhslhs.kind, &rhsrhs.kind) { // `y + 1` and `1 + y` - (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, - (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, } }, // case where `x + 1 <= ...` or `1 + x <= ...` - (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) - if lhskind.node == BinOpKind::Add => - { + (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => { match (&lhslhs.kind, &lhsrhs.kind) { // `1 + x` and `x + 1` - (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => { + (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs) }, - (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs) }, _ => None, } }, // case where `... >= y - 1` or `... >= -1 + y` - (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => { + (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => { match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) { // `-1 + y` - (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => { + (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => { Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs) }, // `y - 1` - (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => { + (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => { Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs) }, _ => None, diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs index 0ef77e03de9..6ea637412d5 100644 --- a/clippy_lints/src/invalid_upcast_comparisons.rs +++ b/clippy_lints/src/invalid_upcast_comparisons.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]); -fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> { +fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> { if let ExprKind::Cast(cast_exp, _) = expr.kind { let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp); let cast_ty = cx.typeck_results().expr_ty(expr); diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs index 06e95728549..b18456ee523 100644 --- a/clippy_lints/src/large_enum_variant.rs +++ b/clippy_lints/src/large_enum_variant.rs @@ -1,12 +1,15 @@ //! lint when there is a large size difference between variants on an enum use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy}; +use clippy_utils::{ + diagnostics::span_lint_and_then, + ty::{approx_ty_size, is_copy, AdtVariantInfo}, +}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty}; +use rustc_middle::ty::{Adt, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; @@ -72,49 +75,6 @@ impl LargeEnumVariant { } } -struct FieldInfo { - ind: usize, - size: u64, -} - -struct VariantInfo { - ind: usize, - size: u64, - fields_size: Vec<FieldInfo>, -} - -fn variants_size<'tcx>( - cx: &LateContext<'tcx>, - adt: AdtDef<'tcx>, - subst: &'tcx List<GenericArg<'tcx>>, -) -> Vec<VariantInfo> { - let mut variants_size = adt - .variants() - .iter() - .enumerate() - .map(|(i, variant)| { - let mut fields_size = variant - .fields - .iter() - .enumerate() - .map(|(i, f)| FieldInfo { - ind: i, - size: approx_ty_size(cx, f.ty(cx.tcx, subst)), - }) - .collect::<Vec<_>>(); - fields_size.sort_by(|a, b| (a.size.cmp(&b.size))); - - VariantInfo { - ind: i, - size: fields_size.iter().map(|info| info.size).sum(), - fields_size, - } - }) - .collect::<Vec<_>>(); - variants_size.sort_by(|a, b| (b.size.cmp(&a.size))); - variants_size -} - impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]); impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { @@ -130,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { if adt.variants().len() <= 1 { return; } - let variants_size = variants_size(cx, *adt, subst); + let variants_size = AdtVariantInfo::new(cx, *adt, subst); let mut difference = variants_size[0].size - variants_size[1].size; if difference > self.maximum_size_difference_allowed { @@ -173,16 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant { .fields_size .iter() .rev() - .map_while(|val| { + .map_while(|&(ind, size)| { if difference > self.maximum_size_difference_allowed { - difference = difference.saturating_sub(val.size); + difference = difference.saturating_sub(size); Some(( - fields[val.ind].ty.span, + fields[ind].ty.span, format!( "Box<{}>", snippet_with_applicability( cx, - fields[val.ind].ty.span, + fields[ind].ty.span, "..", &mut applicability ) diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index b0cba40c27a..4c133c06a15 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -366,8 +366,7 @@ fn check_for_is_empty<'tcx>( } fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) { - if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) - { + if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) { // check if we are in an is_empty() method if let Some(name) = get_item_name(cx, method) { if name.as_str() == "is_empty" { diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs index b7798b1c1d7..61f87b91400 100644 --- a/clippy_lints/src/let_underscore.rs +++ b/clippy_lints/src/let_underscore.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type}; +use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_must_use_func_call, paths}; -use if_chain::if_chain; use rustc_hir::{Local, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Symbol}; declare_clippy_lint! { /// ### What it does @@ -30,13 +28,14 @@ declare_clippy_lint! { #[clippy::version = "1.42.0"] pub LET_UNDERSCORE_MUST_USE, restriction, - "non-binding let on a `#[must_use]` expression" + "non-binding `let` on a `#[must_use]` expression" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = sync_lock`. - /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`. + /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in + /// `parking_lot`. For `std` locks see the `rustc` lint + /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock) /// /// ### Why is this bad? /// This statement immediately drops the lock instead of @@ -57,50 +56,41 @@ declare_clippy_lint! { #[clippy::version = "1.43.0"] pub LET_UNDERSCORE_LOCK, correctness, - "non-binding let on a synchronization lock" + "non-binding `let` on a synchronization lock" } declare_clippy_lint! { /// ### What it does - /// Checks for `let _ = <expr>` - /// where expr has a type that implements `Drop` + /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future` /// /// ### Why is this bad? - /// This statement immediately drops the initializer - /// expression instead of extending its lifetime to the end of the scope, which - /// is often not intended. To extend the expression's lifetime to the end of the - /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to - /// explicitly drop the expression, `std::mem::drop` conveys your intention - /// better and is less error-prone. + /// Futures must be polled for work to be done. The original intention was most likely to await the future + /// and ignore the resulting value. /// /// ### Example /// ```rust - /// # struct DroppableItem; - /// { - /// let _ = DroppableItem; - /// // ^ dropped here - /// /* more code */ + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo(); /// ``` /// /// Use instead: /// ```rust - /// # struct DroppableItem; - /// { - /// let _droppable = DroppableItem; - /// /* more code */ - /// // dropped at end of scope + /// # async fn context() { + /// async fn foo() -> Result<(), ()> { + /// Ok(()) /// } + /// let _ = foo().await; + /// # } /// ``` - #[clippy::version = "1.50.0"] - pub LET_UNDERSCORE_DROP, - pedantic, - "non-binding let on a type that implements `Drop`" + #[clippy::version = "1.66"] + pub LET_UNDERSCORE_FUTURE, + suspicious, + "non-binding `let` on a future" } -declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]); - -const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard]; +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]); const SYNC_GUARD_PATHS: [&[&str]; 3] = [ &paths::PARKING_LOT_MUTEX_GUARD, @@ -110,64 +100,53 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { - if in_external_macro(cx.tcx.sess, local.span) { - return; - } - - if_chain! { - if let PatKind::Wild = local.pat.kind; - if let Some(init) = local.init; - then { - let init_ty = cx.typeck_results().expr_ty(init); - let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { - GenericArgKind::Type(inner_ty) => { - SYNC_GUARD_SYMS - .iter() - .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym)) - || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)) - }, - - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, - }); - if contains_sync_guard { - span_lint_and_help( - cx, - LET_UNDERSCORE_LOCK, - local.span, - "non-binding let on a synchronization lock", - None, - "consider using an underscore-prefixed named \ - binding or dropping explicitly with `std::mem::drop`", - ); - } else if init_ty.needs_drop(cx.tcx, cx.param_env) { - span_lint_and_help( - cx, - LET_UNDERSCORE_DROP, - local.span, - "non-binding `let` on a type that implements `Drop`", - None, - "consider using an underscore-prefixed named \ + if !in_external_macro(cx.tcx.sess, local.span) + && let PatKind::Wild = local.pat.kind + && let Some(init) = local.init + { + let init_ty = cx.typeck_results().expr_ty(init); + let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() { + GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)), + GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false, + }); + if contains_sync_guard { + span_lint_and_help( + cx, + LET_UNDERSCORE_LOCK, + local.span, + "non-binding `let` on a synchronization lock", + None, + "consider using an underscore-prefixed named \ binding or dropping explicitly with `std::mem::drop`", - ); - } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on an expression with `#[must_use]` type", - None, - "consider explicitly using expression value", - ); - } else if is_must_use_func_call(cx, init) { - span_lint_and_help( - cx, - LET_UNDERSCORE_MUST_USE, - local.span, - "non-binding let on a result of a `#[must_use]` function", - None, - "consider explicitly using function result", - ); - } + ); + } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { + span_lint_and_help( + cx, + LET_UNDERSCORE_FUTURE, + local.span, + "non-binding `let` on a future", + None, + "consider awaiting the future or dropping explicitly with `std::mem::drop`" + ); + } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on an expression with `#[must_use]` type", + None, + "consider explicitly using expression value", + ); + } else if is_must_use_func_call(cx, init) { + span_lint_and_help( + cx, + LET_UNDERSCORE_MUST_USE, + local.span, + "non-binding `let` on a result of a `#[must_use]` function", + None, + "consider explicitly using function result", + ); } } } diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs deleted file mode 100644 index c455e1561b7..00000000000 --- a/clippy_lints/src/lib.register_all.rs +++ /dev/null @@ -1,368 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::all", Some("clippy_all"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(box_default::BOX_DEFAULT), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(entry::MAP_ENTRY), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(escape::BOXED_LOCAL), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(format::USELESS_FORMAT), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(format_args::UNUSED_FORMAT_SPECS), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_FIRST), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::USELESS_ASREF), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::CMP_OWNED), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::IDENTITY_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ptr::PTR_ARG), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(reference::DEREF_ADDROF), - LintId::of(regex::INVALID_REGEX), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(swap::MANUAL_SWAP), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(unwrap::PANICKING_UNWRAP), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_cargo.rs b/clippy_lints/src/lib.register_cargo.rs deleted file mode 100644 index c890523fe5a..00000000000 --- a/clippy_lints/src/lib.register_cargo.rs +++ /dev/null @@ -1,11 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::cargo", Some("clippy_cargo"), vec![ - LintId::of(cargo::CARGO_COMMON_METADATA), - LintId::of(cargo::MULTIPLE_CRATE_VERSIONS), - LintId::of(cargo::NEGATIVE_FEATURE_NAMES), - LintId::of(cargo::REDUNDANT_FEATURE_NAMES), - LintId::of(cargo::WILDCARD_DEPENDENCIES), -]) diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs deleted file mode 100644 index 8be9dc4baf1..00000000000 --- a/clippy_lints/src/lib.register_complexity.rs +++ /dev/null @@ -1,111 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ - LintId::of(attrs::DEPRECATED_CFG_ATTR), - LintId::of(booleans::NONMINIMAL_BOOL), - LintId::of(borrow_deref_ref::BORROW_DEREF_REF), - LintId::of(casts::CHAR_LIT_AS_U8), - LintId::of(casts::UNNECESSARY_CAST), - LintId::of(dereference::EXPLICIT_AUTO_DEREF), - LintId::of(derivable_impls::DERIVABLE_IMPLS), - LintId::of(double_parens::DOUBLE_PARENS), - LintId::of(explicit_write::EXPLICIT_WRITE), - LintId::of(format::USELESS_FORMAT), - LintId::of(format_args::UNUSED_FORMAT_SPECS), - LintId::of(functions::TOO_MANY_ARGUMENTS), - LintId::of(int_plus_one::INT_PLUS_ONE), - LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES), - LintId::of(lifetimes::NEEDLESS_LIFETIMES), - LintId::of(loops::EXPLICIT_COUNTER_LOOP), - LintId::of(loops::MANUAL_FIND), - LintId::of(loops::MANUAL_FLATTEN), - LintId::of(loops::SINGLE_ELEMENT_LOOP), - LintId::of(loops::WHILE_LET_LOOP), - LintId::of(manual_clamp::MANUAL_CLAMP), - LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID), - LintId::of(manual_strip::MANUAL_STRIP), - LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN), - LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), - LintId::of(matches::MANUAL_FILTER), - LintId::of(matches::MANUAL_UNWRAP_OR), - LintId::of(matches::MATCH_AS_REF), - LintId::of(matches::MATCH_SINGLE_BINDING), - LintId::of(matches::NEEDLESS_MATCH), - LintId::of(matches::WILDCARD_IN_OR_PATTERNS), - LintId::of(methods::BIND_INSTEAD_OF_MAP), - LintId::of(methods::BYTES_COUNT_TO_LEN), - LintId::of(methods::CLONE_ON_COPY), - LintId::of(methods::FILTER_MAP_IDENTITY), - LintId::of(methods::FILTER_NEXT), - LintId::of(methods::FLAT_MAP_IDENTITY), - LintId::of(methods::GET_LAST_WITH_LEN), - LintId::of(methods::INSPECT_FOR_EACH), - LintId::of(methods::ITER_COUNT), - LintId::of(methods::ITER_KV_MAP), - LintId::of(methods::MANUAL_FILTER_MAP), - LintId::of(methods::MANUAL_FIND_MAP), - LintId::of(methods::MANUAL_SPLIT_ONCE), - LintId::of(methods::MAP_FLATTEN), - LintId::of(methods::MAP_IDENTITY), - LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), - LintId::of(methods::NEEDLESS_OPTION_TAKE), - LintId::of(methods::NEEDLESS_SPLITN), - LintId::of(methods::OPTION_AS_REF_DEREF), - LintId::of(methods::OPTION_FILTER_MAP), - LintId::of(methods::OR_THEN_UNWRAP), - LintId::of(methods::RANGE_ZIP_WITH_LEN), - LintId::of(methods::REPEAT_ONCE), - LintId::of(methods::SEARCH_IS_SOME), - LintId::of(methods::SKIP_WHILE_NEXT), - LintId::of(methods::UNNECESSARY_FILTER_MAP), - LintId::of(methods::UNNECESSARY_FIND_MAP), - LintId::of(methods::UNNECESSARY_SORT_BY), - LintId::of(methods::USELESS_ASREF), - LintId::of(misc::SHORT_CIRCUIT_STATEMENT), - LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), - LintId::of(misc_early::ZERO_PREFIXED_LITERAL), - LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), - LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), - LintId::of(needless_bool::BOOL_COMPARISON), - LintId::of(needless_bool::NEEDLESS_BOOL), - LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE), - LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK), - LintId::of(needless_update::NEEDLESS_UPDATE), - LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), - LintId::of(no_effect::NO_EFFECT), - LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), - LintId::of(operators::DOUBLE_COMPARISONS), - LintId::of(operators::DURATION_SUBSEC), - LintId::of(operators::IDENTITY_OP), - LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), - LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), - LintId::of(precedence::PRECEDENCE), - LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), - LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), - LintId::of(redundant_slicing::REDUNDANT_SLICING), - LintId::of(reference::DEREF_ADDROF), - LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), - LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), - LintId::of(swap::MANUAL_SWAP), - LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT), - LintId::of(transmute::CROSSPOINTER_TRANSMUTE), - LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS), - LintId::of(transmute::TRANSMUTE_BYTES_TO_STR), - LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT), - LintId::of(transmute::TRANSMUTE_INT_TO_BOOL), - LintId::of(transmute::TRANSMUTE_INT_TO_CHAR), - LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT), - LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), - LintId::of(transmute::TRANSMUTE_PTR_TO_REF), - LintId::of(transmute::USELESS_TRANSMUTE), - LintId::of(types::BORROWED_BOX), - LintId::of(types::TYPE_COMPLEXITY), - LintId::of(types::VEC_BOX), - LintId::of(unit_types::UNIT_ARG), - LintId::of(unwrap::UNNECESSARY_UNWRAP), - LintId::of(useless_conversion::USELESS_CONVERSION), - LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), -]) diff --git a/clippy_lints/src/lib.register_correctness.rs b/clippy_lints/src/lib.register_correctness.rs deleted file mode 100644 index bb94037ec2e..00000000000 --- a/clippy_lints/src/lib.register_correctness.rs +++ /dev/null @@ -1,78 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![ - LintId::of(approx_const::APPROX_CONSTANT), - LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC), - LintId::of(attrs::DEPRECATED_SEMVER), - LintId::of(attrs::MISMATCHED_TARGET_OS), - LintId::of(attrs::USELESS_ATTRIBUTE), - LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), - LintId::of(casts::CAST_REF_TO_MUT), - LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), - LintId::of(copies::IFS_SAME_COND), - LintId::of(copies::IF_SAME_THEN_ELSE), - LintId::of(derive::DERIVE_HASH_XOR_EQ), - LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), - LintId::of(drop_forget_ref::DROP_COPY), - LintId::of(drop_forget_ref::DROP_REF), - LintId::of(drop_forget_ref::FORGET_COPY), - LintId::of(drop_forget_ref::FORGET_REF), - LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), - LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), - LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), - LintId::of(formatting::POSSIBLE_MISSING_COMMA), - LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), - LintId::of(if_let_mutex::IF_LET_MUTEX), - LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), - LintId::of(infinite_iter::INFINITE_ITER), - LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY), - LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY), - LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED), - LintId::of(let_underscore::LET_UNDERSCORE_LOCK), - LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES), - LintId::of(loops::ITER_NEXT_LOOP), - LintId::of(loops::NEVER_LOOP), - LintId::of(loops::WHILE_IMMUTABLE_CONDITION), - LintId::of(matches::MATCH_STR_CASE_MISMATCH), - LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), - LintId::of(methods::CLONE_DOUBLE_REF), - LintId::of(methods::ITERATOR_STEP_BY_ZERO), - LintId::of(methods::NONSENSICAL_OPEN_OPTIONS), - LintId::of(methods::SUSPICIOUS_SPLITN), - LintId::of(methods::UNINIT_ASSUMED_INIT), - LintId::of(methods::UNIT_HASH), - LintId::of(methods::VEC_RESIZE_TO_ZERO), - LintId::of(methods::ZST_OFFSET), - LintId::of(minmax::MIN_MAX), - LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), - LintId::of(operators::ABSURD_EXTREME_COMPARISONS), - LintId::of(operators::BAD_BIT_MASK), - LintId::of(operators::CMP_NAN), - LintId::of(operators::EQ_OP), - LintId::of(operators::ERASING_OP), - LintId::of(operators::INEFFECTIVE_BIT_MASK), - LintId::of(operators::MODULO_ONE), - LintId::of(operators::SELF_ASSIGNMENT), - LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), - LintId::of(ptr::INVALID_NULL_PTR_USAGE), - LintId::of(ptr::MUT_FROM_REF), - LintId::of(ranges::REVERSED_EMPTY_RANGES), - LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), - LintId::of(regex::INVALID_REGEX), - LintId::of(serde_api::SERDE_API_MISUSE), - LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), - LintId::of(swap::ALMOST_SWAPPED), - LintId::of(transmute::TRANSMUTING_NULL), - LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), - LintId::of(transmute::WRONG_TRANSMUTE), - LintId::of(unicode::INVISIBLE_CHARACTERS), - LintId::of(uninit_vec::UNINIT_VEC), - LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), - LintId::of(unit_types::UNIT_CMP), - LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), - LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), - LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), - LintId::of(unwrap::PANICKING_UNWRAP), -]) diff --git a/clippy_lints/src/lib.register_internal.rs b/clippy_lints/src/lib.register_internal.rs deleted file mode 100644 index 40c94c6e8d3..00000000000 --- a/clippy_lints/src/lib.register_internal.rs +++ /dev/null @@ -1,22 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![ - LintId::of(utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL), - LintId::of(utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS), - LintId::of(utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS), - LintId::of(utils::internal_lints::if_chain_style::IF_CHAIN_STYLE), - LintId::of(utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL), - LintId::of(utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR), - LintId::of(utils::internal_lints::invalid_paths::INVALID_PATHS), - LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON), - LintId::of(utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT), - LintId::of(utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS), - LintId::of(utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE), - LintId::of(utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL), - LintId::of(utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA), - LintId::of(utils::internal_lints::produce_ice::PRODUCE_ICE), - LintId::of(utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH), -]) diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs deleted file mode 100644 index 800e3a87671..00000000000 --- a/clippy_lints/src/lib.register_lints.rs +++ /dev/null @@ -1,620 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_lints(&[ - #[cfg(feature = "internal")] - utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal")] - utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal")] - utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal")] - utils::internal_lints::if_chain_style::IF_CHAIN_STYLE, - #[cfg(feature = "internal")] - utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal")] - utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR, - #[cfg(feature = "internal")] - utils::internal_lints::invalid_paths::INVALID_PATHS, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal")] - utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal")] - utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL, - #[cfg(feature = "internal")] - utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal")] - utils::internal_lints::produce_ice::PRODUCE_ICE, - #[cfg(feature = "internal")] - utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH, - almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE, - approx_const::APPROX_CONSTANT, - as_conversions::AS_CONVERSIONS, - asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, - asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, - assertions_on_constants::ASSERTIONS_ON_CONSTANTS, - assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES, - async_yields_async::ASYNC_YIELDS_ASYNC, - attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON, - attrs::BLANKET_CLIPPY_RESTRICTION_LINTS, - attrs::DEPRECATED_CFG_ATTR, - attrs::DEPRECATED_SEMVER, - attrs::EMPTY_LINE_AFTER_OUTER_ATTR, - attrs::INLINE_ALWAYS, - attrs::MISMATCHED_TARGET_OS, - attrs::USELESS_ATTRIBUTE, - await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, - await_holding_invalid::AWAIT_HOLDING_LOCK, - await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, - blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS, - bool_assert_comparison::BOOL_ASSERT_COMPARISON, - bool_to_int_with_if::BOOL_TO_INT_WITH_IF, - booleans::NONMINIMAL_BOOL, - booleans::OVERLY_COMPLEX_BOOL_EXPR, - borrow_deref_ref::BORROW_DEREF_REF, - box_default::BOX_DEFAULT, - cargo::CARGO_COMMON_METADATA, - cargo::MULTIPLE_CRATE_VERSIONS, - cargo::NEGATIVE_FEATURE_NAMES, - cargo::REDUNDANT_FEATURE_NAMES, - cargo::WILDCARD_DEPENDENCIES, - casts::AS_PTR_CAST_MUT, - casts::AS_UNDERSCORE, - casts::BORROW_AS_PTR, - casts::CAST_ABS_TO_UNSIGNED, - casts::CAST_ENUM_CONSTRUCTOR, - casts::CAST_ENUM_TRUNCATION, - casts::CAST_LOSSLESS, - casts::CAST_NAN_TO_INT, - casts::CAST_POSSIBLE_TRUNCATION, - casts::CAST_POSSIBLE_WRAP, - casts::CAST_PRECISION_LOSS, - casts::CAST_PTR_ALIGNMENT, - casts::CAST_REF_TO_MUT, - casts::CAST_SIGN_LOSS, - casts::CAST_SLICE_DIFFERENT_SIZES, - casts::CAST_SLICE_FROM_RAW_PARTS, - casts::CHAR_LIT_AS_U8, - casts::FN_TO_NUMERIC_CAST, - casts::FN_TO_NUMERIC_CAST_ANY, - casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION, - casts::PTR_AS_PTR, - casts::UNNECESSARY_CAST, - checked_conversions::CHECKED_CONVERSIONS, - cognitive_complexity::COGNITIVE_COMPLEXITY, - collapsible_if::COLLAPSIBLE_ELSE_IF, - collapsible_if::COLLAPSIBLE_IF, - comparison_chain::COMPARISON_CHAIN, - copies::BRANCHES_SHARING_CODE, - copies::IFS_SAME_COND, - copies::IF_SAME_THEN_ELSE, - copies::SAME_FUNCTIONS_IN_IF_CONDITION, - copy_iterator::COPY_ITERATOR, - crate_in_macro_def::CRATE_IN_MACRO_DEF, - create_dir::CREATE_DIR, - dbg_macro::DBG_MACRO, - default::DEFAULT_TRAIT_ACCESS, - default::FIELD_REASSIGN_WITH_DEFAULT, - default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY, - default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK, - default_union_representation::DEFAULT_UNION_REPRESENTATION, - dereference::EXPLICIT_AUTO_DEREF, - dereference::EXPLICIT_DEREF_METHODS, - dereference::NEEDLESS_BORROW, - dereference::REF_BINDING_TO_REFERENCE, - derivable_impls::DERIVABLE_IMPLS, - derive::DERIVE_HASH_XOR_EQ, - derive::DERIVE_ORD_XOR_PARTIAL_ORD, - derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, - derive::EXPL_IMPL_CLONE_ON_COPY, - derive::UNSAFE_DERIVE_DESERIALIZE, - disallowed_macros::DISALLOWED_MACROS, - disallowed_methods::DISALLOWED_METHODS, - disallowed_names::DISALLOWED_NAMES, - disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS, - disallowed_types::DISALLOWED_TYPES, - doc::DOC_LINK_WITH_QUOTES, - doc::DOC_MARKDOWN, - doc::MISSING_ERRORS_DOC, - doc::MISSING_PANICS_DOC, - doc::MISSING_SAFETY_DOC, - doc::NEEDLESS_DOCTEST_MAIN, - double_parens::DOUBLE_PARENS, - drop_forget_ref::DROP_COPY, - drop_forget_ref::DROP_NON_DROP, - drop_forget_ref::DROP_REF, - drop_forget_ref::FORGET_COPY, - drop_forget_ref::FORGET_NON_DROP, - drop_forget_ref::FORGET_REF, - drop_forget_ref::UNDROPPED_MANUALLY_DROPS, - duplicate_mod::DUPLICATE_MOD, - else_if_without_else::ELSE_IF_WITHOUT_ELSE, - empty_drop::EMPTY_DROP, - empty_enum::EMPTY_ENUM, - empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, - entry::MAP_ENTRY, - enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, - enum_variants::ENUM_VARIANT_NAMES, - enum_variants::MODULE_INCEPTION, - enum_variants::MODULE_NAME_REPETITIONS, - equatable_if_let::EQUATABLE_IF_LET, - escape::BOXED_LOCAL, - eta_reduction::REDUNDANT_CLOSURE, - eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS, - excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS, - excessive_bools::STRUCT_EXCESSIVE_BOOLS, - exhaustive_items::EXHAUSTIVE_ENUMS, - exhaustive_items::EXHAUSTIVE_STRUCTS, - exit::EXIT, - explicit_write::EXPLICIT_WRITE, - fallible_impl_from::FALLIBLE_IMPL_FROM, - float_literal::EXCESSIVE_PRECISION, - float_literal::LOSSY_FLOAT_LITERAL, - floating_point_arithmetic::IMPRECISE_FLOPS, - floating_point_arithmetic::SUBOPTIMAL_FLOPS, - format::USELESS_FORMAT, - format_args::FORMAT_IN_FORMAT_ARGS, - format_args::TO_STRING_IN_FORMAT_ARGS, - format_args::UNINLINED_FORMAT_ARGS, - format_args::UNUSED_FORMAT_SPECS, - format_impl::PRINT_IN_FORMAT_IMPL, - format_impl::RECURSIVE_FORMAT_IMPL, - format_push_string::FORMAT_PUSH_STRING, - formatting::POSSIBLE_MISSING_COMMA, - formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, - formatting::SUSPICIOUS_ELSE_FORMATTING, - formatting::SUSPICIOUS_UNARY_OP_FORMATTING, - from_over_into::FROM_OVER_INTO, - from_str_radix_10::FROM_STR_RADIX_10, - functions::DOUBLE_MUST_USE, - functions::MUST_USE_CANDIDATE, - functions::MUST_USE_UNIT, - functions::NOT_UNSAFE_PTR_ARG_DEREF, - functions::RESULT_LARGE_ERR, - functions::RESULT_UNIT_ERR, - functions::TOO_MANY_ARGUMENTS, - functions::TOO_MANY_LINES, - future_not_send::FUTURE_NOT_SEND, - if_let_mutex::IF_LET_MUTEX, - if_not_else::IF_NOT_ELSE, - if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, - implicit_hasher::IMPLICIT_HASHER, - implicit_return::IMPLICIT_RETURN, - implicit_saturating_add::IMPLICIT_SATURATING_ADD, - implicit_saturating_sub::IMPLICIT_SATURATING_SUB, - inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR, - index_refutable_slice::INDEX_REFUTABLE_SLICE, - indexing_slicing::INDEXING_SLICING, - indexing_slicing::OUT_OF_BOUNDS_INDEXING, - infinite_iter::INFINITE_ITER, - infinite_iter::MAYBE_INFINITE_ITER, - inherent_impl::MULTIPLE_INHERENT_IMPL, - inherent_to_string::INHERENT_TO_STRING, - inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY, - init_numbered_fields::INIT_NUMBERED_FIELDS, - inline_fn_without_body::INLINE_FN_WITHOUT_BODY, - int_plus_one::INT_PLUS_ONE, - invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS, - invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED, - items_after_statements::ITEMS_AFTER_STATEMENTS, - iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, - large_const_arrays::LARGE_CONST_ARRAYS, - large_enum_variant::LARGE_ENUM_VARIANT, - large_include_file::LARGE_INCLUDE_FILE, - large_stack_arrays::LARGE_STACK_ARRAYS, - len_zero::COMPARISON_TO_EMPTY, - len_zero::LEN_WITHOUT_IS_EMPTY, - len_zero::LEN_ZERO, - let_if_seq::USELESS_LET_IF_SEQ, - let_underscore::LET_UNDERSCORE_DROP, - let_underscore::LET_UNDERSCORE_LOCK, - let_underscore::LET_UNDERSCORE_MUST_USE, - lifetimes::EXTRA_UNUSED_LIFETIMES, - lifetimes::NEEDLESS_LIFETIMES, - literal_representation::DECIMAL_LITERAL_REPRESENTATION, - literal_representation::INCONSISTENT_DIGIT_GROUPING, - literal_representation::LARGE_DIGIT_GROUPS, - literal_representation::MISTYPED_LITERAL_SUFFIXES, - literal_representation::UNREADABLE_LITERAL, - literal_representation::UNUSUAL_BYTE_GROUPINGS, - loops::EMPTY_LOOP, - loops::EXPLICIT_COUNTER_LOOP, - loops::EXPLICIT_INTO_ITER_LOOP, - loops::EXPLICIT_ITER_LOOP, - loops::FOR_KV_MAP, - loops::ITER_NEXT_LOOP, - loops::MANUAL_FIND, - loops::MANUAL_FLATTEN, - loops::MANUAL_MEMCPY, - loops::MISSING_SPIN_LOOP, - loops::MUT_RANGE_BOUND, - loops::NEEDLESS_COLLECT, - loops::NEEDLESS_RANGE_LOOP, - loops::NEVER_LOOP, - loops::SAME_ITEM_PUSH, - loops::SINGLE_ELEMENT_LOOP, - loops::WHILE_IMMUTABLE_CONDITION, - loops::WHILE_LET_LOOP, - loops::WHILE_LET_ON_ITERATOR, - macro_use::MACRO_USE_IMPORTS, - main_recursion::MAIN_RECURSION, - manual_assert::MANUAL_ASSERT, - manual_async_fn::MANUAL_ASYNC_FN, - manual_bits::MANUAL_BITS, - manual_clamp::MANUAL_CLAMP, - manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, - manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, - manual_rem_euclid::MANUAL_REM_EUCLID, - manual_retain::MANUAL_RETAIN, - manual_string_new::MANUAL_STRING_NEW, - manual_strip::MANUAL_STRIP, - map_unit_fn::OPTION_MAP_UNIT_FN, - map_unit_fn::RESULT_MAP_UNIT_FN, - match_result_ok::MATCH_RESULT_OK, - matches::COLLAPSIBLE_MATCH, - matches::INFALLIBLE_DESTRUCTURING_MATCH, - matches::MANUAL_FILTER, - matches::MANUAL_MAP, - matches::MANUAL_UNWRAP_OR, - matches::MATCH_AS_REF, - matches::MATCH_BOOL, - matches::MATCH_LIKE_MATCHES_MACRO, - matches::MATCH_ON_VEC_ITEMS, - matches::MATCH_OVERLAPPING_ARM, - matches::MATCH_REF_PATS, - matches::MATCH_SAME_ARMS, - matches::MATCH_SINGLE_BINDING, - matches::MATCH_STR_CASE_MISMATCH, - matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS, - matches::MATCH_WILD_ERR_ARM, - matches::NEEDLESS_MATCH, - matches::REDUNDANT_PATTERN_MATCHING, - matches::REST_PAT_IN_FULLY_BOUND_STRUCTS, - matches::SIGNIFICANT_DROP_IN_SCRUTINEE, - matches::SINGLE_MATCH, - matches::SINGLE_MATCH_ELSE, - matches::TRY_ERR, - matches::WILDCARD_ENUM_MATCH_ARM, - matches::WILDCARD_IN_OR_PATTERNS, - mem_forget::MEM_FORGET, - mem_replace::MEM_REPLACE_OPTION_WITH_NONE, - mem_replace::MEM_REPLACE_WITH_DEFAULT, - mem_replace::MEM_REPLACE_WITH_UNINIT, - methods::BIND_INSTEAD_OF_MAP, - methods::BYTES_COUNT_TO_LEN, - methods::BYTES_NTH, - methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, - methods::CHARS_LAST_CMP, - methods::CHARS_NEXT_CMP, - methods::CLONED_INSTEAD_OF_COPIED, - methods::CLONE_DOUBLE_REF, - methods::CLONE_ON_COPY, - methods::CLONE_ON_REF_PTR, - methods::COLLAPSIBLE_STR_REPLACE, - methods::ERR_EXPECT, - methods::EXPECT_FUN_CALL, - methods::EXPECT_USED, - methods::EXTEND_WITH_DRAIN, - methods::FILETYPE_IS_FILE, - methods::FILTER_MAP_IDENTITY, - methods::FILTER_MAP_NEXT, - methods::FILTER_NEXT, - methods::FLAT_MAP_IDENTITY, - methods::FLAT_MAP_OPTION, - methods::FROM_ITER_INSTEAD_OF_COLLECT, - methods::GET_FIRST, - methods::GET_LAST_WITH_LEN, - methods::GET_UNWRAP, - methods::IMPLICIT_CLONE, - methods::INEFFICIENT_TO_STRING, - methods::INSPECT_FOR_EACH, - methods::INTO_ITER_ON_REF, - methods::IS_DIGIT_ASCII_RADIX, - methods::ITERATOR_STEP_BY_ZERO, - methods::ITER_CLONED_COLLECT, - methods::ITER_COUNT, - methods::ITER_KV_MAP, - methods::ITER_NEXT_SLICE, - methods::ITER_NTH, - methods::ITER_NTH_ZERO, - methods::ITER_ON_EMPTY_COLLECTIONS, - methods::ITER_ON_SINGLE_ITEMS, - methods::ITER_OVEREAGER_CLONED, - methods::ITER_SKIP_NEXT, - methods::ITER_WITH_DRAIN, - methods::MANUAL_FILTER_MAP, - methods::MANUAL_FIND_MAP, - methods::MANUAL_OK_OR, - methods::MANUAL_SATURATING_ARITHMETIC, - methods::MANUAL_SPLIT_ONCE, - methods::MANUAL_STR_REPEAT, - methods::MAP_CLONE, - methods::MAP_COLLECT_RESULT_UNIT, - methods::MAP_ERR_IGNORE, - methods::MAP_FLATTEN, - methods::MAP_IDENTITY, - methods::MAP_UNWRAP_OR, - methods::MUT_MUTEX_LOCK, - methods::NAIVE_BYTECOUNT, - methods::NEEDLESS_OPTION_AS_DEREF, - methods::NEEDLESS_OPTION_TAKE, - methods::NEEDLESS_SPLITN, - methods::NEW_RET_NO_SELF, - methods::NONSENSICAL_OPEN_OPTIONS, - methods::NO_EFFECT_REPLACE, - methods::OBFUSCATED_IF_ELSE, - methods::OK_EXPECT, - methods::OPTION_AS_REF_DEREF, - methods::OPTION_FILTER_MAP, - methods::OPTION_MAP_OR_NONE, - methods::OR_FUN_CALL, - methods::OR_THEN_UNWRAP, - methods::PATH_BUF_PUSH_OVERWRITE, - methods::RANGE_ZIP_WITH_LEN, - methods::REPEAT_ONCE, - methods::RESULT_MAP_OR_INTO_OPTION, - methods::SEARCH_IS_SOME, - methods::SHOULD_IMPLEMENT_TRAIT, - methods::SINGLE_CHAR_ADD_STR, - methods::SINGLE_CHAR_PATTERN, - methods::SKIP_WHILE_NEXT, - methods::STABLE_SORT_PRIMITIVE, - methods::STRING_EXTEND_CHARS, - methods::SUSPICIOUS_MAP, - methods::SUSPICIOUS_SPLITN, - methods::SUSPICIOUS_TO_OWNED, - methods::UNINIT_ASSUMED_INIT, - methods::UNIT_HASH, - methods::UNNECESSARY_FILTER_MAP, - methods::UNNECESSARY_FIND_MAP, - methods::UNNECESSARY_FOLD, - methods::UNNECESSARY_JOIN, - methods::UNNECESSARY_LAZY_EVALUATIONS, - methods::UNNECESSARY_SORT_BY, - methods::UNNECESSARY_TO_OWNED, - methods::UNWRAP_OR_ELSE_DEFAULT, - methods::UNWRAP_USED, - methods::USELESS_ASREF, - methods::VEC_RESIZE_TO_ZERO, - methods::VERBOSE_FILE_READS, - methods::WRONG_SELF_CONVENTION, - methods::ZST_OFFSET, - minmax::MIN_MAX, - misc::SHORT_CIRCUIT_STATEMENT, - misc::TOPLEVEL_REF_ARG, - misc::USED_UNDERSCORE_BINDING, - misc::ZERO_PTR, - misc_early::BUILTIN_TYPE_SHADOW, - misc_early::DOUBLE_NEG, - misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, - misc_early::MIXED_CASE_HEX_LITERALS, - misc_early::REDUNDANT_PATTERN, - misc_early::SEPARATED_LITERAL_SUFFIX, - misc_early::UNNEEDED_FIELD_PATTERN, - misc_early::UNNEEDED_WILDCARD_PATTERN, - misc_early::UNSEPARATED_LITERAL_SUFFIX, - misc_early::ZERO_PREFIXED_LITERAL, - mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER, - missing_const_for_fn::MISSING_CONST_FOR_FN, - missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS, - missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES, - missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS, - missing_trait_methods::MISSING_TRAIT_METHODS, - mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION, - mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, - module_style::MOD_MODULE_FILES, - module_style::SELF_NAMED_MODULE_FILES, - multi_assignments::MULTI_ASSIGNMENTS, - mut_key::MUTABLE_KEY_TYPE, - mut_mut::MUT_MUT, - mut_reference::UNNECESSARY_MUT_PASSED, - mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, - mutex_atomic::MUTEX_ATOMIC, - mutex_atomic::MUTEX_INTEGER, - needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE, - needless_bool::BOOL_COMPARISON, - needless_bool::NEEDLESS_BOOL, - needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE, - needless_continue::NEEDLESS_CONTINUE, - needless_for_each::NEEDLESS_FOR_EACH, - needless_late_init::NEEDLESS_LATE_INIT, - needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS, - needless_pass_by_value::NEEDLESS_PASS_BY_VALUE, - needless_question_mark::NEEDLESS_QUESTION_MARK, - needless_update::NEEDLESS_UPDATE, - neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD, - neg_multiply::NEG_MULTIPLY, - new_without_default::NEW_WITHOUT_DEFAULT, - no_effect::NO_EFFECT, - no_effect::NO_EFFECT_UNDERSCORE_BINDING, - no_effect::UNNECESSARY_OPERATION, - non_copy_const::BORROW_INTERIOR_MUTABLE_CONST, - non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST, - non_expressive_names::JUST_UNDERSCORES_AND_DIGITS, - non_expressive_names::MANY_SINGLE_CHAR_NAMES, - non_expressive_names::SIMILAR_NAMES, - non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS, - non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY, - nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, - octal_escapes::OCTAL_ESCAPES, - only_used_in_recursion::ONLY_USED_IN_RECURSION, - operators::ABSURD_EXTREME_COMPARISONS, - operators::ARITHMETIC_SIDE_EFFECTS, - operators::ASSIGN_OP_PATTERN, - operators::BAD_BIT_MASK, - operators::CMP_NAN, - operators::CMP_OWNED, - operators::DOUBLE_COMPARISONS, - operators::DURATION_SUBSEC, - operators::EQ_OP, - operators::ERASING_OP, - operators::FLOAT_ARITHMETIC, - operators::FLOAT_CMP, - operators::FLOAT_CMP_CONST, - operators::FLOAT_EQUALITY_WITHOUT_ABS, - operators::IDENTITY_OP, - operators::INEFFECTIVE_BIT_MASK, - operators::INTEGER_ARITHMETIC, - operators::INTEGER_DIVISION, - operators::MISREFACTORED_ASSIGN_OP, - operators::MODULO_ARITHMETIC, - operators::MODULO_ONE, - operators::NEEDLESS_BITWISE_BOOL, - operators::OP_REF, - operators::PTR_EQ, - operators::SELF_ASSIGNMENT, - operators::VERBOSE_BIT_MASK, - option_env_unwrap::OPTION_ENV_UNWRAP, - option_if_let_else::OPTION_IF_LET_ELSE, - overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL, - panic_in_result_fn::PANIC_IN_RESULT_FN, - panic_unimplemented::PANIC, - panic_unimplemented::TODO, - panic_unimplemented::UNIMPLEMENTED, - panic_unimplemented::UNREACHABLE, - partial_pub_fields::PARTIAL_PUB_FIELDS, - partialeq_ne_impl::PARTIALEQ_NE_IMPL, - partialeq_to_none::PARTIALEQ_TO_NONE, - pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, - pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, - pattern_type_mismatch::PATTERN_TYPE_MISMATCH, - precedence::PRECEDENCE, - ptr::CMP_NULL, - ptr::INVALID_NULL_PTR_USAGE, - ptr::MUT_FROM_REF, - ptr::PTR_ARG, - ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, - pub_use::PUB_USE, - question_mark::QUESTION_MARK, - ranges::MANUAL_RANGE_CONTAINS, - ranges::RANGE_MINUS_ONE, - ranges::RANGE_PLUS_ONE, - ranges::REVERSED_EMPTY_RANGES, - rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, - read_zero_byte_vec::READ_ZERO_BYTE_VEC, - redundant_clone::REDUNDANT_CLONE, - redundant_closure_call::REDUNDANT_CLOSURE_CALL, - redundant_else::REDUNDANT_ELSE, - redundant_field_names::REDUNDANT_FIELD_NAMES, - redundant_pub_crate::REDUNDANT_PUB_CRATE, - redundant_slicing::DEREF_BY_SLICING, - redundant_slicing::REDUNDANT_SLICING, - redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES, - ref_option_ref::REF_OPTION_REF, - reference::DEREF_ADDROF, - regex::INVALID_REGEX, - regex::TRIVIAL_REGEX, - return_self_not_must_use::RETURN_SELF_NOT_MUST_USE, - returns::LET_AND_RETURN, - returns::NEEDLESS_RETURN, - same_name_method::SAME_NAME_METHOD, - self_named_constructors::SELF_NAMED_CONSTRUCTORS, - semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED, - serde_api::SERDE_API_MISUSE, - shadow::SHADOW_REUSE, - shadow::SHADOW_SAME, - shadow::SHADOW_UNRELATED, - single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, - single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, - size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, - slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, - std_instead_of_core::ALLOC_INSTEAD_OF_CORE, - std_instead_of_core::STD_INSTEAD_OF_ALLOC, - std_instead_of_core::STD_INSTEAD_OF_CORE, - strings::STRING_ADD, - strings::STRING_ADD_ASSIGN, - strings::STRING_FROM_UTF8_AS_BYTES, - strings::STRING_LIT_AS_BYTES, - strings::STRING_SLICE, - strings::STRING_TO_STRING, - strings::STR_TO_STRING, - strings::TRIM_SPLIT_WHITESPACE, - strlen_on_c_strings::STRLEN_ON_C_STRINGS, - suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, - suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, - suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL, - swap::ALMOST_SWAPPED, - swap::MANUAL_SWAP, - swap_ptr_to_ref::SWAP_PTR_TO_REF, - tabs_in_doc_comments::TABS_IN_DOC_COMMENTS, - temporary_assignment::TEMPORARY_ASSIGNMENT, - to_digit_is_some::TO_DIGIT_IS_SOME, - trailing_empty_array::TRAILING_EMPTY_ARRAY, - trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS, - trait_bounds::TYPE_REPETITION_IN_BOUNDS, - transmute::CROSSPOINTER_TRANSMUTE, - transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS, - transmute::TRANSMUTE_BYTES_TO_STR, - transmute::TRANSMUTE_FLOAT_TO_INT, - transmute::TRANSMUTE_INT_TO_BOOL, - transmute::TRANSMUTE_INT_TO_CHAR, - transmute::TRANSMUTE_INT_TO_FLOAT, - transmute::TRANSMUTE_NUM_TO_BYTES, - transmute::TRANSMUTE_PTR_TO_PTR, - transmute::TRANSMUTE_PTR_TO_REF, - transmute::TRANSMUTE_UNDEFINED_REPR, - transmute::TRANSMUTING_NULL, - transmute::UNSOUND_COLLECTION_TRANSMUTE, - transmute::USELESS_TRANSMUTE, - transmute::WRONG_TRANSMUTE, - types::BORROWED_BOX, - types::BOX_COLLECTION, - types::LINKEDLIST, - types::OPTION_OPTION, - types::RC_BUFFER, - types::RC_MUTEX, - types::REDUNDANT_ALLOCATION, - types::TYPE_COMPLEXITY, - types::VEC_BOX, - undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS, - unicode::INVISIBLE_CHARACTERS, - unicode::NON_ASCII_LITERAL, - unicode::UNICODE_NOT_NFC, - uninit_vec::UNINIT_VEC, - unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, - unit_types::LET_UNIT_VALUE, - unit_types::UNIT_ARG, - unit_types::UNIT_CMP, - unnamed_address::FN_ADDRESS_COMPARISONS, - unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, - unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, - unnecessary_wraps::UNNECESSARY_WRAPS, - unnested_or_patterns::UNNESTED_OR_PATTERNS, - unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, - unused_async::UNUSED_ASYNC, - unused_io_amount::UNUSED_IO_AMOUNT, - unused_peekable::UNUSED_PEEKABLE, - unused_rounding::UNUSED_ROUNDING, - unused_self::UNUSED_SELF, - unused_unit::UNUSED_UNIT, - unwrap::PANICKING_UNWRAP, - unwrap::UNNECESSARY_UNWRAP, - unwrap_in_result::UNWRAP_IN_RESULT, - upper_case_acronyms::UPPER_CASE_ACRONYMS, - use_self::USE_SELF, - useless_conversion::USELESS_CONVERSION, - vec::USELESS_VEC, - vec_init_then_push::VEC_INIT_THEN_PUSH, - wildcard_imports::ENUM_GLOB_USE, - wildcard_imports::WILDCARD_IMPORTS, - write::PRINTLN_EMPTY_STRING, - write::PRINT_LITERAL, - write::PRINT_STDERR, - write::PRINT_STDOUT, - write::PRINT_WITH_NEWLINE, - write::USE_DEBUG, - write::WRITELN_EMPTY_STRING, - write::WRITE_LITERAL, - write::WRITE_WITH_NEWLINE, - zero_div_zero::ZERO_DIVIDED_BY_ZERO, - zero_sized_map_values::ZERO_SIZED_MAP_VALUES, -]) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs deleted file mode 100644 index 65616d28d8f..00000000000 --- a/clippy_lints/src/lib.register_nursery.rs +++ /dev/null @@ -1,39 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ - LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), - LintId::of(casts::AS_PTR_CAST_MUT), - LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), - LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), - LintId::of(equatable_if_let::EQUATABLE_IF_LET), - LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), - LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), - LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS), - LintId::of(future_not_send::FUTURE_NOT_SEND), - LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE), - LintId::of(let_if_seq::USELESS_LET_IF_SEQ), - LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), - LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS), - LintId::of(methods::ITER_ON_SINGLE_ITEMS), - LintId::of(methods::ITER_WITH_DRAIN), - LintId::of(methods::PATH_BUF_PUSH_OVERWRITE), - LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), - LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), - LintId::of(mutex_atomic::MUTEX_ATOMIC), - LintId::of(mutex_atomic::MUTEX_INTEGER), - LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), - LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), - LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), - LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), - LintId::of(regex::TRIVIAL_REGEX), - LintId::of(strings::STRING_LIT_AS_BYTES), - LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), - LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), - LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), - LintId::of(unused_peekable::UNUSED_PEEKABLE), - LintId::of(unused_rounding::UNUSED_ROUNDING), - LintId::of(use_self::USE_SELF), -]) diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs deleted file mode 100644 index 44e969585b5..00000000000 --- a/clippy_lints/src/lib.register_pedantic.rs +++ /dev/null @@ -1,104 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ - LintId::of(attrs::INLINE_ALWAYS), - LintId::of(casts::BORROW_AS_PTR), - LintId::of(casts::CAST_LOSSLESS), - LintId::of(casts::CAST_POSSIBLE_TRUNCATION), - LintId::of(casts::CAST_POSSIBLE_WRAP), - LintId::of(casts::CAST_PRECISION_LOSS), - LintId::of(casts::CAST_PTR_ALIGNMENT), - LintId::of(casts::CAST_SIGN_LOSS), - LintId::of(casts::PTR_AS_PTR), - LintId::of(checked_conversions::CHECKED_CONVERSIONS), - LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION), - LintId::of(copy_iterator::COPY_ITERATOR), - LintId::of(default::DEFAULT_TRAIT_ACCESS), - LintId::of(dereference::EXPLICIT_DEREF_METHODS), - LintId::of(dereference::REF_BINDING_TO_REFERENCE), - LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY), - LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE), - LintId::of(doc::DOC_LINK_WITH_QUOTES), - LintId::of(doc::DOC_MARKDOWN), - LintId::of(doc::MISSING_ERRORS_DOC), - LintId::of(doc::MISSING_PANICS_DOC), - LintId::of(empty_enum::EMPTY_ENUM), - LintId::of(enum_variants::MODULE_NAME_REPETITIONS), - LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS), - LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS), - LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS), - LintId::of(format_args::UNINLINED_FORMAT_ARGS), - LintId::of(functions::MUST_USE_CANDIDATE), - LintId::of(functions::TOO_MANY_LINES), - LintId::of(if_not_else::IF_NOT_ELSE), - LintId::of(implicit_hasher::IMPLICIT_HASHER), - LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR), - LintId::of(infinite_iter::MAYBE_INFINITE_ITER), - LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS), - LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS), - LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR), - LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS), - LintId::of(let_underscore::LET_UNDERSCORE_DROP), - LintId::of(literal_representation::LARGE_DIGIT_GROUPS), - LintId::of(literal_representation::UNREADABLE_LITERAL), - LintId::of(loops::EXPLICIT_INTO_ITER_LOOP), - LintId::of(loops::EXPLICIT_ITER_LOOP), - LintId::of(macro_use::MACRO_USE_IMPORTS), - LintId::of(manual_assert::MANUAL_ASSERT), - LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED), - LintId::of(manual_string_new::MANUAL_STRING_NEW), - LintId::of(matches::MATCH_BOOL), - LintId::of(matches::MATCH_ON_VEC_ITEMS), - LintId::of(matches::MATCH_SAME_ARMS), - LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), - LintId::of(matches::MATCH_WILD_ERR_ARM), - LintId::of(matches::SINGLE_MATCH_ELSE), - LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), - LintId::of(methods::CLONED_INSTEAD_OF_COPIED), - LintId::of(methods::FILTER_MAP_NEXT), - LintId::of(methods::FLAT_MAP_OPTION), - LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), - LintId::of(methods::IMPLICIT_CLONE), - LintId::of(methods::INEFFICIENT_TO_STRING), - LintId::of(methods::MANUAL_OK_OR), - LintId::of(methods::MAP_UNWRAP_OR), - LintId::of(methods::NAIVE_BYTECOUNT), - LintId::of(methods::STABLE_SORT_PRIMITIVE), - LintId::of(methods::UNNECESSARY_JOIN), - LintId::of(misc::USED_UNDERSCORE_BINDING), - LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), - LintId::of(mut_mut::MUT_MUT), - LintId::of(needless_continue::NEEDLESS_CONTINUE), - LintId::of(needless_for_each::NEEDLESS_FOR_EACH), - LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE), - LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING), - LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES), - LintId::of(non_expressive_names::SIMILAR_NAMES), - LintId::of(operators::FLOAT_CMP), - LintId::of(operators::NEEDLESS_BITWISE_BOOL), - LintId::of(operators::VERBOSE_BIT_MASK), - LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE), - LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF), - LintId::of(ranges::RANGE_MINUS_ONE), - LintId::of(ranges::RANGE_PLUS_ONE), - LintId::of(redundant_else::REDUNDANT_ELSE), - LintId::of(ref_option_ref::REF_OPTION_REF), - LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), - LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), - LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), - LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), - LintId::of(types::LINKEDLIST), - LintId::of(types::OPTION_OPTION), - LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), - LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), - LintId::of(unused_async::UNUSED_ASYNC), - LintId::of(unused_self::UNUSED_SELF), - LintId::of(wildcard_imports::ENUM_GLOB_USE), - LintId::of(wildcard_imports::WILDCARD_IMPORTS), - LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES), -]) diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs deleted file mode 100644 index 8e927470e02..00000000000 --- a/clippy_lints/src/lib.register_perf.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ - LintId::of(box_default::BOX_DEFAULT), - LintId::of(entry::MAP_ENTRY), - LintId::of(escape::BOXED_LOCAL), - LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), - LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), - LintId::of(functions::RESULT_LARGE_ERR), - LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), - LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), - LintId::of(loops::MANUAL_MEMCPY), - LintId::of(loops::MISSING_SPIN_LOOP), - LintId::of(loops::NEEDLESS_COLLECT), - LintId::of(manual_retain::MANUAL_RETAIN), - LintId::of(methods::COLLAPSIBLE_STR_REPLACE), - LintId::of(methods::EXPECT_FUN_CALL), - LintId::of(methods::EXTEND_WITH_DRAIN), - LintId::of(methods::ITER_NTH), - LintId::of(methods::ITER_OVEREAGER_CLONED), - LintId::of(methods::MANUAL_STR_REPEAT), - LintId::of(methods::OR_FUN_CALL), - LintId::of(methods::SINGLE_CHAR_PATTERN), - LintId::of(methods::UNNECESSARY_TO_OWNED), - LintId::of(operators::CMP_OWNED), - LintId::of(redundant_clone::REDUNDANT_CLONE), - LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(types::BOX_COLLECTION), - LintId::of(types::REDUNDANT_ALLOCATION), - LintId::of(vec::USELESS_VEC), - LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), -]) diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs deleted file mode 100644 index f62d57af5b4..00000000000 --- a/clippy_lints/src/lib.register_restriction.rs +++ /dev/null @@ -1,90 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ - LintId::of(as_conversions::AS_CONVERSIONS), - LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX), - LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), - LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), - LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), - LintId::of(casts::AS_UNDERSCORE), - LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), - LintId::of(create_dir::CREATE_DIR), - LintId::of(dbg_macro::DBG_MACRO), - LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK), - LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), - LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), - LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), - LintId::of(empty_drop::EMPTY_DROP), - LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), - LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), - LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), - LintId::of(exit::EXIT), - LintId::of(float_literal::LOSSY_FLOAT_LITERAL), - LintId::of(format_push_string::FORMAT_PUSH_STRING), - LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE), - LintId::of(implicit_return::IMPLICIT_RETURN), - LintId::of(indexing_slicing::INDEXING_SLICING), - LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), - LintId::of(large_include_file::LARGE_INCLUDE_FILE), - LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), - LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), - LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS), - LintId::of(matches::TRY_ERR), - LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), - LintId::of(mem_forget::MEM_FORGET), - LintId::of(methods::CLONE_ON_REF_PTR), - LintId::of(methods::EXPECT_USED), - LintId::of(methods::FILETYPE_IS_FILE), - LintId::of(methods::GET_UNWRAP), - LintId::of(methods::MAP_ERR_IGNORE), - LintId::of(methods::UNWRAP_USED), - LintId::of(methods::VERBOSE_FILE_READS), - LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX), - LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), - LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), - LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS), - LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES), - LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS), - LintId::of(missing_trait_methods::MISSING_TRAIT_METHODS), - LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION), - LintId::of(module_style::MOD_MODULE_FILES), - LintId::of(module_style::SELF_NAMED_MODULE_FILES), - LintId::of(operators::ARITHMETIC_SIDE_EFFECTS), - LintId::of(operators::FLOAT_ARITHMETIC), - LintId::of(operators::FLOAT_CMP_CONST), - LintId::of(operators::INTEGER_ARITHMETIC), - LintId::of(operators::INTEGER_DIVISION), - LintId::of(operators::MODULO_ARITHMETIC), - LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN), - LintId::of(panic_unimplemented::PANIC), - LintId::of(panic_unimplemented::TODO), - LintId::of(panic_unimplemented::UNIMPLEMENTED), - LintId::of(panic_unimplemented::UNREACHABLE), - LintId::of(partial_pub_fields::PARTIAL_PUB_FIELDS), - LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), - LintId::of(pub_use::PUB_USE), - LintId::of(redundant_slicing::DEREF_BY_SLICING), - LintId::of(same_name_method::SAME_NAME_METHOD), - LintId::of(shadow::SHADOW_REUSE), - LintId::of(shadow::SHADOW_SAME), - LintId::of(shadow::SHADOW_UNRELATED), - LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), - LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC), - LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE), - LintId::of(strings::STRING_ADD), - LintId::of(strings::STRING_SLICE), - LintId::of(strings::STRING_TO_STRING), - LintId::of(strings::STR_TO_STRING), - LintId::of(types::RC_BUFFER), - LintId::of(types::RC_MUTEX), - LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS), - LintId::of(unicode::NON_ASCII_LITERAL), - LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), - LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), - LintId::of(write::PRINT_STDERR), - LintId::of(write::PRINT_STDOUT), - LintId::of(write::USE_DEBUG), -]) diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs deleted file mode 100644 index 3312f564855..00000000000 --- a/clippy_lints/src/lib.register_style.rs +++ /dev/null @@ -1,131 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::style", Some("clippy_style"), vec![ - LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS), - LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS), - LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), - LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF), - LintId::of(casts::FN_TO_NUMERIC_CAST), - LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), - LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF), - LintId::of(collapsible_if::COLLAPSIBLE_IF), - LintId::of(comparison_chain::COMPARISON_CHAIN), - LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), - LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY), - LintId::of(dereference::NEEDLESS_BORROW), - LintId::of(disallowed_macros::DISALLOWED_MACROS), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_names::DISALLOWED_NAMES), - LintId::of(disallowed_types::DISALLOWED_TYPES), - LintId::of(doc::MISSING_SAFETY_DOC), - LintId::of(doc::NEEDLESS_DOCTEST_MAIN), - LintId::of(enum_variants::ENUM_VARIANT_NAMES), - LintId::of(enum_variants::MODULE_INCEPTION), - LintId::of(eta_reduction::REDUNDANT_CLOSURE), - LintId::of(float_literal::EXCESSIVE_PRECISION), - LintId::of(from_over_into::FROM_OVER_INTO), - LintId::of(from_str_radix_10::FROM_STR_RADIX_10), - LintId::of(functions::DOUBLE_MUST_USE), - LintId::of(functions::MUST_USE_UNIT), - LintId::of(functions::RESULT_UNIT_ERR), - LintId::of(implicit_saturating_add::IMPLICIT_SATURATING_ADD), - LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB), - LintId::of(inherent_to_string::INHERENT_TO_STRING), - LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), - LintId::of(len_zero::COMPARISON_TO_EMPTY), - LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY), - LintId::of(len_zero::LEN_ZERO), - LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING), - LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS), - LintId::of(loops::FOR_KV_MAP), - LintId::of(loops::NEEDLESS_RANGE_LOOP), - LintId::of(loops::SAME_ITEM_PUSH), - LintId::of(loops::WHILE_LET_ON_ITERATOR), - LintId::of(main_recursion::MAIN_RECURSION), - LintId::of(manual_async_fn::MANUAL_ASYNC_FN), - LintId::of(manual_bits::MANUAL_BITS), - LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), - LintId::of(match_result_ok::MATCH_RESULT_OK), - LintId::of(matches::COLLAPSIBLE_MATCH), - LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH), - LintId::of(matches::MANUAL_MAP), - LintId::of(matches::MATCH_LIKE_MATCHES_MACRO), - LintId::of(matches::MATCH_OVERLAPPING_ARM), - LintId::of(matches::MATCH_REF_PATS), - LintId::of(matches::REDUNDANT_PATTERN_MATCHING), - LintId::of(matches::SINGLE_MATCH), - LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE), - LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT), - LintId::of(methods::BYTES_NTH), - LintId::of(methods::CHARS_LAST_CMP), - LintId::of(methods::CHARS_NEXT_CMP), - LintId::of(methods::ERR_EXPECT), - LintId::of(methods::GET_FIRST), - LintId::of(methods::INTO_ITER_ON_REF), - LintId::of(methods::IS_DIGIT_ASCII_RADIX), - LintId::of(methods::ITER_CLONED_COLLECT), - LintId::of(methods::ITER_NEXT_SLICE), - LintId::of(methods::ITER_NTH_ZERO), - LintId::of(methods::ITER_SKIP_NEXT), - LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), - LintId::of(methods::MAP_CLONE), - LintId::of(methods::MAP_COLLECT_RESULT_UNIT), - LintId::of(methods::MUT_MUTEX_LOCK), - LintId::of(methods::NEW_RET_NO_SELF), - LintId::of(methods::OBFUSCATED_IF_ELSE), - LintId::of(methods::OK_EXPECT), - LintId::of(methods::OPTION_MAP_OR_NONE), - LintId::of(methods::RESULT_MAP_OR_INTO_OPTION), - LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), - LintId::of(methods::SINGLE_CHAR_ADD_STR), - LintId::of(methods::STRING_EXTEND_CHARS), - LintId::of(methods::UNNECESSARY_FOLD), - LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), - LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), - LintId::of(methods::WRONG_SELF_CONVENTION), - LintId::of(misc::TOPLEVEL_REF_ARG), - LintId::of(misc::ZERO_PTR), - LintId::of(misc_early::BUILTIN_TYPE_SHADOW), - LintId::of(misc_early::DOUBLE_NEG), - LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT), - LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), - LintId::of(misc_early::REDUNDANT_PATTERN), - LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(needless_late_init::NEEDLESS_LATE_INIT), - LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), - LintId::of(neg_multiply::NEG_MULTIPLY), - LintId::of(new_without_default::NEW_WITHOUT_DEFAULT), - LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST), - LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST), - LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), - LintId::of(operators::ASSIGN_OP_PATTERN), - LintId::of(operators::OP_REF), - LintId::of(operators::PTR_EQ), - LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE), - LintId::of(ptr::CMP_NULL), - LintId::of(ptr::PTR_ARG), - LintId::of(question_mark::QUESTION_MARK), - LintId::of(ranges::MANUAL_RANGE_CONTAINS), - LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), - LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES), - LintId::of(returns::LET_AND_RETURN), - LintId::of(returns::NEEDLESS_RETURN), - LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), - LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), - LintId::of(strings::TRIM_SPLIT_WHITESPACE), - LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), - LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), - LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), - LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), - LintId::of(unused_unit::UNUSED_UNIT), - LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), - LintId::of(write::PRINTLN_EMPTY_STRING), - LintId::of(write::PRINT_LITERAL), - LintId::of(write::PRINT_WITH_NEWLINE), - LintId::of(write::WRITELN_EMPTY_STRING), - LintId::of(write::WRITE_LITERAL), - LintId::of(write::WRITE_WITH_NEWLINE), -]) diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs deleted file mode 100644 index b70c4bb73e5..00000000000 --- a/clippy_lints/src/lib.register_suspicious.rs +++ /dev/null @@ -1,38 +0,0 @@ -// This file was generated by `cargo dev update_lints`. -// Use that command to update this file and do not edit by hand. -// Manual edits will be overwritten. - -store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ - LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE), - LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), - LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), - LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), - LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), - LintId::of(casts::CAST_ABS_TO_UNSIGNED), - LintId::of(casts::CAST_ENUM_CONSTRUCTOR), - LintId::of(casts::CAST_ENUM_TRUNCATION), - LintId::of(casts::CAST_NAN_TO_INT), - LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS), - LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), - LintId::of(drop_forget_ref::DROP_NON_DROP), - LintId::of(drop_forget_ref::FORGET_NON_DROP), - LintId::of(duplicate_mod::DUPLICATE_MOD), - LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), - LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), - LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), - LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING), - LintId::of(loops::EMPTY_LOOP), - LintId::of(loops::MUT_RANGE_BOUND), - LintId::of(methods::NO_EFFECT_REPLACE), - LintId::of(methods::SUSPICIOUS_MAP), - LintId::of(methods::SUSPICIOUS_TO_OWNED), - LintId::of(multi_assignments::MULTI_ASSIGNMENTS), - LintId::of(mut_key::MUTABLE_KEY_TYPE), - LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), - LintId::of(operators::MISREFACTORED_ASSIGN_OP), - LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), - LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), - LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), - LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), -]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1307096b28d..b481314abed 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -32,8 +32,8 @@ extern crate rustc_driver; extern crate rustc_errors; extern crate rustc_hir; extern crate rustc_hir_analysis; -extern crate rustc_hir_typeck; extern crate rustc_hir_pretty; +extern crate rustc_hir_typeck; extern crate rustc_index; extern crate rustc_infer; extern crate rustc_lexer; @@ -47,122 +47,24 @@ extern crate rustc_trait_selection; #[macro_use] extern crate clippy_utils; +#[macro_use] +extern crate declare_clippy_lint; + +use std::io; +use std::path::PathBuf; use clippy_utils::parse_msrv; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::LintId; +use rustc_lint::{Lint, LintId}; use rustc_semver::RustcVersion; use rustc_session::Session; -/// Macro used to declare a Clippy lint. -/// -/// Every lint declaration consists of 4 parts: -/// -/// 1. The documentation, which is used for the website -/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions. -/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or -/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of. -/// 4. The `description` that contains a short explanation on what's wrong with code where the -/// lint is triggered. -/// -/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are -/// enabled by default. As said in the README.md of this repository, if the lint level mapping -/// changes, please update README.md. -/// -/// # Example -/// -/// ``` -/// #![feature(rustc_private)] -/// extern crate rustc_session; -/// use rustc_session::declare_tool_lint; -/// use clippy_lints::declare_clippy_lint; -/// -/// declare_clippy_lint! { -/// /// ### What it does -/// /// Checks for ... (describe what the lint matches). -/// /// -/// /// ### Why is this bad? -/// /// Supply the reason for linting the code. -/// /// -/// /// ### Example -/// /// ```rust -/// /// Insert a short example of code that triggers the lint -/// /// ``` -/// /// -/// /// Use instead: -/// /// ```rust -/// /// Insert a short example of improved code that doesn't trigger the lint -/// /// ``` -/// pub LINT_NAME, -/// pedantic, -/// "description" -/// } -/// ``` -/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints -#[macro_export] -macro_rules! declare_clippy_lint { - { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true - } - }; - { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => { - declare_tool_lint! { - $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true - } - }; -} - #[cfg(feature = "internal")] pub mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod declared_lints; mod renamed_lints; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -231,6 +133,7 @@ mod format_impl; mod format_push_string; mod formatting; mod from_over_into; +mod from_raw_with_void_ptr; mod from_str_radix_10; mod functions; mod future_not_send; @@ -249,6 +152,7 @@ mod inherent_impl; mod inherent_to_string; mod init_numbered_fields; mod inline_fn_without_body; +mod instant_subtraction; mod int_plus_one; mod invalid_upcast_comparisons; mod invalid_utf8_in_unchecked; @@ -270,7 +174,8 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; -mod manual_instant_elapsed; +mod manual_is_ascii_check; +mod manual_let_else; mod manual_non_exhaustive; mod manual_rem_euclid; mod manual_retain; @@ -365,6 +270,7 @@ mod strings; mod strlen_on_c_strings; mod suspicious_operation_groupings; mod suspicious_trait_impl; +mod suspicious_xor_used_as_pow; mod swap; mod swap_ptr_to_ref; mod tabs_in_doc_comments; @@ -404,8 +310,8 @@ mod zero_div_zero; mod zero_sized_map_values; // end lints modules, do not remove this comment, it’s used in `update_lints` -pub use crate::utils::conf::Conf; use crate::utils::conf::{format_error, TryConf}; +pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Register all pre expansion lints /// @@ -462,8 +368,8 @@ fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> { } #[doc(hidden)] -pub fn read_conf(sess: &Session) -> Conf { - let file_name = match utils::conf::lookup_conf_file() { +pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf { + let file_name = match path { Ok(Some(path)) => path, Ok(None) => return Conf::default(), Err(error) => { @@ -473,7 +379,7 @@ pub fn read_conf(sess: &Session) -> Conf { }, }; - let TryConf { conf, errors, warnings } = utils::conf::read(&file_name); + let TryConf { conf, errors, warnings } = utils::conf::read(file_name); // all conf errors are non-fatal, we just use the default conf in case of error for error in errors { sess.err(format!( @@ -495,31 +401,121 @@ pub fn read_conf(sess: &Session) -> Conf { conf } +#[derive(Default)] +struct RegistrationGroups { + all: Vec<LintId>, + cargo: Vec<LintId>, + complexity: Vec<LintId>, + correctness: Vec<LintId>, + nursery: Vec<LintId>, + pedantic: Vec<LintId>, + perf: Vec<LintId>, + restriction: Vec<LintId>, + style: Vec<LintId>, + suspicious: Vec<LintId>, + #[cfg(feature = "internal")] + internal: Vec<LintId>, +} + +impl RegistrationGroups { + #[rustfmt::skip] + fn register(self, store: &mut rustc_lint::LintStore) { + store.register_group(true, "clippy::all", Some("clippy_all"), self.all); + store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo); + store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity); + store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness); + store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery); + store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic); + store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf); + store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction); + store.register_group(true, "clippy::style", Some("clippy_style"), self.style); + store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious); + #[cfg(feature = "internal")] + store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal); + } +} + +#[derive(Copy, Clone)] +pub(crate) enum LintCategory { + Cargo, + Complexity, + Correctness, + Nursery, + Pedantic, + Perf, + Restriction, + Style, + Suspicious, + #[cfg(feature = "internal")] + Internal, +} +#[allow(clippy::enum_glob_use)] +use LintCategory::*; + +impl LintCategory { + fn is_all(self) -> bool { + matches!(self, Correctness | Suspicious | Style | Complexity | Perf) + } + + fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> { + match self { + Cargo => &mut groups.cargo, + Complexity => &mut groups.complexity, + Correctness => &mut groups.correctness, + Nursery => &mut groups.nursery, + Pedantic => &mut groups.pedantic, + Perf => &mut groups.perf, + Restriction => &mut groups.restriction, + Style => &mut groups.style, + Suspicious => &mut groups.suspicious, + #[cfg(feature = "internal")] + Internal => &mut groups.internal, + } + } +} + +pub(crate) struct LintInfo { + /// Double reference to maintain pointer equality + lint: &'static &'static Lint, + category: LintCategory, + explanation: &'static str, +} + +pub fn explain(name: &str) { + let target = format!("clippy::{}", name.to_ascii_uppercase()); + match declared_lints::LINTS.iter().find(|info| info.lint.name == target) { + Some(info) => print!("{}", info.explanation), + None => println!("unknown lint: {name}"), + } +} + +fn register_categories(store: &mut rustc_lint::LintStore) { + let mut groups = RegistrationGroups::default(); + + for LintInfo { lint, category, .. } in declared_lints::LINTS { + if category.is_all() { + groups.all.push(LintId::of(lint)); + } + + category.group(&mut groups).push(LintId::of(lint)); + } + + let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect(); + + store.register_lints(&lints); + groups.register(store); +} + /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. #[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); + register_categories(store); include!("lib.deprecated.rs"); - include!("lib.register_lints.rs"); - include!("lib.register_restriction.rs"); - include!("lib.register_pedantic.rs"); - - #[cfg(feature = "internal")] - include!("lib.register_internal.rs"); - - include!("lib.register_all.rs"); - include!("lib.register_style.rs"); - include!("lib.register_complexity.rs"); - include!("lib.register_correctness.rs"); - include!("lib.register_suspicious.rs"); - include!("lib.register_perf.rs"); - include!("lib.register_cargo.rs"); - include!("lib.register_nursery.rs"); - #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { @@ -614,6 +610,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); + let matches_for_let_else = conf.matches_for_let_else; + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); @@ -735,7 +733,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_trait_bounds = conf.max_trait_bounds; store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds))); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); - store.register_late_pass(|_| Box::new(mut_key::MutableKeyType)); + let ignore_interior_mutability = conf.ignore_interior_mutability.clone(); + store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new())); @@ -794,10 +793,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_early_pass(|| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); - store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports)); + store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()); let max_fn_params_bools = conf.max_fn_params_bools; let max_struct_bools = conf.max_struct_bools; - store.register_early_pass(move || { + store.register_late_pass(move |_| { Box::new(excessive_bools::ExcessiveBools::new( max_struct_bools, max_fn_params_bools, @@ -879,13 +878,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); + let allow_print_in_tests = conf.allow_print_in_tests; + store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move |_| { Box::new(cargo::Cargo { ignore_publish: cargo_ignore_publish, }) }); - store.register_late_pass(|_| Box::<write::Write>::default()); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); @@ -908,7 +908,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); - store.register_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed)); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); @@ -919,6 +919,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd)); store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields)); store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); + store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); + store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 54c316358a1..0bb9eca1528 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +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_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; @@ -152,6 +152,7 @@ fn check_fn_inner<'tcx>( .params .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); + for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { if pred.origin == PredicateOrigin::WhereClause { @@ -188,15 +189,30 @@ fn check_fn_inner<'tcx>( } } } - if could_use_elision(cx, decl, body, trait_sig, generics.params) { - span_lint( + + if let Some(elidable_lts) = could_use_elision(cx, 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()) + .map(|ident| ident.to_string()) + .collect::<Vec<_>>() + .join(", "); + + span_lint_and_then( cx, NEEDLESS_LIFETIMES, span.with_hi(decl.output.span().hi()), - "explicit lifetimes given in parameter types where they could be elided \ - (or replaced with `'_` if needed by type declaration)", + &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 report_extra_lifetimes { self::report_extra_lifetimes(cx, decl, generics); } @@ -227,7 +243,7 @@ fn could_use_elision<'tcx>( body: Option<BodyId>, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], -) -> bool { +) -> Option<Vec<(LocalDefId, Option<Span>)>> { // 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 @@ -254,7 +270,7 @@ fn could_use_elision<'tcx>( } if input_visitor.abort() || output_visitor.abort() { - return false; + return None; } let input_lts = input_visitor.lts; @@ -262,7 +278,7 @@ fn could_use_elision<'tcx>( if let Some(trait_sig) = trait_sig { if explicit_self_type(cx, func, trait_sig.first().copied()) { - return false; + return None; } } @@ -271,7 +287,7 @@ fn could_use_elision<'tcx>( let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); if explicit_self_type(cx, func, first_ident) { - return false; + return None; } let mut checker = BodyLifetimeChecker { @@ -279,14 +295,14 @@ fn could_use_elision<'tcx>( }; checker.visit_expr(body.value); if checker.lifetimes_used_in_body { - return false; + return None; } } // check for lifetimes from higher scopes for lt in input_lts.iter().chain(output_lts.iter()) { if !allowed_lts.contains(lt) { - return false; + return None; } } @@ -302,48 +318,45 @@ fn could_use_elision<'tcx>( 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 false; + 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 false; + return None; } } } } - // no input lifetimes? easy case! - if input_lts.is_empty() { - false - } else if output_lts.is_empty() { - // no output lifetimes, check distinctness of input lifetimes + // A lifetime can be newly elided if: + // - It occurs only once among the inputs. + // - If there are multiple input lifetimes, then the newly elided lifetime does not occur among the + // outputs (because eliding such an lifetime would create an ambiguity). + 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(), + )) + } else { + None + } + }) + .collect::<Vec<_>>(); - // only unnamed and static, ok - let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static); - if unnamed_and_static { - return false; - } - // we have no output reference, so we only need all distinct lifetimes - input_lts.len() == unique_lifetimes(&input_lts) + if elidable_lts.is_empty() { + None } else { - // we have output references, so we need one input reference, - // and all output lifetimes must be the same - if unique_lifetimes(&output_lts) > 1 { - return false; - } - if input_lts.len() == 1 { - match (&input_lts[0], &output_lts[0]) { - (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true, - (&RefLt::Named(_), &RefLt::Unnamed) => true, - _ => false, /* already elided, different named lifetimes - * or something static going on */ - } - } else { - false - } + Some(elidable_lts) } } @@ -359,16 +372,31 @@ fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxH allowed_lts } -/// Number of unique lifetimes in the given vector. +/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve +/// relative order. #[must_use] -fn unique_lifetimes(lts: &[RefLt]) -> usize { - lts.iter().collect::<FxHashSet<_>>().len() +fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> { + let mut occurrences = Vec::new(); + for lt in lts { + if let &RefLt::Named(curr_def_id) = lt { + if let Some(pair) = occurrences + .iter_mut() + .find(|(prev_def_id, _)| *prev_def_id == curr_def_id) + { + pair.1 += 1; + } else { + occurrences.push((curr_def_id, 1)); + } + } + } + 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>, unelided_trait_object_lifetime: bool, } @@ -378,6 +406,7 @@ impl<'a, 'tcx> RefVisitor<'a, 'tcx> { Self { cx, lts: Vec::new(), + lifetime_generic_arg_spans: FxHashMap::default(), nested_elision_site_lts: Vec::new(), unelided_trait_object_lifetime: false, } @@ -467,6 +496,22 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { _ => 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.name + { + self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span); + } + // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. + // walk_generic_arg(self, generic_arg); + match generic_arg { + GenericArg::Lifetime(lt) => self.visit_lifetime(lt), + GenericArg::Type(ty) => self.visit_ty(ty), + GenericArg::Const(ct) => self.visit_anon_const(&ct.value), + GenericArg::Infer(inf) => self.visit_infer(inf), + } + } } /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index bcf278d9c83..8e52cac4323 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -9,7 +9,6 @@ mod manual_flatten; mod manual_memcpy; mod missing_spin_loop; mod mut_range_bound; -mod needless_collect; mod needless_range_loop; mod never_loop; mod same_item_push; @@ -207,28 +206,6 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for functions collecting an iterator when collect - /// is not needed. - /// - /// ### Why is this bad? - /// `collect` causes the allocation of a new data structure, - /// when this allocation may not be needed. - /// - /// ### Example - /// ```rust - /// # let iterator = vec![1].into_iter(); - /// let len = iterator.clone().collect::<Vec<_>>().len(); - /// // should be - /// let len = iterator.count(); - /// ``` - #[clippy::version = "1.30.0"] - pub NEEDLESS_COLLECT, - perf, - "collecting an iterator when collect is not needed" -} - -declare_clippy_lint! { - /// ### What it does /// Checks `for` loops over slices with an explicit counter /// and suggests the use of `.enumerate()`. /// @@ -605,7 +582,6 @@ declare_lint_pass!(Loops => [ EXPLICIT_INTO_ITER_LOOP, ITER_NEXT_LOOP, WHILE_LET_LOOP, - NEEDLESS_COLLECT, EXPLICIT_COUNTER_LOOP, EMPTY_LOOP, WHILE_LET_ON_ITERATOR, @@ -667,8 +643,6 @@ impl<'tcx> LateLintPass<'tcx> for Loops { while_immutable_condition::check(cx, condition, body); missing_spin_loop::check(cx, condition, body); } - - needless_collect::check(expr, cx); } } diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index 91b321c4474..4dae93f6028 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -52,8 +52,8 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> None } -fn check_for_mutation<'tcx>( - cx: &LateContext<'tcx>, +fn check_for_mutation( + cx: &LateContext<'_>, body: &Expr<'_>, bound_id_start: Option<HirId>, bound_id_end: Option<HirId>, @@ -113,13 +113,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> { } } - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } impl MutatePairDelegate<'_, '_> { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 16b00ad6637..14f161f5102 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; +use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_span::Span; use std::iter::{once, Iterator}; @@ -16,7 +16,7 @@ pub(super) fn check( span: Span, for_loop: Option<&ForLoop<'_>>, ) { - match never_loop_block(block, loop_id) { + match never_loop_block(block, &mut Vec::new(), loop_id) { NeverLoopResult::AlwaysBreak => { span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| { if let Some(ForLoop { @@ -92,35 +92,34 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult } } -fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult { - let mut iter = block +fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult { + let iter = block .stmts .iter() .filter_map(stmt_to_expr) .chain(block.expr.map(|expr| (expr, None))); - never_loop_expr_seq(&mut iter, main_loop_id) -} -fn never_loop_expr_seq<'a, T: Iterator<Item = (&'a Expr<'a>, Option<&'a Block<'a>>)>>( - es: &mut T, - main_loop_id: HirId, -) -> NeverLoopResult { - es.map(|(e, els)| { - let e = never_loop_expr(e, main_loop_id); - els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id))) + iter.map(|(e, els)| { + let e = never_loop_expr(e, ignore_ids, main_loop_id); + // els is an else block in a let...else binding + els.map_or(e, |els| { + combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id)) + }) }) .fold(NeverLoopResult::Otherwise, combine_seq) } fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> { match stmt.kind { - StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)), + StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)), + // add the let...else expression (if present) StmtKind::Local(local) => local.init.map(|init| (init, local.els)), StmtKind::Item(..) => None, } } -fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { +#[allow(clippy::too_many_lines)] +fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult { match expr.kind { ExprKind::Box(e) | ExprKind::Unary(_, e) @@ -129,47 +128,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { | ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) | ExprKind::Repeat(e, _) - | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id), - ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id), - ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id), - ExprKind::MethodCall(_, receiver, es, _) => { - never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id) - }, + | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id), + ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id), + ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id), + ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all( + &mut std::iter::once(receiver).chain(es.iter()), + ignore_ids, + main_loop_id, + ), ExprKind::Struct(_, fields, base) => { - let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id); + let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id); if let Some(base) = base { - combine_both(fields, never_loop_expr(base, main_loop_id)) + combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id)) } else { fields } }, - ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id), + ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id), ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id), + | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. - absorb_break(never_loop_block(b, main_loop_id)) + absorb_break(never_loop_block(b, ignore_ids, main_loop_id)) }, ExprKind::If(e, e2, e3) => { - let e1 = never_loop_expr(e, main_loop_id); - let e2 = never_loop_expr(e2, main_loop_id); - let e3 = e3 - .as_ref() - .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id)); + let e1 = never_loop_expr(e, ignore_ids, main_loop_id); + let e2 = never_loop_expr(e2, ignore_ids, main_loop_id); + let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| { + never_loop_expr(e, ignore_ids, main_loop_id) + }); combine_seq(e1, combine_branches(e2, e3)) }, ExprKind::Match(e, arms, _) => { - let e = never_loop_expr(e, main_loop_id); + let e = never_loop_expr(e, ignore_ids, main_loop_id); if arms.is_empty() { e } else { - let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id); + let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id); combine_seq(e, arms) } }, - ExprKind::Block(b, _) => never_loop_block(b, main_loop_id), + ExprKind::Block(b, l) => { + if l.is_some() { + ignore_ids.push(b.hir_id); + } + let ret = never_loop_block(b, ignore_ids, main_loop_id); + ignore_ids.pop(); + ret + }, ExprKind::Continue(d) => { let id = d .target_id @@ -180,20 +188,32 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { NeverLoopResult::AlwaysBreak } }, + // checks if break targets a block instead of a loop + ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e + .map_or(NeverLoopResult::Otherwise, |e| { + combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise) + }), ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| { - combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak) + combine_seq( + never_loop_expr(e, ignore_ids, main_loop_id), + NeverLoopResult::AlwaysBreak, + ) }), ExprKind::InlineAsm(asm) => asm .operands .iter() .map(|(o, _)| match o { InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { - never_loop_expr(expr, main_loop_id) + never_loop_expr(expr, ignore_ids, main_loop_id) }, - InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id), - InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id) + InlineAsmOperand::Out { expr, .. } => { + never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id) }, + InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all( + &mut once(*in_expr).chain(out_expr.iter().copied()), + ignore_ids, + main_loop_id, + ), InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise, @@ -208,13 +228,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { } } -fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult { - es.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>( + es: &mut T, + ignore_ids: &mut Vec<HirId>, + main_loop_id: HirId, +) -> NeverLoopResult { + es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::Otherwise, combine_both) } -fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult { - e.map(|e| never_loop_expr(e, main_loop_id)) +fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>( + e: &mut T, + ignore_ids: &mut Vec<HirId>, + main_loop_id: HirId, +) -> NeverLoopResult { + e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id)) .fold(NeverLoopResult::AlwaysBreak, combine_branches) } diff --git a/clippy_lints/src/manual_instant_elapsed.rs b/clippy_lints/src/manual_instant_elapsed.rs deleted file mode 100644 index 331cda1db89..00000000000 --- a/clippy_lints/src/manual_instant_elapsed.rs +++ /dev/null @@ -1,69 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind}; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Spanned; - -declare_clippy_lint! { - /// ### What it does - /// Lints subtraction between `Instant::now()` and another `Instant`. - /// - /// ### Why is this bad? - /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns - /// as `Instant` subtraction saturates. - /// - /// `prev_instant.elapsed()` also more clearly signals intention. - /// - /// ### Example - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = Instant::now() - prev_instant; - /// ``` - /// Use instead: - /// ```rust - /// use std::time::Instant; - /// let prev_instant = Instant::now(); - /// let duration = prev_instant.elapsed(); - /// ``` - #[clippy::version = "1.64.0"] - pub MANUAL_INSTANT_ELAPSED, - pedantic, - "subtraction between `Instant::now()` and previous `Instant`" -} - -declare_lint_pass!(ManualInstantElapsed => [MANUAL_INSTANT_ELAPSED]); - -impl LateLintPass<'_> for ManualInstantElapsed { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { - if let ExprKind::Binary(Spanned {node: BinOpKind::Sub, ..}, lhs, rhs) = expr.kind - && check_instant_now_call(cx, lhs) - && let ty_resolved = cx.typeck_results().expr_ty(rhs) - && let rustc_middle::ty::Adt(def, _) = ty_resolved.kind() - && clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT) - && let Some(sugg) = clippy_utils::sugg::Sugg::hir_opt(cx, rhs) - { - span_lint_and_sugg( - cx, - MANUAL_INSTANT_ELAPSED, - expr.span, - "manual implementation of `Instant::elapsed`", - "try", - format!("{}.elapsed()", sugg.maybe_par()), - Applicability::MachineApplicable, - ); - } - } -} - -fn check_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool { - if let ExprKind::Call(fn_expr, []) = expr_block.kind - && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr) - && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW) - { - true - } else { - false - } -} diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs new file mode 100644 index 00000000000..bb8c142f8e4 --- /dev/null +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -0,0 +1,158 @@ +use rustc_ast::LitKind::{Byte, Char}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{def_id::DefId, sym}; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, +}; + +declare_clippy_lint! { + /// ### What it does + /// Suggests to use dedicated built-in methods, + /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range + /// + /// ### Why is this bad? + /// Using the built-in functions is more readable and makes it + /// clear that it's not a specific subset of characters, but all + /// ASCII (lowercase|uppercase|digit) characters. + /// ### Example + /// ```rust + /// fn main() { + /// assert!(matches!('x', 'a'..='z')); + /// assert!(matches!(b'X', b'A'..=b'Z')); + /// assert!(matches!('2', '0'..='9')); + /// assert!(matches!('x', 'A'..='Z' | 'a'..='z')); + /// } + /// ``` + /// Use instead: + /// ```rust + /// fn main() { + /// assert!('x'.is_ascii_lowercase()); + /// assert!(b'X'.is_ascii_uppercase()); + /// assert!('2'.is_ascii_digit()); + /// assert!('x'.is_ascii_alphabetic()); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub MANUAL_IS_ASCII_CHECK, + style, + "use dedicated method to check ascii range" +} +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); + +pub struct ManualIsAsciiCheck { + msrv: Option<RustcVersion>, +} + +impl ManualIsAsciiCheck { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +#[derive(Debug, PartialEq)] +enum CharRange { + /// 'a'..='z' | b'a'..=b'z' + LowerChar, + /// 'A'..='Z' | b'A'..=b'Z' + UpperChar, + /// AsciiLower | AsciiUpper + FullChar, + /// '0..=9' + Digit, + Otherwise, +} + +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + return; + } + + if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + return; + } + + let Some(macro_call) = root_macro_call(expr.span) else { return }; + + if is_matches_macro(cx, macro_call.def_id) { + if let ExprKind::Match(recv, [arm, ..], _) = expr.kind { + let range = check_pat(&arm.pat.kind); + + if let Some(sugg) = match range { + CharRange::UpperChar => Some("is_ascii_uppercase"), + CharRange::LowerChar => Some("is_ascii_lowercase"), + CharRange::FullChar => Some("is_ascii_alphabetic"), + CharRange::Digit => Some("is_ascii_digit"), + CharRange::Otherwise => None, + } { + let default_snip = ".."; + // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for + // macro span, so we check applicability manually by comparing `recv` is not default. + let recv = snippet(cx, recv.span, default_snip); + + let applicability = if recv == default_snip { + Applicability::HasPlaceholders + } else { + Applicability::MachineApplicable + }; + + span_lint_and_sugg( + cx, + MANUAL_IS_ASCII_CHECK, + macro_call.span, + "manual check for common ascii range", + "try", + format!("{recv}.{sugg}()"), + applicability, + ); + } + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange { + match pat_kind { + PatKind::Or(pats) => { + let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::<Vec<_>>(); + + if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) { + CharRange::FullChar + } else { + CharRange::Otherwise + } + }, + PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end), + _ => CharRange::Otherwise, + } +} + +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { + if let ExprKind::Lit(start_lit) = &start.kind + && let ExprKind::Lit(end_lit) = &end.kind { + match (&start_lit.node, &end_lit.node) { + (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar, + (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar, + (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit, + _ => CharRange::Otherwise, + } + } else { + CharRange::Otherwise + } +} + +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool { + if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) { + return sym::matches_macro == name; + } + + false +} diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs new file mode 100644 index 00000000000..1846596fa4c --- /dev/null +++ b/clippy_lints/src/manual_let_else.rs @@ -0,0 +1,297 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::{meets_msrv, msrvs, peel_blocks}; +use if_chain::if_chain; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::symbol::sym; +use rustc_span::Span; +use serde::Deserialize; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// + /// Warn of cases where `let...else` could be used + /// + /// ### Why is this bad? + /// + /// `let...else` provides a standard construct for this pattern + /// that people can easily recognize. It's also more compact. + /// + /// ### Example + /// + /// ```rust + /// # let w = Some(0); + /// let v = if let Some(v) = w { v } else { return }; + /// ``` + /// + /// Could be written: + /// + /// ```rust + /// # #![feature(let_else)] + /// # fn main () { + /// # let w = Some(0); + /// let Some(v) = w else { return }; + /// # } + /// ``` + #[clippy::version = "1.67.0"] + pub MANUAL_LET_ELSE, + pedantic, + "manual implementation of a let...else statement" +} + +pub struct ManualLetElse { + msrv: Option<RustcVersion>, + matches_behaviour: MatchLintBehaviour, +} + +impl ManualLetElse { + #[must_use] + pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self { + Self { + msrv, + matches_behaviour, + } + } +} + +impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); + +impl<'tcx> LateLintPass<'tcx> for ManualLetElse { + fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { + let if_let_or_match = if_chain! { + if meets_msrv(self.msrv, msrvs::LET_ELSE); + if !in_external_macro(cx.sess(), stmt.span); + if let StmtKind::Local(local) = stmt.kind; + if let Some(init) = local.init; + if local.els.is_none(); + if local.ty.is_none(); + if init.span.ctxt() == stmt.span.ctxt(); + if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init); + then { + if_let_or_match + } else { + return; + } + }; + + match if_let_or_match { + IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! { + if expr_is_simple_identity(let_pat, if_then); + if let Some(if_else) = if_else; + if expr_diverges(cx, if_else); + then { + emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else); + } + }, + IfLetOrMatch::Match(match_expr, arms, source) => { + if self.matches_behaviour == MatchLintBehaviour::Never { + return; + } + if source != MatchSource::Normal { + return; + } + // Any other number than two arms doesn't (neccessarily) + // have a trivial mapping to let else. + if arms.len() != 2 { + return; + } + // Guards don't give us an easy mapping either + if arms.iter().any(|arm| arm.guard.is_some()) { + return; + } + let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; + let diverging_arm_opt = arms + .iter() + .enumerate() + .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types)); + let Some((idx, diverging_arm)) = diverging_arm_opt else { return; }; + let pat_arm = &arms[1 - idx]; + if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) { + return; + } + + emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body); + }, + } + } + + extract_msrv_attr!(LateContext); +} + +fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) { + span_lint_and_then( + cx, + MANUAL_LET_ELSE, + span, + "this could be rewritten as `let...else`", + |diag| { + // This is far from perfect, for example there needs to be: + // * mut additions for the bindings + // * renamings of the bindings + // * unused binding collision detection with existing ones + // * putting patterns with at the top level | inside () + // for this to be machine applicable. + let app = Applicability::HasPlaceholders; + + if let Some(sn_pat) = snippet_opt(cx, pat.span) && + let Some(sn_expr) = snippet_opt(cx, expr.span) && + let Some(sn_else) = snippet_opt(cx, else_body.span) + { + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else + } else { + format!("{{ {sn_else} }}") + }; + let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); + } + }, + ); +} + +fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { + if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) { + return ty.is_never(); + } + false + } + // We can't just call is_never on expr and be done, because the type system + // sometimes coerces the ! type to something different before we can get + // our hands on it. So instead, we do a manual search. We do fall back to + // is_never in some places when there is no better alternative. + for_each_expr(expr, |ex| { + match ex.kind { + ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()), + ExprKind::Call(call, _) => { + if is_never(cx, ex) || is_never(cx, call) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::MethodCall(..) => { + if is_never(cx, ex) { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::Yes) + }, + ExprKind::If(if_expr, if_then, if_else) => { + let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex)); + let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then)); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + ExprKind::Match(match_expr, match_arms, _) => { + let diverges = expr_diverges(cx, match_expr) + || match_arms.iter().all(|arm| { + let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body())); + guard_diverges || expr_diverges(cx, arm.body) + }); + if diverges { + return ControlFlow::Break(()); + } + ControlFlow::Continue(Descend::No) + }, + + // Don't continue into loops or labeled blocks, as they are breakable, + // and we'd have to start checking labels. + ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + + // Default: descend + _ => ControlFlow::Continue(Descend::Yes), + } + }) + .is_some() +} + +fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { + // Check whether the pattern contains any bindings, as the + // binding might potentially be used in the body. + // TODO: only look for *used* bindings. + let mut has_bindings = false; + pat.each_binding_or_first(&mut |_, _, _, _| has_bindings = true); + if has_bindings { + return false; + } + + // If we shouldn't check the types, exit early. + if !check_types { + return true; + } + + // Check whether any possibly "unknown" patterns are included, + // because users might not know which values some enum has. + // Well-known enums are excepted, as we assume people know them. + // We do a deep check, to be able to disallow Err(En::Foo(_)) + // for usage of the En::Foo variant, as we disallow En::Foo(_), + // but we allow Err(_). + let typeck_results = cx.typeck_results(); + let mut has_disallowed = false; + pat.walk_always(|pat| { + // Only do the check if the type is "spelled out" in the pattern + if !matches!( + pat.kind, + PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + ) { + return; + }; + let ty = typeck_results.pat_ty(pat); + // Option and Result are allowed, everything else isn't. + if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { + has_disallowed = true; + } + }); + !has_disallowed +} + +/// Checks if the passed block is a simple identity referring to bindings created by the pattern +fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool { + // We support patterns with multiple bindings and tuples, like: + // let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... } + let peeled = peel_blocks(expr); + let paths = match peeled.kind { + ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs, + ExprKind::Path(_) => std::slice::from_ref(peeled), + _ => return false, + }; + let mut pat_bindings = FxHashSet::default(); + pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| { + pat_bindings.insert(ident); + }); + if pat_bindings.len() < paths.len() { + return false; + } + for path in paths { + if_chain! { + if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind; + if let [path_seg] = path.segments; + then { + if !pat_bindings.remove(&path_seg.ident) { + return false; + } + } else { + return false; + } + } + } + true +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)] +pub enum MatchLintBehaviour { + AllTypes, + WellKnownTypes, + Never, +} diff --git a/clippy_lints/src/map_unit_fn.rs b/clippy_lints/src/map_unit_fn.rs index 32da37a862d..59195d1ae4e 100644 --- a/clippy_lints/src/map_unit_fn.rs +++ b/clippy_lints/src/map_unit_fn.rs @@ -119,7 +119,7 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { /// semicolons, which causes problems when generating a suggestion. Given an /// expression that evaluates to '()' or '!', recursively remove useless braces /// and semi-colons until is suitable for including in the suggestion template -fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<Span> { +fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> { if !is_unit_expression(cx, expr) { return None; } diff --git a/clippy_lints/src/matches/infallible_destructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs index 2472acb6f6e..d18c92caba2 100644 --- a/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { if let PatKind::TupleStruct( QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; if args.len() == 1; - if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind; + if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; let body = peel_blocks(arms[0].body); if path_to_local_id(body, arg); @@ -30,8 +30,9 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { Consider using `let`", "try this", format!( - "let {}({}) = {};", + "let {}({}{}) = {};", snippet_with_applicability(cx, variant_name.span, "..", &mut applicability), + if binding.0 == ByRef::Yes { "ref " } else { "" }, snippet_with_applicability(cx, local.pat.span, "..", &mut applicability), snippet_with_applicability(cx, target.span, "..", &mut applicability), ), diff --git a/clippy_lints/src/matches/manual_filter.rs b/clippy_lints/src/matches/manual_filter.rs index 66ba1f6f9c5..d521a529e0d 100644 --- a/clippy_lints/src/matches/manual_filter.rs +++ b/clippy_lints/src/matches/manual_filter.rs @@ -62,7 +62,7 @@ fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> { // <expr> // } // Returns true if <expr> resolves to `Some(x)`, `false` otherwise -fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool { +fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { // there can be not statements in the block as they would be removed when switching to `.filter` if let ExprKind::Call(callee, [arg]) = inner_expr.kind { diff --git a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 85269e533a0..f587c69f730 100644 --- a/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -83,8 +83,8 @@ fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'t /// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that /// may have a surprising lifetime. -fn has_significant_drop_in_scrutinee<'tcx, 'a>( - cx: &'a LateContext<'tcx>, +fn has_significant_drop_in_scrutinee<'tcx>( + cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>, source: MatchSource, ) -> Option<(Vec<FoundSigDrop>, &'static str)> { @@ -226,7 +226,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { /// This will try to set the current suggestion (so it can be moved into the suggestions vec /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us /// an opportunity to look for another type in the chain that will be trivially copyable. - /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// However, if we are at the end of the chain, we want to accept whatever is there. (The /// suggestion won't actually be output, but the diagnostic message will be output, so the user /// can determine the best way to handle the lint.) fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { @@ -377,7 +377,7 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> { } } -fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> { +fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> { let mut helper = ArmSigDropHelper::new(cx); for arm in arms { helper.visit_expr(arm.body); diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs index e5a15b2e1a1..19b49c44d57 100644 --- a/clippy_lints/src/matches/single_match.rs +++ b/clippy_lints/src/matches/single_match.rs @@ -153,7 +153,7 @@ fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> } /// Returns `true` if the given type is an enum we know won't be expanded in the future -fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool { +fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { // list of candidate `Enum`s we know will never get any more members let candidates = [sym::Cow, sym::Option, sym::Result]; diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs index 7e808760663..27a05337a29 100644 --- a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs @@ -9,8 +9,8 @@ use rustc_lint::LateContext; use rustc_lint::Lint; /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`. -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>, chain_methods: &[&str], lint: &'static Lint, diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs index 07bbc5ca1bf..2efff4c3c54 100644 --- a/clippy_lints/src/methods/chars_last_cmp.rs +++ b/clippy_lints/src/methods/chars_last_cmp.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs index c29ee0ec8c8..5b8713f7d79 100644 --- a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs @@ -4,7 +4,7 @@ use rustc_lint::LateContext; use super::CHARS_LAST_CMP; /// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") { true } else { diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs index a6701d8830e..b631fecab97 100644 --- a/clippy_lints/src/methods/chars_next_cmp.rs +++ b/clippy_lints/src/methods/chars_next_cmp.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs index 28ede28e935..caf21d3ff3b 100644 --- a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs +++ b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs @@ -3,6 +3,6 @@ use rustc_lint::LateContext; use super::CHARS_NEXT_CMP; /// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`. -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { +pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool { crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with") } diff --git a/clippy_lints/src/methods/collapsible_str_replace.rs b/clippy_lints/src/methods/collapsible_str_replace.rs index 501646863fe..ac61b437788 100644 --- a/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/clippy_lints/src/methods/collapsible_str_replace.rs @@ -23,7 +23,7 @@ pub(super) fn check<'tcx>( // If the parent node's `to` argument is the same as the `to` argument // of the last replace call in the current chain, don't lint as it was already linted if let Some(parent) = get_parent_expr(cx, expr) - && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent) + && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent) && eq_expr_value(cx, to, current_to) && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind() { @@ -48,7 +48,7 @@ fn collect_replace_calls<'tcx>( let mut from_args = VecDeque::new(); let _: Option<()> = for_each_expr(expr, |e| { - if let Some(("replace", _, [from, to], _)) = method_call(e) { + if let Some(("replace", _, [from, to], _, _)) = method_call(e) { if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() { methods.push_front(e); from_args.push_front(from); @@ -78,7 +78,7 @@ fn check_consecutive_replace_calls<'tcx>( .collect(); let app = Applicability::MachineApplicable; let earliest_replace_call = replace_methods.methods.front().unwrap(); - if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) { + if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) { span_lint_and_sugg( cx, COLLAPSIBLE_STR_REPLACE, diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index d59fefa1ddc..cce8f797e98 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_in_test_function; +use clippy_utils::is_in_cfg_test; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an Option", "None", "")) + Some((EXPECT_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method = if is_err { "expect_err" } else { "expect" }; - if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -36,7 +36,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `{method}()` on `{kind}` value"), + &format!("used `{method}()` on {kind} value"), None, &format!("if this value is {none_prefix}`{none_value}`, it will panic"), ); diff --git a/clippy_lints/src/methods/filter_map.rs b/clippy_lints/src/methods/filter_map.rs index 9719b2f1c51..f888c58a72d 100644 --- a/clippy_lints/src/methods/filter_map.rs +++ b/clippy_lints/src/methods/filter_map.rs @@ -17,7 +17,7 @@ use super::MANUAL_FILTER_MAP; use super::MANUAL_FIND_MAP; use super::OPTION_FILTER_MAP; -fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { +fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool { match &expr.kind { hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name, hir::ExprKind::Path(QPath::Resolved(_, segments)) => { @@ -46,7 +46,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy } } -fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { +fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool { is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some)) } @@ -66,8 +66,8 @@ fn is_filter_some_map_unwrap( /// lint use of `filter().map()` or `find().map()` for `Iterators` #[allow(clippy::too_many_arguments)] -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, filter_recv: &hir::Expr<'_>, filter_arg: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/inefficient_to_string.rs b/clippy_lints/src/methods/inefficient_to_string.rs index 4f4f543e8a9..d8c821bc9ee 100644 --- a/clippy_lints/src/methods/inefficient_to_string.rs +++ b/clippy_lints/src/methods/inefficient_to_string.rs @@ -12,8 +12,8 @@ use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; /// Checks for the `INEFFICIENT_TO_STRING` lint -pub fn check<'tcx>( - cx: &LateContext<'tcx>, +pub fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, receiver: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs index 68d906c3ea3..c830958d5c8 100644 --- a/clippy_lints/src/methods/iter_nth_zero.rs +++ b/clippy_lints/src/methods/iter_nth_zero.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::ITER_NTH_ZERO; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, expr, sym::Iterator); if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg); diff --git a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 4f73b3ec422..70abe4891d9 100644 --- a/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -25,7 +25,7 @@ impl IterType { } } -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) { let item = match recv.kind { ExprKind::Array([]) => None, ExprKind::Array([e]) => Some(e), diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs index b80541b8647..a7284c64497 100644 --- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs +++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs @@ -67,7 +67,7 @@ enum MinMax { Max, } -fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> { +fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> { // `T::max_value()` `T::min_value()` inherent methods if_chain! { if let hir::ExprKind::Call(func, args) = &expr.kind; diff --git a/clippy_lints/src/methods/manual_str_repeat.rs b/clippy_lints/src/methods/manual_str_repeat.rs index 13c47c03a80..a08f7254053 100644 --- a/clippy_lints/src/methods/manual_str_repeat.rs +++ b/clippy_lints/src/methods/manual_str_repeat.rs @@ -59,10 +59,8 @@ pub(super) fn check( if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind; if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat); if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String); - if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id); if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id); if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id); if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id); if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg); let ctxt = collect_expr.span.ctxt(); diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 7ce14ec080b..6bc783c6d50 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -15,11 +15,11 @@ use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check<'tcx>( +pub(super) fn check( cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, - arg: &'tcx hir::Expr<'_>, + arg: &hir::Expr<'_>, msrv: Option<RustcVersion>, ) { if_chain! { diff --git a/clippy_lints/src/methods/map_collect_result_unit.rs b/clippy_lints/src/methods/map_collect_result_unit.rs index d420f144eea..a0300d27870 100644 --- a/clippy_lints/src/methods/map_collect_result_unit.rs +++ b/clippy_lints/src/methods/map_collect_result_unit.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_trait_method; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use if_chain::if_chain; @@ -11,18 +10,10 @@ use rustc_span::symbol::sym; use super::MAP_COLLECT_RESULT_UNIT; -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - iter: &hir::Expr<'_>, - map_fn: &hir::Expr<'_>, - collect_recv: &hir::Expr<'_>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) { + // return of collect `Result<(),_>` + let collect_ret_ty = cx.typeck_results().expr_ty(expr); if_chain! { - // called on Iterator - if is_trait_method(cx, collect_recv, sym::Iterator); - // return of collect `Result<(),_>` - let collect_ret_ty = cx.typeck_results().expr_ty(expr); if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result); if let ty::Adt(_, substs) = collect_ret_ty.kind(); if let Some(result_t) = substs.types().next(); diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs index 1fb6617145e..b773b3e423f 100644 --- a/clippy_lints/src/methods/map_err_ignore.rs +++ b/clippy_lints/src/methods/map_err_ignore.rs @@ -6,7 +6,7 @@ use rustc_span::sym; use super::MAP_ERR_IGNORE; -pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) && let Some(impl_id) = cx.tcx.impl_of_method(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result) diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 8a76ba0b064..38165ab4fb2 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -54,6 +54,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod mut_mutex_lock; +mod needless_collect; mod needless_option_as_deref; mod needless_option_take; mod no_effect_replace; @@ -69,6 +70,8 @@ mod path_buf_push_overwrite; mod range_zip_with_len; mod repeat_once; mod search_is_some; +mod seek_from_current; +mod seek_to_start_instead_of_rewind; mod single_char_add_str; mod single_char_insert_string; mod single_char_pattern; @@ -101,12 +104,11 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; -use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind}; +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -156,9 +158,9 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l"); + /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l"); /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub COLLAPSIBLE_STR_REPLACE, perf, "collapse consecutive calls to str::replace (2 or more) into a single call" @@ -829,32 +831,30 @@ declare_clippy_lint! { /// etc. instead. /// /// ### Why is this bad? - /// The function will always be called and potentially - /// allocate an object acting as the default. + /// The function will always be called. This is only bad if it allocates or + /// does some non-trivial amount of work. /// /// ### Known problems - /// If the function has side-effects, not calling it will - /// change the semantic of the program, but you shouldn't rely on that anyway. + /// If the function has side-effects, not calling it will change the + /// semantic of the program, but you shouldn't rely on that. + /// + /// The lint also cannot figure out whether the function you call is + /// actually expensive to call or not. /// /// ### Example /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or(String::new()); + /// foo.unwrap_or(String::from("empty")); /// ``` /// /// Use instead: /// ```rust /// # let foo = Some(String::new()); - /// foo.unwrap_or_else(String::new); - /// - /// // or - /// - /// # let foo = Some(String::new()); - /// foo.unwrap_or_default(); + /// foo.unwrap_or_else(|| String::from("empty")); /// ``` #[clippy::version = "pre 1.29.0"] pub OR_FUN_CALL, - perf, + nursery, "using any `*or` method with a function call, which suggests `*or_else`" } @@ -1728,7 +1728,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str). + /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str). /// /// ### Why is this bad? /// Readability, this can be written more concisely as @@ -2094,8 +2094,7 @@ declare_clippy_lint! { /// let s = "Hello world!"; /// let cow = Cow::Borrowed(s); /// - /// let data = cow.into_owned(); - /// assert!(matches!(data, String)) + /// let _data: String = cow.into_owned(); /// ``` #[clippy::version = "1.65.0"] pub SUSPICIOUS_TO_OWNED, @@ -2426,7 +2425,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_SINGLE_ITEMS, nursery, "Iterator for array of length 1" @@ -2458,7 +2457,7 @@ declare_clippy_lint! { /// ### Known problems /// /// The type of the resulting iterator might become incompatible with its usage - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub ITER_ON_EMPTY_COLLECTIONS, nursery, "Iterator for empty array" @@ -3066,6 +3065,102 @@ declare_clippy_lint! { "iterating on map using `iter` when `keys` or `values` would do" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks an argument of `seek` method of `Seek` trait + /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead. + /// + /// ### Why is this bad? + /// + /// Readability. Use dedicated method. + /// + /// ### Example + /// + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?); + /// + /// Ok(()) + /// } + /// ``` + /// Use instead: + /// ```rust,no_run + /// use std::fs::File; + /// use std::io::{self, Write, Seek, SeekFrom}; + /// + /// fn main() -> io::Result<()> { + /// let mut f = File::create("foo.txt")?; + /// f.write_all(b"Hello")?; + /// eprintln!("Written {} bytes", f.stream_position()?); + /// + /// Ok(()) + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub SEEK_FROM_CURRENT, + complexity, + "use dedicated method for seek from current position" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for jumps to the start of a stream that implements `Seek` + /// and uses the `seek` method providing `Start` as parameter. + /// + /// ### Why is this bad? + /// + /// Readability. There is a specific method that was implemented for + /// this exact scenario. + /// + /// ### Example + /// ```rust + /// # use std::io; + /// fn foo<T: io::Seek>(t: &mut T) { + /// t.seek(io::SeekFrom::Start(0)); + /// } + /// ``` + /// Use instead: + /// ```rust + /// # use std::io; + /// fn foo<T: io::Seek>(t: &mut T) { + /// t.rewind(); + /// } + /// ``` + #[clippy::version = "1.66.0"] + pub SEEK_TO_START_INSTEAD_OF_REWIND, + complexity, + "jumping to the start of stream using `seek` method" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for functions collecting an iterator when collect + /// is not needed. + /// + /// ### Why is this bad? + /// `collect` causes the allocation of a new data structure, + /// when this allocation may not be needed. + /// + /// ### Example + /// ```rust + /// # let iterator = vec![1].into_iter(); + /// let len = iterator.clone().collect::<Vec<_>>().len(); + /// // should be + /// let len = iterator.count(); + /// ``` + #[clippy::version = "1.30.0"] + pub NEEDLESS_COLLECT, + nursery, + "collecting an iterator when collect is not needed" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>, @@ -3190,16 +3285,19 @@ impl_lint_pass!(Methods => [ VEC_RESIZE_TO_ZERO, VERBOSE_FILE_READS, ITER_KV_MAP, + SEEK_FROM_CURRENT, + SEEK_TO_START_INSTEAD_OF_REWIND, + NEEDLESS_COLLECT, ]); /// Extracts a method call name, args, and `Span` of the method name. fn method_call<'tcx>( recv: &'tcx hir::Expr<'tcx>, -) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> { - if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind { +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> { + if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() { let name = path.ident.name.as_str(); - return Some((name, receiver, args, path.ident.span)); + return Some((name, receiver, args, path.ident.span, call_span)); } } None @@ -3316,36 +3414,10 @@ impl<'tcx> LateLintPass<'tcx> for Methods { if let hir::ImplItemKind::Fn(_, _) = impl_item.kind { let ret_ty = return_ty(cx, impl_item.hir_id()); - // walk the return type and check for Self (this does not check associated types) - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(ret_ty, self_adt) { - return; - } - } else if ret_ty.contains(self_ty) { + if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) { return; } - // if return type is impl trait, check the associated types - if let ty::Opaque(def_id, _) = *ret_ty.kind() { - // one of the associated types must be Self - for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) { - if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() { - let assoc_ty = match projection_predicate.term.unpack() { - ty::TermKind::Ty(ty) => ty, - ty::TermKind::Const(_c) => continue, - }; - // walk the associated type and check for Self - if let Some(self_adt) = self_ty.ty_adt_def() { - if contains_adt_constructor(assoc_ty, self_adt) { - return; - } - } else if assoc_ty.contains(self_ty) { - return; - } - } - } - } - if name == "new" && ret_ty != self_ty { span_lint( cx, @@ -3411,7 +3483,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { impl Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if let Some((name, recv, args, span)) = method_call(expr) { + if let Some((name, recv, args, span, call_span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -3430,28 +3502,31 @@ impl Methods { ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), recv2, [], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); - }, - Some(("map", m_recv, [m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); - }, - Some(("take", take_self_arg, [take_arg], _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); - } - }, - _ => {}, + ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { + needless_collect::check(cx, span, expr, recv, call_span); + match method_call(recv) { + Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); + }, + Some(("map", m_recv, [m_arg], _, _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg); + }, + Some(("take", take_self_arg, [take_arg], _, _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } + }, + _ => {}, + } }, ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) { - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => { + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => { iter_count::check(cx, expr, recv2, name2); }, - Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg), - Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg), - Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2), + Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg), + Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg), + Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2), _ => {}, }, ("drain", [arg]) => { @@ -3463,8 +3538,8 @@ impl Methods { } }, ("expect", [_]) => match method_call(recv) { - Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3484,13 +3559,13 @@ impl Methods { flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => match method_call(recv) { - Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), + Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true), _ => {}, }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", _, [_], span2)) = method_call(recv) { + if let Some(("inspect", _, [_], span2, _)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -3510,12 +3585,12 @@ impl Methods { iter_on_single_or_empty_collections::check(cx, expr, name, recv); }, ("join", [join_arg]) => { - if let Some(("collect", _, _, span)) = method_call(recv) { + if let Some(("collect", _, _, span, _)) = method_call(recv) { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, ("last", []) | ("skip", [_]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3527,13 +3602,13 @@ impl Methods { (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { map_clone::check(cx, expr, recv, m_arg, self.msrv); - if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) { + if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } } else { map_err_ignore::check(cx, expr, m_arg); } - if let Some((name, recv2, args, span2)) = method_call(recv) { + if let Some((name, recv2, args, span2,_)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), @@ -3553,7 +3628,7 @@ impl Methods { manual_ok_or::check(cx, expr, recv, def, map); }, ("next", []) => { - if let Some((name2, recv2, args2, _)) = method_call(recv) { + if let Some((name2, recv2, args2, _, _)) = method_call(recv) { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), @@ -3566,10 +3641,10 @@ impl Methods { } }, ("nth", [n_arg]) => match method_call(recv) { - Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), - Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), + Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), _ => iter_nth_zero::check(cx, expr, recv, n_arg), }, ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), @@ -3604,6 +3679,14 @@ impl Methods { ("resize", [count_arg, default_arg]) => { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, + ("seek", [arg]) => { + if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) { + seek_from_current::check(cx, expr, recv, arg); + } + if meets_msrv(self.msrv, msrvs::SEEK_REWIND) { + seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); + } + }, ("sort", []) => { stable_sort_primitive::check(cx, expr, recv); }, @@ -3626,7 +3709,7 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2)) = method_call(recv) { + if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { if let ("cloned", []) = (name2, args2) { iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } @@ -3649,13 +3732,13 @@ impl Methods { }, ("unwrap", []) => { match method_call(recv) { - Some(("get", recv, [get_arg], _)) => { + Some(("get", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, false); }, - Some(("get_mut", recv, [get_arg], _)) => { + Some(("get_mut", recv, [get_arg], _, _)) => { get_unwrap::check(cx, expr, recv, get_arg, true); }, - Some(("or", recv, [or_arg], or_span)) => { + Some(("or", recv, [or_arg], or_span, _)) => { or_then_unwrap::check(cx, expr, recv, or_arg, or_span); }, _ => {}, @@ -3664,19 +3747,19 @@ impl Methods { }, ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests), ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", m_recv, [m_arg], span)) => { + Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); }, - Some(("then_some", t_recv, [t_arg], _)) => { + Some(("then_some", t_recv, [t_arg], _, _)) => { obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); }, _ => {}, }, ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", recv, [map_arg], _)) + Some(("map", recv, [map_arg], _, _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -3697,7 +3780,7 @@ impl Methods { } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) { + if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -3906,14 +3989,6 @@ impl OutType { } } -fn is_bool(ty: &hir::Ty<'_>) -> bool { - if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - matches!(path.res, Res::PrimTy(PrimTy::Bool)) - } else { - false - } -} - fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool { expected.constness == actual.constness && expected.unsafety == actual.unsafety diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 66f9e28596e..b088e642e0e 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -3,94 +3,99 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind}; -use if_chain::if_chain; +use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection}; +use clippy_utils::{ + can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id, + CaptureKind, +}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::{self, Ty}; -use rustc_span::sym; -use rustc_span::Span; +use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; +use rustc_span::symbol::Ident; +use rustc_span::{sym, Span, Symbol}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; -pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - check_needless_collect_direct_usage(expr, cx); - check_needless_collect_indirect_usage(expr, cx); -} -fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if_chain! { - if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind; - if let ExprKind::MethodCall(chain_method, ..) = receiver.kind; - if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator); - then { - let ty = cx.typeck_results().expr_ty(receiver); - let mut applicability = Applicability::MaybeIncorrect; - let is_empty_sugg = "next().is_none()".to_string(); - let method_name = method.ident.name.as_str(); - let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::LinkedList) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) { - match method_name { - "len" => "count()".to_string(), - "is_empty" => is_empty_sugg, - "contains" => { - let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability); - let (arg, pred) = contains_arg - .strip_prefix('&') - .map_or(("&x", &*contains_arg), |s| ("x", s)); - format!("any(|{arg}| x == {pred})") - } - _ => return, - } - } - else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) || - is_type_diagnostic_item(cx, ty, sym::HashMap) { - match method_name { - "is_empty" => is_empty_sugg, - _ => return, - } - } - else { - return; - }; - span_lint_and_sugg( - cx, - NEEDLESS_COLLECT, - chain_method.ident.span.with_hi(expr.span.hi()), - NEEDLESS_COLLECT_MSG, - "replace with", - sugg, - applicability, - ); - } - } -} +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + name_span: Span, + collect_expr: &'tcx Expr<'_>, + iter_expr: &'tcx Expr<'tcx>, + call_span: Span, +) { + if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) { + match parent { + Node::Expr(parent) => { + if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind { + let mut app = Applicability::MachineApplicable; + let name = name.ident.as_str(); + let collect_ty = cx.typeck_results().expr_ty(collect_expr); -fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) { - if let ExprKind::Block(block, _) = expr.kind { - for stmt in block.stmts { - if_chain! { - if let StmtKind::Local(local) = stmt.kind; - if let PatKind::Binding(_, id, ..) = local.pat.kind; - if let Some(init_expr) = local.init; - if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind; - if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator); - let ty = cx.typeck_results().expr_ty(init_expr); - if is_type_diagnostic_item(cx, ty, sym::Vec) || - is_type_diagnostic_item(cx, ty, sym::VecDeque) || - is_type_diagnostic_item(cx, ty, sym::BinaryHeap) || - is_type_diagnostic_item(cx, ty, sym::LinkedList); - let iter_ty = cx.typeck_results().expr_ty(iter_source); - if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)); - if let [iter_call] = &*iter_calls; - then { + let sugg: String = match name { + "len" => { + if let Some(adt) = collect_ty.ty_adt_def() + && matches!( + cx.tcx.get_diagnostic_name(adt.did()), + Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap) + ) + { + "count()".into() + } else { + return; + } + }, + "is_empty" + if is_is_empty_sig(cx, parent.hir_id) + && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) => + { + "next().is_none()".into() + }, + "contains" => { + if is_contains_sig(cx, parent.hir_id, iter_expr) + && let Some(arg) = args.first() + { + let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind { + (arg.span, "") + } else { + (arg.span, "*") + }; + let snip = snippet_with_applicability(cx, span, "??", &mut app); + format!("any(|x| x == {prefix}{snip})") + } else { + return; + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + NEEDLESS_COLLECT, + call_span.with_hi(parent.span.hi()), + NEEDLESS_COLLECT_MSG, + "replace with", + sugg, + app, + ); + } + }, + Node::Local(l) => { + if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) + = l.pat.kind + && let ty = cx.typeck_results().expr_ty(collect_expr) + && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter() + .any(|item| is_type_diagnostic_item(cx, ty, item)) + && let iter_ty = cx.typeck_results().expr_ty(iter_expr) + && let Some(block) = get_enclosing_block(cx, l.hir_id) + && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty)) + && let [iter_call] = &*iter_calls + { let mut used_count_visitor = UsedCountVisitor { cx, id, @@ -102,20 +107,20 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo } // Suggest replacing iter_call with iter_replacement, and removing stmt - let mut span = MultiSpan::from_span(method_name.ident.span); + let mut span = MultiSpan::from_span(name_span); span.push_span_label(iter_call.span, "the iterator could be used here instead"); span_lint_hir_and_then( cx, super::NEEDLESS_COLLECT, - init_expr.hir_id, + collect_expr.hir_id, span, NEEDLESS_COLLECT_MSG, |diag| { - let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx)); + let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx)); diag.multipart_suggestion( iter_call.get_suggestion_text(), vec![ - (stmt.span, String::new()), + (l.span, String::new()), (iter_call.span, iter_replacement) ], Applicability::MaybeIncorrect, @@ -123,11 +128,61 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo }, ); } - } + }, + _ => (), } } } +/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` +fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { + cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| { + let sig = cx.tcx.fn_sig(id).skip_binder(); + sig.inputs().len() == 1 && sig.output().is_bool() + }) +} + +/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item` +fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool { + let item = Symbol::intern("Item"); + if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator) + && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty]) + && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty]) + && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions( + cx.param_env, + cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs) + ) + { + iter_item_ty == into_iter_item_ty + } else { + false + } +} + +/// Checks if the given method call matches the expected signature of +/// `([&[mut]] self, &<iter_ty as Iterator>::Item) -> bool` +fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool { + let typeck = cx.typeck_results(); + if let Some(id) = typeck.type_dependent_def_id(call_id) + && let sig = cx.tcx.fn_sig(id) + && sig.skip_binder().output().is_bool() + && let [_, search_ty] = *sig.skip_binder().inputs() + && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind() + && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) + && let Some(iter_item) = cx.tcx + .associated_items(iter_trait) + .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait) + && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter()) + && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs) + && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty) + { + item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id)) + } else { + false + } +} + struct IterFunction { func: IterFunctionKind, span: Span, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index 742483e6b2e..e6eb64bcbde 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -13,8 +13,8 @@ use rustc_span::sym; use super::OPTION_AS_REF_DEREF; /// lint use of `_.as_ref().map(Deref::deref)` for `Option`s -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, +pub(super) fn check( + cx: &LateContext<'_>, expr: &hir::Expr<'_>, as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, diff --git a/clippy_lints/src/methods/or_fun_call.rs b/clippy_lints/src/methods/or_fun_call.rs index 991d3dd538b..4460f38fcc1 100644 --- a/clippy_lints/src/methods/or_fun_call.rs +++ b/clippy_lints/src/methods/or_fun_call.rs @@ -83,6 +83,8 @@ pub(super) fn check<'tcx>( method_span: Span, self_expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, + // `Some` if fn has second argument + second_arg: Option<&hir::Expr<'_>>, span: Span, // None if lambda is required fun_span: Option<Span>, @@ -109,30 +111,40 @@ pub(super) fn check<'tcx>( if poss.contains(&name); then { - let macro_expanded_snipped; - let sugg: Cow<'_, str> = { + let sugg = { let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) { (false, Some(fun_span)) => (fun_span, false), _ => (arg.span, true), }; - let snippet = { - let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, ".."); - if not_macro_argument_snippet == "vec![]" { - macro_expanded_snipped = snippet(cx, snippet_span, ".."); + + let format_span = |span: Span| { + let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, ".."); + let snip = if not_macro_argument_snippet == "vec![]" { + let macro_expanded_snipped = snippet(cx, snippet_span, ".."); match macro_expanded_snipped.strip_prefix("$crate::vec::") { - Some(stripped) => Cow::from(stripped), + Some(stripped) => Cow::Owned(stripped.to_owned()), None => macro_expanded_snipped, } } else { not_macro_argument_snippet - } + }; + + snip.to_string() }; - if use_lambda { + let snip = format_span(snippet_span); + let snip = if use_lambda { let l_arg = if fn_has_arguments { "_" } else { "" }; - format!("|{l_arg}| {snippet}").into() + format!("|{l_arg}| {snip}") } else { - snippet + snip + }; + + if let Some(f) = second_arg { + let f = format_span(f.span); + format!("{snip}, {f}") + } else { + snip } }; let span_replace_word = method_span.with_hi(span.hi()); @@ -149,8 +161,8 @@ pub(super) fn check<'tcx>( } } - if let [arg] = args { - let inner_arg = if let hir::ExprKind::Block( + let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| { + if let hir::ExprKind::Block( hir::Block { stmts: [], expr: Some(expr), @@ -162,19 +174,32 @@ pub(super) fn check<'tcx>( expr } else { arg - }; + } + }; + + if let [arg] = args { + let inner_arg = extract_inner_arg(arg); match inner_arg.kind { hir::ExprKind::Call(fun, or_args) => { let or_has_args = !or_args.is_empty(); if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) { let fun_span = if or_has_args { None } else { Some(fun.span) }; - check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span); } }, hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => { - check_general_case(cx, name, method_span, receiver, arg, expr.span, None); + check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None); }, _ => (), } } + + // `map_or` takes two arguments + if let [arg, lambda] = args { + let inner_arg = extract_inner_arg(arg); + if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind { + let fun_span = if or_args.is_empty() { Some(fun.span) } else { None }; + check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span); + } + } } diff --git a/clippy_lints/src/methods/seek_from_current.rs b/clippy_lints/src/methods/seek_from_current.rs new file mode 100644 index 00000000000..361a3082f94 --- /dev/null +++ b/clippy_lints/src/methods/seek_from_current.rs @@ -0,0 +1,48 @@ +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; + +use clippy_utils::{ + diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability, + ty::implements_trait, +}; + +use super::SEEK_FROM_CURRENT; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + + if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) { + if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) { + let mut applicability = Applicability::MachineApplicable; + let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability); + + span_lint_and_sugg( + cx, + SEEK_FROM_CURRENT, + expr.span, + "using `SeekFrom::Current` to start from current position", + "replace with", + format!("{snip}.stream_position()"), + applicability, + ); + } + } +} + +fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { + if let ExprKind::Call(f, args) = expr.kind && + let ExprKind::Path(ref path) = f.kind && + let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) { + // check if argument of `SeekFrom::Current` is `0` + if args.len() == 1 && + let ExprKind::Lit(ref lit) = args[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node { + return true + } + } + + false +} diff --git a/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs new file mode 100644 index 00000000000..7e3bed1e41a --- /dev/null +++ b/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -0,0 +1,45 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, match_def_path, paths}; +use rustc_ast::ast::{LitIntType, LitKind}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::SEEK_TO_START_INSTEAD_OF_REWIND; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + recv: &'tcx Expr<'_>, + arg: &'tcx Expr<'_>, + name_span: Span, +) { + // Get receiver type + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) && + implements_trait(cx, ty, seek_trait_id, &[]) && + let ExprKind::Call(func, args1) = arg.kind && + let ExprKind::Path(ref path) = func.kind && + let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() && + match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) && + args1.len() == 1 && + let ExprKind::Lit(ref lit) = args1[0].kind && + let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node + { + let method_call_span = expr.span.with_lo(name_span.lo()); + span_lint_and_then( + cx, + SEEK_TO_START_INSTEAD_OF_REWIND, + method_call_span, + "used `seek` to go to the start of the stream", + |diag| { + let app = Applicability::MachineApplicable; + + diag.span_suggestion(method_call_span, "replace with", "rewind()", app); + }, + ); + } +} diff --git a/clippy_lints/src/methods/string_extend_chars.rs b/clippy_lints/src/methods/string_extend_chars.rs index 6f4cec546e9..f35d81cee8e 100644 --- a/clippy_lints/src/methods/string_extend_chars.rs +++ b/clippy_lints/src/methods/string_extend_chars.rs @@ -18,7 +18,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr let target = &arglists[0].0; let self_ty = cx.typeck_results().expr_ty(target).peel_refs(); let ref_str = if *self_ty.kind() == ty::Str { - "" + if matches!(target.kind, hir::ExprKind::Index(..)) { + "&" + } else { + "" + } } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) { "&" } else { diff --git a/clippy_lints/src/methods/suspicious_map.rs b/clippy_lints/src/methods/suspicious_map.rs index 851cdf54455..2ac0786b37b 100644 --- a/clippy_lints/src/methods/suspicious_map.rs +++ b/clippy_lints/src/methods/suspicious_map.rs @@ -8,7 +8,7 @@ use rustc_span::sym; use super::SUSPICIOUS_MAP; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { +pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) { if_chain! { if is_trait_method(cx, count_recv, sym::Iterator); let closure = expr_or_init(cx, map_arg); diff --git a/clippy_lints/src/methods/unnecessary_join.rs b/clippy_lints/src/methods/unnecessary_join.rs index c9b87bc6bf2..087e1e4343b 100644 --- a/clippy_lints/src/methods/unnecessary_join.rs +++ b/clippy_lints/src/methods/unnecessary_join.rs @@ -31,7 +31,7 @@ pub(super) fn check<'tcx>( cx, UNNECESSARY_JOIN, span.with_hi(expr.span.hi()), - r#"called `.collect<Vec<String>>().join("")` on an iterator"#, + r#"called `.collect::<Vec<String>>().join("")` on an iterator"#, "try using", "collect::<String>()".to_owned(), applicability, diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index ee17f2d7889..90983f249cd 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_test_function, is_lint_allowed}; +use clippy_utils::{is_in_cfg_test, is_lint_allowed}; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::sym; @@ -18,16 +18,16 @@ pub(super) fn check( let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an Option", "None", "")) + Some((UNWRAP_USED, "an `Option`", "None", "")) } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an ")) + Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) } else { None }; let method_suffix = if is_err { "_err" } else { "" }; - if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) { return; } @@ -45,7 +45,7 @@ pub(super) fn check( cx, lint, expr.span, - &format!("used `unwrap{method_suffix}()` on `{kind}` value"), + &format!("used `unwrap{method_suffix}()` on {kind} value"), None, &help, ); diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index 872679f25ab..4712846939e 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -59,7 +59,7 @@ impl LateLintPass<'_> for ImportRename { fn check_crate(&mut self, cx: &LateContext<'_>) { for Rename { path, rename } in &self.conf_renames { let segs = path.split("::").collect::<Vec<_>>(); - if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) { + for id in clippy_utils::def_path_def_ids(cx, &segs) { self.renames.insert(id, Symbol::intern(rename)); } } diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs index 6752976348f..321fa4b7f99 100644 --- a/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/clippy_lints/src/mixed_read_write_in_expression.rs @@ -218,7 +218,7 @@ enum StopEarly { Stop, } -fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { +fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly { if expr.hir_id == vis.last_expr.hir_id { return StopEarly::KeepGoing; } @@ -265,7 +265,7 @@ fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) - StopEarly::KeepGoing } -fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { +fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly { match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr), // If the declaration is of a local variable, check its initializer diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs index 4b62dcdffe2..a651020ca65 100644 --- a/clippy_lints/src/mut_key.rs +++ b/clippy_lints/src/mut_key.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::trait_ref_of_method; +use clippy_utils::{def_path_def_ids, trait_ref_of_method}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TypeVisitable; use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; use std::iter; @@ -78,26 +79,44 @@ declare_clippy_lint! { "Check for mutable `Map`/`Set` key type" } -declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); +#[derive(Clone)] +pub struct MutableKeyType { + ignore_interior_mutability: Vec<String>, + ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>, +} + +impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]); impl<'tcx> LateLintPass<'tcx> for MutableKeyType { + fn check_crate(&mut self, cx: &LateContext<'tcx>) { + self.ignore_mut_def_ids.clear(); + let mut path = Vec::new(); + for ty in &self.ignore_interior_mutability { + path.extend(ty.split("::")); + for id in def_path_def_ids(cx, &path[..]) { + self.ignore_mut_def_ids.insert(id); + } + path.clear(); + } + } + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { if let hir::ItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) { if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind { if trait_ref_of_method(cx, item.owner_id.def_id).is_none() { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) { if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind { - check_sig(cx, item.hir_id(), sig.decl); + self.check_sig(cx, item.hir_id(), sig.decl); } } @@ -105,73 +124,81 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { if let hir::PatKind::Wild = local.pat.kind { return; } - check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat)); + self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat)); } } -fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { - let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); - let fn_sig = cx.tcx.fn_sig(fn_def_id); - for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { - check_ty(cx, hir_ty.span, *ty); +impl MutableKeyType { + pub fn new(ignore_interior_mutability: Vec<String>) -> Self { + Self { + ignore_interior_mutability, + ignore_mut_def_ids: FxHashSet::default(), + } } - check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); -} -// We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased -// generics (because the compiler cannot ensure immutability for unknown types). -fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { - let ty = ty.peel_refs(); - if let Adt(def, substs) = ty.kind() { - let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) { - span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); + fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) { + let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id); + let fn_sig = cx.tcx.fn_sig(fn_def_id); + for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) { + self.check_ty_(cx, hir_ty.span, *ty); } + self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output())); } -} -/// Determines if a type contains interior mutability which would affect its implementation of -/// [`Hash`] or [`Ord`]. -fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool { - match *ty.kind() { - Ref(_, inner_ty, mutbl) => { - mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span) - } - Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span), - Array(inner_ty, size) => { - size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) - && is_interior_mutable_type(cx, inner_ty, span) - } - Tuple(fields) => fields.iter().any(|ty| is_interior_mutable_type(cx, ty, span)), - Adt(def, substs) => { - // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to - // that of their type parameters. Note: we don't include `HashSet` and `HashMap` - // because they have no impl for `Hash` or `Ord`. - let is_std_collection = [ - sym::Option, - sym::Result, - sym::LinkedList, - sym::Vec, - sym::VecDeque, - sym::BTreeMap, - sym::BTreeSet, - sym::Rc, - sym::Arc, - ] - .iter() - .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); - let is_box = Some(def.did()) == cx.tcx.lang_items().owned_box(); - if is_std_collection || is_box { - // The type is mutable if any of its type parameters are - substs.types().any(|ty| is_interior_mutable_type(cx, ty, span)) - } else { - !ty.has_escaping_bound_vars() - && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() - && !ty.is_freeze(cx.tcx, cx.param_env) + // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased + // generics (because the compiler cannot ensure immutability for unknown types). + fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) { + let ty = ty.peel_refs(); + if let Adt(def, substs) = ty.kind() { + let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did())); + if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) { + span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type"); } } - _ => false, + } + + /// Determines if a type contains interior mutability which would affect its implementation of + /// [`Hash`] or [`Ord`]. + fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + match *ty.kind() { + Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty), + Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty), + Array(inner_ty, size) => { + size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0) + && self.is_interior_mutable_type(cx, inner_ty) + }, + Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)), + Adt(def, substs) => { + // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to + // that of their type parameters. Note: we don't include `HashSet` and `HashMap` + // because they have no impl for `Hash` or `Ord`. + let def_id = def.did(); + let is_std_collection = [ + sym::Option, + sym::Result, + sym::LinkedList, + sym::Vec, + sym::VecDeque, + sym::BTreeMap, + sym::BTreeSet, + sym::Rc, + sym::Arc, + ] + .iter() + .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id)); + let is_box = Some(def_id) == cx.tcx.lang_items().owned_box(); + if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) { + // The type is mutable if any of its type parameters are + substs.types().any(|ty| self.is_interior_mutable_type(cx, ty)) + } else { + !ty.has_escaping_bound_vars() + && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok() + && !ty.is_freeze(cx.tcx, cx.param_env) + } + }, + _ => false, + } } } diff --git a/clippy_lints/src/mut_mut.rs b/clippy_lints/src/mut_mut.rs index cb16f00047a..bc90e131b7f 100644 --- a/clippy_lints/src/mut_mut.rs +++ b/clippy_lints/src/mut_mut.rs @@ -68,13 +68,15 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> { expr.span, "generally you want to avoid `&mut &mut _` if possible", ); - } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { - span_lint( - self.cx, - MUT_MUT, - expr.span, - "this expression mutably borrows a mutable reference. Consider reborrowing", - ); + } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() { + if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) { + span_lint( + self.cx, + MUT_MUT, + expr.span, + "this expression mutably borrows a mutable reference. Consider reborrowing", + ); + } } } } diff --git a/clippy_lints/src/needless_borrowed_ref.rs b/clippy_lints/src/needless_borrowed_ref.rs index 10c3ff026b6..498e1408e52 100644 --- a/clippy_lints/src/needless_borrowed_ref.rs +++ b/clippy_lints/src/needless_borrowed_ref.rs @@ -36,14 +36,14 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]); impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { - fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { - if pat.span.from_expansion() { + fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) { + if ref_pat.span.from_expansion() { // OK, simple enough, lints doesn't check in macro. return; } // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms - for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) { + for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) { let Node::Pat(pat) = node else { break }; if matches!(pat.kind, PatKind::Or(_)) { @@ -52,20 +52,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { } // Only lint immutable refs, because `&mut ref T` may be useful. - let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return }; + let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return }; - match sub_pat.kind { + match pat.kind { // Check sub_pat got a `ref` keyword (excluding `ref mut`). PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { span_lint_and_then( cx, NEEDLESS_BORROWED_REFERENCE, - pat.span, + ref_pat.span, "this pattern takes a reference on something that is being dereferenced", |diag| { // `&ref ident` // ^^^^^ - let span = pat.span.until(ident.span); + let span = ref_pat.span.until(ident.span); diag.span_suggestion_verbose( span, "try removing the `&ref` part", @@ -84,41 +84,71 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef { }), after, ) => { - let mut suggestions = Vec::new(); - - for element_pat in itertools::chain(before, after) { - if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind { - // `&[..., ref ident, ...]` - // ^^^^ - let span = element_pat.span.until(ident.span); - suggestions.push((span, String::new())); - } else { - return; - } - } + check_subpatterns( + cx, + "dereferencing a slice pattern where every element takes a reference", + ref_pat, + pat, + itertools::chain(before, after), + ); + }, + PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => { + check_subpatterns( + cx, + "dereferencing a tuple pattern where every element takes a reference", + ref_pat, + pat, + subpatterns, + ); + }, + PatKind::Struct(_, fields, _) => { + check_subpatterns( + cx, + "dereferencing a struct pattern where every field's pattern takes a reference", + ref_pat, + pat, + fields.iter().map(|field| field.pat), + ); + }, + _ => {}, + } + } +} - if !suggestions.is_empty() { - span_lint_and_then( - cx, - NEEDLESS_BORROWED_REFERENCE, - pat.span, - "dereferencing a slice pattern where every element takes a reference", - |diag| { - // `&[...]` - // ^ - let span = pat.span.until(sub_pat.span); - suggestions.push((span, String::new())); +fn check_subpatterns<'tcx>( + cx: &LateContext<'tcx>, + message: &str, + ref_pat: &Pat<'_>, + pat: &Pat<'_>, + subpatterns: impl IntoIterator<Item = &'tcx Pat<'tcx>>, +) { + let mut suggestions = Vec::new(); - diag.multipart_suggestion( - "try removing the `&` and `ref` parts", - suggestions, - Applicability::MachineApplicable, - ); - }, - ); - } + for subpattern in subpatterns { + match subpattern.kind { + PatKind::Binding(BindingAnnotation::REF, _, ident, None) => { + // `ref ident` + // ^^^^ + let span = subpattern.span.until(ident.span); + suggestions.push((span, String::new())); }, - _ => {}, + PatKind::Wild => {}, + _ => return, } } + + if !suggestions.is_empty() { + span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| { + // `&pat` + // ^ + let span = ref_pat.span.until(pat.span); + suggestions.push((span, String::new())); + + diag.multipart_suggestion( + "try removing the `&` and `ref` parts", + suggestions, + Applicability::MachineApplicable, + ); + }); + } } diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index 6f0e755466e..38a75034cd3 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -287,7 +287,7 @@ const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause"; const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression"; -fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) { +fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) { // snip is the whole *help* message that appears after the warning. // message is the warning message. // expr is the expression which the lint warning message refers to. @@ -313,7 +313,7 @@ fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, ); } -fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)); @@ -327,7 +327,7 @@ fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &' ) } -fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String { +fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String { let cond_code = snippet(cx, data.if_cond.span, ".."); // Region B @@ -361,7 +361,7 @@ fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: ) } -fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) { +fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { if_chain! { if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind; if let Some(last_stmt) = loop_block.stmts.last(); diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index eeff15bbfb4..55599cd03eb 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -340,11 +340,5 @@ impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt { fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {} - fn fake_read( - &mut self, - _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, - _: FakeReadCause, - _: HirId, - ) { - } + fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {} } diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 2a7159764e4..ae0a41db918 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -58,9 +58,9 @@ impl EarlyLintPass for OctalEscapes { if let ExprKind::Lit(token_lit) = &expr.kind { if matches!(token_lit.kind, LitKind::Str) { - check_lit(cx, &token_lit, expr.span, true); + check_lit(cx, token_lit, expr.span, true); } else if matches!(token_lit.kind, LitKind::ByteStr) { - check_lit(cx, &token_lit, expr.span, false); + check_lit(cx, token_lit, expr.span, false); } } } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 8827daaa3ee..20b82d81a2a 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -1,5 +1,9 @@ use super::ARITHMETIC_SIDE_EFFECTS; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use clippy_utils::{ + consts::{constant, constant_simple}, + diagnostics::span_lint, + peel_hir_expr_refs, +}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; @@ -38,24 +42,6 @@ impl ArithmeticSideEffects { } } - /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a - /// non-constant environment that won't overflow. - fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::Lit(ref lit) = expr.kind && - let ast::LitKind::Int(value, _) = lit.node - { - match (&op.node, value) { - (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, - (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) - | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) - | (hir::BinOpKind::Mul, 0 | 1) => true, - _ => false, - } - } else { - false - } - } - /// Checks if the given `expr` has any of the inner `allowed` elements. fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { self.allowed @@ -74,15 +60,14 @@ impl ArithmeticSideEffects { self.expr_span = Some(expr.span); } - /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. - fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option<LiteralIntegerTy<'expr, 'tcx>> { - if matches!(expr.kind, hir::ExprKind::Lit(_)) { - return Some(LiteralIntegerTy::Value(expr)); + /// If `expr` is not a literal integer like `1`, returns `None`. + fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> { + if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node { + Some(n) } - if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { - return Some(LiteralIntegerTy::Ref(inn)); + else { + None } - None } /// Manages when the lint should be triggered. Operations in constant environments, hard coded @@ -117,10 +102,20 @@ impl ArithmeticSideEffects { return; } let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { - match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { - (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), - (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, - (None, None) | (Some(_), Some(_)) => false, + let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); + let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); + match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) { + (None, None) => false, + (None, Some(n)) | (Some(n), None) => match (&op.node, n) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + }, + (Some(_), Some(_)) => { + matches!((lhs_ref_counter, rhs_ref_counter), (0, 0)) + }, } } else { false @@ -129,21 +124,45 @@ impl ArithmeticSideEffects { self.issue_lint(cx, expr); } } + + fn manage_unary_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + un_expr: &hir::Expr<'tcx>, + un_op: hir::UnOp, + ) { + let hir::UnOp::Neg = un_op else { return; }; + if constant(cx, cx.typeck_results(), un_expr).is_some() { + return; + } + let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + if self.is_allowed_ty(ty) { + return; + } + let actual_un_expr = peel_hir_expr_refs(un_expr).0; + if Self::literal_integer(actual_un_expr).is_some() { + return; + } + self.issue_lint(cx, expr); + } + + fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool { + self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) + } } impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { - if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + if self.should_skip_expr(expr) { return; } match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op, lhs, rhs); }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } + hir::ExprKind::Unary(un_op, un_expr) => { + self.manage_unary_ops(cx, expr, un_expr, *un_op); }, _ => {}, } @@ -177,22 +196,3 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { } } } - -/// Tells if an expression is a integer declared by value or by reference. -/// -/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather -/// than `hirExprKind::Addr`. -enum LiteralIntegerTy<'expr, 'tcx> { - /// For example, `&199` - Ref(&'expr hir::Expr<'tcx>), - /// For example, `1` or `i32::MAX` - Value(&'expr hir::Expr<'tcx>), -} - -impl<'expr, 'tcx> From<LiteralIntegerTy<'expr, 'tcx>> for &'expr hir::Expr<'tcx> { - fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { - match from { - LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, - } - } -} diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs index 71b31b5e4a5..d7917e86a86 100644 --- a/clippy_lints/src/operators/op_ref.rs +++ b/clippy_lints/src/operators/op_ref.rs @@ -199,7 +199,7 @@ fn in_impl<'tcx>( } } -fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { +fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool { if_chain! { if let ty::Adt(adt_def, _) = middle_ty.kind(); if let Some(local_did) = adt_def.did().as_local(); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index 4eb42da1fed..472f52380bb 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -213,11 +213,14 @@ fn try_convert_match<'tcx>( cx: &LateContext<'tcx>, arms: &[Arm<'tcx>], ) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { - if arms.len() == 2 { - return if is_none_or_err_arm(cx, &arms[1]) { - Some((arms[0].pat, arms[0].body, arms[1].body)) - } else if is_none_or_err_arm(cx, &arms[0]) { - Some((arms[1].pat, arms[1].body, arms[0].body)) + if let [first_arm, second_arm] = arms + && first_arm.guard.is_none() + && second_arm.guard.is_none() + { + return if is_none_or_err_arm(cx, second_arm) { + Some((first_arm.pat, first_arm.body, second_arm.body)) + } else if is_none_or_err_arm(cx, first_arm) { + Some((second_arm.pat, second_arm.body, first_arm.body)) } else { None }; diff --git a/clippy_lints/src/partialeq_to_none.rs b/clippy_lints/src/partialeq_to_none.rs index 6810a243175..456ded3fc02 100644 --- a/clippy_lints/src/partialeq_to_none.rs +++ b/clippy_lints/src/partialeq_to_none.rs @@ -33,7 +33,7 @@ declare_clippy_lint! { /// if f.is_some() { "yay" } else { "nay" } /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub PARTIALEQ_TO_NONE, style, "Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded" diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index a4d265111f9..97b5a4ce364 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -130,7 +130,7 @@ enum DerefPossible { Impossible, } -fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { +fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool { let maybe_mismatch = find_first_mismatch(cx, pat); if let Some((span, mutability, level)) = maybe_mismatch { span_lint_and_help( @@ -163,7 +163,7 @@ enum Level { Lower, } -fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { +fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { if result.is_some() { diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs index 72dda67c72b..47b8891e123 100644 --- a/clippy_lints/src/ptr_offset_with_cast.rs +++ b/clippy_lints/src/ptr_offset_with_cast.rs @@ -105,17 +105,17 @@ fn expr_as_ptr_offset_call<'tcx>( } // Is the type of the expression a usize? -fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize } // Is the type of the expression a raw pointer? -fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool { +fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { cx.typeck_results().expr_ty(expr).is_unsafe_ptr() } -fn build_suggestion<'tcx>( - cx: &LateContext<'tcx>, +fn build_suggestion( + cx: &LateContext<'_>, method: Method, receiver_expr: &Expr<'_>, cast_lhs_expr: &Expr<'_>, diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs index bb86fb3b7d4..5269bbd1f1a 100644 --- a/clippy_lints/src/question_mark.rs +++ b/clippy_lints/src/question_mark.rs @@ -189,6 +189,7 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_ && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym))) || is_res_lang_ctor(cx, res, ResultErr) && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym)) + && if_else.is_none() }, _ => false, } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 4cbe9597c53..8e675d34a18 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -105,8 +105,8 @@ impl EarlyLintPass for RedundantClosureCall { impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { - fn count_closure_usage<'a, 'tcx>( - cx: &'a LateContext<'tcx>, + fn count_closure_usage<'tcx>( + cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>, path: &'tcx hir::Path<'tcx>, ) -> usize { diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index 26075e9f70f..833dc4913b4 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -70,7 +70,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } if let ItemKind::Mod { .. } = item.kind { - self.is_exported.push(cx.effective_visibilities.is_exported(item.owner_id.def_id)); + self.is_exported + .push(cx.effective_visibilities.is_exported(item.owner_id.def_id)); } } diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs index 76d6ad0b23e..8e214218f23 100644 --- a/clippy_lints/src/renamed_lints.rs +++ b/clippy_lints/src/renamed_lints.rs @@ -11,8 +11,6 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::disallowed_method", "clippy::disallowed_methods"), ("clippy::disallowed_type", "clippy::disallowed_types"), ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"), - ("clippy::for_loop_over_option", "for_loops_over_fallibles"), - ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::identity_conversion", "clippy::useless_conversion"), ("clippy::if_let_some_result", "clippy::match_result_ok"), ("clippy::logic_bug", "clippy::overly_complex_bool_expr"), @@ -31,10 +29,13 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[ ("clippy::to_string_in_display", "clippy::recursive_format_impl"), ("clippy::zero_width_space", "clippy::invisible_characters"), ("clippy::drop_bounds", "drop_bounds"), + ("clippy::for_loop_over_option", "for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "for_loops_over_fallibles"), ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"), ("clippy::into_iter_on_array", "array_into_iter"), ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), ("clippy::invalid_ref", "invalid_value"), + ("clippy::let_underscore_drop", "let_underscore_drop"), ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), ("clippy::panic_params", "non_fmt_panics"), ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"), diff --git a/clippy_lints/src/single_component_path_imports.rs b/clippy_lints/src/single_component_path_imports.rs index 66b79513032..2036e85db7e 100644 --- a/clippy_lints/src/single_component_path_imports.rs +++ b/clippy_lints/src/single_component_path_imports.rs @@ -1,8 +1,9 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; +use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{edition::Edition, symbol::kw, Span, Symbol}; declare_clippy_lint! { @@ -33,51 +34,32 @@ declare_clippy_lint! { "imports with single component path are redundant" } -declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); +#[derive(Default)] +pub struct SingleComponentPathImports { + /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected + found: NodeMap<Vec<SingleUse>>, +} + +struct SingleUse { + name: Symbol, + span: Span, + item_id: NodeId, + can_suggest: bool, +} + +impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); impl EarlyLintPass for SingleComponentPathImports { fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { if cx.sess().opts.edition < Edition::Edition2018 { return; } - check_mod(cx, &krate.items); - } -} -fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) { - // keep track of imports reused with `self` keyword, - // such as `self::crypto_hash` in the example below - // ```rust,ignore - // use self::crypto_hash::{Algorithm, Hasher}; - // ``` - let mut imports_reused_with_self = Vec::new(); - - // keep track of single use statements - // such as `crypto_hash` in the example below - // ```rust,ignore - // use crypto_hash; - // ``` - let mut single_use_usages = Vec::new(); - - // keep track of macros defined in the module as we don't want it to trigger on this (#7106) - // ```rust,ignore - // macro_rules! foo { () => {} }; - // pub(crate) use foo; - // ``` - let mut macros = Vec::new(); - - for item in items { - track_uses( - cx, - item, - &mut imports_reused_with_self, - &mut single_use_usages, - &mut macros, - ); + self.check_mod(cx, &krate.items); } - for (name, span, can_suggest) in single_use_usages { - if !imports_reused_with_self.contains(&name) { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() { if can_suggest { span_lint_and_sugg( cx, @@ -102,74 +84,127 @@ fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) { } } -fn track_uses( - cx: &EarlyContext<'_>, - item: &Item, - imports_reused_with_self: &mut Vec<Symbol>, - single_use_usages: &mut Vec<(Symbol, Span, bool)>, - macros: &mut Vec<Symbol>, -) { - if item.span.from_expansion() || item.vis.kind.is_pub() { - return; - } +impl SingleComponentPathImports { + fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) { + // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example + // below. Removing the `use crypto_hash;` would make this a compile error + // ``` + // use crypto_hash; + // + // use self::crypto_hash::{Algorithm, Hasher}; + // ``` + let mut imports_reused_with_self = Vec::new(); - match &item.kind { - ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { - check_mod(cx, items); - }, - ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { - macros.push(item.ident.name); - }, - ItemKind::Use(use_tree) => { - let segments = &use_tree.prefix.segments; - - // keep track of `use some_module;` usages - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = use_tree.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, item.span, true)); - } - } - return; + // keep track of single use statements such as `crypto_hash` in the example below + // ``` + // use crypto_hash; + // ``` + let mut single_use_usages = Vec::new(); + + // keep track of macros defined in the module as we don't want it to trigger on this (#7106) + // ``` + // macro_rules! foo { () => {} }; + // pub(crate) use foo; + // ``` + let mut macros = Vec::new(); + + for item in items { + self.track_uses( + cx, + item, + &mut imports_reused_with_self, + &mut single_use_usages, + &mut macros, + ); + } + + for usage in single_use_usages { + if !imports_reused_with_self.contains(&usage.name) { + self.found.entry(usage.item_id).or_default().push(usage); } + } + } - if segments.is_empty() { - // keep track of `use {some_module, some_other_module};` usages - if let UseTreeKind::Nested(trees) = &use_tree.kind { - for tree in trees { - let segments = &tree.0.prefix.segments; - if segments.len() == 1 { - if let UseTreeKind::Simple(None, _, _) = tree.0.kind { - let name = segments[0].ident.name; - if !macros.contains(&name) { - single_use_usages.push((name, tree.0.span, false)); - } - } + fn track_uses( + &mut self, + cx: &EarlyContext<'_>, + item: &Item, + imports_reused_with_self: &mut Vec<Symbol>, + single_use_usages: &mut Vec<SingleUse>, + macros: &mut Vec<Symbol>, + ) { + if item.span.from_expansion() || item.vis.kind.is_pub() { + return; + } + + match &item.kind { + ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => { + self.check_mod(cx, items); + }, + ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => { + macros.push(item.ident.name); + }, + ItemKind::Use(use_tree) => { + let segments = &use_tree.prefix.segments; + + // keep track of `use some_module;` usages + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = use_tree.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: item.span, + item_id: item.id, + can_suggest: true, + }); } } + return; } - } else { - // keep track of `use self::some_module` usages - if segments[0].ident.name == kw::SelfLower { - // simple case such as `use self::module::SomeStruct` - if segments.len() > 1 { - imports_reused_with_self.push(segments[1].ident.name); - return; - } - // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if segments.is_empty() { + // keep track of `use {some_module, some_other_module};` usages if let UseTreeKind::Nested(trees) = &use_tree.kind { for tree in trees { let segments = &tree.0.prefix.segments; - if !segments.is_empty() { - imports_reused_with_self.push(segments[0].ident.name); + if segments.len() == 1 { + if let UseTreeKind::Simple(None, _, _) = tree.0.kind { + let name = segments[0].ident.name; + if !macros.contains(&name) { + single_use_usages.push(SingleUse { + name, + span: tree.0.span, + item_id: item.id, + can_suggest: false, + }); + } + } + } + } + } + } else { + // keep track of `use self::some_module` usages + if segments[0].ident.name == kw::SelfLower { + // simple case such as `use self::module::SomeStruct` + if segments.len() > 1 { + imports_reused_with_self.push(segments[1].ident.name); + return; + } + + // nested case such as `use self::{module1::Struct1, module2::Struct2}` + if let UseTreeKind::Nested(trees) = &use_tree.kind { + for tree in trees { + let segments = &tree.0.prefix.segments; + if !segments.is_empty() { + imports_reused_with_self.push(segments[0].ident.name); + } } } } } - } - }, - _ => {}, + }, + _ => {}, + } } } diff --git a/clippy_lints/src/slow_vector_initialization.rs b/clippy_lints/src/slow_vector_initialization.rs index 76039923151..a2109038a05 100644 --- a/clippy_lints/src/slow_vector_initialization.rs +++ b/clippy_lints/src/slow_vector_initialization.rs @@ -168,7 +168,7 @@ impl SlowVectorInit { }; } - fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { + fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) { let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len"); span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| { diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs new file mode 100644 index 00000000000..301aa5798bf --- /dev/null +++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -0,0 +1,53 @@ +use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal. + /// ### Why is this bad? + /// It's most probably a typo and may lead to unexpected behaviours. + /// ### Example + /// ```rust + /// let x = 3_i32 ^ 4_i32; + /// ``` + /// Use instead: + /// ```rust + /// let x = 3_i32.pow(4); + /// ``` + #[clippy::version = "1.66.0"] + pub SUSPICIOUS_XOR_USED_AS_POW, + restriction, + "XOR (`^`) operator possibly used as exponentiation operator" +} +declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]); + +impl LateLintPass<'_> for ConfusingXorAndPow { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if !in_external_macro(cx.sess(), expr.span) && + let ExprKind::Binary(op, left, right) = &expr.kind && + op.node == BinOpKind::BitXor && + left.span.ctxt() == right.span.ctxt() && + let ExprKind::Lit(lit_left) = &left.kind && + let ExprKind::Lit(lit_right) = &right.kind && + let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && + let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) && + let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) && + left_val.is_decimal() && + right_val.is_decimal() { + clippy_utils::diagnostics::span_lint_and_sugg( + cx, + SUSPICIOUS_XOR_USED_AS_POW, + expr.span, + "`^` is not the exponentiation operator", + "did you mean to write", + format!("{}.pow({})", left_val.format(), right_val.format()), + Applicability::MaybeIncorrect, + ); + } + } +} diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index f46c21e1265..c374529d1ea 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core}; +use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind}; @@ -16,6 +16,8 @@ declare_clippy_lint! { /// ### What it does /// Checks for manual swapping. /// + /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable. + /// /// ### Why is this bad? /// The `std::mem::swap` function exposes the intent better /// without deinitializing or copying either variable. @@ -138,6 +140,10 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa /// Implementation of the `MANUAL_SWAP` lint. fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) { + if in_constant(cx, block.hir_id) { + return; + } + for w in block.stmts.windows(3) { if_chain! { // let t = foo(); diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index 8cf3efc8dc7..63b326048a4 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_help; -use rustc_hir::{HirId, Item, ItemKind}; +use clippy_utils::has_repr_attr; +use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Const; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -72,7 +72,3 @@ fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_ } } } - -fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr)) -} diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 3d4bbbf648c..34642f4b122 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -27,32 +27,24 @@ pub(super) fn check<'tcx>( // `Repr(C)` <-> unordered type. // If the first field of the `Repr(C)` type matches then the transmute is ok - ( - ReducedTy::OrderedFields(_, Some(from_sub_ty)), - ReducedTy::UnorderedFields(to_sub_ty), - ) - | ( - ReducedTy::UnorderedFields(from_sub_ty), - ReducedTy::OrderedFields(_, Some(to_sub_ty)), - ) => { + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty)) + | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } - (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) - if reduced_tys.to_fat_ptr => - { + }, + (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) if reduced_tys.from_fat_ptr => { from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, // ptr <-> ptr (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty)) @@ -62,19 +54,19 @@ pub(super) fn check<'tcx>( from_ty = from_sub_ty; to_ty = to_sub_ty; continue; - } + }, // fat ptr <-> (*size, *size) (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty)) if reduced_tys.from_fat_ptr && is_size_pair(to_ty) => { return false; - } + }, (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_)) if reduced_tys.to_fat_ptr && is_size_pair(from_ty) => { return false; - } + }, // fat ptr -> some struct | some struct -> fat ptr (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => { @@ -85,14 +77,12 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty.peel_refs() { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); return true; - } + }, (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => { span_lint_and_then( cx, @@ -101,18 +91,14 @@ pub(super) fn check<'tcx>( &format!("transmute to `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty.peel_refs() { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); return true; - } + }, - (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) - if from_ty != to_ty => - { + (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => { let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs)) = (from_ty.kind(), to_ty.kind()) && from_def == to_def @@ -139,25 +125,19 @@ pub(super) fn check<'tcx>( )); } else { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } } }, ); return true; - } + }, ( ReducedTy::UnorderedFields(from_ty), - ReducedTy::Other(_) - | ReducedTy::OrderedFields(..) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, ) => { span_lint_and_then( cx, @@ -166,18 +146,14 @@ pub(super) fn check<'tcx>( &format!("transmute from `{from_ty_orig}` which has an undefined layout"), |diag| { if from_ty_orig.peel_refs() != from_ty { - diag.note(&format!( - "the contained type `{from_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{from_ty}` has an undefined layout")); } }, ); return true; - } + }, ( - ReducedTy::Other(_) - | ReducedTy::OrderedFields(..) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true }, ReducedTy::UnorderedFields(to_ty), ) => { span_lint_and_then( @@ -187,25 +163,19 @@ pub(super) fn check<'tcx>( &format!("transmute into `{to_ty_orig}` which has an undefined layout"), |diag| { if to_ty_orig.peel_refs() != to_ty { - diag.note(&format!( - "the contained type `{to_ty}` has an undefined layout" - )); + diag.note(&format!("the contained type `{to_ty}` has an undefined layout")); } }, ); return true; - } + }, ( - ReducedTy::OrderedFields(..) - | ReducedTy::Other(_) - | ReducedTy::TypeErasure { raw_ptr_only: true }, - ReducedTy::OrderedFields(..) - | ReducedTy::Other(_) - | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, + ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true }, ) | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => { break; - } + }, } } @@ -223,38 +193,42 @@ struct ReducedTys<'tcx> { } /// Remove references so long as both types are references. -fn reduce_refs<'tcx>( - cx: &LateContext<'tcx>, - mut from_ty: Ty<'tcx>, - mut to_ty: Ty<'tcx>, -) -> ReducedTys<'tcx> { +fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> { let mut from_raw_ptr = false; let mut to_raw_ptr = false; - let (from_fat_ptr, to_fat_ptr) = - loop { - break match (from_ty.kind(), to_ty.kind()) { - ( - &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), - &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), - ) => { - from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); - from_ty = from_sub_ty; - to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); - to_ty = to_sub_ty; - continue; - } - ( - &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), - _, - ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (true, false), - ( - _, - &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), - ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (false, true), - _ => (false, false), - }; + let (from_fat_ptr, to_fat_ptr) = loop { + break match (from_ty.kind(), to_ty.kind()) { + ( + &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })), + &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })), + ) => { + from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_)); + from_ty = from_sub_ty; + to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_)); + to_ty = to_sub_ty; + continue; + }, + (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _) + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + { + (true, false) + }, + (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))) + if !unsized_ty.is_sized(cx.tcx, cx.param_env) => + { + (false, true) + }, + _ => (false, false), }; - ReducedTys { from_ty, to_ty, from_raw_ptr, to_raw_ptr, from_fat_ptr, to_fat_ptr } + }; + ReducedTys { + from_ty, + to_ty, + from_raw_ptr, + to_raw_ptr, + from_fat_ptr, + to_fat_ptr, + } } enum ReducedTy<'tcx> { @@ -277,11 +251,11 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> return match *ty.kind() { ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => { ReducedTy::TypeErasure { raw_ptr_only: false } - } + }, ty::Array(sub_ty, _) | ty::Slice(sub_ty) => { ty = sub_ty; continue; - } + }, ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Tuple(args) => { let mut iter = args.iter(); @@ -293,7 +267,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> continue; } ReducedTy::UnorderedFields(ty) - } + }, ty::Adt(def, substs) if def.is_struct() => { let mut iter = def .non_enum_variant() @@ -312,12 +286,10 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> } else { ReducedTy::UnorderedFields(ty) } - } - ty::Adt(def, _) - if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => - { + }, + ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => { ReducedTy::TypeErasure { raw_ptr_only: false } - } + }, // TODO: Check if the conversion to or from at least one of a union's fields is valid. ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false }, ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false }, @@ -356,11 +328,7 @@ fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> b for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) { match (ty1.kind(), ty2.kind()) { (ty::Param(_), _) | (_, ty::Param(_)) => (), - (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) - if adt1 == adt2 && same_except_params(subs1, subs2) => - { - () - } + (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (), _ => return false, } } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index 641cdf5d330..49d863ec03f 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,9 +1,9 @@ +use rustc_hir as hir; use rustc_hir::Expr; use rustc_hir_typeck::{cast, FnCtxt, Inherited}; use rustc_lint::LateContext; use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; -use rustc_hir as hir; // check if the component types of the transmuted collection and the result have different ABI, // size or alignment @@ -55,9 +55,14 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> ); if let Ok(check) = cast::CastCheck::new( - &fn_ctxt, e, from_ty, to_ty, + &fn_ctxt, + e, + from_ty, + to_ty, // We won't show any error to the user, so we don't care what the span is here. - DUMMY_SP, DUMMY_SP, hir::Constness::NotConst, + DUMMY_SP, + DUMMY_SP, + hir::Constness::NotConst, ) { let res = check.do_check(&fn_ctxt); diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs index 802415e163d..43665a922d4 100644 --- a/clippy_lints/src/types/box_collection.rs +++ b/clippy_lints/src/types/box_collection.rs @@ -39,12 +39,19 @@ fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> let id = path_def_id(cx, param)?; cx.tcx .get_diagnostic_name(id) - .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet - | sym::VecDeque - | sym::LinkedList - | sym::BTreeMap - | sym::BTreeSet - | sym::BinaryHeap)) + .filter(|&name| { + matches!( + name, + sym::HashMap + | sym::Vec + | sym::HashSet + | sym::VecDeque + | sym::LinkedList + | sym::BTreeMap + | sym::BTreeSet + | sym::BinaryHeap + ) + }) .or_else(|| { cx.tcx .lang_items() diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index f6de87b0526..20978e81dc5 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -379,7 +379,9 @@ impl<'tcx> LateLintPass<'tcx> for Types { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(field.hir_id)); + let is_exported = cx + .effective_visibilities + .is_exported(cx.tcx.hir().local_def_id(field.hir_id)); self.check_ty( cx, diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 2b964b64a33..fae5385ffc8 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -5,16 +5,12 @@ use rustc_errors::Applicability; use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::LateContext; +use rustc_middle::ty::TypeVisitable; use rustc_span::symbol::sym; use super::{utils, REDUNDANT_ALLOCATION}; -pub(super) fn check( - cx: &LateContext<'_>, - hir_ty: &hir::Ty<'_>, - qpath: &QPath<'_>, - def_id: DefId, -) -> bool { +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() { "Box" @@ -34,12 +30,7 @@ pub(super) fn check( hir_ty.span, &format!("usage of `{outer_sym}<{generic_snippet}>`"), |diag| { - diag.span_suggestion( - hir_ty.span, - "try", - format!("{generic_snippet}"), - applicability, - ); + diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability); diag.note(&format!( "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap" )); @@ -61,15 +52,16 @@ pub(super) fn check( return false }; let inner_span = match qpath_generic_tys(inner_qpath).next() { - Some(ty) => { + Some(hir_ty) => { // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use // here because `mod.rs` guarantees this lint is only run on types outside of bodies and // is not run on locals. - if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx, cx.param_env) { + let ty = hir_ty_to_ty(cx.tcx, hir_ty); + if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) { return false; } - ty.span - } + hir_ty.span + }, None => return false, }; if inner_sym == outer_sym { diff --git a/clippy_lints/src/types/vec_box.rs b/clippy_lints/src/types/vec_box.rs index 9ad2cb853d3..7a3c7cd8a99 100644 --- a/clippy_lints/src/types/vec_box.rs +++ b/clippy_lints/src/types/vec_box.rs @@ -42,7 +42,7 @@ pub(super) fn check( if !ty_ty.has_escaping_bound_vars(); if ty_ty.is_sized(cx.tcx, cx.param_env); if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes()); - if ty_ty_size <= box_size_threshold; + if ty_ty_size < box_size_threshold; then { span_lint_and_sugg( cx, diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index d2e675a783e..e8f15a44473 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -68,7 +68,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) && !is_unsafe_from_proc_macro(cx, block.span) - && !block_has_safety_comment(cx, block) + && !block_has_safety_comment(cx, block.span) + && !block_parents_have_safety_comment(cx, block.hir_id) { let source_map = cx.tcx.sess.source_map(); let span = if source_map.is_multiline(block.span) { @@ -126,8 +127,41 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { .map_or(true, |src| !src.starts_with("unsafe")) } +// Checks if any parent {expression, statement, block, local, const, static} +// has a safety comment +fn block_parents_have_safety_comment(cx: &LateContext<'_>, id: hir::HirId) -> bool { + if let Some(node) = get_parent_node(cx.tcx, id) { + return match node { + Node::Expr(expr) => !is_branchy(expr) && span_in_body_has_safety_comment(cx, expr.span), + Node::Stmt(hir::Stmt { + kind: + hir::StmtKind::Local(hir::Local { span, .. }) + | hir::StmtKind::Expr(hir::Expr { span, .. }) + | hir::StmtKind::Semi(hir::Expr { span, .. }), + .. + }) + | Node::Local(hir::Local { span, .. }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Const(..) | ItemKind::Static(..), + span, + .. + }) => span_in_body_has_safety_comment(cx, *span), + _ => false, + }; + } + false +} + +/// Checks if an expression is "branchy", e.g. loop, match/if/etc. +fn is_branchy(expr: &hir::Expr<'_>) -> bool { + matches!( + expr.kind, + hir::ExprKind::If(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..) + ) +} + /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -136,7 +170,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> boo // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) + span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span) } /// Checks if the lines immediately preceding the item contain a safety comment. diff --git a/clippy_lints/src/unsafe_removed_from_name.rs b/clippy_lints/src/unsafe_removed_from_name.rs index 32cd4681201..95258652768 100644 --- a/clippy_lints/src/unsafe_removed_from_name.rs +++ b/clippy_lints/src/unsafe_removed_from_name.rs @@ -50,7 +50,7 @@ fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) { }, UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {}, UseTreeKind::Nested(ref nested_use_tree) => { - for &(ref use_tree, _) in nested_use_tree { + for (use_tree, _) in nested_use_tree { check_use_tree(use_tree, cx, span); } }, diff --git a/clippy_lints/src/unused_peekable.rs b/clippy_lints/src/unused_peekable.rs index b452be08409..4ee16d9a5e4 100644 --- a/clippy_lints/src/unused_peekable.rs +++ b/clippy_lints/src/unused_peekable.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { /// // ... /// } /// ``` - #[clippy::version = "1.64.0"] + #[clippy::version = "1.65.0"] pub UNUSED_PEEKABLE, nursery, "creating a peekable iterator without using any of its methods" diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index 5ab351bc29c..aac6719a8dc 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -30,16 +30,15 @@ declare_clippy_lint! { declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { - if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind - && let method_name = seg.ident.name.as_str() + if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind + && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind && token_lit.is_semantic_float() { - let f = token_lit.symbol.as_str().parse::<f64>().unwrap(); let mut f_str = token_lit.symbol.to_string(); - match token_lit.suffix { - Some(suffix) => f_str.push_str(suffix.as_str()), - None => {} + let f = f_str.trim_end_matches('_').parse::<f64>().unwrap(); + if let Some(suffix) = token_lit.suffix { + f_str.push_str(suffix.as_str()); } if f.fract() == 0.0 { Some((method_name, f_str)) diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index cd1d90e860b..cad8da18c2f 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -1,8 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{position_before_rarrow, snippet_opt}; use if_chain::if_chain; -use rustc_ast::ast; -use rustc_ast::visit::FnKind; +use rustc_ast::{ast, visit::FnKind, ClosureBinder}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -43,6 +42,11 @@ impl EarlyLintPass for UnusedUnit { if let ast::TyKind::Tup(ref vals) = ty.kind; if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span); then { + // implicit types in closure signatures are forbidden when `for<...>` is present + if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind { + return; + } + lint_unneeded_unit_return(cx, ty, span); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index c6cdf3f85fc..e2860db71a5 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -30,7 +30,6 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust @@ -103,6 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { if parameters.as_ref().map_or(true, |params| { !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_))) }); + if !item.span.from_expansion(); if !is_from_proc_macro(cx, item); // expensive, should be last check then { StackItem::Check { @@ -234,24 +234,13 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { then {} else { return; } } match expr.kind { - ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res { - Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => (), - Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path), - _ => span_lint(cx, path.span), - }, - // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`) + ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path), ExprKind::Call(fun, _) => { if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind { - if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res { - match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - } - } + check_path(cx, path); } }, - // unit enum variants (`Enum::A`) - ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path), + ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path), _ => (), } } @@ -267,15 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind; if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id); then { - match path.res { - Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of { - CtorOf::Variant => lint_path_to_variant(cx, path), - CtorOf::Struct => span_lint(cx, path.span), - }, - Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path), - Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span), - _ => () - } + check_path(cx, path); } } } @@ -313,6 +294,16 @@ fn span_lint(cx: &LateContext<'_>, span: Span) { ); } +fn check_path(cx: &LateContext<'_>, path: &Path<'_>) { + match path.res { + Res::Def(DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant, ..) => { + lint_path_to_variant(cx, path); + }, + Res::Def(DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct, ..) => span_lint(cx, path.span), + _ => (), + } +} + fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) { if let [.., self_seg, _variant] = path.segments { let span = path diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 668123e4d6e..b37d4239477 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -53,11 +53,11 @@ impl DisallowedPath { path } - pub fn reason(&self) -> Option<&str> { + pub fn reason(&self) -> Option<String> { match self { Self::WithReason { reason: Some(reason), .. - } => Some(reason), + } => Some(format!("{reason} (from clippy.toml)")), _ => None, } } @@ -213,7 +213,7 @@ define_Conf! { /// /// 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. + /// 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. /// /// The minimum rust version that the project supports (msrv: Option<String> = None), @@ -335,6 +335,12 @@ define_Conf! { /// /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other (upper_case_acronyms_aggressive: bool = false), + /// Lint: MANUAL_LET_ELSE. + /// + /// Whether the matches should be considered by the lint, and whether there should + /// be filtering for common types. + (matches_for_let_else: crate::manual_let_else::MatchLintBehaviour = + crate::manual_let_else::MatchLintBehaviour::WellKnownTypes), /// Lint: _CARGO_COMMON_METADATA. /// /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. @@ -373,23 +379,36 @@ define_Conf! { (max_include_file_size: u64 = 1_000_000), /// Lint: EXPECT_USED. /// - /// Whether `expect` should be allowed in test functions + /// Whether `expect` should be allowed within `#[cfg(test)]` (allow_expect_in_tests: bool = false), /// Lint: UNWRAP_USED. /// - /// Whether `unwrap` should be allowed in test functions + /// Whether `unwrap` should be allowed in test cfg (allow_unwrap_in_tests: bool = false), /// Lint: DBG_MACRO. /// /// Whether `dbg!` should be allowed in test functions (allow_dbg_in_tests: bool = false), - /// Lint: RESULT_LARGE_ERR + /// Lint: PRINT_STDOUT, PRINT_STDERR. + /// + /// Whether print macros (ex. `println!`) should be allowed in test functions + (allow_print_in_tests: bool = false), + /// Lint: RESULT_LARGE_ERR. /// /// The maximum size of the `Err`-variant in a `Result` returned from a function (large_error_threshold: u64 = 128), + /// Lint: MUTABLE_KEY. + /// + /// A list of paths to types that should be treated like `Arc`, i.e. ignored but + /// for the generic parameters for determining interior mutability + (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])), } /// Search for the configuration file. +/// +/// # Errors +/// +/// Returns any unexpected filesystem error encountered when searching for the config file pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> { /// Possible filename to search for. const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"]; diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs index 096b601572b..4b33d492a0e 100644 --- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs +++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs @@ -2,7 +2,7 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; -use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths}; +use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol { } for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] { - if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() { + for def_id in def_path_def_ids(cx, module) { for item in cx.tcx.module_children(def_id).iter() { if_chain! { if let Res::Def(DefKind::Const, item_def_id) = item.res; diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs index 22a5aa5351a..680935f2329 100644 --- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -3,7 +3,7 @@ use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use if_chain::if_chain; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::DefKind; use rustc_hir::Item; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { // This is not a complete resolver for paths. It works on all the paths currently used in the paths // module. That's all it does and all it needs to do. pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { - if def_path_res(cx, path, None) != Res::Err { + if !def_path_res(cx, path).is_empty() { return true; } diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 0dac64376b0..1aebb8b3104 100644 --- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } } -pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool { +pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { if let TyKind::Rptr( _, MutTy { diff --git a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index f2276395fed..393988dbad3 100644 --- a/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; +use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_data_structures::fx::FxHashSet; @@ -11,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, Local, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc}; -use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::symbol::Symbol; use rustc_span::Span; use std::str; @@ -110,7 +110,7 @@ impl UnnecessaryDefPath { // Extract the path to the matched type if let Some(segments) = path_to_matched_type(cx, item_arg); let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect(); - if let Some(def_id) = inherent_def_path_res(cx, &segments[..]); + if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next(); then { // Check if the target item is a diagnostic item or LangItem. #[rustfmt::skip] @@ -209,7 +209,7 @@ impl UnnecessaryDefPath { fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) { let Some(path) = path_from_array(elements) else { return }; - if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { + for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) { self.array_def_ids.insert((def_id, span)); } } @@ -246,7 +246,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> { let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() { - let &alloc = alloc.provenance().values().next()?; + let &alloc = alloc.provenance().ptrs().values().next()?; if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { (alloc.inner(), ty) } else { @@ -262,6 +262,7 @@ fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation { alloc .provenance() + .ptrs() .values() .map(|&alloc| { if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) { @@ -293,38 +294,6 @@ fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> { .collect() } -// def_path_res will match field names before anything else, but for this we want to match -// inherent functions first. -fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> { - def_path_res(cx, segments, None).opt_def_id().map(|def_id| { - if cx.tcx.def_kind(def_id) == DefKind::Field { - let method_name = *segments.last().unwrap(); - cx.tcx - .def_key(def_id) - .parent - .and_then(|parent_idx| { - cx.tcx - .inherent_impls(DefId { - index: parent_idx, - krate: def_id.krate, - }) - .iter() - .find_map(|impl_id| { - cx.tcx.associated_items(*impl_id).find_by_name_and_kind( - cx.tcx, - Ident::from_str(method_name), - AssocKind::Fn, - *impl_id, - ) - }) - }) - .map_or(def_id, |item| item.def_id) - } else { - def_id - } - }) -} - fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> { if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) { Some(lang_item.variant_name()) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 36574198f91..6b321765bc0 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall}; use clippy_utils::source::{expand_past_previous_comma, snippet_opt}; +use clippy_utils::{is_in_cfg_test, is_in_test_function}; use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind}; @@ -232,6 +233,16 @@ declare_clippy_lint! { #[derive(Default)] pub struct Write { in_debug_impl: bool, + allow_print_in_tests: bool, +} + +impl Write { + pub fn new(allow_print_in_tests: bool) -> Self { + Self { + allow_print_in_tests, + ..Default::default() + } + } } impl_lint_pass!(Write => [ @@ -271,13 +282,15 @@ impl<'tcx> LateLintPass<'tcx> for Write { .as_ref() .map_or(false, |crate_name| crate_name == "build_script_build"); + let allowed_in_tests = self.allow_print_in_tests + && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)); match diag_name { - sym::print_macro | sym::println_macro => { + sym::print_macro | sym::println_macro if !allowed_in_tests => { if !is_build_script { span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`")); } }, - sym::eprint_macro | sym::eprintln_macro => { + sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => { span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`")); }, sym::write_macro | sym::writeln_macro => {}, |
