Skip to content

Commit 5e63922

Browse files
committed
Provide different suggestions for constructors.
1 parent c0a48b9 commit 5e63922

File tree

9 files changed

+80
-34
lines changed

9 files changed

+80
-34
lines changed

clippy_lints/src/infinite_iter.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clippy_utils::diagnostics::span_lint;
2+
use clippy_utils::higher;
23
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
3-
use clippy_utils::{higher, path_def_id};
44
use rustc_hir::{BorrowKind, Expr, ExprKind};
55
use rustc_lint::{LateContext, LateLintPass};
66
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -167,9 +167,16 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
167167
},
168168
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
169169
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
170-
ExprKind::Call(path, _) => path_def_id(cx, path)
171-
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
172-
.into(),
170+
ExprKind::Call(path, _) => {
171+
if let ExprKind::Path(ref qpath) = path.kind {
172+
cx.qpath_res(qpath, path.hir_id)
173+
.opt_def_id()
174+
.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
175+
.into()
176+
} else {
177+
Finite
178+
}
179+
},
173180
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
174181
_ => Finite,
175182
}

clippy_lints/src/loops/while_let_on_iterator.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ use super::WHILE_LET_ON_ITERATOR;
22
use clippy_utils::diagnostics::span_lint_and_sugg;
33
use clippy_utils::higher;
44
use clippy_utils::source::snippet_with_applicability;
5-
use clippy_utils::{get_enclosing_loop_or_closure, is_lang_item, is_refutable, is_trait_method, visitors::is_res_used};
5+
use clippy_utils::{get_enclosing_loop_or_closure, is_lang_ctor, is_refutable, is_trait_method, visitors::is_res_used};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
88
use rustc_hir::intravisit::{walk_expr, Visitor};
9-
use rustc_hir::{def::Res, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, QPath, UnOp};
9+
use rustc_hir::{def::Res, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp};
1010
use rustc_lint::LateContext;
1111
use rustc_middle::ty::adjustment::Adjust;
1212
use rustc_span::{symbol::sym, Symbol};
@@ -15,9 +15,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1515
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! {
1616
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
1717
// check for `Some(..)` pattern
18-
if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
19-
if let Res::Def(_, pat_did) = pat_path.res;
20-
if is_lang_item(cx, pat_did, LangItem::OptionSome);
18+
if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind;
19+
if is_lang_ctor(cx, pat_path, LangItem::OptionSome);
2120
// check for call to `Iterator::next`
2221
if let ExprKind::MethodCall(method_name, [iter_expr], _) = let_expr.kind;
2322
if method_name.ident.name == sym::next;

clippy_lints/src/methods/inefficient_to_string.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::source::snippet_with_applicability;
33
use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
4-
use clippy_utils::{is_diagnostic_item, match_def_path, paths};
4+
use clippy_utils::{match_def_path, paths};
55
use if_chain::if_chain;
66
use rustc_errors::Applicability;
77
use rustc_hir as hir;
@@ -60,7 +60,7 @@ fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
6060
}
6161

6262
if let ty::Adt(adt, substs) = ty.kind() {
63-
is_diagnostic_item(cx, adt.did(), sym::Cow) && substs.type_at(1).is_str()
63+
cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str()
6464
} else {
6565
false
6666
}

clippy_lints/src/useless_conversion.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
22
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
33
use clippy_utils::sugg::Sugg;
44
use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
5-
use clippy_utils::{get_parent_expr, is_lang_item, is_trait_method, match_def_path, paths};
5+
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, paths};
66
use if_chain::if_chain;
77
use rustc_errors::Applicability;
88
use rustc_hir::{Expr, ExprKind, HirId, LangItem, MatchSource};
@@ -154,7 +154,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
154154
}
155155

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

160160
then {

clippy_lints/src/utils/internal_lints.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use rustc_hir::{
2424
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
2525
use rustc_middle::hir::nested_filter;
2626
use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
27-
use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, FloatTy, Ty};
27+
use rustc_middle::ty::{
28+
self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, AssocKind, DefIdTree, FloatTy, Ty,
29+
};
2830
use rustc_semver::RustcVersion;
2931
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
3032
use rustc_span::source_map::Spanned;
@@ -914,15 +916,31 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
914916
return;
915917
};
916918

919+
let has_ctor = match cx.tcx.def_kind(def_id) {
920+
DefKind::Struct => {
921+
let variant = cx.tcx.adt_def(def_id).non_enum_variant();
922+
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
923+
}
924+
DefKind::Variant => {
925+
let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
926+
variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
927+
}
928+
_ => false,
929+
};
930+
917931
let mut app = Applicability::MachineApplicable;
918932
let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
919933
let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
920934
let sugg = match (which_path, item) {
921935
// match_def_path
936+
(0, Item::DiagnosticItem(item)) if has_ctor =>
937+
format!("is_diagnostic_item_or_ctor({}, {}, sym::{})", cx_snip, def_snip, item),
938+
(0, Item::LangItem(item)) if has_ctor =>
939+
format!("is_lang_item_or_ctor({}, {}, LangItem::{})", cx_snip, def_snip, item),
922940
(0, Item::DiagnosticItem(item)) =>
923-
format!("is_diagnostic_item({}, {}, sym::{})", cx_snip, def_snip, item),
941+
format!("{}.tcx.is_diagnostic_item(sym::{}, {})", cx_snip, item, def_snip),
924942
(0, Item::LangItem(item)) =>
925-
format!("is_lang_item({}, {}, LangItem::{})", cx_snip, def_snip, item),
943+
format!("{}.tcx.lang_items().require(LangItem::{}).ok() == Some({})", cx_snip, item, def_snip),
926944
// match_trait_method
927945
(1, Item::DiagnosticItem(item)) =>
928946
format!("is_trait_method({}, {}, sym::{})", cx_snip, def_snip, item),
@@ -932,12 +950,24 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
932950
(2, Item::LangItem(item)) =>
933951
format!("is_type_lang_item({}, {}, LangItem::{})", cx_snip, def_snip, item),
934952
// is_expr_path_def_path
953+
(3, Item::DiagnosticItem(item)) if has_ctor =>
954+
format!(
955+
"path_res({}, {}).opt_def_id()\
956+
.map_or(false, |id| is_diagnostic_item_or_ctor({}, id, sym::{})",
957+
cx_snip, def_snip, cx_snip, item,
958+
),
959+
(3, Item::LangItem(item)) if has_ctor =>
960+
format!(
961+
"path_res({}, {}).opt_def_id()\
962+
.map_or(false, |id| is_lang_item_or_ctor({}, id, LangItem::{})",
963+
cx_snip, def_snip, cx_snip, item,
964+
),
935965
(3, Item::DiagnosticItem(item)) =>
936966
format!("is_expr_diagnostic_item({}, {}, sym::{})", cx_snip, def_snip, item),
937967
(3, Item::LangItem(item)) =>
938968
format!(
939969
"path_res({}, {}).opt_def_id()\
940-
.map_or(false, |id| is_lang_item({}, id, LangItem::{}))",
970+
.map_or(false, |id| {}.tcx.lang_items().require(LangItem::{}).ok() == Some(id))",
941971
cx_snip, def_snip, cx_snip, item,
942972
),
943973
_ => return,

clippy_utils/src/lib.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,18 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem
242242
false
243243
}
244244

245-
/// Checks if the `DefId` matches the given diagnostic item.
246-
pub fn is_diagnostic_item(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
245+
/// Checks if a `QPath` resolves to a constructor of a diagnostic item.
246+
pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
247+
if let QPath::Resolved(_, path) = qpath {
248+
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
249+
return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
250+
}
251+
}
252+
false
253+
}
254+
255+
/// Checks if the `DefId` matches the given diagnostic item or it's constructor.
256+
pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
247257
let did = match cx.tcx.def_kind(did) {
248258
DefKind::Ctor(..) => cx.tcx.parent(did),
249259
// Constructors for types in external crates seem to have `DefKind::Variant`
@@ -257,8 +267,8 @@ pub fn is_diagnostic_item(cx: &LateContext<'_>, did: DefId, item: Symbol) -> boo
257267
cx.tcx.is_diagnostic_item(item, did)
258268
}
259269

260-
/// Checks if the `DefId` matches the given `LangItem`.
261-
pub fn is_lang_item(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
270+
/// Checks if the `DefId` matches the given `LangItem` or it's constructor.
271+
pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
262272
let did = match cx.tcx.def_kind(did) {
263273
DefKind::Ctor(..) => cx.tcx.parent(did),
264274
// Constructors for types in external crates seem to have `DefKind::Variant`
@@ -403,7 +413,7 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[
403413
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
404414
/// diagnostic item.
405415
pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
406-
path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
416+
path_def_id(cx, expr).map_or(false, |id| is_diagnostic_item_or_ctor(cx, id, diag_item))
407417
}
408418

409419
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the

tests/ui-internal/unnecessary_def_path.fixed

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ extern crate rustc_span;
1414
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
1515
#[allow(unused)]
1616
use clippy_utils::{
17-
is_diagnostic_item, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor, is_lang_item, is_trait_method,
18-
match_def_path, match_trait_method, path_res,
17+
is_diagnostic_ctor, is_diagnostic_item_or_ctor, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor,
18+
is_lang_item_or_ctor, is_trait_method, match_def_path, match_trait_method, path_res,
1919
};
2020

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

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

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

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

6161
fn main() {}

tests/ui-internal/unnecessary_def_path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ extern crate rustc_span;
1414
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
1515
#[allow(unused)]
1616
use clippy_utils::{
17-
is_diagnostic_item, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor, is_lang_item, is_trait_method,
18-
match_def_path, match_trait_method, path_res,
17+
is_diagnostic_ctor, is_diagnostic_item_or_ctor, is_expr_diagnostic_item, is_expr_path_def_path, is_lang_ctor,
18+
is_lang_item_or_ctor, is_trait_method, match_def_path, match_trait_method, path_res,
1919
};
2020

2121
#[allow(unused)]

tests/ui-internal/unnecessary_def_path.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,19 @@ error: use of a def path to a `LangItem`
5757
--> $DIR/unnecessary_def_path.rs:51:13
5858
|
5959
LL | let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
60-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_lang_item(cx, did, LangItem::OwnedBox)`
60+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().require(LangItem::OwnedBox).ok() == Some(did)`
6161

6262
error: use of a def path to a diagnostic item
6363
--> $DIR/unnecessary_def_path.rs:52:13
6464
|
6565
LL | let _ = match_def_path(cx, did, &["core", "option", "Option"]);
66-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_diagnostic_item(cx, did, sym::Option)`
66+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
6767

6868
error: use of a def path to a `LangItem`
6969
--> $DIR/unnecessary_def_path.rs:53:13
7070
|
7171
LL | let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
72-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_lang_item(cx, did, LangItem::OptionSome)`
72+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_lang_item_or_ctor(cx, did, LangItem::OptionSome)`
7373

7474
error: use of a def path to a diagnostic item
7575
--> $DIR/unnecessary_def_path.rs:55:13
@@ -87,7 +87,7 @@ error: use of a def path to a `LangItem`
8787
--> $DIR/unnecessary_def_path.rs:58:13
8888
|
8989
LL | let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
90-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| is_lang_item(cx, id, LangItem::IteratorNext))`
90+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().require(LangItem::IteratorNext).ok() == Some(id))`
9191

9292
error: aborting due to 14 previous errors
9393

0 commit comments

Comments
 (0)