Auto merge of #8856 - xFrednet:rustup, r=Manishearth,Alexendoo
Rustup `@rust-lang/clippy,` `@Jarcho,` `@dswij,` `@Alexendoo.` Could someone review this? It should be pretty straight forward since it's just a sync. I think it's also fine if either one of `@Jarcho,` `@dswij,` `@Alexendoo` approves this, as these are usually not reviewed. I just want to make sure that I didn't break something obvious 🙃 It should be enough to look at the merge commit 🙃 changelog: none changelog: move [`significant_drop_in_scrutinee`] to `suspicious`
This commit is contained in:
commit
b312ad7d0c
43 changed files with 1538 additions and 136 deletions
|
@ -3715,6 +3715,7 @@ Released 2018-09-13
|
||||||
[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
|
[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
|
||||||
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
|
[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
|
||||||
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
|
[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
|
||||||
|
[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
|
||||||
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
|
[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
|
||||||
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
|
[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
|
||||||
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
|
[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy"
|
name = "clippy"
|
||||||
version = "0.1.62"
|
version = "0.1.63"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_lints"
|
name = "clippy_lints"
|
||||||
version = "0.1.62"
|
version = "0.1.63"
|
||||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||||
repository = "https://github.com/rust-lang/rust-clippy"
|
repository = "https://github.com/rust-lang/rust-clippy"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_s
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::MultiSpan;
|
use rustc_errors::MultiSpan;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind};
|
use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
@ -109,7 +109,10 @@ fn check_arm<'tcx>(
|
||||||
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
|
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
|
||||||
};
|
};
|
||||||
// the binding must not be used in the if guard
|
// the binding must not be used in the if guard
|
||||||
if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id));
|
if outer_guard.map_or(
|
||||||
|
true,
|
||||||
|
|(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
|
||||||
|
);
|
||||||
// ...or anywhere in the inner expression
|
// ...or anywhere in the inner expression
|
||||||
if match inner {
|
if match inner {
|
||||||
IfLetOrMatch::IfLet(_, _, body, els) => {
|
IfLetOrMatch::IfLet(_, _, body, els) => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_help;
|
use clippy_utils::diagnostics::span_lint_and_help;
|
||||||
use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks};
|
use clippy_utils::{is_default_equivalent, peel_blocks};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
def::{DefKind, Res},
|
def::{DefKind, Res},
|
||||||
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
|
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
|
||||||
|
@ -71,8 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
||||||
self_ty,
|
self_ty,
|
||||||
..
|
..
|
||||||
}) = item.kind;
|
}) = item.kind;
|
||||||
if let attrs = cx.tcx.hir().attrs(item.hir_id());
|
if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
|
||||||
if !is_automatically_derived(attrs);
|
|
||||||
if !item.span.from_expansion();
|
if !item.span.from_expansion();
|
||||||
if let Some(def_id) = trait_ref.trait_def_id();
|
if let Some(def_id) = trait_ref.trait_def_id();
|
||||||
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
|
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
|
||||||
|
@ -81,6 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
|
||||||
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
|
if let ImplItemKind::Fn(_, b) = &impl_item.kind;
|
||||||
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
|
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
|
||||||
if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
|
if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
|
||||||
|
if let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
if !attrs.iter().any(|attr| attr.doc_str().is_some());
|
if !attrs.iter().any(|attr| attr.doc_str().is_some());
|
||||||
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
|
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
|
||||||
if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
|
if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::paths;
|
use clippy_utils::paths;
|
||||||
use clippy_utils::ty::{implements_trait, is_copy};
|
use clippy_utils::ty::{implements_trait, is_copy};
|
||||||
use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path};
|
use clippy_utils::{is_lint_allowed, match_def_path};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
|
||||||
|
@ -205,8 +205,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||||
}) = item.kind
|
}) = item.kind
|
||||||
{
|
{
|
||||||
let ty = cx.tcx.type_of(item.def_id);
|
let ty = cx.tcx.type_of(item.def_id);
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
|
||||||
let is_automatically_derived = is_automatically_derived(attrs);
|
|
||||||
|
|
||||||
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||||
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
|
check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
|
||||||
|
@ -236,7 +235,7 @@ fn check_hash_peq<'tcx>(
|
||||||
then {
|
then {
|
||||||
// Look for the PartialEq implementations for `ty`
|
// Look for the PartialEq implementations for `ty`
|
||||||
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
|
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
|
||||||
let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
|
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||||
|
|
||||||
if peq_is_automatically_derived == hash_is_automatically_derived {
|
if peq_is_automatically_derived == hash_is_automatically_derived {
|
||||||
return;
|
return;
|
||||||
|
@ -290,7 +289,7 @@ fn check_ord_partial_ord<'tcx>(
|
||||||
then {
|
then {
|
||||||
// Look for the PartialOrd implementations for `ty`
|
// Look for the PartialOrd implementations for `ty`
|
||||||
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
|
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
|
||||||
let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id));
|
let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
|
||||||
|
|
||||||
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
|
if partial_ord_is_automatically_derived == ord_is_automatically_derived {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,7 +11,7 @@ use rustc_errors::Applicability;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
hir_id::HirIdSet,
|
hir_id::HirIdSet,
|
||||||
intravisit::{walk_expr, Visitor},
|
intravisit::{walk_expr, Visitor},
|
||||||
Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp,
|
Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
@ -478,7 +478,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||||
let mut is_map_used = self.is_map_used;
|
let mut is_map_used = self.is_map_used;
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
self.visit_pat(arm.pat);
|
self.visit_pat(arm.pat);
|
||||||
if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard {
|
if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
|
||||||
self.visit_non_tail_expr(guard);
|
self.visit_non_tail_expr(guard);
|
||||||
}
|
}
|
||||||
is_map_used |= self.visit_cond_arm(arm.body);
|
is_map_used |= self.visit_cond_arm(arm.body);
|
||||||
|
|
|
@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||||
if check_inputs(cx, body.params, args);
|
if check_inputs(cx, body.params, args);
|
||||||
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
|
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
|
||||||
let substs = cx.typeck_results().node_substs(body.value.hir_id);
|
let substs = cx.typeck_results().node_substs(body.value.hir_id);
|
||||||
let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
|
let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs);
|
||||||
if check_sig(cx, closure_ty, call_ty);
|
if check_sig(cx, closure_ty, call_ty);
|
||||||
then {
|
then {
|
||||||
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
|
span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
|
||||||
|
|
|
@ -13,13 +13,13 @@ use clippy_utils::attrs::is_proc_macro;
|
||||||
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::is_must_use_ty;
|
use clippy_utils::ty::is_must_use_ty;
|
||||||
use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method};
|
use clippy_utils::{match_def_path, return_ty, trait_ref_of_method};
|
||||||
|
|
||||||
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
|
||||||
|
|
||||||
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
let attr = must_use_attr(attrs);
|
let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
|
||||||
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
|
if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
|
||||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||||
|
@ -44,7 +44,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp
|
||||||
let is_public = cx.access_levels.is_exported(item.def_id);
|
let is_public = cx.access_levels.is_exported(item.def_id);
|
||||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
let attr = must_use_attr(attrs);
|
let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
|
||||||
if let Some(attr) = attr {
|
if let Some(attr) = attr {
|
||||||
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
|
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
|
||||||
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() {
|
} else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() {
|
||||||
|
@ -67,7 +67,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr
|
||||||
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
|
||||||
|
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
||||||
let attr = must_use_attr(attrs);
|
let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use);
|
||||||
if let Some(attr) = attr {
|
if let Some(attr) = attr {
|
||||||
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
|
check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
|
||||||
} else if let hir::TraitFn::Provided(eid) = *eid {
|
} else if let hir::TraitFn::Provided(eid) = *eid {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustc_hir::{Body, FnDecl, HirId};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::subst::Subst;
|
use rustc_middle::ty::subst::Subst;
|
||||||
use rustc_middle::ty::{Opaque, PredicateKind::Trait};
|
use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
|
use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt;
|
||||||
|
@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
||||||
let preds = cx.tcx.explicit_item_bounds(id);
|
let preds = cx.tcx.explicit_item_bounds(id);
|
||||||
let mut is_future = false;
|
let mut is_future = false;
|
||||||
for &(p, _span) in preds {
|
for &(p, _span) in preds {
|
||||||
let p = p.subst(cx.tcx, subst);
|
let p = EarlyBinder(p).subst(cx.tcx, subst);
|
||||||
if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
|
if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
|
||||||
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
|
if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
|
||||||
is_future = true;
|
is_future = true;
|
||||||
|
|
|
@ -278,6 +278,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||||
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
LintId::of(self_assignment::SELF_ASSIGNMENT),
|
||||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||||
|
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||||
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
|
||||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||||
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
|
||||||
|
|
|
@ -473,6 +473,7 @@ store.register_lints(&[
|
||||||
shadow::SHADOW_REUSE,
|
shadow::SHADOW_REUSE,
|
||||||
shadow::SHADOW_SAME,
|
shadow::SHADOW_SAME,
|
||||||
shadow::SHADOW_UNRELATED,
|
shadow::SHADOW_UNRELATED,
|
||||||
|
significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||||
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
|
single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
|
||||||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||||
|
|
|
@ -27,6 +27,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||||
|
LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||||
])
|
])
|
||||||
|
|
|
@ -367,6 +367,7 @@ mod self_named_constructors;
|
||||||
mod semicolon_if_nothing_returned;
|
mod semicolon_if_nothing_returned;
|
||||||
mod serde_api;
|
mod serde_api;
|
||||||
mod shadow;
|
mod shadow;
|
||||||
|
mod significant_drop_in_scrutinee;
|
||||||
mod single_char_lifetime_names;
|
mod single_char_lifetime_names;
|
||||||
mod single_component_path_imports;
|
mod single_component_path_imports;
|
||||||
mod size_of_in_element_count;
|
mod size_of_in_element_count;
|
||||||
|
@ -886,6 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||||
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
|
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
|
||||||
|
store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee));
|
||||||
store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
|
store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
|
||||||
let cargo_ignore_publish = conf.cargo_ignore_publish;
|
let cargo_ignore_publish = conf.cargo_ignore_publish;
|
||||||
store.register_late_pass(move || {
|
store.register_late_pass(move || {
|
||||||
|
|
|
@ -9,8 +9,8 @@ use rustc_hir::intravisit::{
|
||||||
use rustc_hir::FnRetTy::Return;
|
use rustc_hir::FnRetTy::Return;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
|
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
|
||||||
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
|
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin,
|
||||||
TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
|
TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
use rustc_middle::hir::nested_filter as middle_nested_filter;
|
||||||
|
@ -145,7 +145,7 @@ fn check_fn_inner<'tcx>(
|
||||||
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
|
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
|
||||||
for typ in types {
|
for typ in types {
|
||||||
for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
|
for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
|
||||||
if pred.in_where_clause {
|
if pred.origin == PredicateOrigin::WhereClause {
|
||||||
// has_where_lifetimes checked that this predicate contains no lifetime.
|
// has_where_lifetimes checked that this predicate contains no lifetime.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use clippy_utils::attrs::is_doc_hidden;
|
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::{is_lint_allowed, meets_msrv, msrvs};
|
use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs};
|
||||||
use rustc_ast::ast::{self, VisibilityKind};
|
use rustc_ast::ast::{self, VisibilityKind};
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
|
@ -161,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
|
||||||
let id = cx.tcx.hir().local_def_id(v.id);
|
let id = cx.tcx.hir().local_def_id(v.id);
|
||||||
(matches!(v.data, hir::VariantData::Unit(_))
|
(matches!(v.data, hir::VariantData::Unit(_))
|
||||||
&& v.ident.as_str().starts_with('_')
|
&& v.ident.as_str().starts_with('_')
|
||||||
&& is_doc_hidden(cx.tcx.get_attrs(id.to_def_id())))
|
&& is_doc_hidden(cx.tcx.hir().attrs(v.id)))
|
||||||
.then(|| (id, v.span))
|
.then(|| (id, v.span))
|
||||||
});
|
});
|
||||||
if let Some((id, span)) = iter.next()
|
if let Some((id, span)) = iter.next()
|
||||||
|
|
|
@ -192,6 +192,5 @@ impl<'a> CommonPrefixSearcher<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
|
fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
|
||||||
let attrs = cx.tcx.get_attrs(variant_def.def_id);
|
cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable)
|
||||||
clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed {
|
||||||
ExprKind::MethodCall(path, arguments, _) => {
|
ExprKind::MethodCall(path, arguments, _) => {
|
||||||
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
|
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
|
||||||
let substs = cx.typeck_results().node_substs(e.hir_id);
|
let substs = cx.typeck_results().node_substs(e.hir_id);
|
||||||
let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs);
|
let method_type = cx.tcx.bound_type_of(def_id).subst(cx.tcx, substs);
|
||||||
check_arguments(cx, arguments, method_type, path.ident.as_str(), "method");
|
check_arguments(cx, arguments, method_type, path.ident.as_str(), "method");
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
||||||
// can't be implemented for unsafe new
|
// can't be implemented for unsafe new
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) {
|
if cx.tcx.is_doc_hidden(impl_item.def_id) {
|
||||||
// shouldn't be implemented when it is hidden in docs
|
// shouldn't be implemented when it is hidden in docs
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -596,7 +596,7 @@ impl<'tcx> SideEffectVisit<'tcx> {
|
||||||
let mut vars = std::mem::take(&mut self.ret_vars);
|
let mut vars = std::mem::take(&mut self.ret_vars);
|
||||||
let _ = arm.guard.as_ref().map(|guard| {
|
let _ = arm.guard.as_ref().map(|guard| {
|
||||||
self.visit_expr(match guard {
|
self.visit_expr(match guard {
|
||||||
Guard::If(expr) | Guard::IfLet(_, expr) => expr,
|
Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
|
||||||
});
|
});
|
||||||
vars.append(&mut self.ret_vars);
|
vars.append(&mut self.ret_vars);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use clippy_utils::diagnostics::span_lint_hir;
|
use clippy_utils::diagnostics::span_lint_hir;
|
||||||
use clippy_utils::is_automatically_derived;
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_hir::{Impl, Item, ItemKind};
|
use rustc_hir::{Impl, Item, ItemKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
@ -37,8 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
|
||||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind;
|
if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind;
|
||||||
let attrs = cx.tcx.hir().attrs(item.hir_id());
|
if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
|
||||||
if !is_automatically_derived(attrs);
|
|
||||||
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
|
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
|
||||||
if trait_ref.path.res.def_id() == eq_trait;
|
if trait_ref.path.res.def_id() == eq_trait;
|
||||||
then {
|
then {
|
||||||
|
|
|
@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give up on loops
|
// Give up on loops
|
||||||
if terminator.successors().any(|s| *s == bb) {
|
if terminator.successors().any(|s| s == bb) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +439,7 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>,
|
||||||
// Short-circuit
|
// Short-circuit
|
||||||
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
|
if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
|
||||||
// Give up on loops
|
// Give up on loops
|
||||||
tdata.terminator().successors().any(|s| *s == bb)
|
tdata.terminator().successors().any(|s| s == bb)
|
||||||
{
|
{
|
||||||
return CloneUsage {
|
return CloneUsage {
|
||||||
cloned_used: true,
|
cloned_used: true,
|
||||||
|
|
|
@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
|
||||||
let mut map = FxHashMap::<Res, ExistingName>::default();
|
let mut map = FxHashMap::<Res, ExistingName>::default();
|
||||||
|
|
||||||
for id in cx.tcx.hir().items() {
|
for id in cx.tcx.hir().items() {
|
||||||
if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl)
|
if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl)
|
||||||
&& let item = cx.tcx.hir().item(id)
|
&& let item = cx.tcx.hir().item(id)
|
||||||
&& let ItemKind::Impl(Impl {
|
&& let ItemKind::Impl(Impl {
|
||||||
items,
|
items,
|
||||||
|
|
406
clippy_lints/src/significant_drop_in_scrutinee.rs
Normal file
406
clippy_lints/src/significant_drop_in_scrutinee.rs
Normal file
|
@ -0,0 +1,406 @@
|
||||||
|
use crate::FxHashSet;
|
||||||
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
|
use clippy_utils::get_attr;
|
||||||
|
use clippy_utils::source::{indent_of, snippet};
|
||||||
|
use rustc_errors::{Applicability, Diagnostic};
|
||||||
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
|
use rustc_hir::{Expr, ExprKind};
|
||||||
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
|
use rustc_middle::ty::subst::GenericArgKind;
|
||||||
|
use rustc_middle::ty::{Ty, TypeAndMut};
|
||||||
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
declare_clippy_lint! {
|
||||||
|
/// ### What it does
|
||||||
|
/// Check for temporaries returned from function calls in a match scrutinee that have the
|
||||||
|
/// `clippy::has_significant_drop` attribute.
|
||||||
|
///
|
||||||
|
/// ### Why is this bad?
|
||||||
|
/// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
|
||||||
|
/// an important side-effect, such as unlocking a mutex, making it important for users to be
|
||||||
|
/// able to accurately understand their lifetimes. When a temporary is returned in a function
|
||||||
|
/// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
|
||||||
|
/// be surprising.
|
||||||
|
///
|
||||||
|
/// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
|
||||||
|
/// function call that returns a `MutexGuard` and then tries to lock again in one of the match
|
||||||
|
/// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
|
||||||
|
/// the match block and thus will not unlock.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
/// ```rust.ignore
|
||||||
|
/// # use std::sync::Mutex;
|
||||||
|
///
|
||||||
|
/// # struct State {}
|
||||||
|
///
|
||||||
|
/// # impl State {
|
||||||
|
/// # fn foo(&self) -> bool {
|
||||||
|
/// # true
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// # fn bar(&self) {}
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// let mutex = Mutex::new(State {});
|
||||||
|
///
|
||||||
|
/// match mutex.lock().unwrap().foo() {
|
||||||
|
/// true => {
|
||||||
|
/// mutex.lock().unwrap().bar(); // Deadlock!
|
||||||
|
/// }
|
||||||
|
/// false => {}
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// println!("All done!");
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// Use instead:
|
||||||
|
/// ```rust
|
||||||
|
/// # use std::sync::Mutex;
|
||||||
|
///
|
||||||
|
/// # struct State {}
|
||||||
|
///
|
||||||
|
/// # impl State {
|
||||||
|
/// # fn foo(&self) -> bool {
|
||||||
|
/// # true
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// # fn bar(&self) {}
|
||||||
|
/// # }
|
||||||
|
///
|
||||||
|
/// let mutex = Mutex::new(State {});
|
||||||
|
///
|
||||||
|
/// let is_foo = mutex.lock().unwrap().foo();
|
||||||
|
/// match is_foo {
|
||||||
|
/// true => {
|
||||||
|
/// mutex.lock().unwrap().bar();
|
||||||
|
/// }
|
||||||
|
/// false => {}
|
||||||
|
/// };
|
||||||
|
///
|
||||||
|
/// println!("All done!");
|
||||||
|
/// ```
|
||||||
|
#[clippy::version = "1.60.0"]
|
||||||
|
pub SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||||
|
suspicious,
|
||||||
|
"warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||||
|
if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) {
|
||||||
|
for found in suggestions {
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
SIGNIFICANT_DROP_IN_SCRUTINEE,
|
||||||
|
found.found_span,
|
||||||
|
"temporary with significant drop in match scrutinee",
|
||||||
|
|diag| set_diagnostic(diag, cx, expr, found),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
|
||||||
|
if found.lint_suggestion == LintSuggestion::MoveAndClone {
|
||||||
|
// If our suggestion is to move and clone, then we want to leave it to the user to
|
||||||
|
// decide how to address this lint, since it may be that cloning is inappropriate.
|
||||||
|
// Therefore, we won't to emit a suggestion.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let original = snippet(cx, found.found_span, "..");
|
||||||
|
let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
|
||||||
|
|
||||||
|
let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy {
|
||||||
|
format!("let value = *{};\n{}", original, trailing_indent)
|
||||||
|
} else if found.is_unit_return_val {
|
||||||
|
// If the return value of the expression to be moved is unit, then we don't need to
|
||||||
|
// capture the result in a temporary -- we can just replace it completely with `()`.
|
||||||
|
format!("{};\n{}", original, trailing_indent)
|
||||||
|
} else {
|
||||||
|
format!("let value = {};\n{}", original, trailing_indent)
|
||||||
|
};
|
||||||
|
|
||||||
|
let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly {
|
||||||
|
"try moving the temporary above the match"
|
||||||
|
} else {
|
||||||
|
"try moving the temporary above the match and create a copy"
|
||||||
|
};
|
||||||
|
|
||||||
|
let scrutinee_replacement = if found.is_unit_return_val {
|
||||||
|
"()".to_owned()
|
||||||
|
} else {
|
||||||
|
"value".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
suggestion_message,
|
||||||
|
vec![
|
||||||
|
(expr.span.shrink_to_lo(), replacement),
|
||||||
|
(found.found_span, scrutinee_replacement),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
|
||||||
|
/// may have a surprising lifetime.
|
||||||
|
fn has_significant_drop_in_scrutinee<'tcx, 'a>(
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
expr: &'tcx Expr<'tcx>,
|
||||||
|
) -> Option<Vec<FoundSigDrop>> {
|
||||||
|
let mut helper = SigDropHelper::new(cx);
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SigDropHelper<'a, 'tcx> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
is_chain_end: bool,
|
||||||
|
seen_types: FxHashSet<Ty<'tcx>>,
|
||||||
|
has_significant_drop: bool,
|
||||||
|
current_sig_drop: Option<FoundSigDrop>,
|
||||||
|
sig_drop_spans: Option<Vec<FoundSigDrop>>,
|
||||||
|
special_handling_for_binary_op: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::enum_variant_names)]
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
enum LintSuggestion {
|
||||||
|
MoveOnly,
|
||||||
|
MoveAndDerefToCopy,
|
||||||
|
MoveAndClone,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct FoundSigDrop {
|
||||||
|
found_span: Span,
|
||||||
|
is_unit_return_val: bool,
|
||||||
|
lint_suggestion: LintSuggestion,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
|
||||||
|
fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> {
|
||||||
|
SigDropHelper {
|
||||||
|
cx,
|
||||||
|
is_chain_end: true,
|
||||||
|
seen_types: FxHashSet::default(),
|
||||||
|
has_significant_drop: false,
|
||||||
|
current_sig_drop: None,
|
||||||
|
sig_drop_spans: None,
|
||||||
|
special_handling_for_binary_op: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> {
|
||||||
|
self.visit_expr(match_expr);
|
||||||
|
|
||||||
|
// If sig drop spans is empty but we found a significant drop, it means that we didn't find
|
||||||
|
// a type that was trivially copyable as we moved up the chain after finding a significant
|
||||||
|
// drop, so move the entire scrutinee.
|
||||||
|
if self.has_significant_drop && self.sig_drop_spans.is_none() {
|
||||||
|
self.try_setting_current_suggestion(match_expr, true);
|
||||||
|
self.move_current_suggestion();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.sig_drop_spans.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This will try to set the current suggestion (so it can be moved into the suggestions vec
|
||||||
|
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
|
||||||
|
/// an opportunity to look for another type in the chain that will be trivially copyable.
|
||||||
|
/// However, if we are at the the end of the chain, we want to accept whatever is there. (The
|
||||||
|
/// suggestion won't actually be output, but the diagnostic message will be output, so the user
|
||||||
|
/// can determine the best way to handle the lint.)
|
||||||
|
fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
|
||||||
|
if self.current_sig_drop.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ty = self.get_type(expr);
|
||||||
|
if ty.is_ref() {
|
||||||
|
// We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
|
||||||
|
// but let's avoid any chance of an ICE
|
||||||
|
if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
|
||||||
|
if ty.is_trivially_pure_clone_copy() {
|
||||||
|
self.current_sig_drop.replace(FoundSigDrop {
|
||||||
|
found_span: expr.span,
|
||||||
|
is_unit_return_val: false,
|
||||||
|
lint_suggestion: LintSuggestion::MoveAndDerefToCopy,
|
||||||
|
});
|
||||||
|
} else if allow_move_and_clone {
|
||||||
|
self.current_sig_drop.replace(FoundSigDrop {
|
||||||
|
found_span: expr.span,
|
||||||
|
is_unit_return_val: false,
|
||||||
|
lint_suggestion: LintSuggestion::MoveAndClone,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ty.is_trivially_pure_clone_copy() {
|
||||||
|
self.current_sig_drop.replace(FoundSigDrop {
|
||||||
|
found_span: expr.span,
|
||||||
|
is_unit_return_val: false,
|
||||||
|
lint_suggestion: LintSuggestion::MoveOnly,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_current_suggestion(&mut self) {
|
||||||
|
if let Some(current) = self.current_sig_drop.take() {
|
||||||
|
self.sig_drop_spans.get_or_insert_with(Vec::new).push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
|
||||||
|
self.cx.typeck_results().expr_ty(ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
|
||||||
|
!self.seen_types.insert(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_exprs_for_binary_ops(
|
||||||
|
&mut self,
|
||||||
|
left: &'tcx Expr<'_>,
|
||||||
|
right: &'tcx Expr<'_>,
|
||||||
|
is_unit_return_val: bool,
|
||||||
|
span: Span,
|
||||||
|
) {
|
||||||
|
self.special_handling_for_binary_op = true;
|
||||||
|
self.visit_expr(left);
|
||||||
|
self.visit_expr(right);
|
||||||
|
|
||||||
|
// If either side had a significant drop, suggest moving the entire scrutinee to avoid
|
||||||
|
// unnecessary copies and to simplify cases where both sides have significant drops.
|
||||||
|
if self.has_significant_drop {
|
||||||
|
self.current_sig_drop.replace(FoundSigDrop {
|
||||||
|
found_span: span,
|
||||||
|
is_unit_return_val,
|
||||||
|
lint_suggestion: LintSuggestion::MoveOnly,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
self.special_handling_for_binary_op = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
|
if let Some(adt) = ty.ty_adt_def() {
|
||||||
|
if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match ty.kind() {
|
||||||
|
rustc_middle::ty::Adt(a, b) => {
|
||||||
|
for f in a.all_fields() {
|
||||||
|
let ty = f.ty(cx.tcx, b);
|
||||||
|
if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for generic_arg in b.iter() {
|
||||||
|
if let GenericArgKind::Type(ty) = generic_arg.unpack() {
|
||||||
|
if self.has_sig_drop_attr(cx, ty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
},
|
||||||
|
rustc_middle::ty::Array(ty, _)
|
||||||
|
| rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
|
||||||
|
| rustc_middle::ty::Ref(_, ty, _)
|
||||||
|
| rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
|
||||||
|
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
|
||||||
|
if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) {
|
||||||
|
self.has_significant_drop = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.is_chain_end = false;
|
||||||
|
|
||||||
|
match ex.kind {
|
||||||
|
ExprKind::MethodCall(_, [ref expr, ..], _) => {
|
||||||
|
self.visit_expr(expr);
|
||||||
|
}
|
||||||
|
ExprKind::Binary(_, left, right) => {
|
||||||
|
self.visit_exprs_for_binary_ops(left, right, false, ex.span);
|
||||||
|
}
|
||||||
|
ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => {
|
||||||
|
self.visit_exprs_for_binary_ops(left, right, true, ex.span);
|
||||||
|
}
|
||||||
|
ExprKind::Tup(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.visit_expr(expr);
|
||||||
|
if self.has_significant_drop {
|
||||||
|
// We may have not have set current_sig_drop if all the suggestions were
|
||||||
|
// MoveAndClone, so add this tuple item's full expression in that case.
|
||||||
|
if self.current_sig_drop.is_none() {
|
||||||
|
self.try_setting_current_suggestion(expr, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we are guaranteed to have something, so add it to the final vec.
|
||||||
|
self.move_current_suggestion();
|
||||||
|
}
|
||||||
|
// Reset `has_significant_drop` after each tuple expression so we can look for
|
||||||
|
// additional cases.
|
||||||
|
self.has_significant_drop = false;
|
||||||
|
}
|
||||||
|
if self.sig_drop_spans.is_some() {
|
||||||
|
self.has_significant_drop = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExprKind::Box(..) |
|
||||||
|
ExprKind::Array(..) |
|
||||||
|
ExprKind::Call(..) |
|
||||||
|
ExprKind::Unary(..) |
|
||||||
|
ExprKind::If(..) |
|
||||||
|
ExprKind::Match(..) |
|
||||||
|
ExprKind::Field(..) |
|
||||||
|
ExprKind::Index(..) |
|
||||||
|
ExprKind::Ret(..) |
|
||||||
|
ExprKind::Repeat(..) |
|
||||||
|
ExprKind::Yield(..) |
|
||||||
|
ExprKind::MethodCall(..) => walk_expr(self, ex),
|
||||||
|
ExprKind::AddrOf(_, _, _) |
|
||||||
|
ExprKind::Block(_, _) |
|
||||||
|
ExprKind::Break(_, _) |
|
||||||
|
ExprKind::Cast(_, _) |
|
||||||
|
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
|
||||||
|
ExprKind::Closure(_, _, _, _, _) |
|
||||||
|
ExprKind::ConstBlock(_) |
|
||||||
|
ExprKind::Continue(_) |
|
||||||
|
ExprKind::DropTemps(_) |
|
||||||
|
ExprKind::Err |
|
||||||
|
ExprKind::InlineAsm(_) |
|
||||||
|
ExprKind::Let(_) |
|
||||||
|
ExprKind::Lit(_) |
|
||||||
|
ExprKind::Loop(_, _, _, _) |
|
||||||
|
ExprKind::Path(_) |
|
||||||
|
ExprKind::Struct(_, _, _) |
|
||||||
|
ExprKind::Type(_, _) => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once a significant temporary has been found, we need to go back up at least 1 level to
|
||||||
|
// find the span to extract for replacement, so the temporary gets dropped. However, for
|
||||||
|
// binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to
|
||||||
|
// simplify cases where both sides have significant drops.
|
||||||
|
if self.has_significant_drop && !self.special_handling_for_binary_op {
|
||||||
|
self.try_setting_current_suggestion(ex, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,8 @@ use rustc_data_structures::unhash::UnhashMap;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
|
GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind,
|
||||||
|
WherePredicate,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||||
|
@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
|
||||||
for predicate in item.generics.predicates {
|
for predicate in item.generics.predicates {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||||
|
if bound_predicate.origin != PredicateOrigin::ImplTrait;
|
||||||
if !bound_predicate.span.from_expansion();
|
if !bound_predicate.span.from_expansion();
|
||||||
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
||||||
if let Some(PathSegment {
|
if let Some(PathSegment {
|
||||||
|
@ -168,6 +170,7 @@ impl TraitBounds {
|
||||||
for bound in gen.predicates {
|
for bound in gen.predicates {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let WherePredicate::BoundPredicate(ref p) = bound;
|
if let WherePredicate::BoundPredicate(ref p) = bound;
|
||||||
|
if p.origin != PredicateOrigin::ImplTrait;
|
||||||
if p.bounds.len() as u64 <= self.max_trait_bounds;
|
if p.bounds.len() as u64 <= self.max_trait_bounds;
|
||||||
if !p.span.from_expansion();
|
if !p.span.from_expansion();
|
||||||
if let Some(ref v) = map.insert(
|
if let Some(ref v) = map.insert(
|
||||||
|
@ -223,6 +226,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
|
||||||
for predicate in gen.predicates {
|
for predicate in gen.predicates {
|
||||||
if_chain! {
|
if_chain! {
|
||||||
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
|
||||||
|
if bound_predicate.origin != PredicateOrigin::ImplTrait;
|
||||||
if !bound_predicate.span.from_expansion();
|
if !bound_predicate.span.from_expansion();
|
||||||
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
|
||||||
if let Some(segment) = segments.first();
|
if let Some(segment) = segments.first();
|
||||||
|
|
|
@ -307,7 +307,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
|
||||||
.non_enum_variant()
|
.non_enum_variant()
|
||||||
.fields
|
.fields
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
|
.map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
|
||||||
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
|
||||||
return ReducedTy::TypeErasure;
|
return ReducedTy::TypeErasure;
|
||||||
};
|
};
|
||||||
|
|
|
@ -315,11 +315,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
||||||
out!("if let Some(Guard::If({expr})) = {arm}.guard;");
|
out!("if let Some(Guard::If({expr})) = {arm}.guard;");
|
||||||
self.expr(expr);
|
self.expr(expr);
|
||||||
},
|
},
|
||||||
Some(hir::Guard::IfLet(pat, expr)) => {
|
Some(hir::Guard::IfLet(let_expr)) => {
|
||||||
bind!(self, pat, expr);
|
bind!(self, let_expr);
|
||||||
out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
|
out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;");
|
||||||
self.pat(pat);
|
self.pat(field!(let_expr.pat));
|
||||||
self.expr(expr);
|
self.expr(field!(let_expr.init));
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.expr(field!(arm.body));
|
self.expr(field!(arm.body));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "clippy_utils"
|
name = "clippy_utils"
|
||||||
version = "0.1.62"
|
version = "0.1.63"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use rustc_ast::{ast, attr};
|
use rustc_ast::ast;
|
||||||
|
use rustc_ast::attr;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
@ -21,6 +22,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
|
||||||
("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
|
("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
|
||||||
("dump", DeprecationStatus::None),
|
("dump", DeprecationStatus::None),
|
||||||
("msrv", DeprecationStatus::None),
|
("msrv", DeprecationStatus::None),
|
||||||
|
("has_significant_drop", DeprecationStatus::None),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct LimitStack {
|
pub struct LimitStack {
|
||||||
|
@ -155,8 +157,3 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
|
||||||
.filter_map(ast::Attribute::meta_item_list)
|
.filter_map(ast::Attribute::meta_item_list)
|
||||||
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
.any(|l| attr::list_contains_name(&l, sym::hidden))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the attributes contain `#[unstable]`
|
|
||||||
pub fn is_unstable(attrs: &[ast::Attribute]) -> bool {
|
|
||||||
attrs.iter().any(|attr| attr.has_name(sym::unstable))
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind,
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::mir::interpret::Scalar;
|
use rustc_middle::mir::interpret::Scalar;
|
||||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||||
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt};
|
use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
|
||||||
use rustc_middle::{bug, span_bug};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use std::cmp::Ordering::{self, Equal};
|
use std::cmp::Ordering::{self, Equal};
|
||||||
|
@ -417,7 +417,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
|
||||||
let substs = if self.substs.is_empty() {
|
let substs = if self.substs.is_empty() {
|
||||||
substs
|
substs
|
||||||
} else {
|
} else {
|
||||||
substs.subst(self.lcx.tcx, self.substs)
|
EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
|
|
|
@ -300,7 +300,9 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||||
fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
|
fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
|
(Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
|
||||||
(Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re),
|
(Guard::IfLet(l), Guard::IfLet(r)) => {
|
||||||
|
self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
|
||||||
|
},
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -892,7 +894,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||||
|
|
||||||
pub fn hash_guard(&mut self, g: &Guard<'_>) {
|
pub fn hash_guard(&mut self, g: &Guard<'_>) {
|
||||||
match g {
|
match g {
|
||||||
Guard::If(expr) | Guard::IfLet(_, expr) => {
|
Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
|
||||||
self.hash_expr(expr);
|
self.hash_expr(expr);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,8 @@ use std::lazy::SyncOnceCell;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_ast::ast::{self, Attribute, LitKind};
|
use rustc_ast::ast::{self, LitKind};
|
||||||
|
use rustc_ast::Attribute;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::unhash::UnhashMap;
|
use rustc_data_structures::unhash::UnhashMap;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
|
@ -74,13 +75,12 @@ use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
|
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
|
||||||
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||||
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
||||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
|
||||||
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
|
def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
|
||||||
ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
|
HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
|
||||||
Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
|
Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
|
||||||
TraitRef, TyKind, UnOp,
|
UnOp,
|
||||||
};
|
};
|
||||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||||
use rustc_middle::hir::place::PlaceBase;
|
use rustc_middle::hir::place::PlaceBase;
|
||||||
|
@ -1472,12 +1472,6 @@ pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
|
|
||||||
/// implementations have.
|
|
||||||
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
|
|
||||||
has_attr(attrs, sym::automatically_derived)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_self(slf: &Param<'_>) -> bool {
|
pub fn is_self(slf: &Param<'_>) -> bool {
|
||||||
if let PatKind::Binding(.., name, _) = slf.pat.kind {
|
if let PatKind::Binding(.., name, _) = slf.pat.kind {
|
||||||
name.name == kw::SelfLower
|
name.name == kw::SelfLower
|
||||||
|
@ -1724,11 +1718,6 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds the `#[must_use]` attribute, if any
|
|
||||||
pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
|
|
||||||
attrs.iter().find(|a| a.has_name(sym::must_use))
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if expr is calling method or function with #[must_use] attribute
|
// check if expr is calling method or function with #[must_use] attribute
|
||||||
pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
let did = match expr.kind {
|
let did = match expr.kind {
|
||||||
|
@ -1745,7 +1734,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some())
|
did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an expression represents the identity function
|
/// Checks if an expression represents the identity function
|
||||||
|
@ -2079,35 +2068,6 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestItemNamesVisitor<'tcx> {
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
names: Vec<Symbol>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
|
|
||||||
fn visit_item(&mut self, item: &Item<'_>) {
|
|
||||||
if let ItemKind::Const(ty, _body) = item.kind {
|
|
||||||
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
|
|
||||||
// We could also check for the type name `test::TestDescAndFn`
|
|
||||||
if let Res::Def(DefKind::Struct, _) = path.res {
|
|
||||||
let has_test_marker = self
|
|
||||||
.tcx
|
|
||||||
.hir()
|
|
||||||
.attrs(item.hir_id())
|
|
||||||
.iter()
|
|
||||||
.any(|a| a.has_name(sym::rustc_test_marker));
|
|
||||||
if has_test_marker {
|
|
||||||
self.names.push(item.ident.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
|
|
||||||
fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
|
|
||||||
fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
|
static TEST_ITEM_NAMES_CACHE: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
|
||||||
|
|
||||||
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
|
fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
|
||||||
|
@ -2116,10 +2076,28 @@ fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(
|
||||||
match map.entry(module) {
|
match map.entry(module) {
|
||||||
Entry::Occupied(entry) => f(entry.get()),
|
Entry::Occupied(entry) => f(entry.get()),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() };
|
let mut names = Vec::new();
|
||||||
tcx.hir().visit_item_likes_in_module(module, &mut visitor);
|
for id in tcx.hir().module_items(module) {
|
||||||
visitor.names.sort_unstable();
|
if matches!(tcx.def_kind(id.def_id), DefKind::Const)
|
||||||
f(&*entry.insert(visitor.names))
|
&& let item = tcx.hir().item(id)
|
||||||
|
&& let ItemKind::Const(ty, _body) = item.kind {
|
||||||
|
if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
|
||||||
|
// We could also check for the type name `test::TestDescAndFn`
|
||||||
|
if let Res::Def(DefKind::Struct, _) = path.res {
|
||||||
|
let has_test_marker = tcx
|
||||||
|
.hir()
|
||||||
|
.attrs(item.hir_id())
|
||||||
|
.iter()
|
||||||
|
.any(|a| a.has_name(sym::rustc_test_marker));
|
||||||
|
if has_test_marker {
|
||||||
|
names.push(item.ident.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
names.sort_unstable();
|
||||||
|
f(&*entry.insert(names))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
|
||||||
use rustc_semver::RustcVersion;
|
use rustc_semver::RustcVersion;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
type McfResult = Result<(), (Span, Cow<'static, str>)>;
|
||||||
|
@ -323,7 +322,7 @@ fn check_terminator<'a, 'tcx>(
|
||||||
// within const fns. `transmute` is allowed in all other const contexts.
|
// within const fns. `transmute` is allowed in all other const contexts.
|
||||||
// This won't really scale to more intrinsics or functions. Let's allow const
|
// This won't really scale to more intrinsics or functions. Let's allow const
|
||||||
// transmutes in const fn before we add more hacks to this.
|
// transmutes in const fn before we add more hacks to this.
|
||||||
if tcx.fn_sig(fn_def_id).abi() == RustIntrinsic && tcx.item_name(fn_def_id) == sym::transmute {
|
if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute {
|
||||||
return Err((
|
return Err((
|
||||||
span,
|
span,
|
||||||
"can only call `transmute` from const items, not `const fn`".into(),
|
"can only call `transmute` from const items, not `const fn`".into(),
|
||||||
|
|
|
@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::query::normalize::AtExt;
|
use rustc_trait_selection::traits::query::normalize::AtExt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use crate::{match_def_path, must_use_attr, path_res, paths};
|
use crate::{match_def_path, path_res, paths};
|
||||||
|
|
||||||
// Checks if the given type implements copy.
|
// Checks if the given type implements copy.
|
||||||
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
|
@ -178,18 +178,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
// Returns whether the type has #[must_use] attribute
|
// Returns whether the type has #[must_use] attribute
|
||||||
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did())).is_some(),
|
ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
|
||||||
ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
|
ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
|
||||||
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
|
ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
|
||||||
// for the Array case we don't need to care for the len == 0 case
|
// for the Array case we don't need to care for the len == 0 case
|
||||||
// because we don't want to lint functions returning empty arrays
|
// because we don't want to lint functions returning empty arrays
|
||||||
is_must_use_ty(cx, *ty)
|
is_must_use_ty(cx, *ty)
|
||||||
},
|
},
|
||||||
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
|
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
|
||||||
ty::Opaque(ref def_id, _) => {
|
ty::Opaque(def_id, _) => {
|
||||||
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
|
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
|
||||||
if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
|
if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
|
||||||
if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
|
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||||
ty::Dynamic(binder, _) => {
|
ty::Dynamic(binder, _) => {
|
||||||
for predicate in binder.iter() {
|
for predicate in binder.iter() {
|
||||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
|
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
|
||||||
if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
|
if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -520,7 +520,7 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
|
||||||
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
|
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
|
||||||
match *ty.kind() {
|
match *ty.kind() {
|
||||||
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
|
ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
|
||||||
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))),
|
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
|
||||||
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
|
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
|
||||||
ty::Dynamic(bounds, _) => {
|
ty::Dynamic(bounds, _) => {
|
||||||
let lang_items = cx.tcx.lang_items();
|
let lang_items = cx.tcx.lang_items();
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "nightly-2022-05-05"
|
channel = "nightly-2022-05-19"
|
||||||
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
|
||||||
|
|
|
@ -30,7 +30,15 @@ LL | const VAL: T;
|
||||||
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error: constant expression depends on a generic parameter
|
||||||
|
--> $DIR/ice-6252.rs:13:9
|
||||||
|
|
|
||||||
|
LL | [1; <Multiply<Five, Five>>::VAL];
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this may fail depending on what value the parameter takes
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0046, E0412.
|
Some errors have detailed explanations: E0046, E0412.
|
||||||
For more information about an error, try `rustc --explain E0046`.
|
For more information about an error, try `rustc --explain E0046`.
|
||||||
|
|
142
tests/ui/expect_tool_lint_rfc_2383.rs
Normal file
142
tests/ui/expect_tool_lint_rfc_2383.rs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// check-pass
|
||||||
|
#![feature(lint_reasons)]
|
||||||
|
//! This file tests the `#[expect]` attribute implementation for tool lints. The same
|
||||||
|
//! file is used to test clippy and rustdoc. Any changes to this file should be synced
|
||||||
|
//! to the other test files as well.
|
||||||
|
//!
|
||||||
|
//! Expectations:
|
||||||
|
//! * rustc: only rustc lint expectations are emitted
|
||||||
|
//! * clippy: rustc and Clippy's expectations are emitted
|
||||||
|
//! * rustdoc: only rustdoc lint expectations are emitted
|
||||||
|
//!
|
||||||
|
//! This test can't cover every lint from Clippy, rustdoc and potentially other
|
||||||
|
//! tools that will be developed. This therefore only tests a small subset of lints
|
||||||
|
#![expect(rustdoc::missing_crate_level_docs)]
|
||||||
|
|
||||||
|
mod rustc_ok {
|
||||||
|
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn rustc_lints() {
|
||||||
|
let x = 42.0;
|
||||||
|
|
||||||
|
#[expect(illegal_floating_point_literal_pattern)]
|
||||||
|
match x {
|
||||||
|
5.0 => {},
|
||||||
|
6.0 => {},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod rustc_warn {
|
||||||
|
//! See <https://doc.rust-lang.org/rustc/lints/index.html>
|
||||||
|
|
||||||
|
#[expect(dead_code)]
|
||||||
|
pub fn rustc_lints() {
|
||||||
|
let x = 42;
|
||||||
|
|
||||||
|
#[expect(illegal_floating_point_literal_pattern)]
|
||||||
|
match x {
|
||||||
|
5 => {},
|
||||||
|
6 => {},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rustdoc_ok {
|
||||||
|
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
|
||||||
|
|
||||||
|
#[expect(rustdoc::broken_intra_doc_links)]
|
||||||
|
/// I want to link to [`Nonexistent`] but it doesn't exist!
|
||||||
|
pub fn foo() {}
|
||||||
|
|
||||||
|
#[expect(rustdoc::invalid_html_tags)]
|
||||||
|
/// <h1>
|
||||||
|
pub fn bar() {}
|
||||||
|
|
||||||
|
#[expect(rustdoc::bare_urls)]
|
||||||
|
/// http://example.org
|
||||||
|
pub fn baz() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod rustdoc_warn {
|
||||||
|
//! See <https://doc.rust-lang.org/rustdoc/lints.html>
|
||||||
|
|
||||||
|
#[expect(rustdoc::broken_intra_doc_links)]
|
||||||
|
/// I want to link to [`bar`] but it doesn't exist!
|
||||||
|
pub fn foo() {}
|
||||||
|
|
||||||
|
#[expect(rustdoc::invalid_html_tags)]
|
||||||
|
/// <h1></h1>
|
||||||
|
pub fn bar() {}
|
||||||
|
|
||||||
|
#[expect(rustdoc::bare_urls)]
|
||||||
|
/// <http://example.org>
|
||||||
|
pub fn baz() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod clippy_ok {
|
||||||
|
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
|
||||||
|
|
||||||
|
#[expect(clippy::almost_swapped)]
|
||||||
|
fn foo() {
|
||||||
|
let mut a = 0;
|
||||||
|
let mut b = 9;
|
||||||
|
a = b;
|
||||||
|
b = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::bytes_nth)]
|
||||||
|
fn bar() {
|
||||||
|
let _ = "Hello".bytes().nth(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::if_same_then_else)]
|
||||||
|
fn baz() {
|
||||||
|
let _ = if true { 42 } else { 42 };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::logic_bug)]
|
||||||
|
fn burger() {
|
||||||
|
let a = false;
|
||||||
|
let b = true;
|
||||||
|
|
||||||
|
if a && b || a {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod clippy_warn {
|
||||||
|
//! See <https://rust-lang.github.io/rust-clippy/master/index.html>
|
||||||
|
|
||||||
|
#[expect(clippy::almost_swapped)]
|
||||||
|
fn foo() {
|
||||||
|
let mut a = 0;
|
||||||
|
let mut b = 9;
|
||||||
|
a = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::bytes_nth)]
|
||||||
|
fn bar() {
|
||||||
|
let _ = "Hello".as_bytes().get(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::if_same_then_else)]
|
||||||
|
fn baz() {
|
||||||
|
let _ = if true { 33 } else { 42 };
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::logic_bug)]
|
||||||
|
fn burger() {
|
||||||
|
let a = false;
|
||||||
|
let b = true;
|
||||||
|
let c = false;
|
||||||
|
|
||||||
|
if a && b || c {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
rustc_warn::rustc_lints();
|
||||||
|
}
|
40
tests/ui/expect_tool_lint_rfc_2383.stderr
Normal file
40
tests/ui/expect_tool_lint_rfc_2383.stderr
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:35:14
|
||||||
|
|
|
||||||
|
LL | #[expect(dead_code)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D unfulfilled-lint-expectations` implied by `-D warnings`
|
||||||
|
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:39:18
|
||||||
|
|
|
||||||
|
LL | #[expect(illegal_floating_point_literal_pattern)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:113:14
|
||||||
|
|
|
||||||
|
LL | #[expect(clippy::almost_swapped)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:120:14
|
||||||
|
|
|
||||||
|
LL | #[expect(clippy::bytes_nth)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:125:14
|
||||||
|
|
|
||||||
|
LL | #[expect(clippy::if_same_then_else)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: this lint expectation is unfulfilled
|
||||||
|
--> $DIR/expect_tool_lint_rfc_2383.rs:130:14
|
||||||
|
|
|
||||||
|
LL | #[expect(clippy::logic_bug)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
|
error[E0080]: evaluation of `main::{constant#3}` failed
|
||||||
--> $DIR/indexing_slicing_index.rs:31:14
|
--> $DIR/indexing_slicing_index.rs:31:14
|
||||||
|
|
|
|
||||||
LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
|
LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
|
||||||
|
|
555
tests/ui/significant_drop_in_scrutinee.rs
Normal file
555
tests/ui/significant_drop_in_scrutinee.rs
Normal file
|
@ -0,0 +1,555 @@
|
||||||
|
// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
|
||||||
|
// // run-rustfix
|
||||||
|
|
||||||
|
#![warn(clippy::significant_drop_in_scrutinee)]
|
||||||
|
#![allow(clippy::single_match)]
|
||||||
|
#![allow(clippy::match_single_binding)]
|
||||||
|
#![allow(unused_assignments)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use std::ops::Deref;
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
use std::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
|
struct State {}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn foo(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_trigger_lint_with_mutex_guard_outside_match() {
|
||||||
|
let mutex = Mutex::new(State {});
|
||||||
|
|
||||||
|
// Should not trigger lint because the temporary should drop at the `;` on line before the match
|
||||||
|
let is_foo = mutex.lock().unwrap().foo();
|
||||||
|
match is_foo {
|
||||||
|
true => {
|
||||||
|
mutex.lock().unwrap().bar();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
|
||||||
|
let mutex = Mutex::new(State {});
|
||||||
|
|
||||||
|
// Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
|
||||||
|
// so its lifetime should not be surprising.
|
||||||
|
match mutex.lock() {
|
||||||
|
Ok(guard) => {
|
||||||
|
guard.foo();
|
||||||
|
mutex.lock().unwrap().bar();
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
|
||||||
|
let mutex = Mutex::new(State {});
|
||||||
|
|
||||||
|
// Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
|
||||||
|
// is preserved until the end of the match, but there is no clear indication that this is the
|
||||||
|
// case.
|
||||||
|
match mutex.lock().unwrap().foo() {
|
||||||
|
true => {
|
||||||
|
mutex.lock().unwrap().bar();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_trigger_lint_for_insignificant_drop() {
|
||||||
|
// Should not trigger lint because there are no temporaries whose drops have a significant
|
||||||
|
// side effect.
|
||||||
|
match 1u64.to_string().is_empty() {
|
||||||
|
true => {
|
||||||
|
println!("It was empty")
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
println!("It was not empty")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateWithMutex {
|
||||||
|
m: Mutex<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MutexGuardWrapper<'a> {
|
||||||
|
mg: MutexGuard<'a, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MutexGuardWrapper<'a> {
|
||||||
|
fn get_the_value(&self) -> u64 {
|
||||||
|
*self.mg.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MutexGuardWrapperWrapper<'a> {
|
||||||
|
mg: MutexGuardWrapper<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MutexGuardWrapperWrapper<'a> {
|
||||||
|
fn get_the_value(&self) -> u64 {
|
||||||
|
*self.mg.mg.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateWithMutex {
|
||||||
|
fn lock_m(&self) -> MutexGuardWrapper<'_> {
|
||||||
|
MutexGuardWrapper {
|
||||||
|
mg: self.m.lock().unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
|
||||||
|
MutexGuardWrapperWrapper {
|
||||||
|
mg: MutexGuardWrapper {
|
||||||
|
mg: self.m.lock().unwrap(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_with_wrapped_mutex() {
|
||||||
|
let s = StateWithMutex { m: Mutex::new(1) };
|
||||||
|
|
||||||
|
// Should trigger lint because a temporary contains a type with a significant drop and its
|
||||||
|
// lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
|
||||||
|
// the temporary contains such a type, making it potentially even more surprising.
|
||||||
|
match s.lock_m().get_the_value() {
|
||||||
|
1 => {
|
||||||
|
println!("Got 1. Is it still 1?");
|
||||||
|
println!("{}", s.lock_m().get_the_value());
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
println!("Got 2. Is it still 2?");
|
||||||
|
println!("{}", s.lock_m().get_the_value());
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
println!("All done!");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_with_double_wrapped_mutex() {
|
||||||
|
let s = StateWithMutex { m: Mutex::new(1) };
|
||||||
|
|
||||||
|
// Should trigger lint because a temporary contains a type which further contains a type with a
|
||||||
|
// significant drop and its lifetime is not obvious. Additionally, it is not obvious from
|
||||||
|
// looking at the scrutinee that the temporary contains such a type, making it potentially even
|
||||||
|
// more surprising.
|
||||||
|
match s.lock_m_m().get_the_value() {
|
||||||
|
1 => {
|
||||||
|
println!("Got 1. Is it still 1?");
|
||||||
|
println!("{}", s.lock_m().get_the_value());
|
||||||
|
},
|
||||||
|
2 => {
|
||||||
|
println!("Got 2. Is it still 2?");
|
||||||
|
println!("{}", s.lock_m().get_the_value());
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
println!("All done!");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Counter {
|
||||||
|
i: AtomicU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[clippy::has_significant_drop]
|
||||||
|
struct CounterWrapper<'a> {
|
||||||
|
counter: &'a Counter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CounterWrapper<'a> {
|
||||||
|
fn new(counter: &Counter) -> CounterWrapper {
|
||||||
|
counter.i.fetch_add(1, Ordering::Relaxed);
|
||||||
|
CounterWrapper { counter }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Drop for CounterWrapper<'a> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.counter.i.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Counter {
|
||||||
|
fn temp_increment(&self) -> Vec<CounterWrapper> {
|
||||||
|
vec![CounterWrapper::new(self), CounterWrapper::new(self)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_vec() {
|
||||||
|
let counter = Counter { i: AtomicU64::new(0) };
|
||||||
|
|
||||||
|
// Should trigger lint because the temporary in the scrutinee returns a collection of types
|
||||||
|
// which have significant drops. The types with significant drops are also non-obvious when
|
||||||
|
// reading the expression in the scrutinee.
|
||||||
|
match counter.temp_increment().len() {
|
||||||
|
2 => {
|
||||||
|
let current_count = counter.i.load(Ordering::Relaxed);
|
||||||
|
println!("Current count {}", current_count);
|
||||||
|
assert_eq!(current_count, 0);
|
||||||
|
},
|
||||||
|
1 => {},
|
||||||
|
3 => {},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateWithField {
|
||||||
|
s: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should trigger lint only on the type in the tuple which is created using a temporary
|
||||||
|
// with a significant drop. Additionally, this test ensures that the format of the tuple
|
||||||
|
// is preserved correctly in the suggestion.
|
||||||
|
fn should_trigger_lint_for_tuple_in_scrutinee() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
|
||||||
|
|
||||||
|
{
|
||||||
|
match (mutex1.lock().unwrap().s.len(), true) {
|
||||||
|
(3, _) => {
|
||||||
|
println!("started");
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
println!("done");
|
||||||
|
},
|
||||||
|
(_, _) => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
match (true, mutex1.lock().unwrap().s.len(), true) {
|
||||||
|
(_, 3, _) => {
|
||||||
|
println!("started");
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
println!("done");
|
||||||
|
},
|
||||||
|
(_, _, _) => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
|
||||||
|
match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
|
||||||
|
(3, _, 3) => {
|
||||||
|
println!("started");
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
mutex2.lock().unwrap().s.len();
|
||||||
|
println!("done");
|
||||||
|
},
|
||||||
|
(_, _, _) => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
|
||||||
|
match mutex3.lock().unwrap().s.as_str() {
|
||||||
|
"three" => {
|
||||||
|
println!("started");
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
mutex2.lock().unwrap().s.len();
|
||||||
|
println!("done");
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
match (true, mutex3.lock().unwrap().s.as_str()) {
|
||||||
|
(_, "three") => {
|
||||||
|
println!("started");
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
mutex2.lock().unwrap().s.len();
|
||||||
|
println!("done");
|
||||||
|
},
|
||||||
|
(_, _) => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should trigger lint when either side of a binary operation creates a temporary with a
|
||||||
|
// significant drop.
|
||||||
|
// To avoid potential unnecessary copies or creating references that would trigger the significant
|
||||||
|
// drop problem, the lint recommends moving the entire binary operation.
|
||||||
|
fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
|
||||||
|
let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
|
||||||
|
|
||||||
|
match mutex.lock().unwrap().s.len() > 1 {
|
||||||
|
true => {
|
||||||
|
mutex.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
match 1 < mutex.lock().unwrap().s.len() {
|
||||||
|
true => {
|
||||||
|
mutex.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should trigger lint when both sides of a binary operation creates a temporary with a
|
||||||
|
// significant drop.
|
||||||
|
// To avoid potential unnecessary copies or creating references that would trigger the significant
|
||||||
|
// drop problem, the lint recommends moving the entire binary operation.
|
||||||
|
fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
|
||||||
|
let mutex2 = Mutex::new(StateWithField {
|
||||||
|
s: "statewithfield".to_owned(),
|
||||||
|
});
|
||||||
|
|
||||||
|
match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
|
||||||
|
true => {
|
||||||
|
println!(
|
||||||
|
"{} < {}",
|
||||||
|
mutex1.lock().unwrap().s.len(),
|
||||||
|
mutex2.lock().unwrap().s.len()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
|
||||||
|
true => {
|
||||||
|
println!(
|
||||||
|
"{} >= {}",
|
||||||
|
mutex1.lock().unwrap().s.len(),
|
||||||
|
mutex2.lock().unwrap().s.len()
|
||||||
|
);
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_trigger_lint_for_closure_in_scrutinee() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
|
||||||
|
|
||||||
|
let get_mutex_guard = || mutex1.lock().unwrap().s.len();
|
||||||
|
|
||||||
|
// Should not trigger lint because the temporary with a significant drop will be dropped
|
||||||
|
// at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
|
||||||
|
// surprising lifetime.
|
||||||
|
match get_mutex_guard() > 1 {
|
||||||
|
true => {
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
|
||||||
|
|
||||||
|
let get_mutex_guard = || mutex1.lock().unwrap();
|
||||||
|
|
||||||
|
// Should trigger lint because the temporary with a significant drop is returned from the
|
||||||
|
// closure but not used directly in any match arms, so it has a potentially surprising lifetime.
|
||||||
|
match get_mutex_guard().s.len() > 1 {
|
||||||
|
true => {
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_return_from_match_in_scrutinee() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
|
||||||
|
let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
|
||||||
|
|
||||||
|
let i = 100;
|
||||||
|
|
||||||
|
// Should trigger lint because the nested match within the scrutinee returns a temporary with a
|
||||||
|
// significant drop is but not used directly in any match arms, so it has a potentially
|
||||||
|
// surprising lifetime.
|
||||||
|
match match i {
|
||||||
|
100 => mutex1.lock().unwrap(),
|
||||||
|
_ => mutex2.lock().unwrap(),
|
||||||
|
}
|
||||||
|
.s
|
||||||
|
.len()
|
||||||
|
> 1
|
||||||
|
{
|
||||||
|
true => {
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
println!("nothing to do here");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_return_from_if_in_scrutinee() {
|
||||||
|
let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
|
||||||
|
let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
|
||||||
|
|
||||||
|
let i = 100;
|
||||||
|
|
||||||
|
// Should trigger lint because the nested if-expression within the scrutinee returns a temporary
|
||||||
|
// with a significant drop is but not used directly in any match arms, so it has a potentially
|
||||||
|
// surprising lifetime.
|
||||||
|
match if i > 1 {
|
||||||
|
mutex1.lock().unwrap()
|
||||||
|
} else {
|
||||||
|
mutex2.lock().unwrap()
|
||||||
|
}
|
||||||
|
.s
|
||||||
|
.len()
|
||||||
|
> 1
|
||||||
|
{
|
||||||
|
true => {
|
||||||
|
mutex1.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_trigger_lint_for_if_in_scrutinee() {
|
||||||
|
let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
|
||||||
|
|
||||||
|
let i = 100;
|
||||||
|
|
||||||
|
// Should not trigger the lint because the temporary with a significant drop *is* dropped within
|
||||||
|
// the body of the if-expression nested within the match scrutinee, and therefore does not have
|
||||||
|
// a potentially surprising lifetime.
|
||||||
|
match if i > 1 {
|
||||||
|
mutex.lock().unwrap().s.len() > 1
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
true => {
|
||||||
|
mutex.lock().unwrap().s.len();
|
||||||
|
},
|
||||||
|
false => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateWithBoxedMutexGuard {
|
||||||
|
u: Mutex<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateWithBoxedMutexGuard {
|
||||||
|
fn new() -> StateWithBoxedMutexGuard {
|
||||||
|
StateWithBoxedMutexGuard { u: Mutex::new(42) }
|
||||||
|
}
|
||||||
|
fn lock(&self) -> Box<MutexGuard<u64>> {
|
||||||
|
Box::new(self.u.lock().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_boxed_mutex_guard() {
|
||||||
|
let s = StateWithBoxedMutexGuard::new();
|
||||||
|
|
||||||
|
// Should trigger lint because a temporary Box holding a type with a significant drop in a match
|
||||||
|
// scrutinee may have a potentially surprising lifetime.
|
||||||
|
match s.lock().deref().deref() {
|
||||||
|
0 | 1 => println!("Value was less than 2"),
|
||||||
|
_ => println!("Value is {}", s.lock().deref()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateStringWithBoxedMutexGuard {
|
||||||
|
s: Mutex<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateStringWithBoxedMutexGuard {
|
||||||
|
fn new() -> StateStringWithBoxedMutexGuard {
|
||||||
|
StateStringWithBoxedMutexGuard {
|
||||||
|
s: Mutex::new("A String".to_owned()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn lock(&self) -> Box<MutexGuard<String>> {
|
||||||
|
Box::new(self.s.lock().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
|
||||||
|
let s = StateStringWithBoxedMutexGuard::new();
|
||||||
|
|
||||||
|
let matcher = String::from("A String");
|
||||||
|
|
||||||
|
// Should trigger lint because a temporary Box holding a type with a significant drop in a match
|
||||||
|
// scrutinee may have a potentially surprising lifetime.
|
||||||
|
match s.lock().deref().deref() {
|
||||||
|
matcher => println!("Value is {}", s.lock().deref()),
|
||||||
|
_ => println!("Value was not a match"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StateWithIntField {
|
||||||
|
i: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should trigger lint when either side of an assign expression contains a temporary with a
|
||||||
|
// significant drop, because the temporary's lifetime will be extended to the end of the match.
|
||||||
|
// To avoid potential unnecessary copies or creating references that would trigger the significant
|
||||||
|
// drop problem, the lint recommends moving the entire binary operation.
|
||||||
|
fn should_trigger_lint_in_assign_expr() {
|
||||||
|
let mutex = Mutex::new(StateWithIntField { i: 10 });
|
||||||
|
|
||||||
|
let mut i = 100;
|
||||||
|
|
||||||
|
match mutex.lock().unwrap().i = i {
|
||||||
|
_ => {
|
||||||
|
println!("{}", mutex.lock().unwrap().i);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match i = mutex.lock().unwrap().i {
|
||||||
|
_ => {
|
||||||
|
println!("{}", mutex.lock().unwrap().i);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match mutex.lock().unwrap().i += 1 {
|
||||||
|
_ => {
|
||||||
|
println!("{}", mutex.lock().unwrap().i);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
match i += mutex.lock().unwrap().i {
|
||||||
|
_ => {
|
||||||
|
println!("{}", mutex.lock().unwrap().i);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum RecursiveEnum {
|
||||||
|
Foo(Option<Box<RecursiveEnum>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum GenericRecursiveEnum<T> {
|
||||||
|
Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_not_cause_stack_overflow() {
|
||||||
|
// Test that when a type recursively contains itself, a stack overflow does not occur when
|
||||||
|
// checking sub-types for significant drops.
|
||||||
|
let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
|
||||||
|
match f {
|
||||||
|
RecursiveEnum::Foo(Some(f)) => {
|
||||||
|
println!("{:?}", f)
|
||||||
|
},
|
||||||
|
RecursiveEnum::Foo(f) => {
|
||||||
|
println!("{:?}", f)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
|
||||||
|
match f {
|
||||||
|
GenericRecursiveEnum::Foo(i, Some(f)) => {
|
||||||
|
println!("{} {:?}", i, f)
|
||||||
|
},
|
||||||
|
GenericRecursiveEnum::Foo(i, f) => {
|
||||||
|
println!("{} {:?}", i, f)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
283
tests/ui/significant_drop_in_scrutinee.stderr
Normal file
283
tests/ui/significant_drop_in_scrutinee.stderr
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:57:11
|
||||||
|
|
|
||||||
|
LL | match mutex.lock().unwrap().foo() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings`
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex.lock().unwrap().foo();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:130:11
|
||||||
|
|
|
||||||
|
LL | match s.lock_m().get_the_value() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = s.lock_m().get_the_value();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:151:11
|
||||||
|
|
|
||||||
|
LL | match s.lock_m_m().get_the_value() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = s.lock_m_m().get_the_value();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:199:11
|
||||||
|
|
|
||||||
|
LL | match counter.temp_increment().len() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = counter.temp_increment().len();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:222:16
|
||||||
|
|
|
||||||
|
LL | match (mutex1.lock().unwrap().s.len(), true) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex1.lock().unwrap().s.len();
|
||||||
|
LL ~ match (value, true) {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:231:22
|
||||||
|
|
|
||||||
|
LL | match (true, mutex1.lock().unwrap().s.len(), true) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex1.lock().unwrap().s.len();
|
||||||
|
LL ~ match (true, value, true) {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:241:16
|
||||||
|
|
|
||||||
|
LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex1.lock().unwrap().s.len();
|
||||||
|
LL ~ match (value, true, mutex2.lock().unwrap().s.len()) {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:241:54
|
||||||
|
|
|
||||||
|
LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex2.lock().unwrap().s.len();
|
||||||
|
LL ~ match (mutex1.lock().unwrap().s.len(), true, value) {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:252:15
|
||||||
|
|
|
||||||
|
LL | match mutex3.lock().unwrap().s.as_str() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:262:22
|
||||||
|
|
|
||||||
|
LL | match (true, mutex3.lock().unwrap().s.as_str()) {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:281:11
|
||||||
|
|
|
||||||
|
LL | match mutex.lock().unwrap().s.len() > 1 {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex.lock().unwrap().s.len() > 1;
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:288:11
|
||||||
|
|
|
||||||
|
LL | match 1 < mutex.lock().unwrap().s.len() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = 1 < mutex.lock().unwrap().s.len();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:306:11
|
||||||
|
|
|
||||||
|
LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:317:11
|
||||||
|
|
|
||||||
|
LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:352:11
|
||||||
|
|
|
||||||
|
LL | match get_mutex_guard().s.len() > 1 {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = get_mutex_guard().s.len() > 1;
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:369:11
|
||||||
|
|
|
||||||
|
LL | match match i {
|
||||||
|
| ___________^
|
||||||
|
LL | | 100 => mutex1.lock().unwrap(),
|
||||||
|
LL | | _ => mutex2.lock().unwrap(),
|
||||||
|
LL | | }
|
||||||
|
LL | | .s
|
||||||
|
LL | | .len()
|
||||||
|
LL | | > 1
|
||||||
|
| |___________^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = match i {
|
||||||
|
LL + 100 => mutex1.lock().unwrap(),
|
||||||
|
LL + _ => mutex2.lock().unwrap(),
|
||||||
|
LL + }
|
||||||
|
LL + .s
|
||||||
|
LL + .len()
|
||||||
|
...
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:395:11
|
||||||
|
|
|
||||||
|
LL | match if i > 1 {
|
||||||
|
| ___________^
|
||||||
|
LL | | mutex1.lock().unwrap()
|
||||||
|
LL | | } else {
|
||||||
|
LL | | mutex2.lock().unwrap()
|
||||||
|
... |
|
||||||
|
LL | | .len()
|
||||||
|
LL | | > 1
|
||||||
|
| |___________^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ let value = if i > 1 {
|
||||||
|
LL + mutex1.lock().unwrap()
|
||||||
|
LL + } else {
|
||||||
|
LL + mutex2.lock().unwrap()
|
||||||
|
LL + }
|
||||||
|
LL + .s
|
||||||
|
...
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:449:11
|
||||||
|
|
|
||||||
|
LL | match s.lock().deref().deref() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match and create a copy
|
||||||
|
|
|
||||||
|
LL ~ let value = *s.lock().deref().deref();
|
||||||
|
LL ~ match value {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:477:11
|
||||||
|
|
|
||||||
|
LL | match s.lock().deref().deref() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:496:11
|
||||||
|
|
|
||||||
|
LL | match mutex.lock().unwrap().i = i {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ mutex.lock().unwrap().i = i;
|
||||||
|
LL ~ match () {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:502:11
|
||||||
|
|
|
||||||
|
LL | match i = mutex.lock().unwrap().i {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ i = mutex.lock().unwrap().i;
|
||||||
|
LL ~ match () {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:508:11
|
||||||
|
|
|
||||||
|
LL | match mutex.lock().unwrap().i += 1 {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ mutex.lock().unwrap().i += 1;
|
||||||
|
LL ~ match () {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: temporary with significant drop in match scrutinee
|
||||||
|
--> $DIR/significant_drop_in_scrutinee.rs:514:11
|
||||||
|
|
|
||||||
|
LL | match i += mutex.lock().unwrap().i {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try moving the temporary above the match
|
||||||
|
|
|
||||||
|
LL ~ i += mutex.lock().unwrap().i;
|
||||||
|
LL ~ match () {
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 23 previous errors
|
||||||
|
|
|
@ -67,13 +67,5 @@ LL | Self: Iterator<Item = Foo>,
|
||||||
|
|
|
|
||||||
= help: consider removing this trait bound
|
= help: consider removing this trait bound
|
||||||
|
|
||||||
error: this trait bound is already specified in the where clause
|
error: aborting due to 8 previous errors
|
||||||
--> $DIR/trait_duplication_in_bounds.rs:99:23
|
|
||||||
|
|
|
||||||
LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
|
|
||||||
| ^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= help: consider removing this trait bound
|
|
||||||
|
|
||||||
error: aborting due to 9 previous errors
|
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,5 @@ LL | Self: Copy + Default + Ord,
|
||||||
|
|
|
|
||||||
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
|
= help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
|
||||||
|
|
||||||
error: this type has already been used as a bound predicate
|
error: aborting due to 2 previous errors
|
||||||
--> $DIR/type_repetition_in_bounds.rs:83:43
|
|
||||||
|
|
|
||||||
LL | fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
|
|
||||||
| ^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= help: consider combining the bounds: `impl AsRef<str>: AsRef<str> + AsRef<str>`
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue