diff options
| author | Philipp Krones <hello@philkrones.com> | 2024-04-18 17:48:52 +0200 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2024-04-18 17:48:52 +0200 |
| commit | a5aaf33422f110d7a4ca6ba53fc5f2995dd6b0ce (patch) | |
| tree | 951beab03b41d3ad99e2858f9e4d480f0869b354 /clippy_lints | |
| parent | 876d5f00a0515f60b23d4e111249d6340159fa8b (diff) | |
| download | rust-a5aaf33422f110d7a4ca6ba53fc5f2995dd6b0ce.tar.gz rust-a5aaf33422f110d7a4ca6ba53fc5f2995dd6b0ce.zip | |
Merge commit 'ca3b393750ee8d870bf3215dcf6509cafa5c0445' into clippy-subtree-update
Diffstat (limited to 'clippy_lints')
22 files changed, 202 insertions, 213 deletions
diff --git a/clippy_lints/src/arc_with_non_send_sync.rs b/clippy_lints/src/arc_with_non_send_sync.rs index 1102c7fb236..38933897389 100644 --- a/clippy_lints/src/arc_with_non_send_sync.rs +++ b/clippy_lints/src/arc_with_non_send_sync.rs @@ -56,7 +56,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { && let Some(send) = cx.tcx.get_diagnostic_item(sym::Send) && let Some(sync) = cx.tcx.lang_items().sync_trait() && let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[])) - && !(is_send && is_sync) + && let reason = match (is_send, is_sync) { + (false, false) => "neither `Send` nor `Sync`", + (false, true) => "not `Send`", + (true, false) => "not `Sync`", + _ => return, + } && !is_from_proc_macro(cx, expr) { span_lint_and_then( @@ -66,21 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync { "usage of an `Arc` that is not `Send` and `Sync`", |diag| { with_forced_trimmed_paths!({ - diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:")); - - if !is_send { - diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`")); - } - if !is_sync { - diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`")); - } - - diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types"); - - diag.note("if you intend to use `Arc` with `Send` and `Sync` traits"); - diag.note(format!( - "wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`" + "`Arc<{arg_ty}>` is not `Send` and `Sync` as `{arg_ty}` is {reason}" + )); + diag.help("if the `Arc` will not used be across threads replace it with an `Rc`"); + diag.help(format!( + "otherwise make `{arg_ty}` `Send` and `Sync` or consider a wrapper type such as `Mutex`" )); }); }, diff --git a/clippy_lints/src/attrs/duplicated_attributes.rs b/clippy_lints/src/attrs/duplicated_attributes.rs index 3a8844d0754..736ee48641d 100644 --- a/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/clippy_lints/src/attrs/duplicated_attributes.rs @@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES; use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::{Attribute, MetaItem}; use rustc_data_structures::fx::FxHashMap; -use rustc_lint::EarlyContext; +use rustc_lint::LateContext; use rustc_span::{sym, Span}; use std::collections::hash_map::Entry; fn emit_if_duplicated( - cx: &EarlyContext<'_>, + cx: &LateContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap<String, Span>, complete_path: String, @@ -26,7 +26,7 @@ fn emit_if_duplicated( } fn check_duplicated_attr( - cx: &EarlyContext<'_>, + cx: &LateContext<'_>, attr: &MetaItem, attr_paths: &mut FxHashMap<String, Span>, parent: &mut Vec<String>, @@ -64,7 +64,7 @@ fn check_duplicated_attr( } } -pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) { +pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { let mut attr_paths = FxHashMap::default(); for attr in attrs { diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs index 684ad7de2f0..8f47bc7653b 100644 --- a/clippy_lints/src/attrs/mod.rs +++ b/clippy_lints/src/attrs/mod.rs @@ -17,7 +17,7 @@ mod useless_attribute; mod utils; use clippy_config::msrvs::Msrv; -use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem}; +use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, impl_lint_pass}; @@ -534,11 +534,13 @@ declare_lint_pass!(Attributes => [ BLANKET_CLIPPY_RESTRICTION_LINTS, SHOULD_PANIC_WITHOUT_EXPECT, MIXED_ATTRIBUTES_STYLE, + DUPLICATED_ATTRIBUTES, ]); impl<'tcx> LateLintPass<'tcx> for Attributes { fn check_crate(&mut self, cx: &LateContext<'tcx>) { blanket_clippy_restriction_lints::check_command_line(cx); + duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs()); } fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) { @@ -578,6 +580,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { _ => {}, } mixed_attributes_style::check(cx, item.span, attrs); + duplicated_attributes::check(cx, attrs); } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { @@ -606,17 +609,11 @@ impl_lint_pass!(EarlyAttributes => [ MAYBE_MISUSED_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, - DUPLICATED_ATTRIBUTES, ]); impl EarlyLintPass for EarlyAttributes { - fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { - duplicated_attributes::check(cx, &krate.attrs); - } - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { empty_line_after::check(cx, item); - duplicated_attributes::check(cx, &item.attrs); } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 6edfebb5534..b6341b3fe8e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -346,11 +346,18 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> { _ => None, } .and_then(|op| { - Some(format!( - "{}{op}{}", - snippet_opt(cx, lhs.span)?, - snippet_opt(cx, rhs.span)? - )) + let lhs_snippet = snippet_opt(cx, lhs.span)?; + let rhs_snippet = snippet_opt(cx, rhs.span)?; + + if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) { + if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) { + // e.g. `(a as u64) < b`. Without the parens the `<` is + // interpreted as a start of generic arguments for `u64` + return Some(format!("({lhs_snippet}){op}{rhs_snippet}")); + } + } + + Some(format!("{lhs_snippet}{op}{rhs_snippet}")) }) }, ExprKind::MethodCall(path, receiver, [], _) => { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 063aab28238..d14898a8196 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -754,11 +754,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, &self.msrv); - } - - if expr.span.from_expansion() { + if in_external_macro(cx.sess(), expr.span) { return; } @@ -771,7 +767,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cx.typeck_results().expr_ty(expr), ); - if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { + if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); @@ -782,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to); zero_ptr::check(cx, expr, cast_expr, cast_to_hir); - if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) { + if cast_to.is_numeric() { cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span); if cast_from.is_numeric() { cast_possible_wrap::check(cx, expr, cast_from, cast_to); diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index bff40c2ae75..89e2b344968 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1016,9 +1016,18 @@ fn report<'tcx>( }, _ => (0, false), }; + let is_in_tuple = matches!( + get_parent_expr(cx, data.first_expr), + Some(Expr { + kind: ExprKind::Tup(..), + .. + }) + ); + let sugg = if !snip_is_macro && (calls_field || expr.precedence().order() < precedence) && !has_enclosing_paren(&snip) + && !is_in_tuple { format!("({snip})") } else { diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 5f9700b76d9..42cd19fb8ec 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -132,7 +132,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Deriving `serde::Deserialize` will create a constructor - /// that may violate invariants hold by another constructor. + /// that may violate invariants held by another constructor. /// /// ### Example /// ```rust,ignore diff --git a/clippy_lints/src/doc/missing_headers.rs b/clippy_lints/src/doc/missing_headers.rs index 26f120cb33f..f935ae2e3e4 100644 --- a/clippy_lints/src/doc/missing_headers.rs +++ b/clippy_lints/src/doc/missing_headers.rs @@ -11,7 +11,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D pub fn check( cx: &LateContext<'_>, owner_id: OwnerId, - sig: &FnSig<'_>, + sig: FnSig<'_>, headers: DocHeaders, body_id: Option<BodyId>, panic_span: Option<Span>, diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index b135e4e3577..4bced104d3b 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::Visitable; -use clippy_utils::{is_entrypoint_fn, method_chain_args}; +use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args}; use pulldown_cmark::Event::{ Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, }; @@ -11,9 +11,8 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph} use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use rustc_ast::ast::Attribute; use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{AnonConst, Expr}; +use rustc_hir::{AnonConst, Expr, ImplItemKind, ItemKind, Node, TraitItemKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -366,7 +365,6 @@ declare_clippy_lint! { #[derive(Clone)] pub struct Documentation { valid_idents: FxHashSet<String>, - in_trait_impl: bool, check_private_items: bool, } @@ -374,7 +372,6 @@ impl Documentation { pub fn new(valid_idents: &[String], check_private_items: bool) -> Self { Self { valid_idents: valid_idents.iter().cloned().collect(), - in_trait_impl: false, check_private_items, } } @@ -394,36 +391,72 @@ impl_lint_pass!(Documentation => [ ]); impl<'tcx> LateLintPass<'tcx> for Documentation { - fn check_crate(&mut self, cx: &LateContext<'tcx>) { - let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID); - check_attrs(cx, &self.valid_idents, attrs); - } - - fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::Variant<'tcx>) { - let attrs = cx.tcx.hir().attrs(variant.hir_id); - check_attrs(cx, &self.valid_idents, attrs); - } - - fn check_field_def(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::FieldDef<'tcx>) { - let attrs = cx.tcx.hir().attrs(variant.hir_id); - check_attrs(cx, &self.valid_idents, attrs); - } - - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); + fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { 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)) { + match cx.tcx.hir_node(cx.last_node_with_lint_attrs) { + Node::Item(item) => match item.kind { + ItemKind::Fn(sig, _, body_id) => { + if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) { + let body = cx.tcx.hir().body(body_id); + + let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + missing_headers::check( + cx, + item.owner_id, + sig, + headers, + Some(body_id), + panic_span, + self.check_private_items, + ); + } + }, + ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) { + (false, Unsafety::Unsafe) => span_lint( + cx, + MISSING_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for unsafe trait missing `# Safety` section", + ), + (true, Unsafety::Normal) => span_lint( + cx, + UNNECESSARY_SAFETY_DOC, + cx.tcx.def_span(item.owner_id), + "docs for safe trait have unnecessary `# Safety` section", + ), + _ => (), + }, + _ => (), + }, + Node::TraitItem(trait_item) => { + if let TraitItemKind::Fn(sig, ..) = trait_item.kind + && !in_external_macro(cx.tcx.sess, trait_item.span) + { + missing_headers::check( + cx, + trait_item.owner_id, + sig, + headers, + None, + None, + self.check_private_items, + ); + } + }, + Node::ImplItem(impl_item) => { + if let ImplItemKind::Fn(sig, body_id) = impl_item.kind + && !in_external_macro(cx.tcx.sess, impl_item.span) + && !is_trait_impl_item(cx, impl_item.hir_id()) + { let body = cx.tcx.hir().body(body_id); - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); + let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value); missing_headers::check( cx, - item.owner_id, + impl_item.owner_id, sig, headers, Some(body_id), @@ -432,67 +465,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation { ); } }, - hir::ItemKind::Impl(impl_) => { - self.in_trait_impl = impl_.of_trait.is_some(); - }, - 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", - ), - _ => (), - }, - _ => (), - } - } - - fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if let hir::ItemKind::Impl { .. } = item.kind { - self.in_trait_impl = false; - } - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - 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) { - missing_headers::check(cx, item.owner_id, sig, headers, None, None, self.check_private_items); - } - } - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - let attrs = cx.tcx.hir().attrs(item.hir_id()); - 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; - } - if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind { - let body = cx.tcx.hir().body(body_id); - - let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); - missing_headers::check( - cx, - item.owner_id, - sig, - headers, - Some(body_id), - panic_span, - self.check_private_items, - ); + _ => {}, } } } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 799ec9d553d..a4c3b06046e 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -7,8 +7,8 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::HirId; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs index 6615122567d..33764d3eb09 100644 --- a/clippy_lints/src/item_name_repetitions.rs +++ b/clippy_lints/src/item_name_repetitions.rs @@ -5,6 +5,7 @@ use clippy_utils::is_bool; use clippy_utils::macros::span_is_local; use clippy_utils::source::is_present_in_source; use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case}; +use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -147,6 +148,7 @@ pub struct ItemNameRepetitions { struct_threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool, + allowed_prefixes: FxHashSet<String>, } impl ItemNameRepetitions { @@ -156,6 +158,7 @@ impl ItemNameRepetitions { struct_threshold: u64, avoid_breaking_exported_api: bool, allow_private_module_inception: bool, + allowed_prefixes: &[String], ) -> Self { Self { modules: Vec::new(), @@ -163,8 +166,13 @@ impl ItemNameRepetitions { struct_threshold, avoid_breaking_exported_api, allow_private_module_inception, + allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(), } } + + fn is_allowed_prefix(&self, prefix: &str) -> bool { + self.allowed_prefixes.contains(prefix) + } } impl_lint_pass!(ItemNameRepetitions => [ @@ -423,7 +431,9 @@ impl LateLintPass<'_> for ItemNameRepetitions { _ => (), } } - if rmatching.char_count == nchars { + if rmatching.char_count == nchars + && !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count]) + { span_lint( cx, MODULE_NAME_REPETITIONS, diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs index c5f1afe68c3..00124dcdd91 100644 --- a/clippy_lints/src/legacy_numeric_constants.rs +++ b/clippy_lints/src/legacy_numeric_constants.rs @@ -17,7 +17,7 @@ declare_clippy_lint! { /// `std::<float>::EPSILON`, etc. /// /// ### Why is this bad? - /// All of these have been superceded by the associated constants on their respective types, + /// All of these have been superseded by the associated constants on their respective types, /// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust. /// /// ### Example diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index b92364a9d14..e2aac58bf97 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -594,6 +594,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { pub_underscore_fields_behavior, ref allowed_duplicate_crates, allow_comparison_to_zero, + ref allowed_prefixes, blacklisted_names: _, cyclomatic_complexity_threshold: _, @@ -864,6 +865,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { struct_field_name_threshold, avoid_breaking_exported_api, allow_private_module_inception, + allowed_prefixes, )) }); store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 2bb63ec2b04..443d6189c1f 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -291,10 +291,7 @@ fn elision_suggestions( }) => { // expand `&'a T` to `&'a T` // ^^ ^^^ - let span = cx - .sess() - .source_map() - .span_extend_while_whitespace(usage.ident.span); + let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span); (span, String::new()) }, diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs index c562ceb5bce..84fb183e3f7 100644 --- a/clippy_lints/src/manual_unwrap_or_default.rs +++ b/clippy_lints/src/manual_unwrap_or_default.rs @@ -1,13 +1,14 @@ -use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::GenericArgKind; use rustc_session::declare_lint_pass; use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; +use clippy_utils::higher::IfLetOrMatch; +use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment}; @@ -105,19 +106,39 @@ fn get_some_and_none_bodies<'tcx>( } } -fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else { - return false; +#[allow(clippy::needless_pass_by_value)] +fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) { + // Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for + // the some arm, the body for the none arm and the binding id of the some arm + let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match { + IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) + // Make sure there are no guards to keep things simple + if arm1.guard.is_none() + && arm2.guard.is_none() + // Get the some and none bodies and the binding id of the some arm + && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) => + { + ("match", condition, body_some, body_none, binding_id) + }, + IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _) + if let Some(binding_id) = get_some(cx, pat) => + { + ("if let", condition, if_expr, else_expr, binding_id) + }, + _ => { + // All other cases (match with number of arms != 2, if let without else, etc.) + return; + }, }; - // We don't want conditions on the arms to simplify things. - if arm1.guard.is_none() - && arm2.guard.is_none() - // We check that the returned type implements the `Default` trait. - && let match_ty = cx.typeck_results().expr_ty(expr) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) - && implements_trait(cx, match_ty, default_trait_id, &[]) - // We now get the bodies for both the `Some` and `None` arms. - && let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) + + // We check if the return type of the expression implements Default. + let expr_type = cx.typeck_results().expr_ty(expr); + if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) + && implements_trait(cx, expr_type, default_trait_id, &[]) + // We check if the initial condition implements Default. + && let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1) + && let GenericArgKind::Type(condition_ty) = condition_ty.unpack() + && implements_trait(cx, condition_ty, default_trait_id, &[]) // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind && let Res::Local(local_id) = path.res @@ -125,8 +146,9 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { // We now check the `None` arm is calling a method equivalent to `Default::default`. && let body_none = peel_blocks(body_none) && is_default_equivalent(cx, body_none) - && let Some(receiver) = Sugg::hir_opt(cx, match_expr).map(Sugg::maybe_par) + && let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par) { + // Machine applicable only if there are no comments present let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { Applicability::MaybeIncorrect } else { @@ -136,48 +158,12 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { cx, MANUAL_UNWRAP_OR_DEFAULT, expr.span, - "match can be simplified with `.unwrap_or_default()`", + format!("{expr_name} can be simplified with `.unwrap_or_default()`"), "replace it with", format!("{receiver}.unwrap_or_default()"), applicability, ); } - true -} - -fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind - && let ExprKind::Let(let_) = cond.kind - && let ExprKind::Block(_, _) = else_expr.kind - // We check that the returned type implements the `Default` trait. - && let match_ty = cx.typeck_results().expr_ty(expr) - && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default) - && implements_trait(cx, match_ty, default_trait_id, &[]) - && let Some(binding_id) = get_some(cx, let_.pat) - // We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`. - && let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(if_block).kind - && let Res::Local(local_id) = path.res - && local_id == binding_id - // We now check the `None` arm is calling a method equivalent to `Default::default`. - && let body_else = peel_blocks(else_expr) - && is_default_equivalent(cx, body_else) - && let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span) - { - let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - span_lint_and_sugg( - cx, - MANUAL_UNWRAP_OR_DEFAULT, - expr.span, - "if let can be simplified with `.unwrap_or_default()`", - "replace it with", - format!("{if_let_expr_snippet}.unwrap_or_default()"), - applicability, - ); - } } impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { @@ -185,8 +171,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault { if expr.span.from_expansion() || in_constant(cx, expr.hir_id) { return; } - if !handle_match(cx, expr) { - handle_if_let(cx, expr); + // Call handle only if the expression is `if let` or `match` + if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) { + handle(cx, if_let_or_match, expr); } } } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 2fb317c8c68..0939c028564 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -3938,7 +3938,6 @@ declare_clippy_lint! { /// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or /// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is /// valid. - /// ``` #[clippy::version = "1.77.0"] pub STR_SPLIT_AT_NEWLINE, pedantic, diff --git a/clippy_lints/src/methods/search_is_some.rs b/clippy_lints/src/methods/search_is_some.rs index ac5cc2f01e5..f5f1e94bbf4 100644 --- a/clippy_lints/src/methods/search_is_some.rs +++ b/clippy_lints/src/methods/search_is_some.rs @@ -2,7 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::deref_closure_args; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::{is_trait_method, strip_pat_refs}; +use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs}; +use hir::ExprKind; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; @@ -35,7 +36,7 @@ pub(super) fn check<'tcx>( // suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()` let mut applicability = Applicability::MachineApplicable; let any_search_snippet = if search_method == "find" - && let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind + && let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind && let closure_body = cx.tcx.hir().body(body) && let Some(closure_arg) = closure_body.params.first() { @@ -72,16 +73,24 @@ pub(super) fn check<'tcx>( ); } else { let iter = snippet(cx, search_recv.span, ".."); + let sugg = if is_receiver_of_method_call(cx, expr) { + format!( + "(!{iter}.any({}))", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + } else { + format!( + "!{iter}.any({})", + any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) + ) + }; span_lint_and_sugg( cx, SEARCH_IS_SOME, expr.span, msg, "consider using", - format!( - "!{iter}.any({})", - any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str) - ), + sugg, applicability, ); } @@ -127,13 +136,18 @@ pub(super) fn check<'tcx>( let string = snippet(cx, search_recv.span, ".."); let mut applicability = Applicability::MachineApplicable; let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability); + let sugg = if is_receiver_of_method_call(cx, expr) { + format!("(!{string}.contains({find_arg}))") + } else { + format!("!{string}.contains({find_arg})") + }; span_lint_and_sugg( cx, SEARCH_IS_SOME, expr.span, msg, "consider using", - format!("!{string}.contains({find_arg})"), + sugg, applicability, ); }, @@ -142,3 +156,13 @@ pub(super) fn check<'tcx>( } } } + +fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + if let Some(parent_expr) = get_parent_expr(cx, expr) + && let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind + && receiver.hir_id == expr.hir_id + { + return true; + } + false +} diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs index c555fc8675c..a24cd4f9c8a 100644 --- a/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -23,7 +23,7 @@ use std::collections::VecDeque; declare_clippy_lint! { /// ### What it does - /// Checks for borrow operations (`&`) that used as a generic argument to a + /// Checks for borrow operations (`&`) that are used as a generic argument to a /// function when the borrowed value could be used. /// /// ### Why is this bad? diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 5ca388d67a1..ff10a841aef 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, DUMMY_SP, InnerSpan, Span}; +use rustc_span::{sym, InnerSpan, Span, DUMMY_SP}; use rustc_target::abi::VariantIdx; // FIXME: this is a correctness problem but there's no suitable @@ -297,12 +297,7 @@ impl NonCopyConst { fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool { let args = cx.typeck_results().node_args(hir_id); - let result = Self::const_eval_resolve( - cx.tcx, - cx.param_env, - ty::UnevaluatedConst::new(def_id, args), - DUMMY_SP, - ); + let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP); self.is_value_unfrozen_raw(cx, result, ty) } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3aa979cb11b..87a3c3874d7 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -300,11 +300,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { e.span, "calling `as_bytes()` on `include_str!(..)`", "consider using `include_bytes!(..)` instead", - snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability).replacen( - "include_str", - "include_bytes", - 1, - ), + snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability) + .replacen("include_str", "include_bytes", 1), applicability, ); } else if lit_content.as_str().is_ascii() diff --git a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 234021f0f47..2bea3be3d60 100644 --- a/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>( }; // FIXME: This can be simplified once `NonZero<T>` is stable. - let coercable_types = [ + let coercible_types = [ ("NonZeroU8", tcx.types.u8), ("NonZeroU16", tcx.types.u16), ("NonZeroU32", tcx.types.u32), @@ -44,7 +44,7 @@ pub(super) fn check<'tcx>( let int_type = substs.type_at(0); - let Some(nonzero_alias) = coercable_types.iter().find_map(|(nonzero_alias, t)| { + let Some(nonzero_alias) = coercible_types.iter().find_map(|(nonzero_alias, t)| { if *t == int_type && *t == from_ty { Some(nonzero_alias) } else { diff --git a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index a6a6e9a3bac..ba8c7d6bfcb 100644 --- a/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -1,10 +1,10 @@ -use rustc_hir_typeck::cast::check_cast; use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use rustc_ast::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; +use rustc_hir_typeck::cast::check_cast; use rustc_lint::LateContext; use rustc_middle::ty::cast::CastKind; use rustc_middle::ty::Ty; |
