mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-27 07:03:45 +00:00
Auto merge of #6865 - Jarcho:explicit_deref_methods, r=llogiq
`explicit_deref_methods` improvements Breaking up #6837 changelog: `explicit_deref_methods` will lint chained `deref` calls and ufcs style calls
This commit is contained in:
commit
28759b242d
@ -1,11 +1,11 @@
|
||||
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_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, 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, 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 +34,265 @@ 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<HirId>,
|
||||
}
|
||||
|
||||
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(Mutability),
|
||||
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;
|
||||
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
|
||||
if Some(expr.hir_id) == self.skip_expr.take() {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let name = method_name.ident.as_str();
|
||||
lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
|
||||
// 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);
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
|
||||
match kind {
|
||||
RefOp::Method(target_mut)
|
||||
if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
|
||||
&& is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
|
||||
{
|
||||
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,
|
||||
},
|
||||
));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
(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));
|
||||
},
|
||||
ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
|
||||
_ => return None,
|
||||
};
|
||||
if tcx.is_diagnostic_item(sym::deref_method, def_id) {
|
||||
Some((RefOp::Method(Mutability::Not), arg))
|
||||
} else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
|
||||
Some((RefOp::Method(Mutability::Mut), arg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// 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<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
|
||||
let parent = match parent {
|
||||
Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => 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,
|
||||
match data.target_mut {
|
||||
Mutability::Not => "explicit `deref` method call",
|
||||
Mutability::Mut => "explicit `deref_mut` method call",
|
||||
},
|
||||
"try this",
|
||||
format!("{}{}{}", addr_of_str, deref_str, expr_str),
|
||||
app,
|
||||
);
|
||||
},
|
||||
"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,
|
||||
);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -1241,7 +1241,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);
|
||||
|
@ -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`.
|
||||
|
@ -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, 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, 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;
|
||||
@ -852,26 +852,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
|
||||
@ -1013,21 +1018,52 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
/// Gets the span of the node, if there is one.
|
||||
pub fn get_node_span(node: Node<'_>) -> Option<Span> {
|
||||
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<Node<'_>> {
|
||||
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;
|
||||
match get_parent_node(cx.tcx, e.hir_id) {
|
||||
Some(Node::Expr(parent)) => Some(parent),
|
||||
_ => None,
|
||||
}
|
||||
map.find(parent_id).and_then(|node| {
|
||||
if let Node::Expr(parent) = node {
|
||||
Some(parent)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
|
||||
|
@ -1,70 +0,0 @@
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.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
|
||||
|
|
||||
LL | let b: &mut str = a.deref_mut();
|
||||
| ^^^^^^^^^^^^^ help: try this: `&mut *a`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.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
|
||||
|
|
||||
LL | let b: String = format!("{}, {}", a.deref(), a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:37:20
|
||||
|
|
||||
LL | println!("{}", a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:40:11
|
||||
|
|
||||
LL | match a.deref() {
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:44:28
|
||||
|
|
||||
LL | let b: String = concat(a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:46:13
|
||||
|
|
||||
LL | let b = just_return(a).deref();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:48:28
|
||||
|
|
||||
LL | let b: String = concat(just_return(a).deref());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:50:19
|
||||
|
|
||||
LL | let b: &str = a.deref().deref();
|
||||
| ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()`
|
||||
|
||||
error: explicit deref method call
|
||||
--> $DIR/dereference.rs:53:13
|
||||
|
|
||||
LL | let b = opt_a.unwrap().deref();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
@ -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();
|
||||
@ -76,6 +76,8 @@ fn main() {
|
||||
}
|
||||
let b: &str = expr_deref!(a);
|
||||
|
||||
let b: &str = expr_deref!(&*a);
|
||||
|
||||
// The struct does not implement Deref trait
|
||||
#[derive(Copy, Clone)]
|
||||
struct NoLint(u32);
|
@ -76,6 +76,8 @@ fn main() {
|
||||
}
|
||||
let b: &str = expr_deref!(a);
|
||||
|
||||
let b: &str = expr_deref!(a.deref());
|
||||
|
||||
// The struct does not implement Deref trait
|
||||
#[derive(Copy, Clone)]
|
||||
struct NoLint(u32);
|
76
tests/ui/explicit_deref_methods.stderr
Normal file
76
tests/ui/explicit_deref_methods.stderr
Normal file
@ -0,0 +1,76 @@
|
||||
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/explicit_deref_methods.rs:32:23
|
||||
|
|
||||
LL | let b: &mut str = a.deref_mut();
|
||||
| ^^^^^^^^^^^^^ help: try this: `&mut **a`
|
||||
|
||||
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/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/explicit_deref_methods.rs:37:20
|
||||
|
|
||||
LL | println!("{}", a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
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/explicit_deref_methods.rs:44:28
|
||||
|
|
||||
LL | let b: String = concat(a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
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)`
|
||||
|
||||
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)`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> $DIR/explicit_deref_methods.rs:50:19
|
||||
|
|
||||
LL | let b: &str = a.deref().deref();
|
||||
| ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
|
||||
|
||||
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()`
|
||||
|
||||
error: explicit `deref` method call
|
||||
--> $DIR/explicit_deref_methods.rs:79:31
|
||||
|
|
||||
LL | let b: &str = expr_deref!(a.deref());
|
||||
| ^^^^^^^^^ help: try this: `&*a`
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user