Skip to content

Commit de2d256

Browse files
reduce false positives of tail-expr-drop-order from consumed values
1 parent 1a1cc05 commit de2d256

File tree

9 files changed

+237
-49
lines changed

9 files changed

+237
-49
lines changed

compiler/rustc_hir_typeck/src/expr_use_visitor.rs

+44
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,50 @@ pub trait TypeInformationCtxt<'tcx> {
150150
fn tcx(&self) -> TyCtxt<'tcx>;
151151
}
152152

153+
impl<'tcx> TypeInformationCtxt<'tcx> for (TyCtxt<'tcx>, LocalDefId) {
154+
type TypeckResults<'a> = &'tcx ty::TypeckResults<'tcx>
155+
where
156+
Self: 'a;
157+
158+
type Error = !;
159+
160+
fn typeck_results(&self) -> Self::TypeckResults<'_> {
161+
self.0.typeck(self.1)
162+
}
163+
164+
fn resolve_vars_if_possible<T: TypeFoldable<TyCtxt<'tcx>>>(&self, t: T) -> T {
165+
t
166+
}
167+
168+
fn try_structurally_resolve_type(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
169+
ty
170+
}
171+
172+
fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error {
173+
span_bug!(span, "{}", msg.to_string())
174+
}
175+
176+
fn error_reported_in_ty(&self, _ty: Ty<'tcx>) -> Result<(), Self::Error> {
177+
Ok(())
178+
}
179+
180+
fn tainted_by_errors(&self) -> Result<(), Self::Error> {
181+
Ok(())
182+
}
183+
184+
fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>) -> bool {
185+
ty.is_copy_modulo_regions(self.0, self.0.param_env(self.1))
186+
}
187+
188+
fn body_owner_def_id(&self) -> LocalDefId {
189+
self.1
190+
}
191+
192+
fn tcx(&self) -> TyCtxt<'tcx> {
193+
self.0
194+
}
195+
}
196+
153197
impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
154198
type TypeckResults<'a> = Ref<'a, ty::TypeckResults<'tcx>>
155199
where

compiler/rustc_hir_typeck/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod fallback;
3030
mod fn_ctxt;
3131
mod gather_locals;
3232
mod intrinsicck;
33+
mod lint;
3334
mod method;
3435
mod op;
3536
mod pat;
@@ -476,5 +477,6 @@ fn fatally_break_rust(tcx: TyCtxt<'_>, span: Span) -> ! {
476477

477478
pub fn provide(providers: &mut Providers) {
478479
method::provide(providers);
480+
lint::provide(providers);
479481
*providers = Providers { typeck, diagnostic_only_typeck, used_trait_imports, ..*providers };
480482
}

compiler/rustc_hir_typeck/src/lint.rs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
use rustc_hir::def_id::DefId;
2+
use rustc_hir::{HirId, HirIdSet};
3+
use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId};
4+
use rustc_middle::mir::FakeReadCause;
5+
use rustc_middle::query::Providers;
6+
use rustc_middle::ty::{self, TyCtxt};
7+
use tracing::instrument;
8+
9+
use crate::expr_use_visitor::{Delegate, ExprUseVisitor};
10+
11+
#[derive(Default)]
12+
struct ExtractConsumingNodeDelegate {
13+
nodes: HirIdSet,
14+
}
15+
16+
impl<'tcx> Delegate<'tcx> for ExtractConsumingNodeDelegate {
17+
#[instrument(level = "debug", skip(self))]
18+
fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, _: HirId) {
19+
match place_with_id.place.base {
20+
PlaceBase::Rvalue => {
21+
self.nodes.insert(place_with_id.hir_id);
22+
}
23+
PlaceBase::Local(id) => {
24+
self.nodes.insert(id);
25+
}
26+
PlaceBase::Upvar(upvar) => {
27+
self.nodes.insert(upvar.var_path.hir_id);
28+
}
29+
PlaceBase::StaticItem => {}
30+
}
31+
}
32+
33+
fn borrow(
34+
&mut self,
35+
_place_with_id: &PlaceWithHirId<'tcx>,
36+
_diag_expr_id: HirId,
37+
_bk: ty::BorrowKind,
38+
) {
39+
// do nothing
40+
}
41+
42+
fn mutate(&mut self, _assignee_place: &PlaceWithHirId<'tcx>, _diag_expr_id: HirId) {
43+
// do nothing
44+
}
45+
46+
fn fake_read(
47+
&mut self,
48+
_place_with_id: &PlaceWithHirId<'tcx>,
49+
_cause: FakeReadCause,
50+
_diag_expr_id: HirId,
51+
) {
52+
// do nothing
53+
}
54+
}
55+
56+
fn extract_tail_expr_consuming_nodes<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx HirIdSet {
57+
let hir = tcx.hir();
58+
let body = hir.body_owned_by(def_id.expect_local());
59+
let mut delegate = ExtractConsumingNodeDelegate::default();
60+
let euv = ExprUseVisitor::new((tcx, def_id.expect_local()), &mut delegate);
61+
let _ = euv.walk_expr(body.value);
62+
tcx.arena.alloc(delegate.nodes)
63+
}
64+
65+
pub(crate) fn provide(providers: &mut Providers) {
66+
providers.extract_tail_expr_consuming_nodes = extract_tail_expr_consuming_nodes;
67+
}

compiler/rustc_lint/messages.ftl

+2-2
Original file line numberDiff line numberDiff line change
@@ -767,8 +767,8 @@ lint_suspicious_double_ref_clone =
767767
lint_suspicious_double_ref_deref =
768768
using `.deref()` on a double reference, which returns `{$ty}` instead of dereferencing the inner type
769769
770-
lint_tail_expr_drop_order = these values and local bindings have significant drop implementation that will have a different drop order from that of Edition 2021
771-
.label = these values have significant drop implementation and will observe changes in drop order under Edition 2024
770+
lint_tail_expr_drop_order = this value of type `{$ty}` has significant drop implementation that will have a different drop order from that of Edition 2021
771+
.label = these local bindings with significant drop implementation may observe changes in drop order under Edition 2024
772772
773773
lint_trailing_semi_macro = trailing semicolon in macro used in expression position
774774
.note1 = macro invocations at the end of a block are treated as expressions

compiler/rustc_lint/src/tail_expr_drop_order.rs

+57-28
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ use std::mem::swap;
33
use rustc_ast::UnOp;
44
use rustc_hir::def::Res;
55
use rustc_hir::intravisit::{self, Visitor};
6-
use rustc_hir::{self as hir, Block, Expr, ExprKind, LetStmt, Pat, PatKind, QPath, StmtKind};
6+
use rustc_hir::{
7+
self as hir, Block, Expr, ExprKind, HirIdMap, HirIdSet, LetStmt, Pat, PatKind, QPath, StmtKind,
8+
};
79
use rustc_macros::LintDiagnostic;
8-
use rustc_middle::ty;
10+
use rustc_middle::hir::place::PlaceBase;
11+
use rustc_middle::ty::{self, Ty};
912
use rustc_session::lint::FutureIncompatibilityReason;
1013
use rustc_session::{declare_lint, declare_lint_pass};
1114
use rustc_span::edition::Edition;
1215
use rustc_span::Span;
16+
use tracing::instrument;
1317

1418
use crate::{LateContext, LateLintPass};
1519

@@ -94,35 +98,41 @@ declare_lint! {
9498
declare_lint_pass!(TailExprDropOrder => [TAIL_EXPR_DROP_ORDER]);
9599

96100
impl TailExprDropOrder {
101+
#[instrument(level = "debug", skip(cx, body))]
97102
fn check_fn_or_closure<'tcx>(
98103
cx: &LateContext<'tcx>,
99104
fn_kind: hir::intravisit::FnKind<'tcx>,
100105
body: &'tcx hir::Body<'tcx>,
101106
def_id: rustc_span::def_id::LocalDefId,
102107
) {
103-
let mut locals = vec![];
108+
let consumed = cx.tcx.extract_tail_expr_consuming_nodes(def_id.to_def_id());
109+
let mut locals = HirIdMap::default();
104110
if matches!(fn_kind, hir::intravisit::FnKind::Closure) {
105111
for &capture in cx.tcx.closure_captures(def_id) {
106112
if matches!(capture.info.capture_kind, ty::UpvarCapture::ByValue)
107113
&& capture.place.ty().has_significant_drop(cx.tcx, cx.param_env)
108114
{
109-
locals.push(capture.var_ident.span);
115+
let hir_id = match capture.place.base {
116+
PlaceBase::Local(id) => id,
117+
PlaceBase::Upvar(upvar) => upvar.var_path.hir_id,
118+
PlaceBase::Rvalue | PlaceBase::StaticItem => continue,
119+
};
120+
if consumed.contains(&hir_id) {
121+
continue;
122+
}
123+
locals.insert(hir_id, capture.var_ident.span);
110124
}
111125
}
112126
}
113127
for param in body.params {
114-
if cx
115-
.typeck_results()
116-
.node_type(param.hir_id)
117-
.has_significant_drop(cx.tcx, cx.param_env)
118-
{
119-
locals.push(param.span);
120-
}
128+
LocalCollector { cx, locals: &mut locals, consumed: &consumed }.visit_pat(param.pat);
121129
}
122130
if let hir::ExprKind::Block(block, _) = body.value.kind {
123-
LintVisitor { cx, locals }.check_block_inner(block);
131+
LintVisitor { cx, locals, consumed }.check_block_inner(block);
124132
} else {
125-
LintTailExpr { cx, locals: &locals, is_root_tail_expr: true }.visit_expr(body.value);
133+
let locals: Vec<_> = locals.values().copied().collect();
134+
LintTailExpr { cx, locals: &locals, consumed, is_root_tail_expr: true }
135+
.visit_expr(body.value);
126136
}
127137
}
128138
}
@@ -146,21 +156,26 @@ impl<'tcx> LateLintPass<'tcx> for TailExprDropOrder {
146156
struct LintVisitor<'tcx, 'a> {
147157
cx: &'a LateContext<'tcx>,
148158
// We only record locals that have significant drops
149-
locals: Vec<Span>,
159+
locals: HirIdMap<Span>,
160+
consumed: &'a HirIdSet,
150161
}
151162

152163
struct LocalCollector<'tcx, 'a> {
153164
cx: &'a LateContext<'tcx>,
154-
locals: &'a mut Vec<Span>,
165+
locals: &'a mut HirIdMap<Span>,
166+
consumed: &'a HirIdSet,
155167
}
156168

157169
impl<'tcx, 'a> Visitor<'tcx> for LocalCollector<'tcx, 'a> {
158170
type Result = ();
159171
fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) {
160172
if let PatKind::Binding(_binding_mode, id, ident, pat) = pat.kind {
173+
if self.consumed.contains(&id) {
174+
return;
175+
}
161176
let ty = self.cx.typeck_results().node_type(id);
162177
if ty.has_significant_drop(self.cx.tcx, self.cx.param_env) {
163-
self.locals.push(ident.span);
178+
self.locals.insert(id, ident.span);
164179
}
165180
if let Some(pat) = pat {
166181
self.visit_pat(pat);
@@ -179,7 +194,8 @@ impl<'tcx, 'a> Visitor<'tcx> for LintVisitor<'tcx, 'a> {
179194
swap(&mut locals, &mut self.locals);
180195
}
181196
fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) {
182-
LocalCollector { cx: self.cx, locals: &mut self.locals }.visit_local(local);
197+
LocalCollector { cx: self.cx, locals: &mut self.locals, consumed: &self.consumed }
198+
.visit_local(local);
183199
}
184200
}
185201

@@ -200,19 +216,26 @@ impl<'tcx, 'a> LintVisitor<'tcx, 'a> {
200216
if self.locals.is_empty() {
201217
return;
202218
}
203-
LintTailExpr { cx: self.cx, locals: &self.locals, is_root_tail_expr: true }
204-
.visit_expr(tail_expr);
219+
let locals: Vec<_> = self.locals.values().copied().collect();
220+
LintTailExpr {
221+
cx: self.cx,
222+
locals: &locals,
223+
is_root_tail_expr: true,
224+
consumed: self.consumed,
225+
}
226+
.visit_expr(tail_expr);
205227
}
206228
}
207229

208230
struct LintTailExpr<'tcx, 'a> {
209231
cx: &'a LateContext<'tcx>,
210232
is_root_tail_expr: bool,
211233
locals: &'a [Span],
234+
consumed: &'a HirIdSet,
212235
}
213236

214237
impl<'tcx, 'a> LintTailExpr<'tcx, 'a> {
215-
fn expr_eventually_point_into_local(mut expr: &Expr<'tcx>) -> bool {
238+
fn expr_eventually_point_into_local(&self, mut expr: &Expr<'tcx>) -> bool {
216239
loop {
217240
match expr.kind {
218241
ExprKind::Index(access, _, _) | ExprKind::Field(access, _) => expr = access,
@@ -231,24 +254,28 @@ impl<'tcx, 'a> LintTailExpr<'tcx, 'a> {
231254
}
232255
}
233256

234-
fn expr_generates_nonlocal_droppy_value(&self, expr: &Expr<'tcx>) -> bool {
235-
if Self::expr_eventually_point_into_local(expr) {
236-
return false;
257+
fn expr_generates_nonlocal_droppy_value(&self, expr: &Expr<'tcx>) -> Option<Ty<'tcx>> {
258+
if self.expr_eventually_point_into_local(expr) {
259+
return None;
260+
}
261+
if self.consumed.contains(&expr.hir_id) {
262+
return None;
237263
}
238-
self.cx.typeck_results().expr_ty(expr).has_significant_drop(self.cx.tcx, self.cx.param_env)
264+
let ty = self.cx.typeck_results().expr_ty(expr);
265+
if ty.has_significant_drop(self.cx.tcx, self.cx.param_env) { Some(ty) } else { None }
239266
}
240267
}
241268

242269
impl<'tcx, 'a> Visitor<'tcx> for LintTailExpr<'tcx, 'a> {
243270
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
244271
if self.is_root_tail_expr {
245272
self.is_root_tail_expr = false;
246-
} else if self.expr_generates_nonlocal_droppy_value(expr) {
273+
} else if let Some(ty) = self.expr_generates_nonlocal_droppy_value(expr) {
247274
self.cx.tcx.emit_node_span_lint(
248275
TAIL_EXPR_DROP_ORDER,
249276
expr.hir_id,
250277
expr.span,
251-
TailExprDropOrderLint { spans: self.locals.to_vec() },
278+
TailExprDropOrderLint { spans: self.locals.to_vec(), ty },
252279
);
253280
return;
254281
}
@@ -294,13 +321,15 @@ impl<'tcx, 'a> Visitor<'tcx> for LintTailExpr<'tcx, 'a> {
294321
}
295322
}
296323
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
297-
LintVisitor { cx: self.cx, locals: <_>::default() }.check_block_inner(block);
324+
LintVisitor { cx: self.cx, locals: <_>::default(), consumed: &<_>::default() }
325+
.check_block_inner(block);
298326
}
299327
}
300328

301329
#[derive(LintDiagnostic)]
302330
#[diag(lint_tail_expr_drop_order)]
303-
struct TailExprDropOrderLint {
331+
struct TailExprDropOrderLint<'a> {
304332
#[label]
305333
pub spans: Vec<Span>,
334+
pub ty: Ty<'a>,
306335
}

compiler/rustc_middle/src/arena.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ macro_rules! arena_types {
114114
[decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph,
115115
[] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls,
116116
[] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>,
117+
[] hir_id_set: rustc_hir::HirIdSet,
117118
]);
118119
)
119120
}

compiler/rustc_middle/src/query/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_hir::def_id::{
2626
CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId,
2727
};
2828
use rustc_hir::lang_items::{LangItem, LanguageItems};
29-
use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate};
29+
use rustc_hir::{Crate, HirIdSet, ItemLocalId, ItemLocalMap, TraitCandidate};
3030
use rustc_index::IndexVec;
3131
use rustc_macros::rustc_queries;
3232
use rustc_query_system::ich::StableHashingContext;
@@ -2279,6 +2279,10 @@ rustc_queries! {
22792279
desc { "whether the item should be made inlinable across crates" }
22802280
separate_provide_extern
22812281
}
2282+
2283+
query extract_tail_expr_consuming_nodes(def_id: DefId) -> &'tcx HirIdSet {
2284+
desc { "extract hir nodes that consumes value" }
2285+
}
22822286
}
22832287

22842288
rustc_query_append! { define_callbacks! }

0 commit comments

Comments
 (0)