diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock
index c99212ba06ba8ce1024167b84d4107db9e801077..ed0d5777d94fee6e5dc65376f34a94168786c342 100644
--- a/substrate/Cargo.lock
+++ b/substrate/Cargo.lock
@@ -852,6 +852,15 @@ dependencies = [
  "nom",
 ]
 
+[[package]]
+name = "cfg-expr"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db"
+dependencies = [
+ "smallvec",
+]
+
 [[package]]
 name = "cfg-if"
 version = "0.1.10"
@@ -2284,7 +2293,9 @@ name = "frame-support-procedural"
 version = "4.0.0-dev"
 dependencies = [
  "Inflector",
+ "cfg-expr",
  "frame-support-procedural-tools",
+ "itertools",
  "proc-macro2",
  "quote",
  "syn",
diff --git a/substrate/frame/support/procedural/Cargo.toml b/substrate/frame/support/procedural/Cargo.toml
index 7ddec39cad9fb1b7773b8a825418a83c7d583069..06b8056aff9829079aeafe5975f77a7f61d7609a 100644
--- a/substrate/frame/support/procedural/Cargo.toml
+++ b/substrate/frame/support/procedural/Cargo.toml
@@ -16,6 +16,8 @@ proc-macro = true
 
 [dependencies]
 Inflector = "0.11.4"
+cfg-expr = "0.10.3"
+itertools = "0.10.3"
 proc-macro2 = "1.0.37"
 quote = "1.0.10"
 syn = { version = "1.0.98", features = ["full"] }
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs
index 801b69035121dddcf982d12e573adad532a7881f..028e68f0198d17abfa1166ced3b35b5c054b569a 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/call.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::Pallet;
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::Ident;
 
 pub fn expand_outer_dispatch(
@@ -30,6 +31,7 @@ pub fn expand_outer_dispatch(
 	let mut variant_patterns = Vec::new();
 	let mut query_call_part_macros = Vec::new();
 	let mut pallet_names = Vec::new();
+	let mut pallet_attrs = Vec::new();
 	let system_path = &system_pallet.path;
 
 	let pallets_with_call = pallet_decls.iter().filter(|decl| decl.exists_part("Call"));
@@ -38,12 +40,24 @@ pub fn expand_outer_dispatch(
 		let name = &pallet_declaration.name;
 		let path = &pallet_declaration.path;
 		let index = pallet_declaration.index;
+		let attr =
+			pallet_declaration.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+				let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			});
 
-		variant_defs.extend(
-			quote!(#[codec(index = #index)] #name( #scrate::dispatch::CallableCallFor<#name, #runtime> ),),
-		);
+		variant_defs.extend(quote! {
+			#attr
+			#[codec(index = #index)]
+			#name( #scrate::dispatch::CallableCallFor<#name, #runtime> ),
+		});
 		variant_patterns.push(quote!(Call::#name(call)));
 		pallet_names.push(name);
+		pallet_attrs.push(attr);
 		query_call_part_macros.push(quote! {
 			#path::__substrate_call_check::is_call_part_defined!(#name);
 		});
@@ -69,6 +83,7 @@ pub fn expand_outer_dispatch(
 				use #scrate::dispatch::Callable;
 				use core::mem::size_of;
 				&[#(
+					#pallet_attrs
 					(
 						stringify!(#pallet_names),
 						size_of::< <#pallet_names as Callable<#runtime>>::Call >(),
@@ -101,7 +116,10 @@ pub fn expand_outer_dispatch(
 		impl #scrate::dispatch::GetDispatchInfo for Call {
 			fn get_dispatch_info(&self) -> #scrate::dispatch::DispatchInfo {
 				match self {
-					#( #variant_patterns => call.get_dispatch_info(), )*
+					#(
+						#pallet_attrs
+						#variant_patterns => call.get_dispatch_info(),
+					)*
 				}
 			}
 		}
@@ -110,6 +128,7 @@ pub fn expand_outer_dispatch(
 				use #scrate::dispatch::GetCallName;
 				match self {
 					#(
+						#pallet_attrs
 						#variant_patterns => {
 							let function_name = call.get_call_name();
 							let pallet_name = stringify!(#pallet_names);
@@ -121,6 +140,7 @@ pub fn expand_outer_dispatch(
 
 			fn get_module_names() -> &'static [&'static str] {
 				&[#(
+					#pallet_attrs
 					stringify!(#pallet_names),
 				)*]
 			}
@@ -129,6 +149,7 @@ pub fn expand_outer_dispatch(
 				use #scrate::dispatch::{Callable, GetCallName};
 				match module {
 					#(
+						#pallet_attrs
 						stringify!(#pallet_names) =>
 							<<#pallet_names as Callable<#runtime>>::Call
 								as GetCallName>::get_call_names(),
@@ -157,6 +178,7 @@ pub fn expand_outer_dispatch(
 			fn dispatch_bypass_filter(self, origin: Origin) -> #scrate::dispatch::DispatchResultWithPostInfo {
 				match self {
 					#(
+						#pallet_attrs
 						#variant_patterns =>
 							#scrate::traits::UnfilteredDispatchable::dispatch_bypass_filter(call, origin),
 					)*
@@ -165,6 +187,7 @@ pub fn expand_outer_dispatch(
 		}
 
 		#(
+			#pallet_attrs
 			impl #scrate::traits::IsSubType<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for Call {
 				#[allow(unreachable_patterns)]
 				fn is_sub_type(&self) -> Option<&#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> {
@@ -176,6 +199,7 @@ pub fn expand_outer_dispatch(
 				}
 			}
 
+			#pallet_attrs
 			impl From<#scrate::dispatch::CallableCallFor<#pallet_names, #runtime>> for Call {
 				fn from(call: #scrate::dispatch::CallableCallFor<#pallet_names, #runtime>) -> Self {
 					#variant_patterns
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs
index a3d70f18529c7f2b5148290e92d4b1bd7d2dc058..9b731a5825a3c21fe9e322d6bdd18a6cf8aa7b28 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/config.rs
@@ -19,6 +19,7 @@ use crate::construct_runtime::Pallet;
 use inflector::Inflector;
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote, ToTokens};
+use std::str::FromStr;
 use syn::Ident;
 
 pub fn expand_outer_config(
@@ -40,11 +41,19 @@ pub fn expand_outer_config(
 			let field_name =
 				&Ident::new(&pallet_name.to_string().to_snake_case(), decl.name.span());
 			let part_is_generic = !pallet_entry.generics.params.is_empty();
+			let attr = &decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+				let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			});
 
-			types.extend(expand_config_types(runtime, decl, &config, part_is_generic));
-			fields.extend(quote!(pub #field_name: #config,));
+			types.extend(expand_config_types(attr, runtime, decl, &config, part_is_generic));
+			fields.extend(quote!(#attr pub #field_name: #config,));
 			build_storage_calls
-				.extend(expand_config_build_storage_call(scrate, runtime, decl, field_name));
+				.extend(expand_config_build_storage_call(scrate, attr, runtime, decl, field_name));
 			query_genesis_config_part_macros.push(quote! {
 				#path::__substrate_genesis_config_check::is_genesis_config_defined!(#pallet_name);
 				#[cfg(feature = "std")]
@@ -88,6 +97,7 @@ pub fn expand_outer_config(
 }
 
 fn expand_config_types(
+	attr: &TokenStream,
 	runtime: &Ident,
 	decl: &Pallet,
 	config: &Ident,
@@ -97,14 +107,17 @@ fn expand_config_types(
 
 	match (decl.instance.as_ref(), part_is_generic) {
 		(Some(inst), true) => quote! {
+			#attr
 			#[cfg(any(feature = "std", test))]
 			pub type #config = #path::GenesisConfig<#runtime, #path::#inst>;
 		},
 		(None, true) => quote! {
+			#attr
 			#[cfg(any(feature = "std", test))]
 			pub type #config = #path::GenesisConfig<#runtime>;
 		},
 		(_, false) => quote! {
+			#attr
 			#[cfg(any(feature = "std", test))]
 			pub type #config = #path::GenesisConfig;
 		},
@@ -113,6 +126,7 @@ fn expand_config_types(
 
 fn expand_config_build_storage_call(
 	scrate: &TokenStream,
+	attr: &TokenStream,
 	runtime: &Ident,
 	decl: &Pallet,
 	field_name: &Ident,
@@ -125,6 +139,7 @@ fn expand_config_build_storage_call(
 	};
 
 	quote! {
+		#attr
 		#scrate::sp_runtime::BuildModuleGenesisStorage::
 			<#runtime, #instance>::build_module_genesis_storage(&self.#field_name, storage)?;
 	}
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs
index b242f9641562c039b17bf385ff46cf1282d1cb0b..f145327d37af59ae73d25bafccc316c3c4d924e6 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/event.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::Pallet;
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::{Generics, Ident};
 
 pub fn expand_outer_event(
@@ -97,19 +98,35 @@ fn expand_event_variant(
 	let path = &pallet.path;
 	let variant_name = &pallet.name;
 	let part_is_generic = !generics.params.is_empty();
+	let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+		let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+			.expect("was successfully parsed before; qed");
+		quote! {
+			#acc
+			#attr
+		}
+	});
 
 	match instance {
-		Some(inst) if part_is_generic => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Event<#runtime, #path::#inst>),)
+		Some(inst) if part_is_generic => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Event<#runtime, #path::#inst>),
 		},
-		Some(inst) => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Event<#path::#inst>),)
+		Some(inst) => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Event<#path::#inst>),
 		},
-		None if part_is_generic => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Event<#runtime>),)
+		None if part_is_generic => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Event<#runtime>),
 		},
-		None => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Event),)
+		None => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Event),
 		},
 	}
 }
@@ -120,13 +137,23 @@ fn expand_event_conversion(
 	pallet_event: &TokenStream,
 ) -> TokenStream {
 	let variant_name = &pallet.name;
+	let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+		let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+			.expect("was successfully parsed before; qed");
+		quote! {
+			#acc
+			#attr
+		}
+	});
 
 	quote! {
+		#attr
 		impl From<#pallet_event> for Event {
 			fn from(x: #pallet_event) -> Self {
 				Event::#variant_name(x)
 			}
 		}
+		#attr
 		impl TryInto<#pallet_event> for Event {
 			type Error = ();
 
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs
index 0f0d538643240811e0a153bb87c89e99ad1f26ee..599b34ba87241e588dd018739a4465bfb0f473ef 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/inherent.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::Pallet;
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::{Ident, TypePath};
 
 pub fn expand_outer_inherent(
@@ -28,14 +29,24 @@ pub fn expand_outer_inherent(
 	scrate: &TokenStream,
 ) -> TokenStream {
 	let mut pallet_names = Vec::new();
+	let mut pallet_attrs = Vec::new();
 	let mut query_inherent_part_macros = Vec::new();
 
 	for pallet_decl in pallet_decls {
 		if pallet_decl.exists_part("Inherent") {
 			let name = &pallet_decl.name;
 			let path = &pallet_decl.path;
+			let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+				let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			});
 
 			pallet_names.push(name);
+			pallet_attrs.push(attr);
 			query_inherent_part_macros.push(quote! {
 				#path::__substrate_inherent_check::is_inherent_part_defined!(#name);
 			});
@@ -60,6 +71,7 @@ pub fn expand_outer_inherent(
 				let mut inherents = Vec::new();
 
 				#(
+					#pallet_attrs
 					if let Some(inherent) = #pallet_names::create_inherent(self) {
 						let inherent = <#unchecked_extrinsic as #scrate::inherent::Extrinsic>::new(
 							inherent.into(),
@@ -90,22 +102,25 @@ pub fn expand_outer_inherent(
 
 					let mut is_inherent = false;
 
-					#({
-						let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt);
-						if let Some(call) = IsSubType::<_>::is_sub_type(call) {
-							if #pallet_names::is_inherent(call) {
-								is_inherent = true;
-								if let Err(e) = #pallet_names::check_inherent(call, self) {
-									result.put_error(
-										#pallet_names::INHERENT_IDENTIFIER, &e
-									).expect("There is only one fatal error; qed");
-									if e.is_fatal_error() {
-										return result;
+					#(
+						#pallet_attrs
+						{
+							let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt);
+							if let Some(call) = IsSubType::<_>::is_sub_type(call) {
+								if #pallet_names::is_inherent(call) {
+									is_inherent = true;
+									if let Err(e) = #pallet_names::check_inherent(call, self) {
+										result.put_error(
+											#pallet_names::INHERENT_IDENTIFIER, &e
+										).expect("There is only one fatal error; qed");
+										if e.is_fatal_error() {
+											return result;
+										}
 									}
 								}
 							}
 						}
-					})*
+					)*
 
 					// Inherents are before any other extrinsics.
 					// No module marked it as inherent thus it is not.
@@ -115,6 +130,7 @@ pub fn expand_outer_inherent(
 				}
 
 				#(
+					#pallet_attrs
 					match #pallet_names::is_inherent_required(self) {
 						Ok(Some(e)) => {
 							let found = block.extrinsics().iter().any(|xt| {
@@ -177,14 +193,17 @@ pub fn expand_outer_inherent(
 						false
 					} else {
 						let mut is_inherent = false;
-						#({
-							let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt);
-							if let Some(call) = IsSubType::<_>::is_sub_type(call) {
-								if #pallet_names::is_inherent(&call) {
-									is_inherent = true;
+						#(
+							#pallet_attrs
+							{
+								let call = <#unchecked_extrinsic as ExtrinsicCall>::call(xt);
+								if let Some(call) = IsSubType::<_>::is_sub_type(call) {
+									if #pallet_names::is_inherent(&call) {
+										is_inherent = true;
+									}
 								}
 							}
-						})*
+						)*
 						is_inherent
 					};
 
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs
index 6e2dd5fc002c6ceb0a282e9780e35fc45d2bb0ca..ec90a0d30f98b1319d417c5e4e5387e05044df45 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/metadata.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::Pallet;
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::{Ident, TypePath};
 
 pub fn expand_runtime_metadata(
@@ -47,8 +48,17 @@ pub fn expand_runtime_metadata(
 			let event = expand_pallet_metadata_events(&filtered_names, runtime, scrate, decl);
 			let constants = expand_pallet_metadata_constants(runtime, decl);
 			let errors = expand_pallet_metadata_errors(runtime, decl);
+			let attr = decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+				let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			});
 
 			quote! {
+				#attr
 				#scrate::metadata::PalletMetadata {
 					name: stringify!(#name),
 					index: #index,
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
index 46f08832f0bb4c4bd9079a7c80ae525d2c6f789d..a4bb0d9cbaa028083f4eb22482743df7aed4d54e 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::{Pallet, SYSTEM_PALLET_NAME};
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::{Generics, Ident};
 
 pub fn expand_outer_origin(
@@ -303,19 +304,35 @@ fn expand_origin_caller_variant(
 	let part_is_generic = !generics.params.is_empty();
 	let variant_name = &pallet.name;
 	let path = &pallet.path;
+	let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+		let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+			.expect("was successfully parsed before; qed");
+		quote! {
+			#acc
+			#attr
+		}
+	});
 
 	match instance {
-		Some(inst) if part_is_generic => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Origin<#runtime, #path::#inst>),)
+		Some(inst) if part_is_generic => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Origin<#runtime, #path::#inst>),
 		},
-		Some(inst) => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Origin<#path::#inst>),)
+		Some(inst) => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Origin<#path::#inst>),
 		},
-		None if part_is_generic => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Origin<#runtime>),)
+		None if part_is_generic => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Origin<#runtime>),
 		},
-		None => {
-			quote!(#[codec(index = #index)] #variant_name(#path::Origin),)
+		None => quote! {
+			#attr
+			#[codec(index = #index)]
+			#variant_name(#path::Origin),
 		},
 	}
 }
@@ -339,14 +356,24 @@ fn expand_origin_pallet_conversions(
 	};
 
 	let doc_string = get_intra_doc_string(" Convert to runtime origin using", &path.module_name());
+	let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+		let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+			.expect("was successfully parsed before; qed");
+		quote! {
+			#acc
+			#attr
+		}
+	});
 
 	quote! {
+		#attr
 		impl From<#pallet_origin> for OriginCaller {
 			fn from(x: #pallet_origin) -> Self {
 				OriginCaller::#variant_name(x)
 			}
 		}
 
+		#attr
 		impl From<#pallet_origin> for Origin {
 			#[doc = #doc_string]
 			fn from(x: #pallet_origin) -> Self {
@@ -355,6 +382,7 @@ fn expand_origin_pallet_conversions(
 			}
 		}
 
+		#attr
 		impl From<Origin> for #scrate::sp_std::result::Result<#pallet_origin, Origin> {
 			/// NOTE: converting to pallet origin loses the origin filter information.
 			fn from(val: Origin) -> Self {
@@ -366,6 +394,7 @@ fn expand_origin_pallet_conversions(
 			}
 		}
 
+		#attr
 		impl TryFrom<OriginCaller> for #pallet_origin {
 			type Error = OriginCaller;
 			fn try_from(
diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/unsigned.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/unsigned.rs
index c0306768020933add2113632f05528bf5c0495d6..310516793472f4d146b1b0d8fed49cceafb9bf86 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/expand/unsigned.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/expand/unsigned.rs
@@ -18,6 +18,7 @@
 use crate::construct_runtime::Pallet;
 use proc_macro2::TokenStream;
 use quote::quote;
+use std::str::FromStr;
 use syn::Ident;
 
 pub fn expand_outer_validate_unsigned(
@@ -26,14 +27,24 @@ pub fn expand_outer_validate_unsigned(
 	scrate: &TokenStream,
 ) -> TokenStream {
 	let mut pallet_names = Vec::new();
+	let mut pallet_attrs = Vec::new();
 	let mut query_validate_unsigned_part_macros = Vec::new();
 
 	for pallet_decl in pallet_decls {
 		if pallet_decl.exists_part("ValidateUnsigned") {
 			let name = &pallet_decl.name;
 			let path = &pallet_decl.path;
+			let attr = pallet_decl.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
+				let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			});
 
 			pallet_names.push(name);
+			pallet_attrs.push(attr);
 			query_validate_unsigned_part_macros.push(quote! {
 				#path::__substrate_validate_unsigned_check::is_validate_unsigned_part_defined!(#name);
 			});
@@ -49,7 +60,10 @@ pub fn expand_outer_validate_unsigned(
 			fn pre_dispatch(call: &Self::Call) -> Result<(), #scrate::unsigned::TransactionValidityError> {
 				#[allow(unreachable_patterns)]
 				match call {
-					#( Call::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call), )*
+					#(
+						#pallet_attrs
+						Call::#pallet_names(inner_call) => #pallet_names::pre_dispatch(inner_call),
+					)*
 					// pre-dispatch should not stop inherent extrinsics, validation should prevent
 					// including arbitrary (non-inherent) extrinsics to blocks.
 					_ => Ok(()),
@@ -63,7 +77,10 @@ pub fn expand_outer_validate_unsigned(
 			) -> #scrate::unsigned::TransactionValidity {
 				#[allow(unreachable_patterns)]
 				match call {
-					#( Call::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call), )*
+					#(
+						#pallet_attrs
+						Call::#pallet_names(inner_call) => #pallet_names::validate_unsigned(source, inner_call),
+					)*
 					_ => #scrate::unsigned::UnknownTransaction::NoUnsignedValidator.into(),
 				}
 			}
diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs
index 042af31be6bf469f1c7a9586c8d54777310823d8..cfd582d0e4472557924c18426f649c4964a95ff4 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs
@@ -144,9 +144,11 @@
 mod expand;
 mod parse;
 
+use cfg_expr::Predicate;
 use frame_support_procedural_tools::{
 	generate_crate_access, generate_crate_access_2018, generate_hidden_includes,
 };
+use itertools::Itertools;
 use parse::{
 	ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration,
 	WhereSection,
@@ -154,6 +156,10 @@ use parse::{
 use proc_macro::TokenStream;
 use proc_macro2::TokenStream as TokenStream2;
 use quote::quote;
+use std::{
+	collections::{HashMap, HashSet},
+	str::FromStr,
+};
 use syn::{Ident, Result};
 
 /// The fixed name of the system pallet.
@@ -223,6 +229,28 @@ fn construct_runtime_final_expansion(
 			 Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`",
 			)
 		})?;
+	if !system_pallet.cfg_pattern.is_empty() {
+		return Err(syn::Error::new(
+			system_pallet.name.span(),
+			"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
+		))
+	}
+
+	let features = pallets
+		.iter()
+		.filter_map(|decl| {
+			(!decl.cfg_pattern.is_empty()).then(|| {
+				decl.cfg_pattern.iter().flat_map(|attr| {
+					attr.predicates().filter_map(|pred| match pred {
+						Predicate::Feature(feat) => Some(feat),
+						Predicate::Test => Some("test"),
+						_ => None,
+					})
+				})
+			})
+		})
+		.flatten()
+		.collect::<HashSet<_>>();
 
 	let hidden_crate_name = "construct_runtime";
 	let scrate = generate_crate_access(hidden_crate_name, "frame-support");
@@ -231,7 +259,7 @@ fn construct_runtime_final_expansion(
 	let outer_event = expand::expand_outer_event(&name, &pallets, &scrate)?;
 
 	let outer_origin = expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?;
-	let all_pallets = decl_all_pallets(&name, pallets.iter());
+	let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
 	let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
 
 	let dispatch = expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate);
@@ -293,40 +321,140 @@ fn construct_runtime_final_expansion(
 fn decl_all_pallets<'a>(
 	runtime: &'a Ident,
 	pallet_declarations: impl Iterator<Item = &'a Pallet>,
+	features: &HashSet<&str>,
 ) -> TokenStream2 {
 	let mut types = TokenStream2::new();
-	let mut names = Vec::new();
+	let mut names_by_feature = features
+		.iter()
+		.powerset()
+		.map(|feat| (feat, Vec::new()))
+		.collect::<HashMap<_, _>>();
 	for pallet_declaration in pallet_declarations {
 		let type_name = &pallet_declaration.name;
 		let pallet = &pallet_declaration.path;
 		let mut generics = vec![quote!(#runtime)];
 		generics.extend(pallet_declaration.instance.iter().map(|name| quote!(#pallet::#name)));
+		let mut attrs = Vec::new();
+		for cfg in &pallet_declaration.cfg_pattern {
+			let feat = format!("#[cfg({})]\n", cfg.original());
+			attrs.extend(TokenStream2::from_str(&feat).expect("was parsed successfully; qed"));
+		}
 		let type_decl = quote!(
+			#(#attrs)*
 			pub type #type_name = #pallet::Pallet <#(#generics),*>;
 		);
 		types.extend(type_decl);
-		names.push(&pallet_declaration.name);
+
+		if pallet_declaration.cfg_pattern.is_empty() {
+			for names in names_by_feature.values_mut() {
+				names.push(&pallet_declaration.name);
+			}
+		} else {
+			for (feature_set, names) in &mut names_by_feature {
+				// Rust tidbit: if we have multiple `#[cfg]` feature on the same item, then the
+				// predicates listed in all `#[cfg]` attributes are effectively joined by `and()`,
+				// meaning that all of them must match in order to activate the item
+				let is_feature_active = pallet_declaration.cfg_pattern.iter().all(|expr| {
+					expr.eval(|pred| match pred {
+						Predicate::Feature(f) => feature_set.contains(&f),
+						Predicate::Test => feature_set.contains(&&"test"),
+						_ => false,
+					})
+				});
+				if is_feature_active {
+					names.push(&pallet_declaration.name);
+				}
+			}
+		}
 	}
 
-	let system_pallet = match names.iter().find(|n| **n == SYSTEM_PALLET_NAME) {
-		Some(name) => name,
-		None =>
-			return syn::Error::new(
-				proc_macro2::Span::call_site(),
-				"`System` pallet declaration is missing. \
-					 Please add this line: `System: frame_system::{Pallet, Call, Storage, Config, Event<T>},`",
-			)
-			.into_compile_error(),
-	};
+	let all_pallets_without_system = names_by_feature.iter().map(|(feature_set, names)| {
+		let mut feature_set = feature_set.iter().collect::<HashSet<_>>();
+		let test_cfg = feature_set.remove(&&&"test").then_some(quote!(test)).into_iter();
+		let feature_set = feature_set.into_iter();
+		let attr = quote!(#[cfg(all( #(#test_cfg),* #(feature = #feature_set),* ))]);
+		let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME);
+		quote! {
+			#attr
+			/// All pallets included in the runtime as a nested tuple of types.
+			/// Excludes the System pallet.
+			pub type AllPalletsWithoutSystem = ( #(#names,)* );
+		}
+	});
 
-	let names_without_system =
-		names.iter().filter(|n| **n != SYSTEM_PALLET_NAME).collect::<Vec<_>>();
-	let names_reversed = names.clone().into_iter().rev().collect::<Vec<_>>();
-	let names_without_system_reverse =
-		names_without_system.clone().into_iter().rev().collect::<Vec<_>>();
-	let names_reversed_with_system_first = std::iter::once(system_pallet)
-		.chain(names_without_system_reverse.clone().into_iter())
-		.collect::<Vec<_>>();
+	let all_pallets_with_system = names_by_feature.iter().map(|(feature_set, names)| {
+		let mut feature_set = feature_set.iter().collect::<HashSet<_>>();
+		let test_cfg = feature_set.remove(&&&"test").then_some(quote!(test)).into_iter();
+		let feature_set = feature_set.into_iter();
+		let attr = quote!(#[cfg(all( #(#test_cfg),* #(feature = #feature_set),* ))]);
+		quote! {
+			#attr
+			/// All pallets included in the runtime as a nested tuple of types.
+			pub type AllPalletsWithSystem = ( #(#names,)* );
+		}
+	});
+
+	let all_pallets_without_system_reversed =
+		names_by_feature.iter().map(|(feature_set, names)| {
+			let mut feature_set = feature_set.iter().collect::<HashSet<_>>();
+			let test_cfg = feature_set.remove(&&&"test").then_some(quote!(test)).into_iter();
+			let feature_set = feature_set.into_iter();
+			let attr = quote!(#[cfg(all( #(#test_cfg),* #(feature = #feature_set),* ))]);
+			let names = names.iter().filter(|n| **n != SYSTEM_PALLET_NAME).rev();
+			quote! {
+				#attr
+				/// All pallets included in the runtime as a nested tuple of types in reversed order.
+				/// Excludes the System pallet.
+				#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
+				`AllPalletWithSystem or AllPalletsWithoutSystem`")]
+				pub type AllPalletsWithoutSystemReversed = ( #(#names,)* );
+			}
+		});
+
+	let all_pallets_with_system_reversed = names_by_feature.iter().map(|(feature_set, names)| {
+		let mut feature_set = feature_set.iter().collect::<HashSet<_>>();
+		let test_cfg = feature_set.remove(&&&"test").then_some(quote!(test)).into_iter();
+		let feature_set = feature_set.into_iter();
+		let attr = quote!(#[cfg(all( #(#test_cfg),* #(feature = #feature_set),* ))]);
+		let names = names.iter().rev();
+		quote! {
+			#attr
+			/// All pallets included in the runtime as a nested tuple of types in reversed order.
+			#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
+			`AllPalletWithSystem or AllPalletsWithoutSystem`")]
+			pub type AllPalletsWithSystemReversed = ( #(#names,)* );
+		}
+	});
+
+	let system_pallet =
+		match names_by_feature[&Vec::new()].iter().find(|n| **n == SYSTEM_PALLET_NAME) {
+			Some(name) => name,
+			None =>
+				return syn::Error::new(
+					proc_macro2::Span::call_site(),
+					"`System` pallet declaration is missing. \
+						 Please add this line: `System: frame_system,`",
+				)
+				.into_compile_error(),
+		};
+
+	let all_pallets_reversed_with_system_first =
+		names_by_feature.iter().map(|(feature_set, names)| {
+			let mut feature_set = feature_set.iter().collect::<HashSet<_>>();
+			let test_cfg = feature_set.remove(&&&"test").then_some(quote!(test)).into_iter();
+			let feature_set = feature_set.into_iter();
+			let attr = quote!(#[cfg(all( #(#test_cfg),* #(feature = #feature_set),* ))]);
+			let names = std::iter::once(system_pallet)
+				.chain(names.iter().rev().filter(|n| **n != SYSTEM_PALLET_NAME));
+			quote! {
+				#attr
+				/// All pallets included in the runtime as a nested tuple of types in reversed order.
+				/// With the system pallet first.
+				#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
+				`AllPalletWithSystem or AllPalletsWithoutSystem`")]
+				pub type AllPalletsReversedWithSystemFirst = ( #(#names,)* );
+			}
+		});
 
 	quote!(
 		#types
@@ -342,29 +470,15 @@ fn decl_all_pallets<'a>(
 			https://github.com/paritytech/substrate/pull/10043")]
 		pub type AllPallets = AllPalletsWithSystem;
 
-		/// All pallets included in the runtime as a nested tuple of types.
-		pub type AllPalletsWithSystem = ( #(#names),* );
+		#( #all_pallets_with_system )*
 
-		/// All pallets included in the runtime as a nested tuple of types.
-		/// Excludes the System pallet.
-		pub type AllPalletsWithoutSystem = ( #(#names_without_system),* );
-
-		/// All pallets included in the runtime as a nested tuple of types in reversed order.
-		/// Excludes the System pallet.
-		#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
-		`AllPalletWithSystem or AllPalletsWithoutSystem`")]
-		pub type AllPalletsWithoutSystemReversed =( #(#names_without_system_reverse),* );
-
-		/// All pallets included in the runtime as a nested tuple of types in reversed order.
-		#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
-		`AllPalletWithSystem or AllPalletsWithoutSystem`")]
-		pub type AllPalletsWithSystemReversed = ( #(#names_reversed),* );
-
-		/// All pallets included in the runtime as a nested tuple of types in reversed order.
-		/// With the system pallet first.
-		#[deprecated(note = "Using reverse pallet orders is deprecated. use only \
-		`AllPalletWithSystem or AllPalletsWithoutSystem`")]
-		pub type AllPalletsReversedWithSystemFirst = ( #(#names_reversed_with_system_first),* );
+		#( #all_pallets_without_system )*
+
+		#( #all_pallets_with_system_reversed )*
+
+		#( #all_pallets_without_system_reversed )*
+
+		#( #all_pallets_reversed_with_system_first )*
 	)
 }
 
@@ -387,6 +501,19 @@ fn decl_pallet_runtime_setup(
 			}
 		})
 		.collect::<Vec<_>>();
+	let pallet_attrs = pallet_declarations
+		.iter()
+		.map(|pallet| {
+			pallet.cfg_pattern.iter().fold(TokenStream2::new(), |acc, pattern| {
+				let attr = TokenStream2::from_str(&format!("#[cfg({})]", pattern.original()))
+					.expect("was successfully parsed before; qed");
+				quote! {
+					#acc
+					#attr
+				}
+			})
+		})
+		.collect::<Vec<_>>();
 
 	quote!(
 		/// Provides an implementation of `PalletInfo` to provide information
@@ -397,6 +524,7 @@ fn decl_pallet_runtime_setup(
 			fn index<P: 'static>() -> Option<usize> {
 				let type_id = #scrate::sp_std::any::TypeId::of::<P>();
 				#(
+					#pallet_attrs
 					if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
 						return Some(#indices)
 					}
@@ -408,6 +536,7 @@ fn decl_pallet_runtime_setup(
 			fn name<P: 'static>() -> Option<&'static str> {
 				let type_id = #scrate::sp_std::any::TypeId::of::<P>();
 				#(
+					#pallet_attrs
 					if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
 						return Some(#name_strings)
 					}
@@ -419,6 +548,7 @@ fn decl_pallet_runtime_setup(
 			fn module_name<P: 'static>() -> Option<&'static str> {
 				let type_id = #scrate::sp_std::any::TypeId::of::<P>();
 				#(
+					#pallet_attrs
 					if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
 						return Some(#module_names)
 					}
@@ -430,6 +560,7 @@ fn decl_pallet_runtime_setup(
 			fn crate_version<P: 'static>() -> Option<#scrate::traits::CrateVersion> {
 				let type_id = #scrate::sp_std::any::TypeId::of::<P>();
 				#(
+					#pallet_attrs
 					if type_id == #scrate::sp_std::any::TypeId::of::<#names>() {
 						return Some(
 							<#pallet_structs as #scrate::traits::PalletInfoAccess>::crate_version()
diff --git a/substrate/frame/support/procedural/src/construct_runtime/parse.rs b/substrate/frame/support/procedural/src/construct_runtime/parse.rs
index 711da85c10cfc8ecc0436fcf94d434a860a83411..7a5acf43b92b031209137d2e5886ed9c713162f8 100644
--- a/substrate/frame/support/procedural/src/construct_runtime/parse.rs
+++ b/substrate/frame/support/procedural/src/construct_runtime/parse.rs
@@ -23,7 +23,7 @@ use syn::{
 	parse::{Parse, ParseStream},
 	punctuated::Punctuated,
 	spanned::Spanned,
-	token, Error, Ident, Path, Result, Token,
+	token, Attribute, Error, Ident, Path, Result, Token,
 };
 
 mod keyword {
@@ -185,6 +185,8 @@ impl Parse for WhereDefinition {
 pub struct PalletDeclaration {
 	/// The name of the pallet, e.g.`System` in `System: frame_system`.
 	pub name: Ident,
+	/// Optional attributes tagged right above a pallet declaration.
+	pub attrs: Vec<Attribute>,
 	/// Optional fixed index, e.g. `MyPallet ...  = 3,`.
 	pub index: Option<u8>,
 	/// The path of the pallet, e.g. `frame_system` in `System: frame_system`.
@@ -212,6 +214,8 @@ pub enum SpecifiedParts {
 
 impl Parse for PalletDeclaration {
 	fn parse(input: ParseStream) -> Result<Self> {
+		let attrs = input.call(Attribute::parse_outer)?;
+
 		let name = input.parse()?;
 		let _: Token![:] = input.parse()?;
 		let path = input.parse()?;
@@ -279,7 +283,7 @@ impl Parse for PalletDeclaration {
 			None
 		};
 
-		Ok(Self { name, path, instance, pallet_parts, specified_parts, index })
+		Ok(Self { attrs, name, path, instance, pallet_parts, specified_parts, index })
 	}
 }
 
@@ -535,6 +539,8 @@ pub struct Pallet {
 	pub instance: Option<Ident>,
 	/// The pallet parts to use for the pallet.
 	pub pallet_parts: Vec<PalletPart>,
+	/// Expressions specified inside of a #[cfg] attribute.
+	pub cfg_pattern: Vec<cfg_expr::Expression>,
 }
 
 impl Pallet {
@@ -647,11 +653,32 @@ fn convert_pallets(pallets: Vec<PalletDeclaration>) -> syn::Result<PalletsConver
 				SpecifiedParts::All => (),
 			}
 
+			let cfg_pattern = pallet
+				.attrs
+				.iter()
+				.map(|attr| {
+					if attr.path.segments.len() != 1 || attr.path.segments[0].ident != "cfg" {
+						let msg = "Unsupported attribute, only #[cfg] is supported on pallet \
+						declarations in `construct_runtime`";
+						return Err(syn::Error::new(attr.span(), msg))
+					}
+
+					attr.parse_args_with(|input: syn::parse::ParseStream| {
+						// Required, otherwise the parse stream doesn't advance and will result in
+						// an error.
+						let input = input.parse::<proc_macro2::TokenStream>()?;
+						cfg_expr::Expression::parse(&input.to_string())
+							.map_err(|e| syn::Error::new(attr.span(), e.to_string()))
+					})
+				})
+				.collect::<Result<Vec<_>>>()?;
+
 			Ok(Pallet {
 				name: pallet.name,
 				index: final_index,
 				path: pallet.path,
 				instance: pallet.instance,
+				cfg_pattern,
 				pallet_parts,
 			})
 		})
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.rs b/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.rs
new file mode 100644
index 0000000000000000000000000000000000000000..79b5632babd95cd38ca6fd95d8671b4e847cbe4d
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.rs
@@ -0,0 +1,14 @@
+use frame_support::construct_runtime;
+
+construct_runtime! {
+	pub enum Runtime where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic
+	{
+		#[cfg(test)]
+		System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
+	}
+}
+
+fn main() {}
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..a86a839615aa01d71d806aea01fc7b4c03f5e0f4
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/feature_gated_system_pallet.stderr
@@ -0,0 +1,5 @@
+error: `System` pallet declaration is feature gated, please remove any `#[cfg]` attributes
+  --> tests/construct_runtime_ui/feature_gated_system_pallet.rs:10:3
+   |
+10 |         System: frame_system::{Pallet, Call, Storage, Config, Event<T>},
+   |         ^^^^^^
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.rs b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a1d39fa76ea851e1c93e694a4e563179c3ca0d98
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.rs
@@ -0,0 +1,15 @@
+use frame_support::construct_runtime;
+
+construct_runtime! {
+	pub enum Runtime where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic
+	{
+		System: system::{Pallet},
+		#[cfg(feature = 1)]
+		Balance: balances::{Config, Call},
+	}
+}
+
+fn main() {}
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..68366a3410bf16a405ac005119d72e3600b8a06f
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/invalid_meta_literal.stderr
@@ -0,0 +1,6 @@
+error: feature = 1
+                 ^ expected one of `<key>`, `all`, `any`, `not` here
+  --> tests/construct_runtime_ui/invalid_meta_literal.rs:10:3
+   |
+10 |         #[cfg(feature = 1)]
+   |         ^
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b93adf9a780a771fdb9fead346b49f3eb32f48d1
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.rs
@@ -0,0 +1,15 @@
+use frame_support::construct_runtime;
+
+construct_runtime! {
+	pub enum Runtime where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic
+	{
+		System: system::{Pallet},
+		#[cfg(feature(test))]
+		Balance: balances::{Config, Call},
+	}
+}
+
+fn main() {}
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..98d99a0d34997aaf1512c89950c91d401cc15e79
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_meta_structure.stderr
@@ -0,0 +1,6 @@
+error: feature(test)
+              ^ expected one of `=`, `,`, `)` here
+  --> tests/construct_runtime_ui/unsupported_meta_structure.rs:10:3
+   |
+10 |         #[cfg(feature(test))]
+   |         ^
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs
new file mode 100644
index 0000000000000000000000000000000000000000..3ec8b9db1d435013aa4ab9c7b7808f1f97d95b95
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.rs
@@ -0,0 +1,15 @@
+use frame_support::construct_runtime;
+
+construct_runtime! {
+	pub enum Runtime where
+		Block = Block,
+		NodeBlock = Block,
+		UncheckedExtrinsic = UncheckedExtrinsic
+	{
+		System: system::{Pallet},
+		#[attr]
+		Balance: balances::{Config, Call},
+	}
+}
+
+fn main() {}
diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.stderr
new file mode 100644
index 0000000000000000000000000000000000000000..fceb2b8a99db8f18578c099e02bf5b0612874efb
--- /dev/null
+++ b/substrate/frame/support/test/tests/construct_runtime_ui/unsupported_pallet_attr.stderr
@@ -0,0 +1,5 @@
+error: Unsupported attribute, only #[cfg] is supported on pallet declarations in `construct_runtime`
+  --> tests/construct_runtime_ui/unsupported_pallet_attr.rs:10:3
+   |
+10 |         #[attr]
+   |         ^
diff --git a/substrate/frame/support/test/tests/pallet.rs b/substrate/frame/support/test/tests/pallet.rs
index e96e52d3d2c6e3a354398e2fa2d499c6dc899d6b..497b7bb04c36aac7b9da4ea8aca74635a884804d 100644
--- a/substrate/frame/support/test/tests/pallet.rs
+++ b/substrate/frame/support/test/tests/pallet.rs
@@ -626,6 +626,8 @@ frame_support::construct_runtime!(
 		System: frame_system exclude_parts { Pallet, Storage },
 		Example: pallet,
 		Example2: pallet2 exclude_parts { Call },
+		#[cfg(feature = "example3")]
+		Example3: pallet3,
 		Example4: pallet4 use_parts { Call },
 	}
 );