Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added keep_attr to #[ink::contract] and #[ink::trait_definition] #1145

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions crates/lang/codegen/src/generator/as_dependency/call_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,11 @@ impl CallBuilder<'_> {
ir::Receiver::Ref => quote! { build },
ir::Receiver::RefMut => quote! { build_mut },
};
let attrs = message.attrs();
let attrs = self
.contract
.config()
.whitelisted_attributes()
.filter_attr(message.attrs().to_vec());
quote_spanned!(span=>
type #output_ident = <<<
Self
Expand Down Expand Up @@ -356,7 +360,11 @@ impl CallBuilder<'_> {
let span = message.span();
let callable = message.callable();
let message_ident = message.ident();
let attrs = message.attrs();
let attrs = self
.contract
.config()
.whitelisted_attributes()
.filter_attr(message.attrs().to_vec());
let selector = message.composed_selector();
let selector_bytes = selector.hex_lits();
let input_bindings = generator::input_bindings(callable.inputs());
Expand Down
12 changes: 10 additions & 2 deletions crates/lang/codegen/src/generator/as_dependency/contract_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,11 @@ impl ContractRef<'_> {
) -> TokenStream2 {
use ir::Callable as _;
let span = message.span();
let attrs = message.attrs();
let attrs = self
.contract
.config()
.whitelisted_attributes()
.filter_attr(message.attrs().to_vec());
let storage_ident = self.contract.module().storage().ident();
let message_ident = message.ident();
let call_operator = match message.receiver() {
Expand Down Expand Up @@ -375,7 +379,11 @@ impl ContractRef<'_> {
constructor: ir::CallableWithSelector<ir::Constructor>,
) -> TokenStream2 {
let span = constructor.span();
let attrs = constructor.attrs();
let attrs = self
.contract
.config()
.whitelisted_attributes()
.filter_attr(constructor.attrs().to_vec());
let constructor_ident = constructor.ident();
let selector_bytes = constructor.composed_selector().hex_lits();
let input_bindings = generator::input_bindings(constructor.inputs());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,12 @@ impl CallBuilder<'_> {
) -> TokenStream2 {
let span = message.span();
let message_ident = message.ident();
let attrs = message.attrs();
let attrs = self
.trait_def
.trait_def
.config()
.whitelisted_attributes()
.filter_attr(message.attrs());
let output_ident = generator::output_ident(message_ident);
let output = message.output();
let output_sig = output.map_or_else(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,12 @@ impl CallForwarder<'_> {
let trait_ident = self.trait_def.trait_def.item().ident();
let forwarder_ident = self.ident();
let message_ident = message.ident();
let attrs = message.attrs();
let attrs = self
.trait_def
.trait_def
.config()
.whitelisted_attributes()
.filter_attr(message.attrs());
let output_ident = generator::output_ident(message_ident);
let output_type = message
.output()
Expand Down
97 changes: 97 additions & 0 deletions crates/lang/ir/src/ir/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

use crate::{
ast,
ast::MetaNameValue,
error::ExtError as _,
};
use core::convert::TryFrom;
use std::collections::HashMap;
use syn::spanned::Spanned;

/// The ink! configuration.
Expand All @@ -38,6 +40,60 @@ pub struct Config {
/// be used to change the underlying environmental types of an ink! smart
/// contract.
env: Option<Environment>,
/// The set of attributes that can be passed to call builder in the codegen.
whitelisted_attributes: WhitelistedAttributes,
}

/// The set of attributes that can be passed to call builder or call forwarder in the codegen.
#[derive(Debug, PartialEq, Eq)]
pub struct WhitelistedAttributes(pub HashMap<String, ()>);

impl Default for WhitelistedAttributes {
fn default() -> Self {
Self(HashMap::from([
// Conditional compilation
("cfg".to_string(), ()),
("cfg_attr".to_string(), ()),
// Diagnostics
("allow".to_string(), ()),
("warn".to_string(), ()),
("deny".to_string(), ()),
("forbid".to_string(), ()),
("deprecated".to_string(), ()),
("must_use".to_string(), ()),
cmichi marked this conversation as resolved.
Show resolved Hide resolved
// Documentation
("doc".to_string(), ()),
]))
}
}

impl WhitelistedAttributes {
pub fn parse_arg_value(&mut self, arg: &MetaNameValue) -> Result<(), syn::Error> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR looks solid, could you add a comment to this pub function here please?

return if let ast::PathOrLit::Lit(syn::Lit::Str(attributes)) = &arg.value {
attributes.value().split(',').for_each(|attribute| {
self.0.insert(attribute.trim().to_string(), ());
});
Ok(())
} else {
Err(format_err_spanned!(
arg,
"expected a string with attributes separated by `,`",
))
}
}

pub fn filter_attr(&self, attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here a comment as well plz.

attrs
.into_iter()
.filter(|attr| {
if let Some(ident) = attr.path.get_ident() {
self.0.contains_key(&ident.to_string())
} else {
false
}
})
.collect()
}
}

/// Return an error to notify about duplicate ink! configuration arguments.
Expand Down Expand Up @@ -65,6 +121,8 @@ impl TryFrom<ast::AttributeArgs> for Config {
let mut dynamic_storage_allocator: Option<(bool, ast::MetaNameValue)> = None;
let mut as_dependency: Option<(bool, ast::MetaNameValue)> = None;
let mut env: Option<(Environment, ast::MetaNameValue)> = None;
let mut whitelisted_attributes = WhitelistedAttributes::default();

for arg in args.into_iter() {
if arg.name.is_ident("dynamic_storage_allocator") {
if let Some((_, ast)) = dynamic_storage_allocator {
Expand Down Expand Up @@ -106,6 +164,10 @@ impl TryFrom<ast::AttributeArgs> for Config {
"expected a path for `env` ink! configuration argument",
))
}
} else if arg.name.is_ident("keep_attr") {
if let Err(err) = whitelisted_attributes.parse_arg_value(&arg) {
return Err(err)
}
} else {
return Err(format_err_spanned!(
arg,
Expand All @@ -117,6 +179,7 @@ impl TryFrom<ast::AttributeArgs> for Config {
dynamic_storage_allocator: dynamic_storage_allocator.map(|(value, _)| value),
as_dependency: as_dependency.map(|(value, _)| value),
env: env.map(|(value, _)| value),
whitelisted_attributes,
})
}
}
Expand Down Expand Up @@ -149,6 +212,11 @@ impl Config {
pub fn is_compile_as_dependency_enabled(&self) -> bool {
self.as_dependency.unwrap_or(false)
}

/// Return set of attributes that can be passed to call builder in the codegen.
pub fn whitelisted_attributes(&self) -> &WhitelistedAttributes {
&self.whitelisted_attributes
}
}

/// The environmental types definition.
Expand Down Expand Up @@ -198,6 +266,7 @@ mod tests {
dynamic_storage_allocator: Some(true),
as_dependency: None,
env: None,
whitelisted_attributes: Default::default(),
}),
)
}
Expand All @@ -220,6 +289,7 @@ mod tests {
dynamic_storage_allocator: None,
as_dependency: Some(false),
env: None,
whitelisted_attributes: Default::default(),
}),
)
}
Expand All @@ -246,6 +316,7 @@ mod tests {
env: Some(Environment {
path: syn::parse_quote! { ::my::env::Types },
}),
whitelisted_attributes: Default::default(),
}),
)
}
Expand Down Expand Up @@ -276,4 +347,30 @@ mod tests {
Err("encountered duplicate ink! `env` configuration argument"),
);
}

#[test]
fn keep_attr_works() {
let mut attrs = WhitelistedAttributes::default();
attrs.0.insert("foo".to_string(), ());
attrs.0.insert("bar".to_string(), ());
assert_try_from(
syn::parse_quote! {
keep_attr = "foo, bar"
},
Ok(Config {
dynamic_storage_allocator: None,
as_dependency: None,
env: None,
whitelisted_attributes: attrs,
}),
)
}

#[test]
fn keep_attr_invalid_value_fails() {
assert_try_from(
syn::parse_quote! { keep_attr = 1u16 },
Err("expected a string with attributes separated by `,`"),
);
}
}
15 changes: 15 additions & 0 deletions crates/lang/ir/src/ir/trait_def/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use crate::{
ast,
error::ExtError as _,
ir::config::WhitelistedAttributes,
};
use core::convert::TryFrom;
use syn::spanned::Spanned;
Expand All @@ -30,6 +31,8 @@ pub struct TraitDefinitionConfig {
/// selectors of the ink! trait messages. This is useful to disambiguate
/// ink! trait definitions with equal names.
namespace: Option<syn::LitStr>,
/// The set of attributes that can be passed to call builder and forwarder in the codegen.
whitelisted_attributes: WhitelistedAttributes,
}

impl TraitDefinitionConfig {
Expand Down Expand Up @@ -69,6 +72,7 @@ impl TryFrom<ast::AttributeArgs> for TraitDefinitionConfig {

fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
let mut namespace: Option<(syn::LitStr, ast::MetaNameValue)> = None;
let mut whitelisted_attributes = WhitelistedAttributes::default();
for arg in args.into_iter() {
if arg.name.is_ident("namespace") {
if let Some((_, meta_name_value)) = namespace {
Expand All @@ -88,6 +92,10 @@ impl TryFrom<ast::AttributeArgs> for TraitDefinitionConfig {
"expected a string literal for `namespace` ink! trait definition configuration argument",
))
}
} else if arg.name.is_ident("keep_attr") {
if let Err(err) = whitelisted_attributes.parse_arg_value(&arg) {
return Err(err)
}
} else {
return Err(format_err_spanned!(
arg,
Expand All @@ -97,6 +105,7 @@ impl TryFrom<ast::AttributeArgs> for TraitDefinitionConfig {
}
Ok(TraitDefinitionConfig {
namespace: namespace.map(|(value, _)| value),
whitelisted_attributes,
})
}
}
Expand All @@ -106,4 +115,10 @@ impl TraitDefinitionConfig {
pub fn namespace(&self) -> Option<&syn::LitStr> {
self.namespace.as_ref()
}

/// Returns the set of attributes that can be passed to call builder and
/// forwarder in the codegen.
pub fn whitelisted_attributes(&self) -> &WhitelistedAttributes {
&self.whitelisted_attributes
}
}
Loading