Skip to content

Commit 128ea96

Browse files
committed
check uniqueness of nested fields
1 parent c0c5a94 commit 128ea96

File tree

13 files changed

+1841
-96
lines changed

13 files changed

+1841
-96
lines changed

compiler/rustc_ast_lowering/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1368,7 +1368,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
13681368
// } // | owner node of struct or union item
13691369
// // ^_____________________|
13701370
// }
1371-
//
1371+
// ```
13721372
TyKind::AnonStruct(def_node_id, fields) | TyKind::AnonUnion(def_node_id, fields) => {
13731373
let (def_kind, item_kind): (DefKind, fn(_, _) -> _) = match t.kind {
13741374
TyKind::AnonStruct(..) => (DefKind::Struct, hir::ItemKind::Struct),

compiler/rustc_ast_passes/src/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl<'a> AstValidator<'a> {
215215
}
216216
}
217217
TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
218-
walk_list!(self, visit_field_def, fields)
218+
walk_list!(self, visit_struct_field_def, fields)
219219
}
220220
_ => visit::walk_ty(self, t),
221221
}

compiler/rustc_hir_analysis/messages.ftl

+19
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,25 @@ hir_analysis_field_already_declared =
127127
.label = field already declared
128128
.previous_decl_label = `{$field_name}` first declared here
129129
130+
hir_analysis_field_already_declared_both_nested =
131+
field `{$field_name}` is already declared
132+
.label = field `{$field_name}` declared in this unnamed field
133+
.nested_field_decl_note = field `{$field_name}` declared here
134+
.previous_decl_label = `{$field_name}` first declared here in this unnamed field
135+
.previous_nested_field_decl_note = field `{$field_name}` first declared here
136+
137+
hir_analysis_field_already_declared_current_nested =
138+
field `{$field_name}` is already declared
139+
.label = field `{$field_name}` declared in this unnamed field
140+
.nested_field_decl_note = field `{$field_name}` declared here
141+
.previous_decl_label = `{$field_name}` first declared here
142+
143+
hir_analysis_field_already_declared_previous_nested =
144+
field `{$field_name}` is already declared
145+
.label = field already declared
146+
.previous_decl_label = `{$field_name}` first declared here in this unnamed field
147+
.previous_nested_field_decl_note = field `{$field_name}` first declared here
148+
130149
hir_analysis_function_not_found_in_trait = function not found in this trait
131150
132151
hir_analysis_function_not_have_default_implementation = function doesn't have a default implementation

compiler/rustc_hir_analysis/src/collect.rs

+137-65
Original file line numberDiff line numberDiff line change
@@ -781,64 +781,100 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
781781
}
782782
}
783783

784-
/*
785-
/// In a type definition, we check that unnamed field names are distinct.
786-
fn check_unnamed_fields_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>) {
787-
let mut seen_fields: FxHashMap<Ident, Option<Span>> = Default::default();
788-
fn check_fields_anon_adt_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, seen_fields: &mut FxHashMap<Ident, Option<Span>>) {
789-
let fields = match &item.kind {
790-
hir::ItemKind::Struct(fields, _) | hir::ItemKind::Union(fields, _) => fields,
791-
_ => return,
792-
};
793-
for field in fields.fields() {
794-
if field.ident.name == kw::Underscore {
795-
if let hir::TyKind::AnonAdt(item_id) = field.ty.kind() {
796-
let item = tcx.hir().item(item_id);
797-
check_fields_anon_adt_defn(tcx, item, &mut *seen_fields);
798-
} else {
799-
let field_ty = match tcx.type_of(field.def_id).instantiate_identity().ty_adt_def() {
800-
Some(adt_ty) => adt_ty,
801-
None => {
802-
tcx.sess.emit_err(err);
803-
return;
804-
}
805-
};
806-
if let Some(def_id) = field_ty.did().as_local() {
807-
let item = tcx.hir().item(hir::ItemId { owner_id: hir::OwnerId { def_id }});
808-
check_fields_anon_adt_defn(tcx, item, &mut *seen_fields);
809-
}
784+
#[derive(Clone, Copy)]
785+
struct NestedSpan {
786+
span: Span,
787+
nested_field_span: Span,
788+
}
789+
790+
#[derive(Clone, Copy)]
791+
enum FieldDeclSpan {
792+
NotNested(Span),
793+
Nested(NestedSpan),
794+
}
795+
796+
impl From<Span> for FieldDeclSpan {
797+
fn from(span: Span) -> Self {
798+
Self::NotNested(span)
799+
}
800+
}
801+
802+
impl From<NestedSpan> for FieldDeclSpan {
803+
fn from(span: NestedSpan) -> Self {
804+
Self::Nested(span)
805+
}
806+
}
807+
808+
/// Check the uniqueness of fields across adt where there are
809+
/// nested fields imported from an unnamed field.
810+
fn check_field_uniqueness_in_nested_adt(
811+
tcx: TyCtxt<'_>,
812+
adt_def: ty::AdtDef<'_>,
813+
check: &mut impl FnMut(Ident, /* nested_field_span */ Span),
814+
) {
815+
for field in adt_def.all_fields() {
816+
if field.is_unnamed() {
817+
// Here we don't care about the generic parameters, so `instantiate_identity` is enough.
818+
match tcx.type_of(field.did).instantiate_identity().kind() {
819+
ty::Adt(adt_def, _) => {
820+
check_field_uniqueness_in_nested_adt(tcx, *adt_def, &mut *check);
810821
}
811-
field_ty.flags()
812-
let inner_adt_def = field_ty.ty_adt_def().expect("expect an adt");
813-
check_fields_anon_adt_defn(tcx, adt_def, &mut *seen_fields);
814-
} else {
815-
let span = field.did.as_local().map(|did| {
816-
let hir_id = tcx.hir().local_def_id_to_hir_id(did);
817-
tcx.hir().span(hir_id)
818-
});
819-
match seen_fields.get(&ident.normalize_to_macros_2_0()).cloned() {
820-
Some(Some(prev_span)) => {
821-
tcx.sess.emit_err(errors::FieldAlreadyDeclared {
822-
field_name: ident,
823-
span: f.span,
824-
prev_span,
825-
});
826-
}
827-
Some(None) => {
828-
tcx.sess.emit_err(errors::FieldAlreadyDeclared {
829-
field_name: f.ident,
830-
span: f.span,
831-
prev_span,
832-
});
822+
ty_kind => bug!(
823+
"Unexpected ty kind in check_field_uniqueness_in_nested_adt(): {ty_kind:?}"
824+
),
825+
}
826+
} else {
827+
check(field.ident(tcx), tcx.def_span(field.did));
828+
}
829+
}
830+
}
831+
832+
/// Check the uniqueness of fields in a struct variant, and recursively
833+
/// check the nested fields if it is an unnamed field with type of an
834+
/// annoymous adt.
835+
fn check_field_uniqueness(
836+
tcx: TyCtxt<'_>,
837+
field: &hir::FieldDef<'_>,
838+
check: &mut impl FnMut(Ident, FieldDeclSpan),
839+
) {
840+
if field.ident.name == kw::Underscore {
841+
let ty_span = field.ty.span;
842+
match &field.ty.kind {
843+
hir::TyKind::AnonAdt(item_id) => {
844+
match &tcx.hir_node(item_id.hir_id()).expect_item().kind {
845+
hir::ItemKind::Struct(variant_data, ..)
846+
| hir::ItemKind::Union(variant_data, ..) => {
847+
variant_data
848+
.fields()
849+
.iter()
850+
.for_each(|f| check_field_uniqueness(tcx, f, &mut *check));
833851
}
834-
None =>
835-
seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
852+
item_kind => span_bug!(
853+
ty_span,
854+
"Unexpected item kind in check_field_uniqueness(): {item_kind:?}"
855+
),
836856
}
837857
}
858+
hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => {
859+
check_field_uniqueness_in_nested_adt(
860+
tcx,
861+
tcx.adt_def(res.def_id()),
862+
&mut |ident, nested_field_span| {
863+
check(ident, NestedSpan { span: field.span, nested_field_span }.into())
864+
},
865+
);
866+
}
867+
// Abort due to errors (there must be an error if an unnamed field
868+
// has any type kind other than an anonymous adt or a named adt)
869+
_ => {
870+
debug_assert!(tcx.sess.has_errors().is_some());
871+
tcx.sess.abort_if_errors()
872+
}
838873
}
874+
return;
839875
}
876+
check(field.ident, field.span.into());
840877
}
841-
*/
842878

843879
fn convert_variant(
844880
tcx: TyCtxt<'_>,
@@ -848,27 +884,61 @@ fn convert_variant(
848884
def: &hir::VariantData<'_>,
849885
adt_kind: ty::AdtKind,
850886
parent_did: LocalDefId,
887+
is_anonymous: bool,
851888
) -> ty::VariantDef {
852889
let mut has_unnamed_fields = false;
853-
let mut seen_fields: FxHashMap<Ident, Span> = Default::default();
890+
let mut seen_fields: FxHashMap<Ident, FieldDeclSpan> = Default::default();
854891
let fields = def
855892
.fields()
856893
.iter()
857894
.inspect(|f| {
858-
// Skip the unnamed field here, we will check it later.
859-
if f.ident.name == kw::Underscore {
860-
has_unnamed_fields = true;
861-
return;
862-
}
863-
let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
864-
if let Some(prev_span) = dup_span {
865-
tcx.dcx().emit_err(errors::FieldAlreadyDeclared {
866-
field_name: f.ident,
867-
span: f.span,
868-
prev_span,
895+
has_unnamed_fields |= f.ident.name == kw::Underscore;
896+
if !is_anonymous {
897+
check_field_uniqueness(tcx, f, &mut |ident, field_decl| {
898+
use FieldDeclSpan::*;
899+
let field_name = ident.name;
900+
let ident = ident.normalize_to_macros_2_0();
901+
match (field_decl, seen_fields.get(&ident).copied()) {
902+
(NotNested(span), Some(NotNested(prev_span))) => {
903+
tcx.sess.emit_err(errors::FieldAlreadyDeclared::NotNested {
904+
field_name,
905+
span,
906+
prev_span,
907+
});
908+
}
909+
(NotNested(span), Some(Nested(prev))) => {
910+
tcx.sess.emit_err(errors::FieldAlreadyDeclared::PreviousNested {
911+
field_name,
912+
span,
913+
prev_span: prev.span,
914+
prev_nested_field_span: prev.nested_field_span,
915+
});
916+
}
917+
(
918+
Nested(NestedSpan { span, nested_field_span }),
919+
Some(NotNested(prev_span)),
920+
) => {
921+
tcx.sess.emit_err(errors::FieldAlreadyDeclared::CurrentNested {
922+
field_name,
923+
span,
924+
nested_field_span,
925+
prev_span,
926+
});
927+
}
928+
(Nested(NestedSpan { span, nested_field_span }), Some(Nested(prev))) => {
929+
tcx.sess.emit_err(errors::FieldAlreadyDeclared::BothNested {
930+
field_name,
931+
span,
932+
nested_field_span,
933+
prev_span: prev.span,
934+
prev_nested_field_span: prev.nested_field_span,
935+
});
936+
}
937+
(field_decl, None) => {
938+
seen_fields.insert(ident, field_decl);
939+
}
940+
}
869941
});
870-
} else {
871-
seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
872942
}
873943
})
874944
.map(|f| ty::FieldDef {
@@ -929,6 +999,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
929999
&v.data,
9301000
AdtKind::Enum,
9311001
def_id,
1002+
is_anonymous,
9321003
)
9331004
})
9341005
.collect();
@@ -948,6 +1019,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
9481019
def,
9491020
adt_kind,
9501021
def_id,
1022+
is_anonymous,
9511023
))
9521024
.collect();
9531025

compiler/rustc_hir_analysis/src/errors.rs

+45-8
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,51 @@ pub struct DropImplOnWrongItem {
186186
}
187187

188188
#[derive(Diagnostic)]
189-
#[diag(hir_analysis_field_already_declared, code = "E0124")]
190-
pub struct FieldAlreadyDeclared {
191-
pub field_name: Ident,
192-
#[primary_span]
193-
#[label]
194-
pub span: Span,
195-
#[label(hir_analysis_previous_decl_label)]
196-
pub prev_span: Span,
189+
pub enum FieldAlreadyDeclared {
190+
#[diag(hir_analysis_field_already_declared, code = "E0124")]
191+
NotNested {
192+
field_name: Symbol,
193+
#[primary_span]
194+
#[label]
195+
span: Span,
196+
#[label(hir_analysis_previous_decl_label)]
197+
prev_span: Span,
198+
},
199+
#[diag(hir_analysis_field_already_declared_current_nested)]
200+
CurrentNested {
201+
field_name: Symbol,
202+
#[primary_span]
203+
#[label]
204+
span: Span,
205+
#[note(hir_analysis_nested_field_decl_note)]
206+
nested_field_span: Span,
207+
#[label(hir_analysis_previous_decl_label)]
208+
prev_span: Span,
209+
},
210+
#[diag(hir_analysis_field_already_declared_previous_nested)]
211+
PreviousNested {
212+
field_name: Symbol,
213+
#[primary_span]
214+
#[label]
215+
span: Span,
216+
#[label(hir_analysis_previous_decl_label)]
217+
prev_span: Span,
218+
#[note(hir_analysis_previous_nested_field_decl_note)]
219+
prev_nested_field_span: Span,
220+
},
221+
#[diag(hir_analysis_field_already_declared_both_nested)]
222+
BothNested {
223+
field_name: Symbol,
224+
#[primary_span]
225+
#[label]
226+
span: Span,
227+
#[note(hir_analysis_nested_field_decl_note)]
228+
nested_field_span: Span,
229+
#[label(hir_analysis_previous_decl_label)]
230+
prev_span: Span,
231+
#[note(hir_analysis_previous_nested_field_decl_note)]
232+
prev_nested_field_span: Span,
233+
},
197234
}
198235

199236
#[derive(Diagnostic)]

compiler/rustc_middle/src/ty/adt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ impl<'tcx> AdtDef<'tcx> {
401401
}
402402

403403
/// Returns an iterator over all fields contained
404-
/// by this ADT.
404+
/// by this ADT (nested unnamed fields are not expanded).
405405
#[inline]
406406
pub fn all_fields(self) -> impl Iterator<Item = &'tcx FieldDef> + Clone {
407407
self.variants().iter().flat_map(|v| v.fields.iter())

compiler/rustc_middle/src/ty/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -2039,6 +2039,11 @@ impl<'tcx> FieldDef {
20392039
pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
20402040
Ident::new(self.name, tcx.def_ident_span(self.did).unwrap())
20412041
}
2042+
2043+
/// Returns whether the field is unnamed
2044+
pub fn is_unnamed(&self) -> bool {
2045+
self.name == rustc_span::symbol::kw::Underscore
2046+
}
20422047
}
20432048

20442049
#[derive(Debug, PartialEq, Eq)]

0 commit comments

Comments
 (0)