From 1ef8eac8ecda084b4e54d1f0b36607fa6db8097d Mon Sep 17 00:00:00 2001 From: Bernhard Schuster <bernhard@ahoi.io> Date: Thu, 29 Apr 2021 12:07:28 +0200 Subject: [PATCH] feat: add proc macro to reduce overseer mock boilerplate (#2949) --- polkadot/Cargo.lock | 12 + polkadot/node/overseer/Cargo.toml | 1 + polkadot/node/overseer/src/lib.rs | 489 +----------------- .../node/overseer/subsystems-gen/Cargo.toml | 18 + .../node/overseer/subsystems-gen/src/lib.rs | 179 +++++++ .../subsystems-gen/tests/ui/err-01-enum.rs | 13 + .../tests/ui/err-01-enum.stderr | 5 + .../tests/ui/err-01-generic-used-twice.rs | 17 + .../tests/ui/err-01-generic-used-twice.stderr | 14 + .../tests/ui/err-01-no-generic.rs | 17 + .../tests/ui/err-01-no-generics.stderr | 14 + .../tests/ui/ok-01-w-generics.rs | 17 + 12 files changed, 309 insertions(+), 487 deletions(-) create mode 100644 polkadot/node/overseer/subsystems-gen/Cargo.toml create mode 100644 polkadot/node/overseer/subsystems-gen/src/lib.rs create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr create mode 100644 polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index fb1c4bea80e..3223f4e9e84 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -6179,6 +6179,7 @@ dependencies = [ "polkadot-node-subsystem", "polkadot-node-subsystem-util", "polkadot-primitives", + "polkadot-procmacro-overseer-subsystems-gen", "sc-client-api", "sp-api", "sp-core", @@ -6229,6 +6230,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "polkadot-procmacro-overseer-subsystems-gen" +version = "0.1.0" +dependencies = [ + "assert_matches", + "proc-macro2", + "quote", + "syn", + "trybuild", +] + [[package]] name = "polkadot-procmacro-subsystem-dispatch-gen" version = "0.1.0" diff --git a/polkadot/node/overseer/Cargo.toml b/polkadot/node/overseer/Cargo.toml index 674c22eb806..3ecb90f45ff 100644 --- a/polkadot/node/overseer/Cargo.toml +++ b/polkadot/node/overseer/Cargo.toml @@ -12,6 +12,7 @@ futures = "0.3.12" futures-timer = "3.0.2" polkadot-node-primitives = { package = "polkadot-node-primitives", path = "../primitives" } polkadot-node-subsystem-util = { path = "../subsystem-util" } +polkadot-procmacro-overseer-subsystems-gen = { path = "./subsystems-gen" } polkadot-primitives = { path = "../../primitives" } polkadot-subsystem = { package = "polkadot-node-subsystem", path = "../subsystem" } tracing = "0.1.25" diff --git a/polkadot/node/overseer/src/lib.rs b/polkadot/node/overseer/src/lib.rs index f16576f6531..9f7b6239a21 100644 --- a/polkadot/node/overseer/src/lib.rs +++ b/polkadot/node/overseer/src/lib.rs @@ -94,6 +94,7 @@ pub use polkadot_subsystem::{ }; use polkadot_node_subsystem_util::{TimeoutExt, metrics::{self, prometheus}, metered, Metronome}; use polkadot_node_primitives::SpawnNamed; +use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen; // A capacity of bounded channels inside the overseer. const CHANNEL_CAPACITY: usize = 1024; @@ -143,7 +144,7 @@ impl<Client> HeadSupportsParachains for Arc<Client> where /// Each [`Subsystem`] is supposed to implement some interface that is generic over /// message type that is specific to this [`Subsystem`]. At the moment not all /// subsystems are implemented and the rest can be mocked with the [`DummySubsystem`]. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, AllSubsystemsGen)] pub struct AllSubsystems< CV = (), CB = (), CS = (), SD = (), AD = (), AR = (), BS = (), BD = (), P = (), RA = (), AS = (), NB = (), CA = (), CG = (), CP = (), ApD = (), ApV = (), @@ -244,492 +245,6 @@ impl<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> } } - /// Replace the `candidate_validation` instance in `self`. - pub fn replace_candidate_validation<NEW>( - self, - candidate_validation: NEW, - ) -> AllSubsystems<NEW, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `candidate_backing` instance in `self`. - pub fn replace_candidate_backing<NEW>( - self, - candidate_backing: NEW, - ) -> AllSubsystems<CV, NEW, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `candidate_selection` instance in `self`. - pub fn replace_candidate_selection<NEW>( - self, - candidate_selection: NEW, - ) -> AllSubsystems<CV, CB, NEW, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `statement_distribution` instance in `self`. - pub fn replace_statement_distribution<NEW>( - self, - statement_distribution: NEW, - ) -> AllSubsystems<CV, CB, CS, NEW, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `availability_distribution` instance in `self`. - pub fn replace_availability_distribution<NEW>( - self, - availability_distribution: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, NEW, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `availability_recovery` instance in `self`. - pub fn replace_availability_recovery<NEW>( - self, - availability_recovery: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, NEW, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `bitfield_signing` instance in `self`. - pub fn replace_bitfield_signing<NEW>( - self, - bitfield_signing: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, NEW, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `bitfield_distribution` instance in `self`. - pub fn replace_bitfield_distribution<NEW>( - self, - bitfield_distribution: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, NEW, P, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `provisioner` instance in `self`. - pub fn replace_provisioner<NEW>( - self, - provisioner: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, NEW, RA, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `runtime_api` instance in `self`. - pub fn replace_runtime_api<NEW>( - self, - runtime_api: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, NEW, AS, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `availability_store` instance in `self`. - pub fn replace_availability_store<NEW>( - self, - availability_store: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, NEW, NB, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `network_bridge` instance in `self`. - pub fn replace_network_bridge<NEW>( - self, - network_bridge: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NEW, CA, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `chain_api` instance in `self`. - pub fn replace_chain_api<NEW>( - self, - chain_api: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, NEW, CG, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `collation_generation` instance in `self`. - pub fn replace_collation_generation<NEW>( - self, - collation_generation: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, NEW, CP, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `collator_protocol` instance in `self`. - pub fn replace_collator_protocol<NEW>( - self, - collator_protocol: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, NEW, ApD, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `approval_distribution` instance in `self`. - pub fn replace_approval_distribution<NEW>( - self, - approval_distribution: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, NEW, ApV, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution, - approval_voting: self.approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `approval_voting` instance in `self`. - pub fn replace_approval_voting<NEW>( - self, - approval_voting: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, NEW, GS> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting, - gossip_support: self.gossip_support, - } - } - - /// Replace the `gossip_support` instance in `self`. - pub fn replace_gossip_support<NEW>( - self, - gossip_support: NEW, - ) -> AllSubsystems<CV, CB, CS, SD, AD, AR, BS, BD, P, RA, AS, NB, CA, CG, CP, ApD, ApV, NEW> { - AllSubsystems { - candidate_validation: self.candidate_validation, - candidate_backing: self.candidate_backing, - candidate_selection: self.candidate_selection, - statement_distribution: self.statement_distribution, - availability_distribution: self.availability_distribution, - availability_recovery: self.availability_recovery, - bitfield_signing: self.bitfield_signing, - bitfield_distribution: self.bitfield_distribution, - provisioner: self.provisioner, - runtime_api: self.runtime_api, - availability_store: self.availability_store, - network_bridge: self.network_bridge, - chain_api: self.chain_api, - collation_generation: self.collation_generation, - collator_protocol: self.collator_protocol, - approval_distribution: self.approval_distribution, - approval_voting: self.approval_voting, - gossip_support, - } - } - fn as_ref(&self) -> AllSubsystems<&'_ CV, &'_ CB, &'_ CS, &'_ SD, &'_ AD, &'_ AR, &'_ BS, &'_ BD, &'_ P, &'_ RA, &'_ AS, &'_ NB, &'_ CA, &'_ CG, &'_ CP, &'_ ApD, &'_ ApV, &'_ GS> { AllSubsystems { candidate_validation: &self.candidate_validation, diff --git a/polkadot/node/overseer/subsystems-gen/Cargo.toml b/polkadot/node/overseer/subsystems-gen/Cargo.toml new file mode 100644 index 00000000000..2720663c293 --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "polkadot-procmacro-overseer-subsystems-gen" +version = "0.1.0" +authors = ["Parity Technologies <admin@parity.io>"] +edition = "2018" +description = "Small proc macro to create mocking level iface type helpers" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1.0.60", features = ["full", "extra-traits"] } +quote = "1.0.9" +proc-macro2 = "1.0.24" +assert_matches = "1.5.0" + +[dev-dependencies] +trybuild = "1.0.41" diff --git a/polkadot/node/overseer/subsystems-gen/src/lib.rs b/polkadot/node/overseer/subsystems-gen/src/lib.rs new file mode 100644 index 00000000000..c15d08bb04f --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/src/lib.rs @@ -0,0 +1,179 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Polkadot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Polkadot. If not, see <http://www.gnu.org/licenses/>. + +use std::collections::HashSet; + +use proc_macro2::TokenStream; +use quote::quote; + +use syn::{Error, GenericParam, Ident, Result, Type, parse2}; + +#[proc_macro_derive(AllSubsystemsGen)] +pub fn subsystems_gen(item: proc_macro::TokenStream) -> proc_macro::TokenStream { + let item: TokenStream = item.into(); + impl_subsystems_gen(item).unwrap_or_else(|err| err.to_compile_error()).into() +} + +fn impl_subsystems_gen(item: TokenStream) -> Result<proc_macro2::TokenStream> { + let span = proc_macro2::Span::call_site(); + let ds = parse2::<syn::ItemStruct>(item.clone())?; + + match ds.fields { + syn::Fields::Named(named) => { + #[derive(Clone)] + struct NameTyTup { + field: Ident, + ty: Type, + } + let mut orig_generics = ds.generics; + // remove default types + orig_generics.params = orig_generics.params.into_iter().map(|mut generic| { + match generic { + GenericParam::Type(ref mut param) => { + param.eq_token = None; + param.default = None; + } + _ => {} + } + generic + }).collect(); + + // prepare a hashmap of generic type to member that uses it + let generic_types = orig_generics.params.iter().filter_map(|generic| { + if let GenericParam::Type(param) = generic { + Some(param.ident.clone()) + } else { + None + } + }).collect::<HashSet<Ident>>(); + + let strukt_ty = ds.ident; + + if generic_types.is_empty() { + return Err(Error::new(strukt_ty.span(), "struct must have at least one generic parameter.")) + } + + // collect all fields that exist, and all fields that are replaceable + let mut replacable_items = Vec::<NameTyTup>::with_capacity(64); + let mut all_fields = replacable_items.clone(); + + + let mut duplicate_generic_detection = HashSet::<Ident>::with_capacity(64); + + for field in named.named { + let field_ident = field.ident.clone().ok_or_else(|| Error::new(span, "Member field must have a name."))?; + let ty = field.ty.clone(); + let ntt = NameTyTup { field: field_ident, ty }; + + replacable_items.push(ntt.clone()); + + + // assure every generic is used exactly once + let ty_ident = match field.ty { + Type::Path(path) => path.path.get_ident().cloned().ok_or_else(|| { + Error::new(proc_macro2::Span::call_site(), "Expected an identifier, but got a path.") + }), + _ => return Err(Error::new(proc_macro2::Span::call_site(), "Must be path.")) + }?; + + if generic_types.contains(&ty_ident) { + if let Some(previous) = duplicate_generic_detection.replace(ty_ident) { + return Err(Error::new(previous.span(), "Generic type parameters may only be used for exactly one field, but is used more than once.")) + } + } + + all_fields.push(ntt); + } + + + let msg = "Generated by #[derive(AllSubsystemsGen)] derive proc-macro."; + let mut additive = TokenStream::new(); + + // generate an impl of `fn replace_#name` + for NameTyTup { field: replacable_item, ty: replacable_item_ty } in replacable_items { + let keeper = all_fields.iter().filter(|ntt| ntt.field != replacable_item).map(|ntt| ntt.field.clone()); + let strukt_ty = strukt_ty.clone(); + let fname = Ident::new(&format!("replace_{}", replacable_item), span); + // adjust the generics such that the appropriate member type is replaced + let mut modified_generics = orig_generics.clone(); + modified_generics.params = modified_generics.params.into_iter().map(|mut generic| { + match generic { + GenericParam::Type(ref mut param) => { + param.eq_token = None; + param.default = None; + if match &replacable_item_ty { + Type::Path(path) => + path.path.get_ident().filter(|&ident| ident == ¶m.ident).is_some(), + _ => false + } { + param.ident = Ident::new("NEW", span); + } + } + _ => {} + } + generic + }).collect(); + + additive.extend(quote! { + impl #orig_generics #strukt_ty #orig_generics { + #[doc = #msg] + pub fn #fname < NEW > (self, replacement: NEW) -> #strukt_ty #modified_generics { + #strukt_ty :: #modified_generics { + #replacable_item: replacement, + #( + #keeper: self.#keeper, + )* + } + } + } + }); + } + + Ok(additive) + } + syn::Fields::Unit => Err(Error::new(span, "Must be a struct with named fields. Not an unit struct.")), + syn::Fields::Unnamed(_) => { + Err(Error::new(span, "Must be a struct with named fields. Not an unnamed fields struct.")) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn basic() { + let item = quote! { + pub struct AllSubsystems<A,B,CD> { + pub a: A, + pub beee: B, + pub dj: CD, + } + }; + + let output = impl_subsystems_gen(item).expect("Simple example always works. qed"); + println!("//generated:"); + println!("{}", output); + } + + #[test] + fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/err-*.rs"); + t.pass("tests/ui/ok-*.rs"); + } +} diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs new file mode 100644 index 00000000000..318636279ea --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.rs @@ -0,0 +1,13 @@ +#![allow(dead_code)] + +use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen; + +#[derive(Clone, AllSubsystemsGen)] +enum AllSubsystems<A,B> { + A(A), + B(B), +} + +fn main() { + let all = AllSubsystems::<u8,u16>::A(0u8); +} diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr new file mode 100644 index 00000000000..5f61df1057c --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-enum.stderr @@ -0,0 +1,5 @@ +error: expected `struct` + --> $DIR/err-01-enum.rs:6:1 + | +6 | enum AllSubsystems<A,B> { + | ^^^^ diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs new file mode 100644 index 00000000000..f89939d5c30 --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen; + +#[derive(Clone, AllSubsystemsGen)] +struct AllSubsystems<X> { + a: X, + b: X, +} + +fn main() { + let all = AllSubsystems::<u16> { + a: 0_u16, + b: 1_u16, + }; + let _all = all.replace_a(77u8); +} diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr new file mode 100644 index 00000000000..23e1404ff82 --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-generic-used-twice.stderr @@ -0,0 +1,14 @@ +error: Generic type parameters may only be used for exactly one field, but is used more than once. + --> $DIR/err-01-generic-used-twice.rs:7:5 + | +7 | a: X, + | ^ + +error[E0599]: no method named `replace_a` found for struct `AllSubsystems<u16>` in the current scope + --> $DIR/err-01-generic-used-twice.rs:16:17 + | +6 | struct AllSubsystems<X> { + | ----------------------- method `replace_a` not found for this +... +16 | let _all = all.replace_a(77u8); + | ^^^^^^^^^ method not found in `AllSubsystems<u16>` diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs new file mode 100644 index 00000000000..0466eb444cd --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generic.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen; + +#[derive(Clone, AllSubsystemsGen)] +struct AllSubsystems { + a: f32, + b: u16, +} + +fn main() { + let all = AllSubsystems { + a: 0_f32, + b: 1_u16, + }; + let _all = all.replace_a(77u8); +} diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr new file mode 100644 index 00000000000..1de880ae433 --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/err-01-no-generics.stderr @@ -0,0 +1,14 @@ +error: Generic type parameters may only be used once have at least one generic parameter. + --> $DIR/err-01-no-generics.rs:7:5 + | +7 | a: X, + | ^ + +error[E0599]: no method named `replace_a` found for struct `AllSubsystems<u16>` in the current scope + --> $DIR/err-01-no-generics.rs:16:17 + | +6 | struct AllSubsystems<X> { + | ----------------------- method `replace_a` not found for this +... +16 | let _all = all.replace_a(77u8); + | ^^^^^^^^^ method not found in `AllSubsystems<u16>` diff --git a/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs b/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs new file mode 100644 index 00000000000..1519990a0a5 --- /dev/null +++ b/polkadot/node/overseer/subsystems-gen/tests/ui/ok-01-w-generics.rs @@ -0,0 +1,17 @@ +#![allow(dead_code)] + +use polkadot_procmacro_overseer_subsystems_gen::AllSubsystemsGen; + +#[derive(Clone, AllSubsystemsGen)] +struct AllSubsystems<A, B> { + a: A, + b: B, +} + +fn main() { + let all = AllSubsystems::<u8, u16> { + a: 0u8, + b: 1u16, + }; + let _all: AllSubsystems<_,_> = all.replace_a::<u32>(777_777u32); +} -- GitLab