Skip to content

Commit

Permalink
Provide different suggestions for constructors.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jarcho committed Feb 19, 2022
1 parent 2b45008 commit af141d2
Show file tree
Hide file tree
Showing 15 changed files with 116 additions and 85 deletions.
58 changes: 22 additions & 36 deletions clippy_lints/src/drop_forget_ref.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::is_diagnostic_item;
use clippy_utils::ty::is_copy;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
Expand Down Expand Up @@ -118,49 +117,36 @@ declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COP
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Call(path, args) = expr.kind;
if let ExprKind::Call(path, [arg]) = expr.kind;
if let ExprKind::Path(ref qpath) = path.kind;
if args.len() == 1;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
then {
let lint;
let msg;
let arg = &args[0];
let arg_ty = cx.typeck_results().expr_ty(arg);

if let ty::Ref(..) = arg_ty.kind() {
if is_diagnostic_item(cx, def_id, sym::mem_drop) {
lint = DROP_REF;
msg = DROP_REF_SUMMARY.to_string();
} else if is_diagnostic_item(cx, def_id, sym::mem_forget) {
lint = FORGET_REF;
msg = FORGET_REF_SUMMARY.to_string();
} else {
return;
let (lint, msg) = if let ty::Ref(..) = arg_ty.kind() {
match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::mem_drop) => (DROP_REF, DROP_REF_SUMMARY),
Some(sym::mem_forget) => (FORGET_REF, FORGET_REF_SUMMARY),
_ => return,
}
span_lint_and_note(cx,
lint,
expr.span,
&msg,
Some(arg.span),
&format!("argument has type `{}`", arg_ty));
} else if is_copy(cx, arg_ty) {
if is_diagnostic_item(cx, def_id, sym::mem_drop) {
lint = DROP_COPY;
msg = DROP_COPY_SUMMARY.to_string();
} else if is_diagnostic_item(cx, def_id, sym::mem_forget) {
lint = FORGET_COPY;
msg = FORGET_COPY_SUMMARY.to_string();
} else {
return;
match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::mem_drop) => (DROP_COPY, DROP_COPY_SUMMARY),
Some(sym::mem_forget) => (FORGET_COPY, FORGET_COPY_SUMMARY),
_ => return,
}
span_lint_and_note(cx,
lint,
expr.span,
&msg,
Some(arg.span),
&format!("argument has type {}", arg_ty));
}
} else {
return;
};

span_lint_and_note(
cx,
lint,
expr.span,
msg,
Some(arg.span),
&format!("argument has type `{}`", arg_ty)
);
}
}
}
Expand Down
15 changes: 11 additions & 4 deletions clippy_lints/src/infinite_iter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::higher;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{higher, path_def_id};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
Expand Down Expand Up @@ -167,9 +167,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
},
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
ExprKind::Call(path, _) => path_def_id(cx, path)
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
.into(),
ExprKind::Call(path, _) => {
if let ExprKind::Path(ref qpath) = path.kind {
cx.qpath_res(qpath, path.hir_id)
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
.into()
} else {
Finite
}
},
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite,
}
Expand Down
9 changes: 4 additions & 5 deletions clippy_lints/src/loops/while_let_on_iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_loop_or_closure, is_lang_item, is_refutable, is_trait_method, visitors::is_res_used};
use clippy_utils::{get_enclosing_loop_or_closure, is_lang_ctor, is_refutable, is_trait_method, visitors::is_res_used};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, QPath, UnOp};
use rustc_hir::{def::Res, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
use rustc_lint::LateContext;
use rustc_middle::ty::adjustment::Adjust;
use rustc_span::{symbol::sym, Symbol};
Expand All @@ -15,9 +15,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
// check for `Some(..)` pattern
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
if let Res::Def(_, pat_did) = pat_path.res;
if is_lang_item(cx, pat_did, LangItem::OptionSome);
if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind;
if is_lang_ctor(cx, pat_path, LangItem::OptionSome);
// check for call to `Iterator::next`
if let ExprKind::MethodCall(method_name, [iter_expr], _) = let_expr.kind;
if method_name.ident.name == sym::next;
Expand Down
3 changes: 1 addition & 2 deletions clippy_lints/src/mem_forget.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_diagnostic_item;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
Expand Down Expand Up @@ -33,7 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for MemForget {
if let ExprKind::Call(path_expr, [ref first_arg, ..]) = e.kind {
if let ExprKind::Path(ref qpath) = path_expr.kind {
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() {
if is_diagnostic_item(cx, def_id, sym::mem_forget) {
if cx.tcx.is_diagnostic_item(sym::mem_forget, def_id) {
let forgot_ty = cx.typeck_results().expr_ty(first_arg);

if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/mem_replace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_non_aggregate_primitive_type;
use clippy_utils::{is_default_equivalent, is_diagnostic_item, is_lang_ctor, meets_msrv, msrvs};
use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
Expand Down Expand Up @@ -249,7 +249,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace {
if let ExprKind::Call(func, func_args) = expr.kind;
if let ExprKind::Path(ref func_qpath) = func.kind;
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
if is_diagnostic_item(cx, def_id, sym::mem_replace);
if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
if let [dest, src] = func_args;
then {
check_replace_option_with_none(cx, src, dest, expr.span);
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/methods/inefficient_to_string.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
use clippy_utils::{is_diagnostic_item, match_def_path, paths};
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
Expand Down Expand Up @@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
}

if let ty::Adt(adt, substs) = ty.kind() {
is_diagnostic_item(cx, adt.did, sym::Cow) && substs.type_at(1).is_str()
cx.tcx.is_diagnostic_item(sym::Cow, adt.did) && substs.type_at(1).is_str()
} else {
false
}
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/redundant_clone.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
use clippy_utils::{fn_has_unsatisfiable_preds, is_lang_item, match_def_path, paths};
use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
use rustc_errors::Applicability;
Expand Down Expand Up @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
}

if let ty::Adt(def, _) = arg_ty.kind() {
if is_lang_item(cx, def.did, LangItem::ManuallyDrop) {
if cx.tcx.lang_items().require(LangItem::ManuallyDrop).ok() == Some(def.did) {
continue;
}
}
Expand Down
5 changes: 2 additions & 3 deletions clippy_lints/src/size_of_in_element_count.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! expecting a count of T

use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{is_diagnostic_item, match_def_path, paths};
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_hir::BinOpKind;
use rustc_hir::{Expr, ExprKind};
Expand Down Expand Up @@ -45,8 +45,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted:
if !inverted;
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if is_diagnostic_item(cx, def_id, sym::mem_size_of)
|| is_diagnostic_item(cx, def_id, sym::mem_size_of_val);
if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val));
then {
cx.typeck_results().node_substs(count_func.hir_id).types().next()
} else {
Expand Down
4 changes: 2 additions & 2 deletions clippy_lints/src/useless_conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
use clippy_utils::{get_parent_expr, is_lang_item, is_trait_method, match_def_path, paths};
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource};
Expand Down Expand Up @@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
}

if_chain! {
if is_lang_item(cx, def_id, LangItem::FromFrom);
if cx.tcx.lang_items().require(LangItem::FromFrom).ok() == Some(def_id);
if same_type_and_consts(a, b);

then {
Expand Down
37 changes: 33 additions & 4 deletions clippy_lints/src/utils/internal_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use rustc_hir::{
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
use rustc_middle::ty::{self, AssocKind, Ty};
use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty};
use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
Expand Down Expand Up @@ -905,15 +905,32 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
return;
};

let has_ctor = match cx.tcx.def_kind(def_id) {
DefKind::Struct => {
let variant = cx.tcx.adt_def(def_id).non_enum_variant();
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
}
DefKind::Variant =>
cx.tcx.parent(def_id).map_or(false, |adt_id| {
let variant = cx.tcx.adt_def(adt_id).variant_with_id(def_id);
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
}),
_ => false,
};

let mut app = Applicability::MachineApplicable;
let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
let sugg = match (which_path, item) {
// match_def_path
(0, Item::DiagnosticItem(item)) if has_ctor =>
format!("is_diagnostic_item_or_ctor({}, {}, sym::{})", cx_snip, def_snip, item),
(0, Item::LangItem(item)) if has_ctor =>
format!("is_lang_item_or_ctor({}, {}, LangItem::{})", cx_snip, def_snip, item),
(0, Item::DiagnosticItem(item)) =>
format!("is_diagnostic_item({}, {}, sym::{})", cx_snip, def_snip, item),
format!("{}.tcx.is_diagnostic_item(sym::{}, {})", cx_snip, item, def_snip),
(0, Item::LangItem(item)) =>
format!("is_lang_item({}, {}, LangItem::{})", cx_snip, def_snip, item),
format!("{}.tcx.lang_items().require(LangItem::{}).ok() == Some({})", cx_snip, item, def_snip),
// match_trait_method
(1, Item::DiagnosticItem(item)) =>
format!("is_trait_method({}, {}, sym::{})", cx_snip, def_snip, item),
Expand All @@ -923,12 +940,24 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
(2, Item::LangItem(item)) =>
format!("is_type_lang_item({}, {}, LangItem::{})", cx_snip, def_snip, item),
// is_expr_path_def_path
(3, Item::DiagnosticItem(item)) if has_ctor =>
format!(
"expr_path_res({}, {}).opt_def_id()\
.map_or(false, |id| is_diagnostic_item_or_ctor({}, id, sym::{})",
cx_snip, def_snip, cx_snip, item,
),
(3, Item::LangItem(item)) if has_ctor =>
format!(
"expr_path_res({}, {}).opt_def_id()\
.map_or(false, |id| is_lang_item_or_ctor({}, id, LangItem::{})",
cx_snip, def_snip, cx_snip, item,
),
(3, Item::DiagnosticItem(item)) =>
format!("is_expr_diagnostic_item({}, {}, sym::{})", cx_snip, def_snip, item),
(3, Item::LangItem(item)) =>
format!(
"path_res({}, {}).opt_def_id()\
.map_or(false, |id| is_lang_item({}, id, LangItem::{}))",
.map_or(false, |id| {}.tcx.lang_items().require(LangItem::{}).ok() == Some(id))",
cx_snip, def_snip, cx_snip, item,
),
_ => return,
Expand Down
22 changes: 17 additions & 5 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,20 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem
false
}

/// Checks if the `DefId` matches the given diagnostic item.
pub fn is_diagnostic_item(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
/// Checks if a `QPath` resolves to a constructor of a diagnostic item.
pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
if let QPath::Resolved(_, path) = qpath {
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
if let Some(variant_id) = cx.tcx.parent(ctor_id) {
return cx.tcx.is_diagnostic_item(diagnostic_item, variant_id);
}
}
}
false
}

/// Checks if the `DefId` matches the given diagnostic item or it's constructor.
pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
let did = match cx.tcx.def_kind(did) {
DefKind::Ctor(..) => match cx.tcx.parent(did) {
Some(did) => did,
Expand All @@ -255,8 +267,8 @@ pub fn is_diagnostic_item(cx: &LateContext<'_>, did: DefId, item: Symbol) -> boo
cx.tcx.is_diagnostic_item(item, did)
}

/// Checks if the `DefId` matches the given `LangItem`.
pub fn is_lang_item(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
/// Checks if the `DefId` matches the given `LangItem` or it's constructor.
pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
let did = match cx.tcx.def_kind(did) {
DefKind::Ctor(..) => match cx.tcx.parent(did) {
Some(did) => did,
Expand Down Expand Up @@ -404,7 +416,7 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
/// diagnostic item.
pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
path_def_id(cx, expr).map_or(false, |id| is_diagnostic_item_or_ctor(cx, id, diag_item))
}

/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
Expand Down
12 changes: 6 additions & 6 deletions tests/ui-internal/unnecessary_def_path.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ extern crate rustc_span;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
#[allow(unused)]
use clippy_utils::{
is_diagnostic_item, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor, is_lang_item, is_trait_method,
match_def_path, match_trait_method, path_res,
is_diagnostic_ctor, is_diagnostic_item_or_ctor, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor,
is_lang_item_or_ctor, is_trait_method, match_def_path, match_trait_method, path_res,
};

#[allow(unused)]
Expand Down Expand Up @@ -48,14 +48,14 @@ fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox);
let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit);

let _ = is_lang_item(cx, did, LangItem::OwnedBox);
let _ = is_diagnostic_item(cx, did, sym::Option);
let _ = is_lang_item(cx, did, LangItem::OptionSome);
let _ = cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did);
let _ = cx.tcx.is_diagnostic_item(sym::Option, did);
let _ = is_lang_item_or_ctor(cx, did, LangItem::OptionSome);

let _ = is_trait_method(cx, expr, sym::AsRef);

let _ = is_expr_diagnostic_item(cx, expr, sym::Option);
let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| is_lang_item(cx, id, LangItem::IteratorNext));
let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id));
}

fn main() {}
Loading

0 comments on commit af141d2

Please sign in to comment.