diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index b5fb51af1c7f..a33634ca34ee 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,11 +1,13 @@ -use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg}; -use if_chain::if_chain; -use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX}; +use crate::utils::{ + get_node_span, get_parent_node, in_macro, is_allowed, peel_mid_ty_refs, snippet_with_context, span_lint_and_sugg, +}; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind}; +use rustc_hir::{BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; +use rustc_middle::ty::{self, adjustment::Adjustment, Ty, TyCtxt, TyS, TypeckResults}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{symbol::sym, Span}; declare_clippy_lint! { /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls. @@ -34,76 +36,313 @@ declare_clippy_lint! { "Explicit use of deref or deref_mut method while not in a method chain." } -declare_lint_pass!(Dereferencing => [ - EXPLICIT_DEREF_METHODS +impl_lint_pass!(Dereferencing => [ + EXPLICIT_DEREF_METHODS, ]); +#[derive(Default)] +pub struct Dereferencing { + state: Option<(State, StateData)>, + + // While parsing a `deref` method call in ufcs form, the path to the function is itself an + // expression. This is to store the id of that expression so it can be skipped when + // `check_expr` is called for it. + skip_expr: Option, +} + +struct StateData { + /// Span of the top level expression + span: Span, + /// The required mutability + target_mut: Mutability, +} + +enum State { + // Any number of deref method calls. + DerefMethod { + // The number of calls in a sequence which changed the referenced type + ty_changed_count: usize, + is_final_ufcs: bool, + }, +} + +// A reference operation considered by this lint pass +enum RefOp { + Method, + Deref, + AddrOf, +} + impl<'tcx> LateLintPass<'tcx> for Dereferencing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if !expr.span.from_expansion(); - if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind; - if args.len() == 1; - - then { - if let Some(parent_expr) = get_parent_expr(cx, expr) { - // Check if we have the whole call chain here - if let ExprKind::MethodCall(..) = parent_expr.kind { - return; - } - // Check for Expr that we don't want to be linted - let precedence = parent_expr.precedence(); - match precedence { - // Lint a Call is ok though - ExprPrecedence::Call | ExprPrecedence::AddrOf => (), - _ => { - if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX { - return; - } - } + // Skip path expressions from deref calls. e.g. `Deref::deref(e)` + if Some(expr.hir_id) == self.skip_expr.take() { + return; + } + + // Stop processing sub expressions when a macro call is seen + if in_macro(expr.span) { + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + } + + let typeck = cx.typeck_results(); + let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) { + x + } else { + // The whole chain of reference operations has been seen + if let Some((state, data)) = self.state.take() { + report(cx, expr, state, data); + } + return; + }; + + match (self.state.take(), kind) { + (None, kind) => { + let parent = get_parent_node(cx.tcx, expr.hir_id); + // This is an odd case. The expression is a macro argument, but the top level + // address of expression is inserted by the compiler. + if matches!(kind, RefOp::AddrOf) && parent.and_then(get_node_span).map_or(false, in_macro) { + return; + } + + let expr_adjustments = find_adjustments(cx.tcx, typeck, expr); + let expr_ty = typeck.expr_ty(expr); + let target_mut = + if let ty::Ref(_, _, mutability) = *expr_adjustments.last().map_or(expr_ty, |a| a.target).kind() { + mutability + } else { + Mutability::Not + }; + + match kind { + RefOp::Method + if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) + && is_linted_explicit_deref_position(parent, expr.hir_id) => + { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) { + 0 + } else { + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + StateData { + span: expr.span, + target_mut, + }, + )); } + _ => (), } - let name = method_name.ident.as_str(); - lint_deref(cx, &*name, &args[0], args[0].span, expr.span); - } + }, + (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method) => { + self.state = Some(( + State::DerefMethod { + ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) { + ty_changed_count + } else { + ty_changed_count + 1 + }, + is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)), + }, + data, + )); + }, + + (Some((state, data)), _) => report(cx, expr, state, data), } } } -fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) { - match method_name { - "deref" => { - let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref method call", - "try this", - format!("&*{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } +fn try_parse_ref_op( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> Option<(RefOp, &'tcx Expr<'tcx>)> { + let (def_id, arg) = match expr.kind { + ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg), + ExprKind::Call( + Expr { + kind: ExprKind::Path(path), + hir_id, + .. + }, + [arg], + ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg), + ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => { + return Some((RefOp::Deref, sub_expr)); }, - "deref_mut" => { - let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| { - implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[]) - }); - if impls_deref_mut_trait { - span_lint_and_sugg( - cx, - EXPLICIT_DEREF_METHODS, - expr_span, - "explicit deref_mut method call", - "try this", - format!("&mut *{}", &snippet(cx, var_span, "..")), - Applicability::MachineApplicable, - ); - } + ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)), + _ => return None, + }; + (tcx.is_diagnostic_item(sym::deref_method, def_id) + || tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()?) + .then(|| (RefOp::Method, arg)) +} + +// Checks whether the type for a deref call actually changed the type, not just the mutability of +// the reference. +fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { + match (result_ty.kind(), arg_ty.kind()) { + (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), + + // The result type for a deref method is always a reference + // Not matching the previous pattern means the argument type is not a reference + // This means that the type did change + _ => false, + } +} + +// Adjustments are sometimes made in the parent block rather than the expression itself. +fn find_adjustments( + tcx: TyCtxt<'tcx>, + typeck: &'tcx TypeckResults<'_>, + expr: &'tcx Expr<'_>, +) -> &'tcx [Adjustment<'tcx>] { + let map = tcx.hir(); + let mut iter = map.parent_iter(expr.hir_id); + let mut prev = expr; + + loop { + match typeck.expr_adjustments(prev) { + [] => (), + a => break a, + }; + + match iter.next().map(|(_, x)| x) { + Some(Node::Block(_)) => { + if let Some((_, Node::Expr(e))) = iter.next() { + prev = e; + } else { + // This shouldn't happen. Blocks are always contained in an expression. + break &[]; + } + }, + Some(Node::Expr(&Expr { + kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _), + .. + })) => { + if let Some(Node::Expr(e)) = map.find(id) { + prev = e; + iter = map.parent_iter(id); + continue; + } + // This shouldn't happen. The destination should definitely exist at this point. + break &[]; + }, + _ => break &[], + } + } +} + +// Checks whether the parent node is a suitable context for switching from a deref method to the +// deref operator. +fn is_linted_explicit_deref_position(parent: Option>, child_id: HirId) -> bool { + let parent = match parent { + Some(Node::Expr(e)) => e, + _ => return true, + }; + match parent.kind { + // Leave deref calls in the middle of a method chain. + // e.g. x.deref().foo() + ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false, + + // Leave deref calls resulting in a called function + // e.g. (x.deref())() + ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false, + + // Makes an ugly suggestion + // e.g. *x.deref() => *&*x + ExprKind::Unary(UnOp::Deref, _) + // Postfix expressions would require parens + | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) + | ExprKind::Field(..) + | ExprKind::Index(..) + | ExprKind::Err => false, + + ExprKind::Box(..) + | ExprKind::ConstBlock(..) + | ExprKind::Array(_) + | ExprKind::Call(..) + | ExprKind::MethodCall(..) + | ExprKind::Tup(..) + | ExprKind::Binary(..) + | ExprKind::Unary(..) + | ExprKind::Lit(..) + | ExprKind::Cast(..) + | ExprKind::Type(..) + | ExprKind::DropTemps(..) + | ExprKind::If(..) + | ExprKind::Loop(..) + | ExprKind::Match(..) + | ExprKind::Closure(..) + | ExprKind::Block(..) + | ExprKind::Assign(..) + | ExprKind::AssignOp(..) + | ExprKind::Path(..) + | ExprKind::AddrOf(..) + | ExprKind::Break(..) + | ExprKind::Continue(..) + | ExprKind::Ret(..) + | ExprKind::InlineAsm(..) + | ExprKind::LlvmInlineAsm(..) + | ExprKind::Struct(..) + | ExprKind::Repeat(..) + | ExprKind::Yield(..) => true, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { + match state { + State::DerefMethod { + ty_changed_count, + is_final_ufcs, + } => { + let mut app = Applicability::MachineApplicable; + let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); + let ty = cx.typeck_results().expr_ty(expr); + let (_, ref_count) = peel_mid_ty_refs(ty); + let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { + // a deref call changing &T -> &U requires two deref operators the first time + // this occurs. One to remove the reference, a second to call the deref impl. + "*".repeat(ty_changed_count + 1) + } else { + "*".repeat(ty_changed_count) + }; + let addr_of_str = if ty_changed_count < ref_count { + // Check if a reborrow from &mut T -> &T is required. + if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) { + "&*" + } else { + "" + } + } else if data.target_mut == Mutability::Mut { + "&mut " + } else { + "&" + }; + + let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX { + format!("({})", expr_str) + } else { + expr_str.into_owned() + }; + + span_lint_and_sugg( + cx, + EXPLICIT_DEREF_METHODS, + data.span, + "explicit `deref` method call", + "try this", + format!("{}{}{}", addr_of_str, deref_str, expr_str), + app, + ); }, - _ => (), } } diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 8259fd3c320b..319b6c3773be 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -1242,7 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); store.register_late_pass(|| box unnamed_address::UnnamedAddress); - store.register_late_pass(|| box dereference::Dereferencing); + store.register_late_pass(|| box dereference::Dereferencing::default()); store.register_late_pass(|| box option_if_let_else::OptionIfLetElse); store.register_late_pass(|| box future_not_send::FutureNotSend); store.register_late_pass(|| box if_let_mutex::IfLetMutex); diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index ac1d51e1993b..ea4cedc67545 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -129,7 +129,7 @@ impl LateLintPass<'_> for ManualMap { // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or // it's being passed by value. let scrutinee = peel_hir_expr_refs(scrutinee).0; - let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); + let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app); let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX { format!("({})", scrutinee_str) @@ -160,7 +160,7 @@ impl LateLintPass<'_> for ManualMap { "|{}{}| {}", annotation, some_binding, - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) }, } @@ -168,8 +168,8 @@ impl LateLintPass<'_> for ManualMap { // TODO: handle explicit reference annotations. format!( "|{}| {}", - snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app), - snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app) + snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0, + snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0 ) } else { // Refutable bindings and mixed reference annotations can't be handled by `map`. diff --git a/clippy_lints/src/redundant_deref.rs b/clippy_lints/src/redundant_deref.rs new file mode 100644 index 000000000000..029583720d2f --- /dev/null +++ b/clippy_lints/src/redundant_deref.rs @@ -0,0 +1,63 @@ +// use crate::utils::{get_parent_expr, snippet_with_applicability, span_lint_and_sugg}; +// use if_chain::if_chain; +// use rustc_errors::Applicability; +// use rustc_hir::{Expr, ExprKind, UnOp}; +// use rustc_lint::{LateContext, LateLintPass, LintContext}; +// use rustc_middle::lint::in_external_macro; +// use rustc_session::{declare_lint_pass, declare_tool_lint}; + +// declare_clippy_lint! { +// /// **What it does:** Checks for uses of the dereference operator which would be covered by +// /// auto-dereferencing. +// /// +// /// **Why is this bad?** This unnecessarily complicates the code. +// /// +// /// **Known problems:** None. +// /// +// /// **Example:** +// /// +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&*String::new()) +// /// ``` +// /// Use instead: +// /// ```rust +// /// fn foo(_: &str) {} +// /// foo(&String::new()) +// /// ``` +// pub REDUNDANT_DEREF, +// style, +// "default lint description" +// } + +// declare_lint_pass!(RedundantDeref => [REDUNDANT_DEREF]); + +// impl LateLintPass<'_> for RedundantDeref { +// fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +// if_chain! { +// if let ExprKind::AddrOf(_, _, addr_expr) = expr.kind; +// if let ExprKind::Unary(UnOp::UnDeref, deref_expr) = addr_expr.kind; +// if !in_external_macro(cx.sess(), expr.span); +// if let Some(parent_expr) = get_parent_expr(cx, expr); +// if match parent_expr.kind { +// ExprKind::Call(func, _) => func.hir_id != expr.hir_id, +// ExprKind::MethodCall(..) => true, +// _ => false, +// }; +// if !cx.typeck_results().expr_ty(deref_expr).is_unsafe_ptr(); +// then { +// let mut app = Applicability::MachineApplicable; +// let sugg = format!("&{}", snippet_with_applicability(cx, deref_expr.span, "_", &mut app)); +// span_lint_and_sugg( +// cx, +// REDUNDANT_DEREF, +// expr.span, +// "redundant dereference", +// "remove the dereference", +// sugg, +// app, +// ); +// } +// } +// } +// } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 3845667802d8..2e7abe035d62 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -61,11 +61,11 @@ use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::Node; use rustc_hir::{ - def, Arm, Block, Body, Constness, Crate, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, - Item, ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, - TraitRef, TyKind, Unsafety, + def, Arm, Block, Body, Constness, Crate, CrateItem, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs, GenericParam, + HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local, MacroDef, MatchSource, Node, Param, + Pat, PatKind, Path, PathSegment, QPath, Stmt, StructField, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety, + Variant, Visibility, }; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -78,7 +78,7 @@ use rustc_session::Session; use rustc_span::hygiene::{self, ExpnKind, MacroKind}; use rustc_span::source_map::original_sp; use rustc_span::sym; -use rustc_span::symbol::{kw, Symbol}; +use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP}; use rustc_target::abi::Integer; use rustc_trait_selection::traits::query::normalize::AtExt; @@ -834,26 +834,31 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>( /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node /// would result in `box []`. If given the context of the address of expression, this function will /// correctly get a snippet of `vec![]`. +/// +/// This will also return whether or not the snippet is a macro call. pub fn snippet_with_context( cx: &LateContext<'_>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, -) -> Cow<'a, str> { +) -> (Cow<'a, str>, bool) { let outer_span = hygiene::walk_chain(span, outer); - let span = if outer_span.ctxt() == outer { - outer_span + let (span, is_macro_call) = if outer_span.ctxt() == outer { + (outer_span, span.ctxt() != outer) } else { // The span is from a macro argument, and the outer context is the macro using the argument if *applicability != Applicability::Unspecified { *applicability = Applicability::MaybeIncorrect; } // TODO: get the argument span. - span + (span, false) }; - snippet_with_applicability(cx, span, default, applicability) + ( + snippet_with_applicability(cx, span, default, applicability), + is_macro_call, + ) } /// Returns a new Span that extends the original Span to the first non-whitespace char of the first @@ -995,21 +1000,52 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, .join("\n") } +/// Gets the span of the node, if there is one. +pub fn get_node_span(node: Node<'_>) -> Option { + match node { + Node::Param(Param { span, .. }) + | Node::Item(Item { span, .. }) + | Node::ForeignItem(ForeignItem { span, .. }) + | Node::TraitItem(TraitItem { span, .. }) + | Node::ImplItem(ImplItem { span, .. }) + | Node::Variant(Variant { span, .. }) + | Node::Field(StructField { span, .. }) + | Node::Expr(Expr { span, .. }) + | Node::Stmt(Stmt { span, .. }) + | Node::PathSegment(PathSegment { + ident: Ident { span, .. }, + .. + }) + | Node::Ty(hir::Ty { span, .. }) + | Node::TraitRef(TraitRef { + path: Path { span, .. }, + .. + }) + | Node::Binding(Pat { span, .. }) + | Node::Pat(Pat { span, .. }) + | Node::Arm(Arm { span, .. }) + | Node::Block(Block { span, .. }) + | Node::Local(Local { span, .. }) + | Node::MacroDef(MacroDef { span, .. }) + | Node::Lifetime(Lifetime { span, .. }) + | Node::GenericParam(GenericParam { span, .. }) + | Node::Visibility(Visibility { span, .. }) + | Node::Crate(CrateItem { span, .. }) => Some(*span), + Node::Ctor(_) | Node::AnonConst(_) => None, + } +} + +/// Gets the parent node, if any. +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option> { + tcx.hir().parent_iter(id).next().map(|(_, node)| node) +} + /// Gets the parent expression, if any –- this is useful to constrain a lint. pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> { - let map = &cx.tcx.hir(); - let hir_id = e.hir_id; - let parent_id = map.get_parent_node(hir_id); - if hir_id == parent_id { - return None; - } - map.find(parent_id).and_then(|node| { - if let Node::Expr(parent) = node { - Some(parent) - } else { - None - } - }) + match get_parent_node(cx.tcx, e.hir_id) { + Some(Node::Expr(parent)) => Some(parent), + _ => None, + } } pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> { diff --git a/tests/ui/dereference.fixed b/tests/ui/explicit_deref_methods.fixed similarity index 92% rename from tests/ui/dereference.fixed rename to tests/ui/explicit_deref_methods.fixed index 459ca91b93b9..8155fdf99503 100644 --- a/tests/ui/dereference.fixed +++ b/tests/ui/explicit_deref_methods.fixed @@ -29,7 +29,7 @@ fn main() { let b: &str = &*a; - let b: &mut str = &mut *a; + let b: &mut str = &mut **a; // both derefs should get linted here let b: String = format!("{}, {}", &*a, &*a); @@ -43,11 +43,11 @@ fn main() { let b: String = concat(&*a); - let b = &*just_return(a); + let b = just_return(a); - let b: String = concat(&*just_return(a)); + let b: String = concat(just_return(a)); - let b: &str = &*a.deref(); + let b: &str = &**a; let opt_a = Some(a.clone()); let b = &*opt_a.unwrap(); diff --git a/tests/ui/dereference.rs b/tests/ui/explicit_deref_methods.rs similarity index 100% rename from tests/ui/dereference.rs rename to tests/ui/explicit_deref_methods.rs diff --git a/tests/ui/dereference.stderr b/tests/ui/explicit_deref_methods.stderr similarity index 55% rename from tests/ui/dereference.stderr rename to tests/ui/explicit_deref_methods.stderr index d26b462a4336..35db6ef0905d 100644 --- a/tests/ui/dereference.stderr +++ b/tests/ui/explicit_deref_methods.stderr @@ -1,67 +1,67 @@ -error: explicit deref method call - --> $DIR/dereference.rs:30:19 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:30:19 | LL | let b: &str = a.deref(); | ^^^^^^^^^ help: try this: `&*a` | = note: `-D clippy::explicit-deref-methods` implied by `-D warnings` -error: explicit deref_mut method call - --> $DIR/dereference.rs:32:23 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:32:23 | LL | let b: &mut str = a.deref_mut(); - | ^^^^^^^^^^^^^ help: try this: `&mut *a` + | ^^^^^^^^^^^^^ help: try this: `&mut **a` -error: explicit deref method call - --> $DIR/dereference.rs:35:39 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:39 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` -error: explicit deref method call - --> $DIR/dereference.rs:35:50 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:35:50 | LL | let b: String = format!("{}, {}", a.deref(), a.deref()); | ^^^^^^^^^ help: try this: `&*a` -error: explicit deref method call - --> $DIR/dereference.rs:37:20 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:37:20 | LL | println!("{}", a.deref()); | ^^^^^^^^^ help: try this: `&*a` -error: explicit deref method call - --> $DIR/dereference.rs:40:11 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:40:11 | LL | match a.deref() { | ^^^^^^^^^ help: try this: `&*a` -error: explicit deref method call - --> $DIR/dereference.rs:44:28 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:44:28 | LL | let b: String = concat(a.deref()); | ^^^^^^^^^ help: try this: `&*a` -error: explicit deref method call - --> $DIR/dereference.rs:46:13 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:46:13 | LL | let b = just_return(a).deref(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` -error: explicit deref method call - --> $DIR/dereference.rs:48:28 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:48:28 | LL | let b: String = concat(just_return(a).deref()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)` -error: explicit deref method call - --> $DIR/dereference.rs:50:19 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:50:19 | LL | let b: &str = a.deref().deref(); - | ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()` + | ^^^^^^^^^^^^^^^^^ help: try this: `&**a` -error: explicit deref method call - --> $DIR/dereference.rs:53:13 +error: explicit `deref` method call + --> $DIR/explicit_deref_methods.rs:53:13 | LL | let b = opt_a.unwrap().deref(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`