Skip to content

Commit e6b6c5e

Browse files
committed
Emit error for unused target expression in glob delegations
1 parent 029c9e1 commit e6b6c5e

12 files changed

Lines changed: 161 additions & 29 deletions

File tree

compiler/rustc_ast_lowering/src/delegation.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,20 @@ use rustc_abi::ExternAbi;
4646
use rustc_ast as ast;
4747
use rustc_ast::node_id::NodeMap;
4848
use rustc_ast::*;
49-
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
49+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
5050
use rustc_hir::attrs::{AttributeKind, InlineAttr};
5151
use rustc_hir::def_id::{DefId, LocalDefId};
5252
use rustc_hir::{self as hir, FnDeclFlags};
5353
use rustc_middle::span_bug;
5454
use rustc_middle::ty::{Asyncness, PerOwnerResolverData, TyCtxt};
5555
use rustc_span::symbol::kw;
56-
use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
56+
use rustc_span::{ErrorGuaranteed, ExpnId, Ident, Span, Symbol};
5757

5858
use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults};
5959
use crate::diagnostics::{
6060
CycleInDelegationSignatureResolution, DelegationAttemptedBlockWithDefsDeletion,
61-
DelegationBlockSpecifiedWhenNoParams, UnresolvedDelegationCallee,
61+
DelegationBlockSpecifiedWhenNoParams, DelegationTargetExprDeletedEverywhere,
62+
UnresolvedDelegationCallee,
6263
};
6364
use crate::{
6465
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode,
@@ -168,6 +169,30 @@ fn check_for_cycles(tcx: TyCtxt<'_>, mut def_id: DefId, span: Span) -> Result<()
168169
}
169170
}
170171

172+
pub(crate) fn check_unused_target_exprs_in_glob_delegations(
173+
tcx: TyCtxt<'_>,
174+
delegations_ids: &FxIndexSet<LocalDefId>,
175+
) {
176+
let mut delegations_by_expansions = FxIndexMap::default();
177+
for &id in delegations_ids {
178+
let expn_id = tcx.expn_that_defined(id);
179+
if expn_id == ExpnId::root() {
180+
continue;
181+
}
182+
183+
let entry = delegations_by_expansions.entry(expn_id);
184+
let entry = entry.or_insert_with(|| (true, tcx.def_span(id)));
185+
186+
entry.0 &= tcx.hir_opt_delegation_info(id).is_some_and(|info| !info.generated_target_expr);
187+
}
188+
189+
for (_, state) in delegations_by_expansions {
190+
if state.0 {
191+
tcx.dcx().emit_err(DelegationTargetExprDeletedEverywhere { span: state.1 });
192+
}
193+
}
194+
}
195+
171196
impl<'hir> LoweringContext<'_, 'hir> {
172197
fn is_method(&self, def_id: DefId, span: Span) -> bool {
173198
match self.tcx.def_kind(def_id) {
@@ -213,6 +238,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
213238
self.lower_delegation_body(delegation, sig_id, param_count, &mut generics, span);
214239

215240
let decl = self.lower_delegation_decl(
241+
delegation,
216242
sig_id,
217243
param_count,
218244
c_variadic,
@@ -375,6 +401,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
375401

376402
fn lower_delegation_decl(
377403
&mut self,
404+
delegation: &Delegation,
378405
sig_id: DefId,
379406
param_count: usize,
380407
c_variadic: bool,
@@ -395,6 +422,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
395422
span,
396423
}));
397424

425+
let is_method = self.is_method(sig_id, span);
426+
427+
// Consider target expression generated if it was not specified, as now
428+
// it is used only for list/glob delegation's unused target expression diagnostic.
429+
let generated_target_expr = delegation.body.is_none()
430+
|| (param_count > 0 && self.should_generate_block(delegation, sig_id, is_method));
431+
398432
let output = self.arena.alloc(hir::Ty {
399433
hir_id: self.next_id(),
400434
kind: hir::TyKind::InferDelegation(hir::InferDelegation::Sig(
@@ -406,6 +440,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
406440
parent_args_segment_id: generics.parent.args_segment_id,
407441
self_ty_id: generics.self_ty_id,
408442
propagate_self_ty: generics.propagate_self_ty,
443+
generated_target_expr,
409444
})),
410445
)),
411446
span,

compiler/rustc_ast_lowering/src/diagnostics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,3 +549,11 @@ pub(crate) struct DelegationAttemptedBlockWithDefsDeletion {
549549
#[primary_span]
550550
pub span: Span,
551551
}
552+
553+
// FIXME(fn_delegation): fix message when list delegations are supported
554+
#[derive(Diagnostic)]
555+
#[diag("unused target expression is specified for glob delegation")]
556+
pub(crate) struct DelegationTargetExprDeletedEverywhere {
557+
#[primary_span]
558+
pub span: Span,
559+
}

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ use rustc_hir::{
6060
use rustc_index::{Idx, IndexSlice, IndexVec};
6161
use rustc_macros::extension;
6262
use rustc_middle::hir::{self as mid_hir};
63-
use rustc_middle::queries::Providers;
6463
use rustc_middle::span_bug;
6564
use rustc_middle::ty::{DelegationInfo, PerOwnerResolverData, ResolverAstLowering, TyCtxt};
65+
use rustc_middle::util::Providers;
6666
use rustc_session::errors::add_feature_diagnostics;
6767
use rustc_span::symbol::{Ident, Symbol, kw, sym};
6868
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
@@ -93,9 +93,11 @@ mod path;
9393
pub mod stability;
9494

9595
pub fn provide(providers: &mut Providers) {
96-
providers.hir_crate = lower_to_hir;
97-
providers.lower_delayed_owner = lower_delayed_owner;
98-
providers.delegations_resolutions = delegation::delegations_resolutions;
96+
providers.queries.hir_crate = lower_to_hir;
97+
providers.queries.lower_delayed_owner = lower_delayed_owner;
98+
providers.queries.delegations_resolutions = delegation::delegations_resolutions;
99+
providers.hooks.check_unused_target_exprs_in_glob_delegations =
100+
delegation::check_unused_target_exprs_in_glob_delegations;
99101
}
100102

101103
struct LoweringContext<'a, 'hir> {

compiler/rustc_hir/src/hir.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3875,6 +3875,7 @@ pub struct DelegationInfo {
38753875
pub child_args_segment_id: Option<HirId>,
38763876
pub self_ty_id: Option<HirId>,
38773877
pub propagate_self_ty: bool,
3878+
pub generated_target_expr: bool,
38783879
}
38793880

38803881
#[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)]

compiler/rustc_hir_analysis/src/delegation.rs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
use std::debug_assert_matches;
66

77
use rustc_data_structures::fx::FxHashMap;
8+
use rustc_hir::PathSegment;
89
use rustc_hir::def::DefKind;
910
use rustc_hir::def_id::{DefId, LocalDefId};
10-
use rustc_hir::{DelegationInfo, PathSegment};
1111
use rustc_middle::ty::{
1212
self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
1313
};
@@ -71,19 +71,6 @@ enum SelfPositionKind {
7171
None,
7272
}
7373

74-
pub fn opt_get_delegation_info(
75-
tcx: TyCtxt<'_>,
76-
delegation_id: LocalDefId,
77-
) -> Option<&DelegationInfo> {
78-
tcx.hir_node(tcx.local_def_id_to_hir_id(delegation_id))
79-
.fn_sig()
80-
.and_then(|sig| sig.decl.opt_delegation_info())
81-
}
82-
83-
fn get_delegation_info(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationInfo {
84-
opt_get_delegation_info(tcx, delegation_id).expect("processing delegation")
85-
}
86-
8774
fn create_self_position_kind(
8875
tcx: TyCtxt<'_>,
8976
delegation_id: LocalDefId,
@@ -96,7 +83,7 @@ fn create_self_position_kind(
9683
| (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero,
9784

9885
(FnKind::Free, FnKind::AssocTrait) => {
99-
let propagate_self_ty = get_delegation_info(tcx, delegation_id).propagate_self_ty;
86+
let propagate_self_ty = tcx.hir_delegation_info(delegation_id).propagate_self_ty;
10087
SelfPositionKind::AfterLifetimes(propagate_self_ty)
10188
}
10289

@@ -282,7 +269,7 @@ fn get_parent_and_inheritance_kind<'tcx>(
282269
}
283270

284271
fn get_delegation_self_ty_or_err(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> Ty<'_> {
285-
get_delegation_info(tcx, delegation_id)
272+
tcx.hir_delegation_info(delegation_id)
286273
.self_ty_id
287274
.map(|id| {
288275
let ctx = ItemCtxt::new(tcx, delegation_id);
@@ -644,7 +631,7 @@ pub(crate) fn delegation_user_specified_args<'tcx>(
644631
tcx: TyCtxt<'tcx>,
645632
delegation_id: LocalDefId,
646633
) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) {
647-
let info = get_delegation_info(tcx, delegation_id);
634+
let info = tcx.hir_delegation_info(delegation_id);
648635

649636
let get_segment = |hir_id| -> Option<(&'tcx PathSegment<'tcx>, DefId)> {
650637
let segment = tcx.hir_node(hir_id).expect_path_segment();

compiler/rustc_hir_typeck/src/callee.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use rustc_hir::def::{self, CtorKind, Namespace, Res};
77
use rustc_hir::def_id::DefId;
88
use rustc_hir::{self as hir, HirId, LangItem, find_attr};
99
use rustc_hir_analysis::autoderef::Autoderef;
10-
use rustc_hir_analysis::delegation::opt_get_delegation_info;
1110
use rustc_infer::infer::BoundRegionConversionTime;
1211
use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode};
1312
use rustc_middle::ty::adjustment::{
@@ -701,7 +700,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
701700
// by comparing their hir ids (otherwise we will encounter errors in nested delegations,
702701
// see tests\ui\delegation\impl-reuse-pass.rs:237).
703702
let parent_def = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id;
704-
let Some(info) = opt_get_delegation_info(self.tcx, parent_def) else { return None };
703+
let Some(info) = self.tcx.hir_opt_delegation_info(parent_def) else {
704+
return None;
705+
};
705706

706707
if call_expr.hir_id != info.call_expr_id {
707708
return None;

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use rustc_hir::def_id::DefId;
1313
use rustc_hir::intravisit::Visitor;
1414
use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, LangItem, Node, QPath, is_range_literal};
1515
use rustc_hir_analysis::check::potentially_plural_count;
16-
use rustc_hir_analysis::delegation::opt_get_delegation_info;
1716
use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath};
1817
use rustc_index::IndexVec;
1918
use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace};
@@ -341,7 +340,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
341340

342341
// If we are processing first arg of delegation then we could have adjusted it
343342
// in `execute_delegation_aware_arguments_check`.
344-
let checked_ty = opt_get_delegation_info(self.tcx, self.body_id)
343+
let checked_ty = self
344+
.tcx
345+
.hir_opt_delegation_info(self.body_id)
345346
.and_then(|_| self.typeck_results.borrow().node_type_opt(provided_arg.hir_id))
346347
.unwrap_or_else(|| self.check_expr_with_expectation(provided_arg, expectation));
347348

compiler/rustc_interface/src/passes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -885,7 +885,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
885885
providers.queries.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1;
886886
providers.queries.early_lint_checks = early_lint_checks;
887887
providers.queries.env_var_os = env_var_os;
888-
rustc_ast_lowering::provide(&mut providers.queries);
888+
rustc_ast_lowering::provide(providers);
889889
limits::provide(&mut providers.queries);
890890
proc_macro_decls::provide(&mut providers.queries);
891891
rustc_expand::provide(&mut providers.queries);

compiler/rustc_middle/src/hir/map.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,14 @@ impl<'tcx> TyCtxt<'tcx> {
865865
self.opt_hir_owner_node(def_id)?.fn_decl()?.opt_delegation_sig_id()
866866
}
867867

868+
pub fn hir_opt_delegation_info(self, def_id: LocalDefId) -> Option<&'tcx DelegationInfo> {
869+
self.opt_hir_owner_node(def_id)?.fn_decl()?.opt_delegation_info()
870+
}
871+
872+
pub fn hir_delegation_info(self, delegation_id: LocalDefId) -> &'tcx DelegationInfo {
873+
self.hir_opt_delegation_info(delegation_id).expect("processing delegation")
874+
}
875+
868876
#[inline]
869877
fn hir_opt_ident(self, id: HirId) -> Option<Ident> {
870878
match self.hir_node(id) {
@@ -1282,6 +1290,8 @@ fn force_delayed_owners_lowering(tcx: TyCtxt<'_>) {
12821290
tcx.ensure_done().lower_delayed_owner(id);
12831291
}
12841292

1293+
tcx.check_unused_target_exprs_in_glob_delegations(&krate.delayed_ids);
1294+
12851295
let (_, krate) = krate.delayed_resolver.steal();
12861296
let prof = tcx.sess.prof.clone();
12871297

compiler/rustc_middle/src/hooks/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! similar to queries, but queries come with a lot of machinery for caching and incremental
44
//! compilation, whereas hooks are just plain function pointers without any of the query magic.
55
6+
use rustc_data_structures::fx::FxIndexSet;
67
use rustc_hir::def_id::{DefId, DefPathHash};
78
use rustc_session::StableCrateId;
89
use rustc_span::def_id::{CrateNum, LocalDefId};
@@ -113,6 +114,8 @@ declare_hooks! {
113114

114115
/// Serializes all eligible query return values into the on-disk cache.
115116
hook encode_query_values(encoder: &mut CacheEncoder<'_, 'tcx>) -> ();
117+
118+
hook check_unused_target_exprs_in_glob_delegations(ids: &FxIndexSet<LocalDefId>) -> ();
116119
}
117120

118121
#[cold]

0 commit comments

Comments
 (0)