Auto merge of #121053 - clubby789:derive-skip, r= · rust-lang/rust@6fc06e2 (original) (raw)
`@@ -188,6 +188,7 @@ use rustc_expand::base::{Annotatable, ExtCtxt};
`
188
188
`use rustc_session::lint::builtin::BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE;
`
189
189
`use rustc_span::symbol::{kw, sym, Ident, Symbol};
`
190
190
`use rustc_span::{Span, DUMMY_SP};
`
``
191
`+
use smallvec::SmallVec;
`
191
192
`use std::cell::RefCell;
`
192
193
`use std::iter;
`
193
194
`use std::ops::Not;
`
`@@ -263,6 +264,7 @@ pub enum FieldlessVariantsStrategy {
`
263
264
`}
`
264
265
``
265
266
`/// All the data about the data structure/method being derived upon.
`
``
267
`+
#[derive(Debug)]
`
266
268
`pub struct Substructure<'a> {
`
267
269
`/// ident of self
`
268
270
`pub type_ident: Ident,
`
`@@ -273,6 +275,7 @@ pub struct Substructure<'a> {
`
273
275
`}
`
274
276
``
275
277
`/// Summary of the relevant parts of a struct/enum field.
`
``
278
`+
#[derive(Debug)]
`
276
279
`pub struct FieldInfo {
`
277
280
`pub span: Span,
`
278
281
`/// None for tuple structs/normal enum variants, Some for normal
`
`@@ -284,9 +287,41 @@ pub struct FieldInfo {
`
284
287
`/// The expressions corresponding to references to this field in
`
285
288
`/// the other selflike arguments.
`
286
289
`pub other_selflike_exprs: Vec<P>,
`
``
290
`+
/// The derives for which this field should be ignored
`
``
291
`+
pub skipped_derives: SkippedDerives,
`
``
292
`+
}
`
``
293
+
``
294
`+
/// Derives for which this field should be ignored
`
``
295
`+
#[derive(Debug)]
`
``
296
`+
pub enum SkippedDerives {
`
``
297
`` +
/// No #[skip]
``
``
298
`+
None,
`
``
299
`` +
/// #[skip(Trait, Names)]
``
``
300
`+
List(SmallVec<[Symbol; 1]>),
`
``
301
`` +
/// #[skip]
with no arguments
``
``
302
`+
All,
`
``
303
`+
}
`
``
304
+
``
305
`+
impl SkippedDerives {
`
``
306
`+
pub fn add(&mut self, derive: Symbol) {
`
``
307
`+
match self {
`
``
308
`+
Self::None => *self = Self::List(SmallVec::from([derive])),
`
``
309
`+
Self::List(idents) => idents.push(derive),
`
``
310
`+
Self::All => (),
`
``
311
`+
}
`
``
312
`+
}
`
``
313
+
``
314
`+
pub fn is_skipped(&self, derive: Symbol) -> bool {
`
``
315
`+
match self {
`
``
316
`+
Self::None => false,
`
``
317
`+
Self::List(idents) => idents.contains(&derive),
`
``
318
`+
Self::All => true,
`
``
319
`+
}
`
``
320
`+
}
`
287
321
`}
`
288
322
``
289
323
`/// Fields for a static method
`
``
324
`+
#[derive(Debug)]
`
290
325
`pub enum StaticFields {
`
291
326
`/// Tuple and unit structs/enum variants like this.
`
292
327
`Unnamed(Vec, bool /is tuple/),
`
`@@ -295,6 +330,7 @@ pub enum StaticFields {
`
295
330
`}
`
296
331
``
297
332
`/// A summary of the possible sets of fields.
`
``
333
`+
#[derive(Debug)]
`
298
334
`pub enum SubstructureFields<'a> {
`
299
335
`` /// A non-static method where Self
is a struct.
``
300
336
`Struct(&'a ast::VariantData, Vec),
`
`@@ -1213,7 +1249,13 @@ impl<'a> MethodDef<'a> {
`
1213
1249
``
1214
1250
`let self_expr = tag_exprs.remove(0);
`
1215
1251
`let other_selflike_exprs = tag_exprs;
`
1216
``
`-
let tag_field = FieldInfo { span, name: None, self_expr, other_selflike_exprs };
`
``
1252
`+
let tag_field = FieldInfo {
`
``
1253
`+
span,
`
``
1254
`+
name: None,
`
``
1255
`+
self_expr,
`
``
1256
`+
other_selflike_exprs,
`
``
1257
`+
skipped_derives: SkippedDerives::None,
`
``
1258
`+
};
`
1217
1259
``
1218
1260
`let tag_let_stmts: ThinVec<_> = iter::zip(&tag_idents, &selflike_args)
`
1219
1261
`.map(|(&ident, selflike_arg)| {
`
`@@ -1517,7 +1559,12 @@ impl<'a> TraitDef<'a> {
`
1517
1559
`.collect()
`
1518
1560
`}
`
1519
1561
``
1520
``
`-
fn create_fields(&self, struct_def: &'a VariantData, mk_exprs: F) -> Vec
`
``
1562
`+
fn create_fields(
`
``
1563
`+
&self,
`
``
1564
`+
cx: &ExtCtxt<'_>,
`
``
1565
`+
struct_def: &'a VariantData,
`
``
1566
`+
mk_exprs: F,
`
``
1567
`+
) -> Vec
`
1521
1568
`where
`
1522
1569
`F: Fn(usize, &ast::FieldDef, Span) -> Vec<Past::Expr>,
`
1523
1570
`{
`
`@@ -1532,11 +1579,76 @@ impl<'a> TraitDef<'a> {
`
1532
1579
`let mut exprs: Vec<_> = mk_exprs(i, struct_field, sp);
`
1533
1580
`let self_expr = exprs.remove(0);
`
1534
1581
`let other_selflike_exprs = exprs;
`
``
1582
`+
let mut skipped_derives = SkippedDerives::None;
`
``
1583
`+
let skip_enabled = cx.ecfg.features.derive_skip
`
``
1584
`+
|| struct_field.span.allows_unstable(sym::derive_skip);
`
``
1585
`+
for attr in attr::filter_by_name(&struct_field.attrs, sym::skip) {
`
``
1586
`+
if !skip_enabled {
`
``
1587
`+
rustc_session::parse::feature_err(
`
``
1588
`+
&cx.sess,
`
``
1589
`+
sym::derive_skip,
`
``
1590
`+
attr.span,
`
``
1591
`` +
"the #[skip]
attribute is experimental",
``
``
1592
`+
)
`
``
1593
`+
.emit();
`
``
1594
`+
}
`
``
1595
`+
let Some(skip_attr) = ast::Attribute::meta_kind(attr) else {
`
``
1596
`+
unreachable!()
`
``
1597
`+
};
`
``
1598
+
``
1599
`+
// FIXME: better errors
`
``
1600
`+
match skip_attr {
`
``
1601
`+
ast::MetaItemKind::Word => {
`
``
1602
`+
skipped_derives = SkippedDerives::All;
`
``
1603
`+
break;
`
``
1604
`+
}
`
``
1605
`+
ast::MetaItemKind::List(items) => {
`
``
1606
`+
for item in items {
`
``
1607
`+
let span = item.span();
`
``
1608
`+
let ast::NestedMetaItem::MetaItem(ast::MetaItem {
`
``
1609
`+
path,
`
``
1610
`+
kind: ast::MetaItemKind::Word,
`
``
1611
`+
..
`
``
1612
`+
}) = item
`
``
1613
`+
else {
`
``
1614
`+
cx.dcx().emit_err(errors::DeriveSkipBadArgument {
`
``
1615
`+
span,
`
``
1616
`+
});
`
``
1617
`+
continue;
`
``
1618
`+
};
`
``
1619
`+
let name = path.segments[0].ident;
`
``
1620
`+
const SUPPORTED_TRAITS: [Symbol; 5] = [
`
``
1621
`+
sym::PartialEq,
`
``
1622
`+
sym::PartialOrd,
`
``
1623
`+
sym::Ord,
`
``
1624
`+
sym::Hash,
`
``
1625
`+
sym::Debug,
`
``
1626
`+
];
`
``
1627
`+
if SUPPORTED_TRAITS.contains(&name.name) {
`
``
1628
`+
skipped_derives.add(path.segments[0].ident.name)
`
``
1629
`+
} else {
`
``
1630
`` +
let traits = SUPPORTED_TRAITS.iter().map(|s| format!("{s}
")).collect::<Vec<_>>().join(", ");
``
``
1631
`+
cx.parse_sess().buffer_lint_with_diagnostic(
`
``
1632
`+
rustc_session::lint::builtin::UNSUPPORTED_DERIVE_SKIP,
`
``
1633
`+
span,
`
``
1634
`+
cx.current_expansion.lint_node_id,
`
``
1635
`+
crate::fluent_generated::builtin_macros_derive_skip_unsupported,
`
``
1636
`+
rustc_session::lint::BuiltinLintDiagnostics::DeriveSkipUnsupported { traits },
`
``
1637
`+
)
`
``
1638
`+
}
`
``
1639
`+
}
`
``
1640
`+
}
`
``
1641
`+
ast::MetaItemKind::NameValue(lit) => {
`
``
1642
`+
cx.dcx().emit_err(errors::DeriveSkipBadArgument { span: lit.span });
`
``
1643
`+
}
`
``
1644
`+
}
`
``
1645
`+
}
`
1535
1646
`FieldInfo {
`
1536
1647
`span: sp.with_ctxt(self.span.ctxt()),
`
1537
1648
`name: struct_field.ident,
`
1538
1649
` self_expr,
`
1539
1650
` other_selflike_exprs,
`
``
1651
`+
skipped_derives,
`
1540
1652
`}
`
1541
1653
`})
`
1542
1654
`.collect()
`
`@@ -1552,7 +1664,7 @@ impl<'a> TraitDef<'a> {
`
1552
1664
`struct_def: &'a VariantData,
`
1553
1665
`prefixes: &[String],
`
1554
1666
`) -> Vec {
`
1555
``
`-
self.create_fields(struct_def, |i, _struct_field, sp| {
`
``
1667
`+
self.create_fields(cx, struct_def, |i, _struct_field, sp| {
`
1556
1668
` prefixes
`
1557
1669
`.iter()
`
1558
1670
`.map(|prefix| {
`
`@@ -1570,7 +1682,7 @@ impl<'a> TraitDef<'a> {
`
1570
1682
`struct_def: &'a VariantData,
`
1571
1683
`is_packed: bool,
`
1572
1684
`) -> Vec {
`
1573
``
`-
self.create_fields(struct_def, |i, struct_field, sp| {
`
``
1685
`+
self.create_fields(cx, struct_def, |i, struct_field, sp| {
`
1574
1686
` selflike_args
`
1575
1687
`.iter()
`
1576
1688
`.map(|selflike_arg| {
`
`@@ -1665,13 +1777,19 @@ pub fn cs_fold(
`
1665
1777
`cx: &mut ExtCtxt<'_>,
`
1666
1778
`trait_span: Span,
`
1667
1779
`substructure: &Substructure<'_>,
`
``
1780
`+
trait_name: Symbol,
`
1668
1781
`mut f: F,
`
1669
1782
`) -> P
`
1670
1783
`where
`
1671
1784
`F: FnMut(&mut ExtCtxt<'_>, CsFold<'_>) -> P,
`
1672
1785
`{
`
1673
1786
`match substructure.fields {
`
1674
1787
`EnumMatching(.., all_fields) | Struct(_, all_fields) => {
`
``
1788
`+
let all_fields = all_fields
`
``
1789
`+
.iter()
`
``
1790
`+
.filter(|fi| !fi.skipped_derives.is_skipped(trait_name))
`
``
1791
`+
.collect::<Vec<&FieldInfo>>();
`
``
1792
+
1675
1793
`if all_fields.is_empty() {
`
1676
1794
`return f(cx, CsFold::Fieldless);
`
1677
1795
`}
`
`@@ -1684,7 +1802,7 @@ where
`
1684
1802
``
1685
1803
`let base_expr = f(cx, CsFold::Single(base_field));
`
1686
1804
``
1687
``
`-
let op = |old, field: &FieldInfo| {
`
``
1805
`+
let op = |old, field: &&FieldInfo| {
`
1688
1806
`let new = f(cx, CsFold::Single(field));
`
1689
1807
`f(cx, CsFold::Combine(field.span, old, new))
`
1690
1808
`};
`