Unverified Commit 2dd6dc3f authored by Peter Goodspeed-Niklaus's avatar Peter Goodspeed-Niklaus Committed by GitHub
Browse files

Improve `#[codec(crate = path)]` attribute: path, not string literal (#277)

* Improve `#[codec(crate = path)]` attribute: path, not string literal

Accepting a path instead of a string literal makes life easier for
the downstream macros which need to actually use that attribute.

* actually test the pass test
parent 459ebe68
Pipeline #144096 passed with stages
in 19 minutes and 58 seconds
......@@ -6,9 +6,17 @@ and this crate adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.h
## Unreleased
## [2.2.0-rc.2] - 2021-06-24
- Updated syntax of `#[codec(crate = <path>)]` attribute macro: no longer expects the crate path to
be a string literal, but a path literal. This improves usability when used within other macros;
the external macro doesn't need to construct a string, but can simply do
`#[codec(crate = $crate::my_codec_reexport)]`.
## [2.2.0-rc.1] - 2021-06-22
### Added
- `MaxEncodedLen` trait for items that have a statically known maximum encoded size. ([#268](https://github.com/paritytech/parity-scale-codec/pull/268))
- `#[codec(crate = "<path>")]` top-level attribute to be used with the new `MaxEncodedLen`
trait, which allows to specify a different path to the crate that contains the `MaxEncodedLen` trait.
......@@ -17,11 +25,13 @@ Useful when using generating a type through a macro and this type should impleme
## [2.1.3] - 2021-06-14
### Changed
- Lint attributes now pass through to the derived impls of `Encode`, `Decode` and `CompactAs`. PR #272
## [2.1.0] - 2021-04-06
### Fix
- Add support for custom where bounds `codec(encode_bound(T: Encode))` and `codec(decode_bound(T: Decode))` when
deriving the traits. Pr #262
- Switch to const generics for array implementations. Pr #261
......@@ -29,11 +39,13 @@ deriving the traits. Pr #262
## [2.0.1] - 2021-02-26
### Fix
- Fix type inference issue in `Decode` derive macro. Pr #254
## [2.0.0] - 2021-01-26
### Added
- `Decode::skip` allows to skip some encoded types. Pr #243
- `Decode::encoded_fixed_size` allows to get the fixed encoded size of a type. PR #243
- `Error` now contains a chain of causes. This full error description can also be activated on
......@@ -41,6 +53,7 @@ deriving the traits. Pr #262
- `Encode::encoded_size` allows to get the encoded size of a type more efficiently. PR #245
### Changed
- `CompactAs::decode_from` now returns result. This allow for decoding to fail from their compact
form.
- derive macro use literal index e.g. `#[codec(index = 15)]` instead of `#[codec(index = "15")]`
......@@ -49,6 +62,7 @@ deriving the traits. Pr #262
- `Output` can now be used as a trait object.
### Removed
- `EncodeAppend::append` is removed in favor of `EncodeAppend::append_or_new`.
- `Output::push` is removed in favor of `Encode::encode_to`.
- Some bounds on `HasCompact::Type` are removed.
......
......@@ -134,31 +134,46 @@ fn crate_access() -> syn::Result<Ident> {
}
}
/// Match `#[codec(crate = ...)]` and return the `...`
fn codec_crate_path_lit(attr: &Attribute) -> Option<Lit> {
/// This struct matches `crate = ...` where the ellipsis is a `Path`.
struct CratePath {
_crate_token: Token![crate],
_eq_token: Token![=],
path: Path,
}
impl Parse for CratePath {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
Ok(CratePath {
_crate_token: input.parse()?,
_eq_token: input.parse()?,
path: input.parse()?,
})
}
}
impl From<CratePath> for Path {
fn from(CratePath { path, ..}: CratePath) -> Self {
path
}
}
/// Match `#[codec(crate = ...)]` and return the `...` if it is a `Path`.
fn codec_crate_path_inner(attr: &Attribute) -> Option<Path> {
// match `#[codec ...]`
if !attr.path.is_ident("codec") {
return None;
};
// match `#[codec(crate = ...)]` and return the `...`
match attr.parse_meta() {
Ok(Meta::NameValue(MetaNameValue { path, lit, .. })) if path.is_ident("crate") => {
Some(lit)
}
_ => None,
}
attr.path.is_ident("codec").then(move || {
// match `#[codec(crate = ...)]` and return the `...`
attr.parse_args::<CratePath>().map(Into::into).ok()
}).flatten()
}
/// Match `#[codec(crate = "...")]` and return the contents as a `Path`
/// Match `#[codec(crate = ...)]` and return the ellipsis as a `Path`.
///
/// If not found, returns the default crate access pattern.
///
/// If multiple items match the pattern, all but the first are ignored.
pub fn codec_crate_path(attrs: &[Attribute]) -> syn::Result<Path> {
match attrs.iter().find_map(codec_crate_path_lit) {
Some(Lit::Str(lit_str)) => lit_str.parse::<Path>(),
Some(lit) => {
Err(Error::new(
lit.span(),
"Expected format: #[codec(crate = \"path::to::codec\")]",
))
}
match attrs.iter().find_map(codec_crate_path_inner) {
Some(path) => Ok(path),
None => crate_access().map(|ident| ident.into()),
}
}
......@@ -227,7 +242,7 @@ pub fn filter_skip_unnamed<'a>(fields: &'a syn::FieldsUnnamed) -> impl Iterator<
/// The top level can have the following attributes:
///
/// * `#[codec(dumb_trait_bound)]`
/// * `#[codec(crate = "path::to::crate")]
/// * `#[codec(crate = path::to::crate)]
///
/// Fields can have the following attributes:
///
......@@ -346,12 +361,12 @@ fn check_variant_attribute(attr: &Attribute) -> syn::Result<()> {
// Only `#[codec(dumb_trait_bound)]` is accepted as top attribute
fn check_top_attribute(attr: &Attribute) -> syn::Result<()> {
let top_error = "Invalid attribute: only `#[codec(dumb_trait_bound)]`, \
`#[codec(encode_bound(T: Encode))]`, `#[codec(crate = \"path::to::crate\")]`, or \
`#[codec(encode_bound(T: Encode))]`, `#[codec(crate = path::to::crate)]`, or \
`#[codec(decode_bound(T: Decode))]` are accepted as top attribute";
if attr.path.is_ident("codec")
&& attr.parse_args::<CustomTraitBound<encode_bound>>().is_err()
&& attr.parse_args::<CustomTraitBound<decode_bound>>().is_err()
&& codec_crate_path_lit(attr).is_none()
&& codec_crate_path_inner(attr).is_none()
{
match attr.parse_meta()? {
Meta::List(ref meta_list) if meta_list.nested.len() == 1 => {
......
......@@ -38,7 +38,7 @@
//! The `Encode` trait is used for encoding of data into the SCALE format. The `Encode` trait
//! contains the following functions:
//!
//!
//! * `size_hint(&self) -> usize`: Gets the capacity (in bytes) required for the encoded data.
//! This is to avoid double-allocation of memory needed for the encoding.
//! It can be an estimate and does not need to be an exact number.
......@@ -331,14 +331,14 @@ pub use max_encoded_len::MaxEncodedLen;
/// # Within other macros
///
/// Sometimes the `MaxEncodedLen` trait and macro are used within another macro, and it can't be
/// guaranteed that the `max_encoded_len` module is available at the call site. In that case, the
/// macro should reexport the `max_encoded_len` module and specify the path to the reexport:
/// guaranteed that the `parity_scale_codec` module is available at the call site. In that case, the
/// macro should reexport the `parity_scale_codec` module and specify the path to the reexport:
///
/// ```ignore
/// pub use parity_scale_codec::max_encoded_len;
/// pub use parity_scale_codec as codec;
///
/// #[derive(Encode, MaxEncodedLen)]
/// #[codec(crate = "$crate::max_encoded_len")]
/// #[codec(crate = $crate::codec)]
/// struct Example;
/// ```
#[cfg(feature = "derive")]
......
......@@ -21,4 +21,5 @@ fn derive_no_bound_ui() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/max_encoded_len_ui/*.rs");
t.pass("tests/max_encoded_len_ui/pass/*.rs");
}
use parity_scale_codec::{Encode, MaxEncodedLen};
#[derive(Encode, MaxEncodedLen)]
#[codec(frame_support::max_encoded_len)]
#[codec(crate = "parity_scale_codec")]
struct Example;
fn main() {
......
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = "path::to::crate")]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
--> $DIR/list_list_item.rs:4:9
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = path::to::crate)]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
--> $DIR/crate_str.rs:4:9
|
4 | #[codec(crate = "foo()")]
4 | #[codec(crate = "parity_scale_codec")]
| ^^^^^
error[E0277]: the trait bound `Example: WrapperTypeEncode` is not satisfied
--> $DIR/list_list_item.rs:3:18
--> $DIR/crate_str.rs:3:18
|
3 | #[derive(Encode, MaxEncodedLen)]
| ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`
......
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = "path::to::crate")]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = path::to::crate)]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
--> $DIR/incomplete_attr.rs:4:9
|
4 | #[codec(crate)]
......
use parity_scale_codec::{Encode, MaxEncodedLen};
#[derive(Encode, MaxEncodedLen)]
#[codec(crate = "foo()")]
#[codec(parity_scale_codec)]
struct Example;
fn main() {
......
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = "path::to::crate")]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
--> $DIR/path_list_item.rs:4:9
error: Invalid attribute: only `#[codec(dumb_trait_bound)]`, `#[codec(encode_bound(T: Encode))]`, `#[codec(crate = path::to::crate)]`, or `#[codec(decode_bound(T: Decode))]` are accepted as top attribute
--> $DIR/missing_crate_specifier.rs:4:9
|
4 | #[codec(frame_support::max_encoded_len)]
| ^^^^^^^^^^^^^
4 | #[codec(parity_scale_codec)]
| ^^^^^^^^^^^^^^^^^^
error[E0277]: the trait bound `Example: WrapperTypeEncode` is not satisfied
--> $DIR/path_list_item.rs:3:18
--> $DIR/missing_crate_specifier.rs:3:18
|
3 | #[derive(Encode, MaxEncodedLen)]
| ^^^^^^^^^^^^^ the trait `WrapperTypeEncode` is not implemented for `Example`
......
//! This test case demonstrates correct use of the `#[codec(crate = path)]` attribute.
use parity_scale_codec::{self as codec, Encode, MaxEncodedLen};
#[derive(Encode, MaxEncodedLen)]
#[codec(crate = codec)]
struct Example;
fn main() {
let _ = Example::max_encoded_len();
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment