diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index 902ba70577b9..a04a56d72bc0 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -433,7 +433,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio }, ExprKind::MethodCall(path, receiver, args, _) => { let type_of_receiver = cx.typeck_results().expr_ty(receiver); - if !type_of_receiver.is_diag_item(cx, sym::Option) && !type_of_receiver.is_diag_item(cx, sym::Result) { + if !matches!(type_of_receiver.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return None; } METHODS_WITH_NEGATION diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index c42998ffc3f5..bd2bd6628464 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -82,7 +82,7 @@ fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Sp // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &[sym::unwrap]) { let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs(); - if receiver_ty.is_diag_item(self.lcx, sym::Option) || receiver_ty.is_diag_item(self.lcx, sym::Result) { + if matches!(receiver_ty.opt_diag_name(self.lcx), Some(sym::Option | sym::Result)) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d2bc0b6d9935..6b154bc6a32c 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -223,25 +223,20 @@ impl<'tcx> ImplicitHasherType<'tcx> { _ => None, }) .collect(); - let params_len = params.len(); let ty = lower_ty(cx.tcx, hir_ty); - if ty.is_diag_item(cx, sym::HashMap) && params_len == 2 { - Some(ImplicitHasherType::HashMap( + match (ty.opt_diag_name(cx), ¶ms[..]) { + (Some(sym::HashMap), [k, v]) => Some(ImplicitHasherType::HashMap( hir_ty.span, ty, - snippet(cx, params[0].span, "K"), - snippet(cx, params[1].span, "V"), - )) - } else if ty.is_diag_item(cx, sym::HashSet) && params_len == 1 { - Some(ImplicitHasherType::HashSet( - hir_ty.span, - ty, - snippet(cx, params[0].span, "T"), - )) - } else { - None + snippet(cx, k.span, "K"), + snippet(cx, v.span, "V"), + )), + (Some(sym::HashSet), [t]) => { + Some(ImplicitHasherType::HashSet(hir_ty.span, ty, snippet(cx, t.span, "T"))) + }, + _ => None, } } else { None diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs index c6b650a1a88b..39b2391c98ec 100644 --- a/clippy_lints/src/loops/for_kv_map.rs +++ b/clippy_lints/src/loops/for_kv_map.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx _ => arg, }; - if ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) { + if matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) { span_lint_and_then( cx, FOR_KV_MAP, diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 0f3d8b336675..38ee4ce104a5 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -374,7 +374,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. - if !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) { + if !matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) { has_disallowed = true; } }); diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index fa44a56af182..00bd1c2ca698 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -16,7 +16,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { ty::Adt(adt_def, _) - if adt_def.is_enum() && !(ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result)) => + if adt_def.is_enum() && !matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) => { adt_def }, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 288f966991ac..e891b2ac6d64 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -26,12 +26,10 @@ pub(super) fn check<'tcx>( let arg_root = get_arg_root(cx, arg); if contains_call(cx, arg_root) && !contains_return(arg_root) { let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver); - let closure_args = if receiver_type.is_diag_item(cx, sym::Option) { - "||" - } else if receiver_type.is_diag_item(cx, sym::Result) { - "|_|" - } else { - return; + let closure_args = match receiver_type.opt_diag_name(cx) { + Some(sym::Option) => "||", + Some(sym::Result) => "|_|", + _ => return, }; let span_replace_word = method_span.with_hi(expr.span.hi()); diff --git a/clippy_lints/src/methods/iter_count.rs b/clippy_lints/src/methods/iter_count.rs index ea2508cd7f38..8b303c0ca5b2 100644 --- a/clippy_lints/src/methods/iter_count.rs +++ b/clippy_lints/src/methods/iter_count.rs @@ -11,26 +11,17 @@ use super::ITER_COUNT; pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx Expr<'tcx>, iter_method: Symbol) { let ty = cx.typeck_results().expr_ty(recv); - let caller_type = if derefs_to_slice(cx, recv, ty).is_some() { - "slice" - } else if ty.is_diag_item(cx, sym::Vec) { - "Vec" - } else if ty.is_diag_item(cx, sym::VecDeque) { - "VecDeque" - } else if ty.is_diag_item(cx, sym::HashSet) { - "HashSet" - } else if ty.is_diag_item(cx, sym::HashMap) { - "HashMap" - } else if ty.is_diag_item(cx, sym::BTreeMap) { - "BTreeMap" - } else if ty.is_diag_item(cx, sym::BTreeSet) { - "BTreeSet" - } else if ty.is_diag_item(cx, sym::LinkedList) { - "LinkedList" - } else if ty.is_diag_item(cx, sym::BinaryHeap) { - "BinaryHeap" - } else { - return; + let caller_type = match ty.opt_diag_name(cx) { + _ if derefs_to_slice(cx, recv, ty).is_some() => "slice", + Some(sym::Vec) => "Vec", + Some(sym::VecDeque) => "VecDeque", + Some(sym::HashSet) => "HashSet", + Some(sym::HashMap) => "HashMap", + Some(sym::BTreeMap) => "BTreeMap", + Some(sym::BTreeSet) => "BTreeSet", + Some(sym::LinkedList) => "LinkedList", + Some(sym::BinaryHeap) => "BinaryHeap", + _ => return, }; let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs index 2d6bc36dc535..16db8663941e 100644 --- a/clippy_lints/src/methods/iter_kv_map.rs +++ b/clippy_lints/src/methods/iter_kv_map.rs @@ -38,7 +38,7 @@ pub(super) fn check<'tcx>( _ => return, } && let ty = cx.typeck_results().expr_ty_adjusted(recv).peel_refs() - && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) + && matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) { let mut applicability = rustc_errors::Applicability::MachineApplicable; let recv_snippet = snippet_with_applicability(cx, recv.span, "map", &mut applicability); diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs index e84b7452c758..905a58afa795 100644 --- a/clippy_lints/src/methods/join_absolute_paths.rs +++ b/clippy_lints/src/methods/join_absolute_paths.rs @@ -13,7 +13,7 @@ use super::JOIN_ABSOLUTE_PATHS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>, expr_span: Span) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if (ty.is_diag_item(cx, sym::Path) || ty.is_diag_item(cx, sym::PathBuf)) + if matches!(ty.opt_diag_name(cx), Some(sym::Path | sym::PathBuf)) && let ExprKind::Lit(spanned) = expr_or_init(cx, join_arg).kind && let LitKind::Str(symbol, _) = spanned.node && let sym_str = symbol.as_str() diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 1bc29c9c1dd1..e00b3ed661e4 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -25,7 +25,7 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_parent_id: De } // We check if it's an `Option` or a `Result`. if let Some(ty) = method_parent_id.opt_impl_ty(cx) { - if !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result) { + if !matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) { return false; } } else { diff --git a/clippy_lints/src/methods/or_then_unwrap.rs b/clippy_lints/src/methods/or_then_unwrap.rs index 07199b84f39e..448ab621a7ce 100644 --- a/clippy_lints/src/methods/or_then_unwrap.rs +++ b/clippy_lints/src/methods/or_then_unwrap.rs @@ -20,24 +20,28 @@ pub(super) fn check<'tcx>( let title; let or_arg_content: Span; - if ty.is_diag_item(cx, sym::Option) { - title = "found `.or(Some(…)).unwrap()`"; - if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { - or_arg_content = content; - } else { + match ty.opt_diag_name(cx) { + Some(sym::Option) => { + title = "found `.or(Some(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::OptionSome) { + or_arg_content = content; + } else { + return; + } + }, + Some(sym::Result) => { + title = "found `.or(Ok(…)).unwrap()`"; + if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { + or_arg_content = content; + } else { + return; + } + }, + _ => { + // Someone has implemented a struct with .or(...).unwrap() chaining, + // but it's not an Option or a Result, so bail return; - } - } else if ty.is_diag_item(cx, sym::Result) { - title = "found `.or(Ok(…)).unwrap()`"; - if let Some(content) = get_content_if_ctor_matches(cx, or_arg, LangItem::ResultOk) { - or_arg_content = content; - } else { - return; - } - } else { - // Someone has implemented a struct with .or(...).unwrap() chaining, - // but it's not an Option or a Result, so bail - return; + }, } let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs index 10ea0c0c3e23..3207c4207fc0 100644 --- a/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -11,11 +11,11 @@ use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; fn is_a_std_set_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.is_diag_item(cx, sym::HashSet) || ty.is_diag_item(cx, sym::BTreeSet) + matches!(ty.opt_diag_name(cx), Some(sym::HashSet | sym::BTreeSet)) } fn is_a_std_map_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap) + matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) } pub(super) fn check( diff --git a/clippy_lints/src/methods/unwrap_expect_used.rs b/clippy_lints/src/methods/unwrap_expect_used.rs index 73a407be4f21..30db2a75df57 100644 --- a/clippy_lints/src/methods/unwrap_expect_used.rs +++ b/clippy_lints/src/methods/unwrap_expect_used.rs @@ -46,19 +46,19 @@ pub(super) fn check( ) { let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - let (kind, none_value, none_prefix) = if ty.is_diag_item(cx, sym::Option) && !is_err { - ("an `Option`", "None", "") - } else if ty.is_diag_item(cx, sym::Result) - && let ty::Adt(_, substs) = ty.kind() - && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() - { - if is_never_like(t_or_e_ty) { - return; - } + let (kind, none_value, none_prefix) = match ty.opt_diag_name(cx) { + Some(sym::Option) if !is_err => ("an `Option`", "None", ""), + Some(sym::Result) + if let ty::Adt(_, substs) = ty.kind() + && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() => + { + if is_never_like(t_or_e_ty) { + return; + } - ("a `Result`", if is_err { "Ok" } else { "Err" }, "an ") - } else { - return; + ("a `Result`", if is_err { "Ok" } else { "Err" }, "an ") + }, + _ => return, }; let method_suffix = if is_err { "_err" } else { "" }; diff --git a/clippy_lints/src/missing_fields_in_debug.rs b/clippy_lints/src/missing_fields_in_debug.rs index 15b773c2c64f..a26f24d15247 100644 --- a/clippy_lints/src/missing_fields_in_debug.rs +++ b/clippy_lints/src/missing_fields_in_debug.rs @@ -112,10 +112,14 @@ fn should_lint<'tcx>( if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); - if path.ident.name == sym::debug_struct && recv_ty.is_diag_item(cx, sym::Formatter) { - has_debug_struct = true; - } else if path.ident.name == sym::finish_non_exhaustive && recv_ty.is_diag_item(cx, sym::DebugStruct) { - has_finish_non_exhaustive = true; + match (path.ident.name, recv_ty.opt_diag_name(cx)) { + (sym::debug_struct, Some(sym::Formatter)) => { + has_debug_struct = true; + }, + (sym::finish_non_exhaustive, Some(sym::DebugStruct)) => { + has_finish_non_exhaustive = true; + }, + _ => {}, } } ControlFlow::::Continue(()) diff --git a/clippy_lints/src/time_subtraction.rs b/clippy_lints/src/time_subtraction.rs index dbd4ec77fd5f..91d4fd74fad4 100644 --- a/clippy_lints/src/time_subtraction.rs +++ b/clippy_lints/src/time_subtraction.rs @@ -93,19 +93,19 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { ) = expr.kind { let typeck = cx.typeck_results(); - let lhs_ty = typeck.expr_ty(lhs); - let rhs_ty = typeck.expr_ty(rhs); + let lhs_name = typeck.expr_ty(lhs).opt_diag_name(cx); + let rhs_name = typeck.expr_ty(rhs).opt_diag_name(cx); - if lhs_ty.is_diag_item(cx, sym::Instant) { + if lhs_name == Some(sym::Instant) { // Instant::now() - instant if is_instant_now_call(cx, lhs) - && rhs_ty.is_diag_item(cx, sym::Instant) + && rhs_name == Some(sym::Instant) && let Some(sugg) = Sugg::hir_opt(cx, rhs) { print_manual_instant_elapsed_sugg(cx, expr, sugg); } // instant - duration - else if rhs_ty.is_diag_item(cx, sym::Duration) + else if rhs_name == Some(sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -122,8 +122,8 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr); } } - } else if lhs_ty.is_diag_item(cx, sym::Duration) - && rhs_ty.is_diag_item(cx, sym::Duration) + } else if lhs_name == Some(sym::Duration) + && rhs_name == Some(sym::Duration) && !expr.span.from_expansion() && self.msrv.meets(cx, msrvs::TRY_FROM) { @@ -170,7 +170,7 @@ fn is_chained_time_subtraction(cx: &LateContext<'_>, lhs: &Expr<'_>) -> bool { /// Returns true if the type is Duration or Instant fn is_time_type(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { - ty.is_diag_item(cx, sym::Duration) || ty.is_diag_item(cx, sym::Instant) + matches!(ty.opt_diag_name(cx), Some(sym::Duration | sym::Instant)) } fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) { diff --git a/clippy_lints/src/zero_sized_map_values.rs b/clippy_lints/src/zero_sized_map_values.rs index bf133d26ed9d..94c2fb20d5f5 100644 --- a/clippy_lints/src/zero_sized_map_values.rs +++ b/clippy_lints/src/zero_sized_map_values.rs @@ -49,7 +49,7 @@ impl LateLintPass<'_> for ZeroSizedMapValues { && !in_trait_impl(cx, hir_ty.hir_id) // We don't care about infer vars && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty()) - && (ty.is_diag_item(cx, sym::HashMap) || ty.is_diag_item(cx, sym::BTreeMap)) + && matches!(ty.opt_diag_name(cx), Some(sym::HashMap | sym::BTreeMap)) && let ty::Adt(_, args) = ty.kind() && let ty = args.type_at(1) // Ensure that no type information is missing, to avoid a delayed bug in the compiler if this is not the case. diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index 95bdf27b019c..14d4139a0065 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -17,6 +17,7 @@ pub static TY_CTXT: PathLookup = type_path!(rustc_middle::ty::TyCtxt); // Paths in clippy itself pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym); +pub static MAYBE_DEF: PathLookup = type_path!(clippy_utils::res::MaybeDef); pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack); pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new); pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then); diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs index d686ba73387c..cca5608fa6be 100644 --- a/clippy_lints_internal/src/lib.rs +++ b/clippy_lints_internal/src/lib.rs @@ -38,6 +38,7 @@ mod lint_without_lint_pass; mod msrv_attr_impl; mod outer_expn_data_pass; mod produce_ice; +mod repeated_is_diagnostic_item; mod symbols; mod unnecessary_def_path; mod unsorted_clippy_utils_paths; @@ -77,4 +78,5 @@ pub fn register_lints(store: &mut LintStore) { store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); + store.register_late_pass(|_| Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem)); } diff --git a/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs new file mode 100644 index 000000000000..55fb78b1e296 --- /dev/null +++ b/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -0,0 +1,561 @@ +use std::iter; +use std::ops::ControlFlow; + +use crate::internal_paths::MAYBE_DEF; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::{snippet_indent, snippet_with_applicability}; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{eq_expr_value, if_sequence, sym}; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Node, StmtKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::print::with_forced_trimmed_paths; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_tool_lint! { + /// ### What it does + /// Checks for repeated use of `MaybeDef::is_diag_item`/`TyCtxt::is_diagnostic_item`; + /// suggests to first call `MaybDef::opt_diag_name`/`TyCtxt::get_diagnostic_name` and then + /// compare the output with all the `Symbol`s. + /// + /// ### Why is this bad? + /// Each of such calls ultimately invokes the `diagnostic_items` query. + /// While the query is cached, it's still better to avoid calling it multiple times if possible. + /// + /// ### Example + /// ```no_run + /// ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result) + /// cx.tcx.is_diagnostic_item(sym::Option, did) || cx.tcx.is_diagnostic_item(sym::Result, did) + /// + /// if ty.is_diag_item(cx, sym::Option) { + /// .. + /// } else if ty.is_diag_item(cx, sym::Result) { + /// .. + /// } else { + /// .. + /// } + /// + /// if cx.tcx.is_diagnostic_item(sym::Option, did) { + /// .. + /// } else if cx.tcx.is_diagnostic_item(sym::Result, did) { + /// .. + /// } else { + /// .. + /// } + /// + /// { + /// if ty.is_diag_item(cx, sym::Option) { + /// .. + /// } + /// if ty.is_diag_item(cx, sym::Result) { + /// .. + /// } + /// } + /// + /// { + /// if cx.tcx.is_diagnostic_item(sym::Option, did) { + /// .. + /// } + /// if cx.tcx.is_diagnostic_item(sym::Result, did) { + /// .. + /// } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)) + /// matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Option | sym::Result)) + /// + /// match ty.opt_diag_name(cx) { + /// Some(sym::Option) => { + /// .. + /// } + /// Some(sym::Result) => { + /// .. + /// } + /// _ => { + /// .. + /// } + /// } + /// + /// match cx.tcx.get_diagnostic_name(did) { + /// Some(sym::Option) => { + /// .. + /// } + /// Some(sym::Result) => { + /// .. + /// } + /// _ => { + /// .. + /// } + /// } + /// + /// { + /// let name = ty.opt_diag_name(cx); + /// if name == Some(sym::Option) { + /// .. + /// } + /// if name == Some(sym::Result) { + /// .. + /// } + /// } + /// + /// { + /// let name = cx.tcx.get_diagnostic_name(did); + /// if name == Some(sym::Option) { + /// .. + /// } + /// if name == Some(sym::Result) { + /// .. + /// } + /// } + /// ``` + pub clippy::REPEATED_IS_DIAGNOSTIC_ITEM, + Warn, + "repeated use of `MaybeDef::is_diag_item`/`TyCtxt::is_diagnostic_item`" +} +declare_lint_pass!(RepeatedIsDiagnosticItem => [REPEATED_IS_DIAGNOSTIC_ITEM]); + +const NOTE: &str = "each call performs the same compiler query -- it's faster to query once, and reuse the results"; + +impl<'tcx> LateLintPass<'tcx> for RepeatedIsDiagnosticItem { + #[expect(clippy::too_many_lines)] + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) { + for [(cond1, stmt1_span), (cond2, stmt2_span)] in block + .stmts + .windows(2) + .filter_map(|pair| { + if let [if1, if2] = pair + && let StmtKind::Expr(e1) | StmtKind::Semi(e1) = if1.kind + && let ExprKind::If(cond1, ..) = e1.kind + && let StmtKind::Expr(e2) | StmtKind::Semi(e2) = if2.kind + && let ExprKind::If(cond2, ..) = e2.kind + { + Some([(cond1, if1.span), (cond2, if2.span)]) + } else { + None + } + }) + .chain( + if let Some(if1) = block.stmts.last() + && let StmtKind::Expr(e1) | StmtKind::Semi(e1) = if1.kind + && let ExprKind::If(cond1, ..) = e1.kind + && let Some(e2) = block.expr + && let ExprKind::If(cond2, ..) = e2.kind + { + Some([(cond1, if1.span), (cond2, e2.span)]) + } else { + None + }, + ) + { + let lint_span = stmt1_span.to(stmt2_span); + + // if recv1.is_diag_item(cx, sym1) && .. { + // .. + // } + // if recv2.is_diag_item(cx, sym2) && .. { + // .. + // } + if let Some(first @ (span1, (cx1, recv1, _))) = extract_nested_is_diag_item(cx, cond1) + && let Some(second @ (span2, (cx2, recv2, _))) = extract_nested_is_diag_item(cx, cond2) + && eq_expr_value(cx, cx1, cx2) + && eq_expr_value(cx, recv1, recv2) + { + let recv_ty = + with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); + let recv_ty = recv_ty.trim_end_matches("<'_>"); + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + lint_span, + format!("repeated calls to `{recv_ty}::is_diag_item`"), + |diag| { + diag.span_labels([span1, span2], "called here"); + diag.note(NOTE); + + let mut app = Applicability::HasPlaceholders; + let cx_str = snippet_with_applicability(cx, cx1.span, "_", &mut app); + let recv = snippet_with_applicability(cx, recv1.span, "_", &mut app); + let indent = snippet_indent(cx, stmt1_span).unwrap_or_default(); + let sugg: Vec<_> = iter::once(( + stmt1_span.shrink_to_lo(), + format!("let /* name */ = {recv}.opt_diag_name({cx_str});\n{indent}"), + )) // call `opt_diag_name` once + .chain([first, second].into_iter().map(|(expr_span, (_, _, sym))| { + let sym = snippet_with_applicability(cx, sym.span, "_", &mut app); + (expr_span, format!("/* name */ == Some({sym})")) + })) + .collect(); + + diag.multipart_suggestion_verbose( + format!("call `{recv_ty}::opt_diag_name`, and reuse the results"), + sugg, + app, + ); + }, + ); + return; + } + + // if cx.tcx.is_diagnostic_item(sym1, did) && .. { + // .. + // } + // if cx.tcx.is_diagnostic_item(sym2, did) && .. { + // .. + // } + if let Some(first @ (span1, (tcx1, did1, _))) = extract_nested_is_diagnostic_item(cx, cond1) + && let Some(second @ (span2, (tcx2, did2, _))) = extract_nested_is_diagnostic_item(cx, cond2) + && eq_expr_value(cx, tcx1, tcx2) + && eq_expr_value(cx, did1, did2) + { + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + lint_span, + "repeated calls to `TyCtxt::is_diagnostic_item`", + |diag| { + diag.span_labels([span1, span2], "called here"); + diag.note(NOTE); + + let mut app = Applicability::HasPlaceholders; + let tcx = snippet_with_applicability(cx, tcx1.span, "_", &mut app); + let did = snippet_with_applicability(cx, did1.span, "_", &mut app); + let indent = snippet_indent(cx, stmt1_span).unwrap_or_default(); + let sugg: Vec<_> = iter::once(( + stmt1_span.shrink_to_lo(), + format!("let /* name */ = {tcx}.get_diagnostic_name({did});\n{indent}"), + )) // call `get_diagnostic_name` once + .chain([first, second].into_iter().map(|(expr_span, (_, _, sym))| { + let sym = snippet_with_applicability(cx, sym.span, "_", &mut app); + (expr_span, format!("/* name */ == Some({sym})")) + })) + .collect(); + + diag.multipart_suggestion_verbose( + "call `TyCtxt::get_diagnostic_name`, and reuse the results", + sugg, + app, + ); + }, + ); + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let ExprKind::Binary(op, left, right) = expr.kind { + if op.node == BinOpKind::Or { + check_ors(cx, expr.span, left, right); + } else if op.node == BinOpKind::And + && let ExprKind::Unary(UnOp::Not, left) = left.kind + && let ExprKind::Unary(UnOp::Not, right) = right.kind + { + check_ands(cx, expr.span, left, right); + } + } else if let (conds, _) = if_sequence(expr) + && !conds.is_empty() + { + check_if_chains(cx, expr, conds); + } + } +} + +fn check_ors(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) { + // recv1.is_diag_item(cx, sym1) || recv2.is_diag_item(cx, sym2) + if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left) + && let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right) + && eq_expr_value(cx, cx1, cx2) + && eq_expr_value(cx, recv1, recv2) + { + let recv_ty = + with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); + let recv_ty = recv_ty.trim_end_matches("<'_>"); + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + span, + format!("repeated calls to `{recv_ty}::is_diag_item`"), + |diag| { + diag.note(NOTE); + + let mut app = Applicability::MachineApplicable; + let cx_str = snippet_with_applicability(cx, cx1.span, "_", &mut app); + let recv = snippet_with_applicability(cx, recv1.span, "_", &mut app); + let sym1 = snippet_with_applicability(cx, sym1.span, "_", &mut app); + let sym2 = snippet_with_applicability(cx, sym2.span, "_", &mut app); + diag.span_suggestion_verbose( + span, + format!("call `{recv_ty}::opt_diag_name`, and reuse the results"), + format!("matches!({recv}.opt_diag_name({cx_str}), Some({sym1} | {sym2}))"), + app, + ); + }, + ); + return; + } + + // cx.tcx.is_diagnostic_item(sym1, did) || cx.tcx.is_diagnostic_item(sym2, did) + if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left) + && let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right) + && eq_expr_value(cx, tcx1, tcx2) + && eq_expr_value(cx, did1, did2) + { + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + span, + "repeated calls to `TyCtxt::is_diagnostic_item`", + |diag| { + diag.note(NOTE); + + let mut app = Applicability::MachineApplicable; + let tcx = snippet_with_applicability(cx, tcx1.span, "_", &mut app); + let did = snippet_with_applicability(cx, did1.span, "_", &mut app); + let sym1 = snippet_with_applicability(cx, sym1.span, "_", &mut app); + let sym2 = snippet_with_applicability(cx, sym2.span, "_", &mut app); + diag.span_suggestion_verbose( + span, + "call `TyCtxt::get_diagnostic_name`, and reuse the results", + format!("matches!({tcx}.get_diagnostic_name({did}), Some({sym1} | {sym2}))"), + app, + ); + }, + ); + } +} + +fn check_ands(cx: &LateContext<'_>, span: Span, left: &Expr<'_>, right: &Expr<'_>) { + // !recv1.is_diag_item(cx, sym1) && !recv2.is_diag_item(cx, sym2) + if let Some((cx1, recv1, sym1)) = extract_is_diag_item(cx, left) + && let Some((cx2, recv2, sym2)) = extract_is_diag_item(cx, right) + && eq_expr_value(cx, cx1, cx2) + && eq_expr_value(cx, recv1, recv2) + { + let recv_ty = + with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); + let recv_ty = recv_ty.trim_end_matches("<'_>"); + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + span, + format!("repeated calls to `{recv_ty}::is_diag_item`"), + |diag| { + diag.note(NOTE); + + let mut app = Applicability::MachineApplicable; + let cx_str = snippet_with_applicability(cx, cx1.span, "_", &mut app); + let recv = snippet_with_applicability(cx, recv1.span, "_", &mut app); + let sym1 = snippet_with_applicability(cx, sym1.span, "_", &mut app); + let sym2 = snippet_with_applicability(cx, sym2.span, "_", &mut app); + diag.span_suggestion_verbose( + span, + format!("call `{recv_ty}::opt_diag_name`, and reuse the results"), + format!("!matches!({recv}.opt_diag_name({cx_str}), Some({sym1} | {sym2}))"), + app, + ); + }, + ); + return; + } + + // !cx.tcx.is_diagnostic_item(sym1, did) && !cx.tcx.is_diagnostic_item(sym2, did) + if let Some((tcx1, did1, sym1)) = extract_is_diagnostic_item(cx, left) + && let Some((tcx2, did2, sym2)) = extract_is_diagnostic_item(cx, right) + && eq_expr_value(cx, tcx1, tcx2) + && eq_expr_value(cx, did1, did2) + { + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + span, + "repeated calls to `TyCtxt::is_diagnostic_item`", + |diag| { + diag.note(NOTE); + + let mut app = Applicability::MachineApplicable; + let tcx = snippet_with_applicability(cx, tcx1.span, "_", &mut app); + let did = snippet_with_applicability(cx, did1.span, "_", &mut app); + let sym1 = snippet_with_applicability(cx, sym1.span, "_", &mut app); + let sym2 = snippet_with_applicability(cx, sym2.span, "_", &mut app); + diag.span_suggestion_verbose( + span, + "call `TyCtxt::get_diagnostic_name`, and reuse the results", + format!("!matches!({tcx}.get_diagnostic_name({did}), Some({sym1} | {sym2}))"), + app, + ); + }, + ); + } +} + +fn check_if_chains<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, conds: Vec<&'tcx Expr<'_>>) { + // if ty.is_diag_item(cx, sym1) { + // .. + // } else if ty.is_diag_item(cx, sym2) { + // .. + // } else { + // .. + // } + let mut found = conds.iter().filter_map(|cond| extract_nested_is_diag_item(cx, cond)); + if let Some(first @ (_, (cx_1, recv1, _))) = found.next() + && let other = + found.filter(|(_, (cx_, recv, _))| eq_expr_value(cx, cx_, cx_1) && eq_expr_value(cx, recv, recv1)) + && let results = iter::once(first).chain(other).collect::>() + && results.len() > 1 + { + let recv_ty = + with_forced_trimmed_paths!(format!("{}", cx.typeck_results().expr_ty_adjusted(recv1).peel_refs())); + let recv_ty = recv_ty.trim_end_matches("<'_>"); + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + expr.span, + format!("repeated calls to `{recv_ty}::is_diag_item`"), + |diag| { + diag.span_labels(results.iter().map(|(span, _)| *span), "called here"); + diag.note(NOTE); + + let mut app = Applicability::HasPlaceholders; + let cx_str = snippet_with_applicability(cx, cx_1.span, "_", &mut app); + let recv = snippet_with_applicability(cx, recv1.span, "_", &mut app); + let span_before = if let Node::LetStmt(let_stmt) = cx.tcx.parent_hir_node(expr.hir_id) { + let_stmt.span + } else { + expr.span + }; + let indent = snippet_indent(cx, span_before).unwrap_or_default(); + let sugg: Vec<_> = iter::once(( + span_before.shrink_to_lo(), + format!("let /* name */ = {recv}.opt_diag_name({cx_str});\n{indent}"), + )) // call `opt_diag_name` once + .chain(results.into_iter().map(|(expr_span, (_, _, sym))| { + let sym = snippet_with_applicability(cx, sym.span, "_", &mut app); + (expr_span, format!("/* name */ == Some({sym})")) + })) + .collect(); + + diag.multipart_suggestion_verbose( + format!("call `{recv_ty}::opt_diag_name`, and reuse the results"), + sugg, + app, + ); + }, + ); + } + + // if cx.tcx.is_diagnostic_item(sym1, did) { + // .. + // } else if cx.tcx.is_diagnostic_item(sym2, did) { + // .. + // } else { + // .. + // } + let mut found = conds + .into_iter() + .filter_map(|cond| extract_nested_is_diagnostic_item(cx, cond)); + if let Some(first @ (_, (tcx1, did1, _))) = found.next() + && let other = found.filter(|(_, (tcx, did, _))| eq_expr_value(cx, tcx, tcx1) && eq_expr_value(cx, did, did1)) + && let results = iter::once(first).chain(other).collect::>() + && results.len() > 1 + { + span_lint_and_then( + cx, + REPEATED_IS_DIAGNOSTIC_ITEM, + expr.span, + "repeated calls to `TyCtxt::is_diagnostic_item`", + |diag| { + diag.span_labels(results.iter().map(|(span, _)| *span), "called here"); + diag.note(NOTE); + + let mut app = Applicability::HasPlaceholders; + let tcx = snippet_with_applicability(cx, tcx1.span, "_", &mut app); + let recv = snippet_with_applicability(cx, did1.span, "_", &mut app); + let span_before = if let Node::LetStmt(let_stmt) = cx.tcx.parent_hir_node(expr.hir_id) { + let_stmt.span + } else { + expr.span + }; + let indent = snippet_indent(cx, span_before).unwrap_or_default(); + let sugg: Vec<_> = iter::once(( + span_before.shrink_to_lo(), + format!("let /* name */ = {tcx}.get_diagnostic_name({recv});\n{indent}"), + )) // call `get_diagnostic_name` once + .chain(results.into_iter().map(|(expr_span, (_, _, sym))| { + let sym = snippet_with_applicability(cx, sym.span, "_", &mut app); + (expr_span, format!("/* name */ == Some({sym})")) + })) + .collect(); + + diag.multipart_suggestion_verbose( + "call `TyCtxt::get_diagnostic_name`, and reuse the results", + sugg, + app, + ); + }, + ); + } +} + +fn extract_is_diag_item<'tcx>( + cx: &LateContext<'_>, + expr: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::MethodCall(is_diag_item, recv, [cx_, sym], _) = expr.kind + && is_diag_item.ident.name == sym::is_diag_item + // Whether this a method from the `MaybeDef` trait + && let Some(did) = cx.ty_based_def(expr).opt_parent(cx).opt_def_id() + && MAYBE_DEF.matches(cx, did) + { + Some((cx_, recv, sym)) + } else { + None + } +} + +fn extract_is_diagnostic_item<'tcx>( + cx: &LateContext<'_>, + expr: &'tcx Expr<'tcx>, +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { + if let ExprKind::MethodCall(is_diag_item, tcx, [sym, did], _) = expr.kind + && is_diag_item.ident.name == sym::is_diagnostic_item + // Whether this is an inherent method on `TyCtxt` + && cx + .ty_based_def(expr) + .opt_parent(cx) + .opt_impl_ty(cx) + .is_diag_item(cx, sym::TyCtxt) + { + Some((tcx, did, sym)) + } else { + None + } +} + +fn extract_nested_is_diag_item<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, +) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { + for_each_expr(cx, cond, |cond_part| { + if let Some(res) = extract_is_diag_item(cx, cond_part) { + ControlFlow::Break((cond_part.span, res)) + } else { + ControlFlow::Continue(()) + } + }) +} + +fn extract_nested_is_diagnostic_item<'tcx>( + cx: &LateContext<'tcx>, + cond: &'tcx Expr<'_>, +) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { + for_each_expr(cx, cond, |cond_part| { + if let Some(res) = extract_is_diagnostic_item(cx, cond_part) { + ControlFlow::Break((cond_part.span, res)) + } else { + ControlFlow::Continue(()) + } + }) +} diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 7b8c7f3f0d6b..31b9db82d1a7 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -806,10 +806,12 @@ impl<'tcx> ConstEvalCtxt<'tcx> { | sym::i128_legacy_const_max ) ) || self.tcx.opt_parent(did).is_some_and(|parent| { - parent.is_diag_item(&self.tcx, sym::f16_consts_mod) - || parent.is_diag_item(&self.tcx, sym::f32_consts_mod) - || parent.is_diag_item(&self.tcx, sym::f64_consts_mod) - || parent.is_diag_item(&self.tcx, sym::f128_consts_mod) + matches!( + parent.opt_diag_name(&self.tcx), + Some( + sym::f16_consts_mod | sym::f32_consts_mod | sym::f64_consts_mod | sym::f128_consts_mod + ) + ) })) => { did diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs index 8e8a80a6a9c9..23c72d2180cd 100644 --- a/clippy_utils/src/sym.rs +++ b/clippy_utils/src/sym.rs @@ -58,6 +58,7 @@ generate! { LowerHex, MAX, MIN, + MaybeDef, MsrvStack, Octal, OpenOptions, @@ -191,6 +192,8 @@ generate! { io, is_ascii, is_char_boundary, + is_diag_item, + is_diagnostic_item, is_digit, is_empty, is_err, @@ -280,6 +283,7 @@ generate! { repeat, replace, replacen, + res, reserve, resize, restriction, diff --git a/tests/ui-internal/repeated_is_diagnostic_item.fixed b/tests/ui-internal/repeated_is_diagnostic_item.fixed new file mode 100644 index 000000000000..fcacf504804c --- /dev/null +++ b/tests/ui-internal/repeated_is_diagnostic_item.fixed @@ -0,0 +1,77 @@ +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +use clippy_utils::res::MaybeDef; +use clippy_utils::sym; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt}; +use rustc_span::Symbol; + +fn binops(cx: &LateContext<'_>, ty: Ty<'_>, adt_def: &AdtDef<'_>) { + let did = ty.opt_def_id().unwrap(); + + let _ = matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + let _ = !matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + let _ = matches!(adt_def.opt_diag_name(cx), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + let _ = !matches!(adt_def.opt_diag_name(cx), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + let _ = matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + let _ = !matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Option | sym::Result)); + //~^ repeated_is_diagnostic_item + + // Don't lint: `is_diagnostic_item` is called not on `TyCtxt` + struct FakeTyCtxt; + impl FakeTyCtxt { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool { + unimplemented!() + } + } + let f = FakeTyCtxt; + let _ = f.is_diagnostic_item(sym::Option, did) || f.is_diagnostic_item(sym::Result, did); + + // Don't lint: `is_diagnostic_item` on `TyCtxt` comes from a(n unrelated) trait + trait IsDiagnosticItem { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool; + } + impl IsDiagnosticItem for TyCtxt<'_> { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool { + unimplemented!() + } + } + let _ = IsDiagnosticItem::is_diagnostic_item(&cx.tcx, sym::Option, did) + || IsDiagnosticItem::is_diagnostic_item(&cx.tcx, sym::Result, did); + + // Don't lint: `is_diag_item` is an inherent method + struct DoesntImplMaybeDef; + impl DoesntImplMaybeDef { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool { + unimplemented!() + } + } + let d = DoesntImplMaybeDef; + let _ = d.is_diag_item(cx, sym::Option) || d.is_diag_item(cx, sym::Result); + + // Don't lint: `is_diag_item` comes from a trait other than `MaybeDef` + trait FakeMaybeDef { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool; + } + struct Bar; + impl FakeMaybeDef for Bar { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool { + unimplemented!() + } + } + let b = Bar; + let _ = b.is_diag_item(cx, sym::Option) || b.is_diag_item(cx, sym::Result); +} + +fn main() {} diff --git a/tests/ui-internal/repeated_is_diagnostic_item.rs b/tests/ui-internal/repeated_is_diagnostic_item.rs new file mode 100644 index 000000000000..7ccbbfd94029 --- /dev/null +++ b/tests/ui-internal/repeated_is_diagnostic_item.rs @@ -0,0 +1,77 @@ +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +use clippy_utils::res::MaybeDef; +use clippy_utils::sym; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt}; +use rustc_span::Symbol; + +fn binops(cx: &LateContext<'_>, ty: Ty<'_>, adt_def: &AdtDef<'_>) { + let did = ty.opt_def_id().unwrap(); + + let _ = ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result); + //~^ repeated_is_diagnostic_item + let _ = !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result); + //~^ repeated_is_diagnostic_item + let _ = adt_def.is_diag_item(cx, sym::Option) || adt_def.is_diag_item(cx, sym::Result); + //~^ repeated_is_diagnostic_item + let _ = !adt_def.is_diag_item(cx, sym::Option) && !adt_def.is_diag_item(cx, sym::Result); + //~^ repeated_is_diagnostic_item + let _ = cx.tcx.is_diagnostic_item(sym::Option, did) || cx.tcx.is_diagnostic_item(sym::Result, did); + //~^ repeated_is_diagnostic_item + let _ = !cx.tcx.is_diagnostic_item(sym::Option, did) && !cx.tcx.is_diagnostic_item(sym::Result, did); + //~^ repeated_is_diagnostic_item + + // Don't lint: `is_diagnostic_item` is called not on `TyCtxt` + struct FakeTyCtxt; + impl FakeTyCtxt { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool { + unimplemented!() + } + } + let f = FakeTyCtxt; + let _ = f.is_diagnostic_item(sym::Option, did) || f.is_diagnostic_item(sym::Result, did); + + // Don't lint: `is_diagnostic_item` on `TyCtxt` comes from a(n unrelated) trait + trait IsDiagnosticItem { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool; + } + impl IsDiagnosticItem for TyCtxt<'_> { + fn is_diagnostic_item(&self, sym: Symbol, did: DefId) -> bool { + unimplemented!() + } + } + let _ = IsDiagnosticItem::is_diagnostic_item(&cx.tcx, sym::Option, did) + || IsDiagnosticItem::is_diagnostic_item(&cx.tcx, sym::Result, did); + + // Don't lint: `is_diag_item` is an inherent method + struct DoesntImplMaybeDef; + impl DoesntImplMaybeDef { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool { + unimplemented!() + } + } + let d = DoesntImplMaybeDef; + let _ = d.is_diag_item(cx, sym::Option) || d.is_diag_item(cx, sym::Result); + + // Don't lint: `is_diag_item` comes from a trait other than `MaybeDef` + trait FakeMaybeDef { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool; + } + struct Bar; + impl FakeMaybeDef for Bar { + fn is_diag_item(&self, cx: &LateContext, sym: Symbol) -> bool { + unimplemented!() + } + } + let b = Bar; + let _ = b.is_diag_item(cx, sym::Option) || b.is_diag_item(cx, sym::Result); +} + +fn main() {} diff --git a/tests/ui-internal/repeated_is_diagnostic_item.stderr b/tests/ui-internal/repeated_is_diagnostic_item.stderr new file mode 100644 index 000000000000..8c52ba561d79 --- /dev/null +++ b/tests/ui-internal/repeated_is_diagnostic_item.stderr @@ -0,0 +1,82 @@ +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:18:13 + | +LL | let _ = ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results + = note: `-D clippy::repeated-is-diagnostic-item` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeated_is_diagnostic_item)]` +help: call `Ty::opt_diag_name`, and reuse the results + | +LL - let _ = ty.is_diag_item(cx, sym::Option) || ty.is_diag_item(cx, sym::Result); +LL + let _ = matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)); + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:20:13 + | +LL | let _ = !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL - let _ = !ty.is_diag_item(cx, sym::Option) && !ty.is_diag_item(cx, sym::Result); +LL + let _ = !matches!(ty.opt_diag_name(cx), Some(sym::Option | sym::Result)); + | + +error: repeated calls to `AdtDef::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:22:13 + | +LL | let _ = adt_def.is_diag_item(cx, sym::Option) || adt_def.is_diag_item(cx, sym::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `AdtDef::opt_diag_name`, and reuse the results + | +LL - let _ = adt_def.is_diag_item(cx, sym::Option) || adt_def.is_diag_item(cx, sym::Result); +LL + let _ = matches!(adt_def.opt_diag_name(cx), Some(sym::Option | sym::Result)); + | + +error: repeated calls to `AdtDef::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:24:13 + | +LL | let _ = !adt_def.is_diag_item(cx, sym::Option) && !adt_def.is_diag_item(cx, sym::Result); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `AdtDef::opt_diag_name`, and reuse the results + | +LL - let _ = !adt_def.is_diag_item(cx, sym::Option) && !adt_def.is_diag_item(cx, sym::Result); +LL + let _ = !matches!(adt_def.opt_diag_name(cx), Some(sym::Option | sym::Result)); + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:26:13 + | +LL | let _ = cx.tcx.is_diagnostic_item(sym::Option, did) || cx.tcx.is_diagnostic_item(sym::Result, did); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL - let _ = cx.tcx.is_diagnostic_item(sym::Option, did) || cx.tcx.is_diagnostic_item(sym::Result, did); +LL + let _ = matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Option | sym::Result)); + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item.rs:28:13 + | +LL | let _ = !cx.tcx.is_diagnostic_item(sym::Option, did) && !cx.tcx.is_diagnostic_item(sym::Result, did); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL - let _ = !cx.tcx.is_diagnostic_item(sym::Option, did) && !cx.tcx.is_diagnostic_item(sym::Result, did); +LL + let _ = !matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Option | sym::Result)); + | + +error: aborting due to 6 previous errors + diff --git a/tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs b/tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs new file mode 100644 index 000000000000..807da07ce8aa --- /dev/null +++ b/tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs @@ -0,0 +1,213 @@ +//@no-rustfix +#![feature(rustc_private)] + +extern crate rustc_hir; +extern crate rustc_lint; +extern crate rustc_middle; +extern crate rustc_span; + +use clippy_utils::res::MaybeDef; +use clippy_utils::sym; +use rustc_hir::def_id::DefId; +use rustc_lint::LateContext; +use rustc_middle::ty::{AdtDef, Ty, TyCtxt}; +use rustc_span::Symbol; + +fn main() {} + +// if-chains with repeated calls on the same `ty` +fn if_chains(cx: &LateContext<'_>, ty: Ty<'_>, adt_def: &AdtDef<'_>) { + let did = ty.opt_def_id().unwrap(); + + let _ = if ty.is_diag_item(cx, sym::Option) { + //~^ repeated_is_diagnostic_item + "Option" + } else if ty.is_diag_item(cx, sym::Result) { + "Result" + } else { + return; + }; + // should ideally suggest the following: + // let _ = match ty.opt_diag_name() { + // Some(sym::Option) => { + // "Option" + // } + // Some(sym::Result) => { + // "Result" + // } + // _ => { + // return; + // } + // }; + + // same but in a stmt + if ty.is_diag_item(cx, sym::Option) { + //~^ repeated_is_diagnostic_item + eprintln!("Option"); + } else if ty.is_diag_item(cx, sym::Result) { + eprintln!("Result"); + } + // should ideally suggest the following: + // match ty.opt_diag_name() { + // Some(sym::Option) => { + // "Option" + // } + // Some(sym::Result) => { + // "Result" + // } + // _ => {} + // }; + + // nested conditions + let _ = if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + //~^ repeated_is_diagnostic_item + "Option" + } else if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + "Result" + } else { + return; + }; + + let _ = if cx.tcx.is_diagnostic_item(sym::Option, did) { + //~^ repeated_is_diagnostic_item + "Option" + } else if cx.tcx.is_diagnostic_item(sym::Result, did) { + "Result" + } else { + return; + }; + // should ideally suggest the following: + // let _ = match cx.get_diagnostic_name(did) { + // Some(sym::Option) => { + // "Option" + // } + // Some(sym::Result) => { + // "Result" + // } + // _ => { + // return; + // } + // }; + + // same but in a stmt + if cx.tcx.is_diagnostic_item(sym::Option, did) { + //~^ repeated_is_diagnostic_item + eprintln!("Option"); + } else if cx.tcx.is_diagnostic_item(sym::Result, did) { + eprintln!("Result"); + } + // should ideally suggest the following: + // match cx.tcx.get_diagnostic_name(did) { + // Some(sym::Option) => { + // "Option" + // } + // Some(sym::Result) => { + // "Result" + // } + // _ => {} + // }; + + // nested conditions + let _ = if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + //~^ repeated_is_diagnostic_item + "Option" + } else if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + "Result" + } else { + return; + }; +} + +// if-chains with repeated calls on the same `ty` +fn consecutive_ifs(cx: &LateContext<'_>, ty: Ty<'_>, adt_def: &AdtDef<'_>) { + let did = ty.opt_def_id().unwrap(); + + { + if ty.is_diag_item(cx, sym::Option) { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if ty.is_diag_item(cx, sym::Result) { + println!("Result"); + } + println!("done!") + } + + // nested conditions + { + if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + println!("Result"); + } + println!("done!") + } + + { + if cx.tcx.is_diagnostic_item(sym::Option, did) { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if cx.tcx.is_diagnostic_item(sym::Result, did) { + println!("Result"); + } + println!("done!") + } + + // nested conditions + { + if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + println!("Result"); + } + println!("done!") + } + + // All the same, but the second if is the final expression + { + if ty.is_diag_item(cx, sym::Option) { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if ty.is_diag_item(cx, sym::Result) { + println!("Result"); + } + } + + // nested conditions + { + if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + println!("Result"); + } + } + + { + if cx.tcx.is_diagnostic_item(sym::Option, did) { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if cx.tcx.is_diagnostic_item(sym::Result, did) { + println!("Result"); + } + } + + // nested conditions + { + if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + //~^ repeated_is_diagnostic_item + println!("Option"); + } + if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + println!("Result"); + } + } +} diff --git a/tests/ui-internal/repeated_is_diagnostic_item_unfixable.stderr b/tests/ui-internal/repeated_is_diagnostic_item_unfixable.stderr new file mode 100644 index 000000000000..890817da5235 --- /dev/null +++ b/tests/ui-internal/repeated_is_diagnostic_item_unfixable.stderr @@ -0,0 +1,374 @@ +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:22:13 + | +LL | let _ = if ty.is_diag_item(cx, sym::Option) { + | ^ -------------------------------- called here + | _____________| + | | +LL | | +LL | | "Option" +LL | | } else if ty.is_diag_item(cx, sym::Result) { + | | -------------------------------- called here +... | +LL | | return; +LL | | }; + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results + = note: `-D clippy::repeated-is-diagnostic-item` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeated_is_diagnostic_item)]` +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ let _ = if /* name */ == Some(sym::Option) { +LL | +LL | "Option" +LL ~ } else if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:44:5 + | +LL | if ty.is_diag_item(cx, sym::Option) { + | ^ -------------------------------- called here + | _____| + | | +LL | | +LL | | eprintln!("Option"); +LL | | } else if ty.is_diag_item(cx, sym::Result) { + | | -------------------------------- called here +LL | | eprintln!("Result"); +LL | | } + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | eprintln!("Option"); +LL ~ } else if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:62:13 + | +LL | let _ = if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + | ^ -------------------------------- called here + | _____________| + | | +LL | | +LL | | "Option" +LL | | } else if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + | | -------------------------------- called here +... | +LL | | return; +LL | | }; + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ let _ = if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | "Option" +LL ~ } else if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:71:13 + | +LL | let _ = if cx.tcx.is_diagnostic_item(sym::Option, did) { + | ^ ------------------------------------------- called here + | _____________| + | | +LL | | +LL | | "Option" +LL | | } else if cx.tcx.is_diagnostic_item(sym::Result, did) { + | | ------------------------------------------- called here +... | +LL | | return; +LL | | }; + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ let _ = if /* name */ == Some(sym::Option) { +LL | +LL | "Option" +LL ~ } else if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:93:5 + | +LL | if cx.tcx.is_diagnostic_item(sym::Option, did) { + | ^ ------------------------------------------- called here + | _____| + | | +LL | | +LL | | eprintln!("Option"); +LL | | } else if cx.tcx.is_diagnostic_item(sym::Result, did) { + | | ------------------------------------------- called here +LL | | eprintln!("Result"); +LL | | } + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | eprintln!("Option"); +LL ~ } else if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:111:13 + | +LL | let _ = if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + | ^ ------------------------------------------- called here + | _____________| + | | +LL | | +LL | | "Option" +LL | | } else if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + | | ------------------------------------------- called here +... | +LL | | return; +LL | | }; + | |_____^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ let _ = if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | "Option" +LL ~ } else if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:126:9 + | +LL | if ty.is_diag_item(cx, sym::Option) { + | ^ -------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if ty.is_diag_item(cx, sym::Result) { + | | -------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:138:9 + | +LL | if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + | ^ -------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + | | -------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:149:9 + | +LL | if cx.tcx.is_diagnostic_item(sym::Option, did) { + | ^ ------------------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if cx.tcx.is_diagnostic_item(sym::Result, did) { + | | ------------------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:161:9 + | +LL | if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + | ^ ------------------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + | | ------------------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:173:9 + | +LL | if ty.is_diag_item(cx, sym::Option) { + | ^ -------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if ty.is_diag_item(cx, sym::Result) { + | | -------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `Ty::is_diag_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:184:9 + | +LL | if ty.is_diag_item(cx, sym::Option) && 4 == 5 { + | ^ -------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if ty.is_diag_item(cx, sym::Result) && 4 == 5 { + | | -------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `Ty::opt_diag_name`, and reuse the results + | +LL ~ let /* name */ = ty.opt_diag_name(cx); +LL ~ if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:194:9 + | +LL | if cx.tcx.is_diagnostic_item(sym::Option, did) { + | ^ ------------------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if cx.tcx.is_diagnostic_item(sym::Result, did) { + | | ------------------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ if /* name */ == Some(sym::Option) { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) { + | + +error: repeated calls to `TyCtxt::is_diagnostic_item` + --> tests/ui-internal/repeated_is_diagnostic_item_unfixable.rs:205:9 + | +LL | if cx.tcx.is_diagnostic_item(sym::Option, did) && 4 == 5 { + | ^ ------------------------------------------- called here + | _________| + | | +LL | | +LL | | println!("Option"); +LL | | } +LL | | if cx.tcx.is_diagnostic_item(sym::Result, did) && 4 == 5 { + | | ------------------------------------------- called here +LL | | println!("Result"); +LL | | } + | |_________^ + | + = note: each call performs the same compiler query -- it's faster to query once, and reuse the results +help: call `TyCtxt::get_diagnostic_name`, and reuse the results + | +LL ~ let /* name */ = cx.tcx.get_diagnostic_name(did); +LL ~ if /* name */ == Some(sym::Option) && 4 == 5 { +LL | +LL | println!("Option"); +LL | } +LL ~ if /* name */ == Some(sym::Result) && 4 == 5 { + | + +error: aborting due to 14 previous errors +