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

`};

`