Skip to content

Commit cc60b4c

Browse files
committed
Auto merge of rust-lang#121270 - clubby789:more-unnamed-fields-checks, r=<try>
Check for accessing unnamed field in `find_field` Fixes rust-lang#121263 I'm not entirely sure this is the right solution, but it seems to work 😓
2 parents 6122397 + c863574 commit cc60b4c

File tree

8 files changed

+149
-37
lines changed

8 files changed

+149
-37
lines changed

compiler/rustc_builtin_macros/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -245,3 +245,9 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal
245245
.other = for example, write `#[derive(Debug)]` for `Debug`
246246
247247
builtin_macros_unnameable_test_items = cannot test inner items
248+
249+
builtin_macros_unnamed_field_derive = only `Copy` and `Clone` may be derived on structs with unnamed fields
250+
.note = unnamed field
251+
252+
builtin_macros_unnamed_field_derive_clone =
253+
deriving `Clone` on a type with unnamed fields requires also deriving `Copy`

compiler/rustc_builtin_macros/src/deriving/clone.rs

+41-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::deriving::generic::ty::*;
22
use crate::deriving::generic::*;
33
use crate::deriving::path_std;
4+
use crate::errors;
45
use rustc_ast::{self as ast, Generics, ItemKind, MetaItem, VariantData};
56
use rustc_data_structures::fx::FxHashSet;
67
use rustc_expand::base::{Annotatable, ExtCtxt};
@@ -32,42 +33,52 @@ pub fn expand_deriving_clone(
3233
let bounds;
3334
let substructure;
3435
let is_simple;
35-
match item {
36-
Annotatable::Item(annitem) => match &annitem.kind {
37-
ItemKind::Struct(_, Generics { params, .. })
38-
| ItemKind::Enum(_, Generics { params, .. }) => {
39-
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
40-
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
41-
if has_derive_copy
42-
&& !params
43-
.iter()
44-
.any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
45-
{
46-
bounds = vec![];
47-
is_simple = true;
48-
substructure = combine_substructure(Box::new(|c, s, sub| {
49-
cs_clone_simple("Clone", c, s, sub, false)
50-
}));
51-
} else {
52-
bounds = vec![];
53-
is_simple = false;
54-
substructure =
55-
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
56-
}
57-
}
58-
ItemKind::Union(..) => {
59-
bounds = vec![Path(path_std!(marker::Copy))];
36+
let Annotatable::Item(annitem) = item else {
37+
cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item")
38+
};
39+
let has_unnamed = if let ItemKind::Struct(VariantData::Struct { fields, .. }, _) = &annitem.kind
40+
{
41+
fields.iter().any(|f| f.ident.is_some_and(|i| i.name == kw::Underscore))
42+
} else {
43+
false
44+
};
45+
match &annitem.kind {
46+
ItemKind::Struct(_, Generics { params, .. })
47+
| ItemKind::Enum(_, Generics { params, .. }) => {
48+
let container_id = cx.current_expansion.id.expn_data().parent.expect_local();
49+
let has_derive_copy = cx.resolver.has_derive_copy(container_id);
50+
if has_derive_copy
51+
&& !params
52+
.iter()
53+
.any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. }))
54+
{
55+
bounds = vec![];
6056
is_simple = true;
6157
substructure = combine_substructure(Box::new(|c, s, sub| {
62-
cs_clone_simple("Clone", c, s, sub, true)
58+
cs_clone_simple("Clone", c, s, sub, false)
6359
}));
60+
} else {
61+
bounds = vec![];
62+
is_simple = false;
63+
substructure =
64+
combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub)));
6465
}
65-
_ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on wrong item kind"),
66-
},
67-
68-
_ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on trait item or impl item"),
66+
}
67+
ItemKind::Union(..) => {
68+
bounds = vec![Path(path_std!(marker::Copy))];
69+
is_simple = true;
70+
substructure = combine_substructure(Box::new(|c, s, sub| {
71+
cs_clone_simple("Clone", c, s, sub, true)
72+
}));
73+
}
74+
_ => cx.dcx().span_bug(span, "`#[derive(Clone)]` on wrong item kind"),
6975
}
7076

77+
if !is_simple && has_unnamed {
78+
cx.dcx().emit_err(errors::UnnamedFieldDeriveClone { span });
79+
return;
80+
};
81+
7182
let trait_def = TraitDef {
7283
span,
7384
path: path_std!(clone::Clone),

compiler/rustc_builtin_macros/src/deriving/generic/mod.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,26 @@ impl<'a> TraitDef<'a> {
475475
false
476476
});
477477

478+
if let ast::ItemKind::Struct(struct_def, _) | ast::ItemKind::Union(struct_def, _) =
479+
&item.kind
480+
&& let Some(&trait_name) = self.path.components().last()
481+
&& !(trait_name == sym::Copy || trait_name == sym::Clone)
482+
{
483+
let unnamed = struct_def
484+
.fields()
485+
.iter()
486+
.filter(|f| f.ident.is_some_and(|i| i.name == kw::Underscore))
487+
.map(|f| f.span)
488+
.collect::<Vec<_>>();
489+
if !unnamed.is_empty() {
490+
cx.dcx().emit_err(errors::UnnamedFieldDerive {
491+
span: self.span,
492+
fields: unnamed,
493+
});
494+
return;
495+
}
496+
};
497+
478498
let newitem = match &item.kind {
479499
ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def(
480500
cx,
@@ -1578,14 +1598,12 @@ impl<'a> TraitDef<'a> {
15781598
// `unwrap_or_else` case otherwise the hygiene is wrong and we get
15791599
// "field `0` of struct `Point` is private" errors on tuple
15801600
// structs.
1581-
let mut field_expr = cx.expr(
1601+
let mut field_expr = cx.expr_field(
15821602
sp,
1583-
ast::ExprKind::Field(
1584-
selflike_arg.clone(),
1585-
struct_field.ident.unwrap_or_else(|| {
1586-
Ident::from_str_and_span(&i.to_string(), struct_field.span)
1587-
}),
1588-
),
1603+
selflike_arg.clone(),
1604+
struct_field.ident.unwrap_or_else(|| {
1605+
Ident::from_str_and_span(&i.to_string(), struct_field.span)
1606+
}),
15891607
);
15901608
if is_packed {
15911609
// In general, fields in packed structs are copied via a

compiler/rustc_builtin_macros/src/deriving/generic/ty.rs

+5
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl Path {
4848
) -> P<ast::Ty> {
4949
cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
5050
}
51+
5152
pub fn to_path(
5253
&self,
5354
cx: &ExtCtxt<'_>,
@@ -69,6 +70,10 @@ impl Path {
6970
}
7071
}
7172
}
73+
74+
pub fn components(&self) -> &[Symbol] {
75+
&self.path
76+
}
7277
}
7378

7479
/// A type. Supports pointers, Self, and literals.

compiler/rustc_builtin_macros/src/errors.rs

+16
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,22 @@ pub(crate) struct TestRunnerNargs {
842842
pub(crate) span: Span,
843843
}
844844

845+
#[derive(Diagnostic)]
846+
#[diag(builtin_macros_unnamed_field_derive)]
847+
pub(crate) struct UnnamedFieldDerive {
848+
#[primary_span]
849+
pub(crate) span: Span,
850+
#[note]
851+
pub(crate) fields: Vec<Span>,
852+
}
853+
854+
#[derive(Diagnostic)]
855+
#[diag(builtin_macros_unnamed_field_derive_clone)]
856+
pub(crate) struct UnnamedFieldDeriveClone {
857+
#[primary_span]
858+
pub(crate) span: Span,
859+
}
860+
845861
#[derive(Diagnostic)]
846862
#[diag(builtin_macros_expected_register_class_or_explicit_register)]
847863
pub(crate) struct ExpectedRegisterClassOrExplicitRegister {

compiler/rustc_expand/src/build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ impl<'a> ExtCtxt<'a> {
259259
}
260260

261261
pub fn expr_field(&self, span: Span, expr: P<Expr>, field: Ident) -> P<ast::Expr> {
262+
debug_assert_ne!(field.name, kw::Underscore);
262263
self.expr(span, ast::ExprKind::Field(expr, field))
263264
}
264265

@@ -323,6 +324,9 @@ impl<'a> ExtCtxt<'a> {
323324
is_placeholder: false,
324325
}
325326
}
327+
pub fn expr_err(&self, span: Span) -> P<ast::Expr> {
328+
self.expr(span, ast::ExprKind::Err)
329+
}
326330
pub fn expr_struct(
327331
&self,
328332
span: Span,
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#![allow(incomplete_features)]
2+
#![feature(unnamed_fields)]
3+
4+
#[derive(Clone, Copy, Debug)]
5+
#[repr(C)]
6+
struct Foo {
7+
a: u8,
8+
}
9+
10+
#[repr(C)]
11+
#[derive(Debug)] //~ ERROR only `Copy` and `Clone` may be derived on structs with unnamed fields
12+
struct TestUnsupported {
13+
_: Foo,
14+
}
15+
16+
#[repr(C)]
17+
#[derive(Clone, Copy)]
18+
struct Test {
19+
_: Foo,
20+
}
21+
22+
#[repr(C)]
23+
#[derive(Clone)] //~ ERROR deriving `Clone` on a type with unnamed fields requires also deriving `Copy`
24+
struct TestClone {
25+
_: Foo,
26+
}
27+
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: only `Copy` and `Clone` may be derived on structs with unnamed fields
2+
--> $DIR/derive.rs:11:10
3+
|
4+
LL | #[derive(Debug)]
5+
| ^^^^^
6+
|
7+
note: unnamed field
8+
--> $DIR/derive.rs:13:5
9+
|
10+
LL | _: Foo,
11+
| ^^^^^^
12+
= note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)
13+
14+
error: deriving `Clone` on a type with unnamed fields requires also deriving `Copy`
15+
--> $DIR/derive.rs:23:10
16+
|
17+
LL | #[derive(Clone)]
18+
| ^^^^^
19+
|
20+
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)