Unverified Commit c24f0ff9 authored by Hero Bird's avatar Hero Bird Committed by GitHub
Browse files

Implement trait support - Part 1/3 - New ink! IR (#451)



* [lang/macro] move some items around

* [lang/macro] rename AttributeFlag -> AttributeArg

This is in order to sync naming with the syn crate.

* [lang/macro] implement ink! config parameter parsing

* [lang/macro] add doc comment to into_nested_meta helper function

* [lang/macro] add preliminary ast module

* [lang/macro] replace usage of ink_primitives::hash module

* [lang/macro] slightly refactor Selector

* [lang/macro] fully implement AttributeArgs definitions + tests

* [lang/macro] add IntoIterator for AttributeArgs

* [lang/macro] base ir2::Config on the new ast::AttributeArgs definitions

* [lang/macro] apply rustfmt

* [lang/macro] add some ToTokens implementations

* [lang/macro] add unit tests for ir2::Config

* [lang/macro] introduce general Error type

* [lang/macro] remove RustItem

* [lang/macro] follow-up: remove RustItem

* [lang/macro] add ir2::contains_ink_attributes helper function

* [lang/macro] initial skeleton for TryFrom<syn::Item> for ir2::Item impl

* [lang/macro] add first_ink_attribute helper function

* [lang/macro] refactor initial skeleton for ir2::Item TryFrom impl

* [lang/macro] re-export first_ink_attribute to ir2 module

* [lang/macro] add unit tests for contains_ink_attributes and first_ink_attribute

* [lang/macro] enhance initial TryFrom skeleton for ir2::Item

* [lang/macro] simplify item_attrs

* [lang/macro] further simplify item_attrs

* [lang/macro] minor improvement

* [lang/macro] add ExtError to extend syn::Error

* [lang/macro] add Attrs trait to extract attributes from syn types

* [lang/macro] re-base error infrastructure on to syn::Error

* [lang/macro] adjust unit tests for new error infrastructure

* [lang/macro] remove old error types

* [lang/macro] use syn::Error in ir2::Config

* [lang/macro] adjust Config unit tests for syn::Error usage

* [lang/macro] rename ensure_no_duplicates -> ensure_no_duplicate_attrs

* [lang/macro] add AttributeArg::kind method

* [lang/macro] use full type path

* [lang/macro] implement InkAttribute::from_expanded

* [lang/macro] implement ir2::Storage TryFrom

* [lang/macro] minor refactor

* [lang/macro] add InkAttribute::ensure_no_conflicts

* [lang/macro] add some failure paths to TryFrom<_> for ink! Storage struct

* [lang/macro] add unit tests for ink! storage struct conversion

* [lang/macro] apply rustfmt

* [lang/macro] fix some clippy warnings

* [lang/macro] rename Storage::item_struct field to ast

* [lang/macro] rename Event::item_struct -> ast

* [lang/macro] add some method and an iterator to ink! event struct

* [lang/macro] add initial implementation of ink! event's TryFrom

* [lang/macro] add derives for Debug, PartialEq and Eq for ink! event

* [lang/macro] add initial success path unit tests for TryFrom for ink! event

* [lang/macro] fix bug with #[ink(topic)] annotated event fields

* [lang/macro] apply rustfmt

* [lang/macro] apply clippy suggestion

* [lang/macro] add first bunch of failure event TryFrom unit tests

* [lang/macro] normalize the current failure event unit tests

* [lang/macro] add more event unit tests for topics on fields

* [lang/macro] add Debug, PartialEq and Eq derives for some ir2 types

* [lang/macro] add unit tests for ir2::Item

* [lang/macro] add ir2::ImplBlock::is_ink_impl_block

* [lang/macro] add doc comment note

* [lang/macro] rename ImplBlockItem::Rust -> Other

* [lang/macro] apply rustfmt

* [lang/macro] add stub TryFrom impls for Constructor and Message

* [lang/macro] add TryFrom impl for ir2::ImplBlockItem

* [lang/macro] add initial skeleton for TryFrom impl for ir::ImplBlock

* [lang/macro] implement ir2::sanitize_attributes

* [lang/macro] split impl_block.rs into multiple files as sub modules

* [lang/macro] apply rustfmt + remove unused imports

* [lang/macro] split item module into multiple modules

* [lang/macro] finalize move into multiple files from commit before

* [lang/macro] add lots of error checks to TryFrom for Message

* [lang/macro] improve TryFrom tests for ir2::ImplBlock

* [lang/macro] make Event and Storage use ir2::sanitize_attributes

* [lang/macro] add additional constructors for Iter{Constructors, Messages}

* [lang/macro] add visibility check to Message TryFrom impl

* [lang/macro] add Message::Visibility and getter

* [lang/macro] implement Message::receiver getter

* [lang/macro] extend ImplBlock doc comment note section

* [lang/macro] add Message::Visibility getters

* [lang/macro] refactor Receiver getters

* [lang/macro] fix doc comment

* [lang/macro] add unit test for Message::visibility getter

* [lang/macro] add unit test for Message::receiver getter

* [lang/macro] move Visibility from message.rs to impl_item.rs

* [lang/macro] add Constructor::visibility getter

* [lang/macro] implement callable.rs

* [lang/macro] make use of new shared callable.rs utilities

* [lang/macro] remove visibility from impl_item.rs

* [lang/macro] fix some imports

* [lang/macro] implement constructor check for missing return type

* [lang/macro] implement constructor check for invalid self receiver

* [lang/macro] add some ink! constructor try_from unit tests

* [lang/macro] add ink! constructor visibility getter unit test

* [lang/macro] fix unit test for ink! message visibility

* [lang/macro] add many unit tests for ink! constructor TryFrom impl

* [lang/macro] extend success TryFrom impl unit tests

* [lang/macro] factor out sanitize_attributes for TryFrom impls

* [lang/macro] remove salt from messages and constructors

* [lang/macro] add salt extraction for ink! impl block

* [lang/macro] add unit test for ink! impl block salt

* [lang/macro] implement InkAttribute::{salt, selector} getters

* [lang/macro] make use of InkAttribute::salt in TryFrom for ir2::ImplBlock

* [lang/macro] apply rustfmt

* [lang/macro] implement InkAttribute::is_payable

* [lang/macro] make use of InkAttribute::{is_payable, selector}

* [lang/macro] add module doc to callable.rs

* [lang/macro] rename ImplBlock -> ItemImpl

* [lang/macro] rename impl_block module to item_impl

* [lang/macro] rename ImplBlockItem -> ImplItem

* [lang/macro] add {Message, Constructor}::is_payable

* [lang/macro] add doc note to ir2::ImplItem

* [lang/macro] add implementation for InputsIter

* [lang/macro] make use of InputsIter in ink! message and constructor

* [lang/macro] add getter for the return type of an ink! message

* [lang/macro] add unit test for ink! message is_payable

* [lang/macro] add unit test for Constructor::is_payable

* [lang/macro] rewrite receiver_works unit test for ink! message

* [lang/macro] add unit test for ink! message inputs

* [lang/macro] add unit test for ink! message output getter

* [lang/macro] add identifier getter to ink! message and constructor

* [lang/macro] apply rustfmt

* [lang/macro] apply rustfmt

* [lang/macro] implement clippy suggestions

* [lang/macro] add unit test for Constructor::inputs

* [lang/macro] refactor Storage::is_ink_storage

* [lang/macro] add Event::is_ink_event

* [lang/macro] add InkItem::is_ink_item

* [lang/macro] apply rustfmt

* [lang/macro] fix many non-dead-code warnings and allow dead-code for ir2

* [lang/macro] implement Contract::config getter

* [lang/macro] allow compile failure of some in-doc tests

* [lang/macro] implement TryFrom for ir2::Module

* [lang/macro] re-export ImplItem from ir2

* [lang/macro] implement visibility check for ink! impl blocks

* [lang/macro] fix unit test

* [lang/macro] improve error message for visibility check

* [lang/macro] add unit test for visibility failure

* [lang/macro] apply rustfmt

* [lang/macro] introduce Callable trait to unify ink! messages and constructors

* [lang/macro] craft initial skeleton of compose_selector function

* [lang/macro] implement ItemImpl::{self_type, trait_path, salt}

* [lang/macro] extend compose_selector docs with a usage recommendation section

* [lang/macro] implement initial version of compose_selector

* [lang/macro] apply clippy suggestion

* [lang/macro] fix some bugs in compose_selector implementation

* [lang/macro] add unit tests for compose_selector

* [lang/macro] rename ir2::Module -> ir2::ItemMod (syn)

* [lang/macro] rename module.rs -> item_mod.rs

* [lang/macro] remove some imports, use names directly

* [lang/macro] rename Event::ast -> item

* [lang/macro] refactor event fields iterator

* rebase resolve conflict

* [lang/macro] move ir2 and ast modules to new crate

* [lang/ir] some general renamings after crate move

* [lang/macro] no longer refer to ast and ir2 modules

* [lang/macro] get rid of all bail! and bail_span! macro calls

They hide significant control flow which is bad for readability.

* [lang/ir] export error macros

* [lang/ir] remove unused import

* [lang/macro] make use of new ink_lang_ir crate

* [lang/ir] improve documentation and module structure

* [lang/ir] apply clippy suggestions + formatting

* [lang/macro] apply clippy suggestions

* [lang/ir] further improvements to the documentation

* [lang/ir] fix error macro docs

* [lang/ir] add missing re-export for ir::Contract

* [lang/ir] improve ir::Contract docs

* [lang/ir] minor refactor of ir::Contract

* [lang/ir] extend docs for ir::Contract

* [lang/ir] make ast::AttributeArgs::args field private

* [lang/ir] apply rustfmt

* [lang/ir] improve docs

* [lang/ir] further documentation improvements

* [lang/ir] add missing exports

* [lang/ir] adjust format_err to require its expr to be T:Spanned

Formerly this was T:ToTokens which is more restrictive than it should have been.

* [lang/ir] refactor format_err_span and format_err macros + docs

* [lang/ir] rename format_err macro -> format_err_spanned

* [lang/ir] rename format_err_span macro -> format_err

* [lang/ir] apply rustfmt

* [lang/ir] minor improvements to format_err{_spanned}

* [lang/ir] implement ToTokens for all ink! IR structures

This is useful because T:ToTokens automatically implements syn::spanned::Spanned while being simpler to implement than syn::spanned::Spanned.

* [lang/ir] add links to tracking issue for format_err{_spanned}

* [lang/ir] minor docs improvements

* [lang/ir] do not re-export everything that belongs to attributes

* [lang/ir] fix compilation in test mode

* [lang/ir] add ir::ItemMod checks for ink! storage, message and constructor quantities

* [lang/ir] make ast module private and move ir module to root

* [lang/ir] rename IterItemImpls -> IterItemImpls

* [lang/ir] remove ir::EnvTypes from public API

* [lang/ir] rename ir::ItemMod::impl_blocks -> item_impls

* [lang/ir] rename ir::Module::item_impls -> impls

* [lang/ir] make ir::Config::env_types return syn::Path

* [lang/ir] move mod doc to lib doc

* [lang/ir] fix event doc test

* [lang/ir] make Event::is_ink_event module private

* [lang/ir] apply rustfmt

* [lang/ir] fix Storage doc test

* [lang/ir] remove unnecessary rust annotation for doc test

* [lang/ir] fix doc tests for constructor and message

* [lang/ir] fix fmt todo! avoidance

* [lang/ir] remove unneded let binding

* [lang/ir] fix doc tests of impl_item/mod.rs

* [lang/ir] make ItemImpl::is_ink_impl_block module private

* [lang/ir] re-export InputsIter

* [lang/ir] add getter for ir::ItemImpl::items

* [lang/ir] rename salt -> namespace

* add doc link in MetaNameValue doc

Co-authored-by: Michael Müller's avatarMichael Müller <mich@elmueller.net>

* [lang/ir] rename Callable::selector -> user_provided_selector

This is going to avoid confusion with composed selector concept.

* [lang/ir] make Storage::is_ink_storage module private

* [lang/ir] fix doc link

* [lang/ir] expose composed selector through messages and constructors iter

* [lang/ir] apply rustfmt

* [lang/ir] fix item_mod.rs doc tests

* [lang/ir] ignore contract doc test for now

It is hard to test because of the unknown ink::contract attribute at the top.

* [lang/ir] add item getter to CallableWithSelector

* [lang/ir] add Callable::statements getter

* [lang/ir] add ir::ItemMod::ensure_no_overlapping_selectors + tests

* [lang/ir] remove duplicate code

* fix typo

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* fix typo

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* fix typo

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [lang/ir] move config.rs -> attr_args.rs

* [lang/ir] update doc comments

* [lang/ir] some more doc polishing

* [lang/ir] fix outdated doc comment

* [lang/ir] remove outdated doc comment

* fix typo

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* fix typo

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [lang/ir] remove unneeded let binding in doc test

* [lang/ir] fix conflicting constructor attribute closure

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [lang/ir] fix doc comment

* [lang/ir] remove outdated doc comment

* fix doc comment

Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>

* [lang/ir] rename 'outer label to 'repeat

* [ci] add lang/ir to ALL_CRATES

* [lang/ir] update blake2 dependency from 0.8 -> 0.9

* [lang/macro] remove unneeded blake2 dependency

* [lang/ir] take last segment of trait path

* [lang/ir] slightly improve overlapping selector error message

* [lang/ir] improve overlapping selector formatting

* [lang/ir] add unit test for composed selector with relative trait path

* [lang/ir] improve unit test for relative trait path composed selector

* [lang/ir] fix bug that namespace was a valid attribute on ink! messages

* [lang/ir] add unit test to check against conflicting attributes on ink! messages

* [lang/ir] fix bug that namespace was an allowed attribute for ink! constructors

* [lang/ir] add unit test to check against conflicting ink! constructor attributes

* [lang/ir] add success tests for ir::ItemMod TryFrom

* [lang/ir] fix out-of-line ink! module error message

* [lang/ir] add a bunch of ink! module TryFrom failure unit tests

* [lang/ir] add unit test for ink! event fields iterator

* [lang/ir] apply rustfmt

Co-authored-by: Michael Müller's avatarMichael Müller <mich@elmueller.net>
Co-authored-by: Andrew Jones's avatarAndrew Jones <ascjones@gmail.com>
parent 2456fdd2
Pipeline #99151 failed with stages
in 10 minutes and 8 seconds
......@@ -17,7 +17,7 @@ variables:
CARGO_TARGET_DIR: "/ci-cache/${CI_PROJECT_NAME}/targets/${CI_COMMIT_REF_NAME}/${CI_JOB_NAME}"
CI_SERVER_NAME: "GitLab CI"
REGISTRY: "paritytech"
ALL_CRATES: "core core/derive alloc prelude primitives lang lang/macro"
ALL_CRATES: "core core/derive alloc prelude primitives lang lang/macro lang/ir"
.collect-artifacts: &collect-artifacts
artifacts:
......
[workspace]
members = [
"metadata",
"alloc",
"core",
"core/derive",
"lang",
"lang/macro",
"lang/ir",
"prelude",
"primitives",
]
......
[package]
name = "ink_lang_ir"
version = "2.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "APACHE-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/ink"
documentation = "https://substrate.dev/substrate-contracts-workshop/#/"
homepage = "https://www.parity.io/"
description = "data structures and algorithms for ink! intermediate representation"
keywords = ["wasm", "parity", "webassembly", "blockchain", "edsl"]
categories = ["no-std", "embedded"]
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE"]
[lib]
name = "ink_lang_ir"
[dependencies]
quote = "1"
syn = { version = "1.0", features = ["parsing", "full", "extra-traits"] }
proc-macro2 = "1.0"
itertools = { version = "0.9", default-features = false }
either = { version = "1.5", default-features = false }
regex = "1.3"
blake2 = "0.9"
[features]
default = ["std"]
std = [
"itertools/use_std",
"either/use_std",
]
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::{
Ident,
TokenStream as TokenStream2,
};
use quote::ToTokens;
use syn::{
ext::IdentExt as _,
parse::{
Parse,
ParseStream,
},
punctuated::Punctuated,
Token,
};
/// The attribute arguments for the configuration of an ink! smart contract.
///
/// These are the segments `env_types = ::my::env_types::EnvTypes` and `compile_as_dependency = true`
/// in `#[ink::contract(env_types = ::my::env_types::EnvTypes, compile_as_dependency = true`.
#[derive(Debug, PartialEq, Eq)]
pub struct AttributeArgs {
args: Punctuated<MetaNameValue, Token![,]>,
}
/// A name-value pair within an attribute, like feature = "nightly".
///
/// The only difference from `syn::MetaNameValue` is that this additionally
/// allows the `value` to be a plain identifier or path.
#[derive(Debug, PartialEq, Eq)]
pub struct MetaNameValue {
pub name: syn::Path,
pub eq_token: syn::token::Eq,
pub value: PathOrLit,
}
/// Either a path or a literal.
#[derive(Debug, PartialEq, Eq)]
pub enum PathOrLit {
Path(syn::Path),
Lit(syn::Lit),
}
impl IntoIterator for AttributeArgs {
type Item = MetaNameValue;
type IntoIter = syn::punctuated::IntoIter<MetaNameValue>;
fn into_iter(self) -> Self::IntoIter {
self.args.into_iter()
}
}
impl Parse for AttributeArgs {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
Ok(Self {
args: Punctuated::parse_terminated(input)?,
})
}
}
impl Parse for MetaNameValue {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
let path = input.call(Self::parse_meta_path)?;
Self::parse_meta_name_value_after_path(path, input)
}
}
impl ToTokens for PathOrLit {
fn to_tokens(&self, tokens: &mut TokenStream2) {
match self {
Self::Lit(lit) => lit.to_tokens(tokens),
Self::Path(path) => path.to_tokens(tokens),
}
}
}
impl ToTokens for MetaNameValue {
fn to_tokens(&self, tokens: &mut TokenStream2) {
self.name.to_tokens(tokens);
self.eq_token.to_tokens(tokens);
self.value.to_tokens(tokens);
}
}
impl MetaNameValue {
/// Like [`syn::Path::parse_mod_style`] but accepts keywords in the path.
///
/// # Note
///
/// This code was taken from the `syn` implementation for a very similar
/// syntactical pattern.
fn parse_meta_path(input: ParseStream) -> Result<syn::Path, syn::Error> {
Ok(syn::Path {
leading_colon: input.parse()?,
segments: {
let mut segments = Punctuated::new();
while input.peek(Ident::peek_any) {
let ident = Ident::parse_any(input)?;
segments.push_value(syn::PathSegment::from(ident));
if !input.peek(syn::Token![::]) {
break
}
let punct = input.parse()?;
segments.push_punct(punct);
}
if segments.is_empty() {
return Err(input.error("expected path"))
} else if segments.trailing_punct() {
return Err(input.error("expected path segment"))
}
segments
},
})
}
fn parse_meta_name_value_after_path(
name: syn::Path,
input: ParseStream,
) -> Result<MetaNameValue, syn::Error> {
Ok(MetaNameValue {
name,
eq_token: input.parse()?,
value: input.parse()?,
})
}
}
impl Parse for PathOrLit {
fn parse(input: ParseStream) -> Result<Self, syn::Error> {
if input.fork().peek(syn::Lit) {
return input.parse::<syn::Lit>().map(PathOrLit::Lit)
}
if input.fork().peek(Ident::peek_any) || input.fork().peek(Token![::]) {
return input.parse::<syn::Path>().map(PathOrLit::Path)
}
Err(input.error("cannot parse into either literal or path"))
}
}
#[cfg(test)]
mod tests {
use super::*;
use quote::quote;
impl AttributeArgs {
/// Creates a new attribute argument list from the given arguments.
pub fn new<I>(args: I) -> Self
where
I: IntoIterator<Item = MetaNameValue>,
{
Self {
args: args.into_iter().collect(),
}
}
}
#[test]
fn empty_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! {}).unwrap(),
AttributeArgs::new(vec![])
)
}
#[test]
fn literal_bool_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = true }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { true }),
}])
)
}
#[test]
fn literal_str_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = "string literal" }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { "string literal" }),
}])
)
}
#[test]
fn ident_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = MyIdentifier }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(syn::parse_quote! { MyIdentifier }),
}])
)
}
#[test]
fn root_path_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = ::this::is::my::Path }).unwrap(),
AttributeArgs::new(vec![MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(syn::parse_quote! { ::this::is::my::Path }),
}])
)
}
#[test]
fn relative_path_value_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = this::is::my::relative::Path })
.unwrap(),
AttributeArgs::new(vec![MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(
syn::parse_quote! { this::is::my::relative::Path }
),
}])
)
}
#[test]
fn trailing_comma_works() {
let mut expected_args = Punctuated::new();
expected_args.push_value(MetaNameValue {
name: syn::parse_quote! { name },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(syn::parse_quote! { value }),
});
expected_args.push_punct(<Token![,]>::default());
assert_eq!(
syn::parse2::<AttributeArgs>(quote! { name = value, }).unwrap(),
AttributeArgs {
args: expected_args,
}
)
}
#[test]
fn many_mixed_works() {
assert_eq!(
syn::parse2::<AttributeArgs>(quote! {
name1 = ::root::Path,
name2 = false,
name3 = "string literal",
name4 = 42,
name5 = 7.7
})
.unwrap(),
AttributeArgs::new(vec![
MetaNameValue {
name: syn::parse_quote! { name1 },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Path(syn::parse_quote! { ::root::Path }),
},
MetaNameValue {
name: syn::parse_quote! { name2 },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { false }),
},
MetaNameValue {
name: syn::parse_quote! { name3 },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { "string literal" }),
},
MetaNameValue {
name: syn::parse_quote! { name4 },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { 42 }),
},
MetaNameValue {
name: syn::parse_quote! { name5 },
eq_token: syn::parse_quote! { = },
value: PathOrLit::Lit(syn::parse_quote! { 7.7 }),
},
])
)
}
}
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Types and abstractions for ink! definitions that require custom syntax.
//!
//! # Note
//!
//! In general we try not to require any sort of custom non-standard Rust
//! syntax.
//!
//! At the time of this writing we currently only use this for the argument
//! parsing of ink! config header `#[ink(env_types = my::env_types::Types, etc...)]` in order
//! to be able to parse identifiers in `name = value` segments for the `value`
//! part.
mod attr_args;
pub use self::attr_args::{
AttributeArgs,
MetaNameValue,
PathOrLit,
};
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Provide macros to simplify error reporting in procedural macros.
pub trait ExtError {
/// Returns `self` combined with the other error.
fn into_combine(self, another: syn::Error) -> Self;
}
impl ExtError for syn::Error {
fn into_combine(mut self, another: syn::Error) -> Self {
self.combine(another);
self
}
}
/// Creates a [`syn::Error`] with the format message and infers the
/// [`Span`](`proc_macro2::Span`) using [`ToTokens`](`quote::ToTokens`).
///
/// # Parameters
///
/// - The first argument must implement [`quote::ToTokens`] in order to
/// infer a [`Span`](`proc_macro2::Span`).
/// - The second argument is a format string.
/// - The rest are format string arguments.
///
/// # Note
///
/// On stable Rust this might yield higher quality error span information to the user
/// than [`format_err`].
/// - Source:
/// [`syn::Error::new_spanned`](https://docs.rs/syn/1.0.33/syn/struct.Error.html#method.new_spanned)
/// - Tracking issue: [`#54725`](https://github.com/rust-lang/rust/issues/54725)
#[macro_export]
macro_rules! format_err_spanned {
($tokens:expr, $($msg:tt)*) => {
::syn::Error::new_spanned(
&$tokens,
format_args!($($msg)*)
)
}
}
/// Creates a [`syn::Error`] with the format message and infers the
/// [`Span`](`proc_macro2::Span`) using [`Spanned`](`syn::spanned::Spanned`).
///
/// # Parameters
///
/// - The first argument must be a type that implements [`syn::spanned::Spanned`].
/// - The second argument is a format string.
/// - The rest are format string arguments.
///
/// # Note
///
/// On stable Rust this might yield worse error span information to the user
/// than [`format_err_spanned`].
/// - Source:
/// [`syn::Error::new_spanned`](https://docs.rs/syn/1.0.33/syn/struct.Error.html#method.new_spanned)
/// - Tracking issue: [`#54725`](https://github.com/rust-lang/rust/issues/54725)
#[macro_export]
macro_rules! format_err {
($spanned:expr, $($msg:tt)*) => {
::syn::Error::new(
<_ as ::syn::spanned::Spanned>::span(&$spanned),
format_args!($($msg)*)
)
}
}
This diff is collapsed.
// Copyright 2018-2020 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
ast,
error::ExtError as _,
};
use core::convert::TryFrom;
use syn::spanned::Spanned;
/// The ink! configuration.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct Config {
/// If `true` enables the dynamic storage allocator
/// facilities and code generation of the ink! smart
/// contract. Does incur some overhead. The default is
/// `true`.
storage_alloc: Option<bool>,
/// If `true` compiles this ink! smart contract always as
/// if it was a dependency of another smart contract.
/// This configuration is mainly needed for testing and
/// the default is `false`.
as_dependency: Option<bool>,
/// The environmental types definition.
///
/// This must be a type that implements `ink_core::env::EnvTypes` and can
/// be used to change the underlying environmental types of an ink! smart
/// contract.
env_types: Option<EnvTypes>,
}
/// Return an error to notify about duplicate ink! config arguments.
fn duplicate_config_err<F, S>(fst: F, snd: S, name: &str) -> syn::Error
where
F: Spanned,
S: Spanned,
{
format_err!(
snd.span(),
"encountered duplicate ink! `{}` config argument",
name,
)
.into_combine(format_err!(
fst.span(),
"first `{}` config argument here",
name
))
}
impl TryFrom<ast::AttributeArgs> for Config {
type Error = syn::Error;
fn try_from(args: ast::AttributeArgs) -> Result<Self, Self::Error> {
let mut storage_alloc: Option<(bool, ast::MetaNameValue)> = None;
let mut as_dependency: Option<(bool, ast::MetaNameValue)> = None;
let mut env_types: Option<(EnvTypes, ast::MetaNameValue)> = None;
for arg in args.into_iter() {
if arg.name.is_ident("storage_alloc") {
if let Some((_, ast)) = storage_alloc {
return Err(duplicate_config_err(ast, arg, "storage_allocator"))
}
if let ast::PathOrLit::Lit(syn::Lit::Bool(lit_bool)) = &arg.value {
storage_alloc = Some((lit_bool.value, arg))
} else {
return Err(format_err_spanned!(
arg,
"expected a bool literal for `storage_allocator` ink! config argument",
))
}
} else if arg.name.is_ident("compile_as_dependency") {
if let Some((_, ast)) = as_dependency {
return Err(duplicate_config_err(ast, arg, "compile_as_dependency"))
}
if let ast::PathOrLit::Lit(syn::Lit::Bool(lit_bool)) = &arg.value {
as_dependency = Some((lit_bool.value, arg))
} else {
return Err(format_err_spanned!(
arg,
"expected a bool literal for `compile_as_dependency` ink! config argument",
))
}
} else if arg.name.is_ident("env_types") {
if let Some((_, ast)) = env_types {
return Err(duplicate_config_err(ast, arg, "env_types"))
}
if let ast::PathOrLit::Path(path) = &arg.value {
env_types = Some((EnvTypes { path: path.clone() }, arg))
} else {
return Err(format_err_spanned!(
arg,
"expected a path for `env_types` ink! config argument",
))
}
} else {
return Err(format_err_spanned!(
arg,
"encountered unknown or unsupported ink! config argument",
))
}
}
Ok(Config {
storage_alloc: storage_alloc.map(|(storage_alloc, _)| storage_alloc),
as_dependency: as_dependency.map(|(as_dependency, _)| as_dependency),
env_types: env_types.map(|(env_types, _)| env_types),
})
}
}
impl Config {
/// Returns the environmental types definition if specified.
/// Otherwise returns the default environmental types definition provided
/// by ink!.