зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1513173 - Part 2: Revendor dependencies. r=emilio
This commit is contained in:
Родитель
5b0fd46634
Коммит
894fb2c90e
|
@ -1 +1 @@
|
|||
{"files":{"CHANGELOG.md":"06091817afa317ff86efb9aee3fa8d165358150a92c6369e4ab2958765c14af8","Cargo.toml":"3ec79fcbc682939731504a261f9f7a10afecdbbf298af8a10a9748abcb79172a","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"f70d772dd28cd14fc817c71d343b07ec8bfbe02dfe8d3e999a0185c6ef8731ec","examples/automatic_bounds.rs":"2716da31a5beed954da77866779ef77600686688a8890fefa7e23db628358104","examples/consume_fields.rs":"abcdd6a919a95a5c295e8d362db0367ab171fe3c8a80b23d83701765cd902d2d","examples/fallible_read.rs":"e06d5c045d3a47c0e00d3c77ed3aa1a1d5173d2c3f462dc1967379db3094af4c","examples/supports_struct.rs":"f2d84a4377e555ed7535e563c9593bd6a398a63b42f7da72381eea2fccec229f","publish.sh":"42795a155247c69402f8c4c917c9874a06dfde5a7606c8b59fc4e9ccd34233dd","src/lib.rs":"07da20edb52c4aba30f2b680b28ca96cf995859566b0db3b1d2e0ee967249a18","src/macros_public.rs":"3ed7eb99f309d9cd600d3a09ff4dcf5cc5d787fb49e8e5ead6bb00e31e5e6793","tests/accrue_errors.rs":"f873acf2a13dba88f508c3eb118d14e36515bf94b7ee914ddec2ae1eb55e381f","tests/computed_bound.rs":"2313da96a250b5948ca06bf86cb7158b55a59eba75334aa8ba27a46b28ede6b5","tests/custom_bound.rs":"4dd3e8fd76734d8f262e857773f53a820229ad6e10fe6fbbbe1f636c7da9eab0","tests/defaults.rs":"b544be90d18be26305a757e8468bf8735356889b59c167294d118e32ee7f82ea","tests/enums_newtype.rs":"723c761eab356f577e804a5e07f5efa244f70fb7525d20fceb1bc3852c8d742e","tests/enums_struct.rs":"e0097d7f947cf9e4612c9450c55ea5075c7f6fcbbf8cac7176449d92eebc9343","tests/enums_unit.rs":"8c075d5897b60b58cb71d6306e7fdd80851f5a147ae3010ba70ea100007364a3","tests/error.rs":"f989a43fad47d61d75da2bc78af6c51bac549f39f14f87a804eea226308ece32","tests/from_generics.rs":"79230ad21e8482cf6e6ceb0c74303bc0cdf77fbb951f46a6ba1006ecf1248fd5","tests/from_type_param.rs":"4369737a9d4c21cfa8595a9159cd5d98d02af3445d8b4085654ad4ce8e8bba3f","tests/from_type_param_default.rs":"a5ee72f22540004e755445c37dd4c425ef12beef496896d608695bae964afe7f","tests/from_variant.rs":"2e804326302a62b979eae963e88f0a2cdb6a21ee9667407c361d178f8c0aadba","tests/generics.rs":"3c0f43b39a8f319b8318e409d3836c7235892ae4f50f5986ea80f69b80a4080b","tests/happy_path.rs":"13cf6cd5f87920d90762fd08e45d9f0bb378563e1c92cf5ffb3ba78decdc4969","tests/multiple.rs":"b76172c396a89414b1669eec2bf854c79a7215fd3b6977123b81591ac6761670","tests/newtype.rs":"c45842fd764174a6a8177851e15445d344ab93bf2124b5f77213780d1306adea","tests/skip.rs":"fd936a5eff92c0a49b0a900e6fcda6b7a10a83dd4096ae2000201ccb3f66a738","tests/split_declaration.rs":"b191ed446bf7b8fdf1d41059354adb4b155523507f37b0d36aed3347617a2492","tests/supports.rs":"acb53d4d810dcd3e46763db13234e4fb42e425cfde2516d9bf5d7b690be72dcc"},"package":"f593353cad5af2df6d54810de2b61aa8acba5b5fbc70b0d75e7cc5bdd80aca73"}
|
||||
{"files":{"CHANGELOG.md":"111bee96e6baf82f8898ada5f5395888253832ed641fc6013ad803ac6afaeaf0","Cargo.toml":"66f4111240094e9fed78c1989fc56b30fb94a58f2465eb0eacbaf050cc027b81","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","README.md":"05357355cb87aea0b5bda29ce9f6ef03331717cf351c6cf7554855f5d825756e","examples/automatic_bounds.rs":"dcac3391ed568475cea17ec64c324283f9771dce61adb0a0dd7b8a9ee0bf954d","examples/consume_fields.rs":"ebf4bd8baf4e54ccaa926f910957183d6a5b1decaa87ab5ebcca6c8d38b6c6b3","examples/fallible_read.rs":"aa9d1e0aaba8f3eb4580e94e133839f7eb77fca61beed254f3d5e0f6d6981f00","examples/supports_struct.rs":"d69124e09d3d6b8e04a3af07a6af96c5df7cbd3d6953a51657b39088cc2c2de7","src/lib.rs":"07da20edb52c4aba30f2b680b28ca96cf995859566b0db3b1d2e0ee967249a18","src/macros_public.rs":"3ed7eb99f309d9cd600d3a09ff4dcf5cc5d787fb49e8e5ead6bb00e31e5e6793","tests/accrue_errors.rs":"f5ac2d8cb0a12284a845d25b9472c4605aa5e8c1cd66a6dd6ad64f1c749b2caf","tests/computed_bound.rs":"2313da96a250b5948ca06bf86cb7158b55a59eba75334aa8ba27a46b28ede6b5","tests/custom_bound.rs":"4dd3e8fd76734d8f262e857773f53a820229ad6e10fe6fbbbe1f636c7da9eab0","tests/defaults.rs":"b544be90d18be26305a757e8468bf8735356889b59c167294d118e32ee7f82ea","tests/enums_newtype.rs":"a09af70072e566bee1f6cad91675f3553c47333e51a0e389ebb47ae82de776a8","tests/enums_struct.rs":"e0097d7f947cf9e4612c9450c55ea5075c7f6fcbbf8cac7176449d92eebc9343","tests/enums_unit.rs":"8c075d5897b60b58cb71d6306e7fdd80851f5a147ae3010ba70ea100007364a3","tests/error.rs":"465f7634fa23f0076c93a9c5fc16ccde16119b874d64c8dabaf275b6b7ae61f2","tests/from_generics.rs":"79230ad21e8482cf6e6ceb0c74303bc0cdf77fbb951f46a6ba1006ecf1248fd5","tests/from_type_param.rs":"7c97745cdcea18a2d6f51e4da7426055a078287bf9f2c93d487057195efa2b30","tests/from_type_param_default.rs":"80787ef6527e7f76c2aac93b1f35a006a9c5e0df8deed61698eaac1127278021","tests/from_variant.rs":"2e804326302a62b979eae963e88f0a2cdb6a21ee9667407c361d178f8c0aadba","tests/generics.rs":"e08aea8a8de8f03e3c243bde650e250defbe340cef3e4c06935b2b3e46d2a376","tests/happy_path.rs":"c32daa68e2becdc2d6204985a19b437cfb49d7f1680e890578f0760dc9749b77","tests/multiple.rs":"0391be49bab07771d7e37d35fe17f6d9bf1aa6dc57e2c0e5c0912ab8e043128f","tests/newtype.rs":"3f029724467abc7a33aaf7b6a1feb7b6898eba578576afff338e73deb9fd2b3b","tests/skip.rs":"604861aa4d1dfdc4e5f38f8261be19b7240c650eb493b64ce0178f54c24a8d2d","tests/split_declaration.rs":"f509390f88549164af9218f1e5b07564b169a4481a20b738432ffb03c517b599","tests/suggestions.rs":"0afb756949be876aaae76974f119be811c783fb134a54978ff0453c537ff3174","tests/supports.rs":"3b096c3d893bf8e76f70219bc276cbfe04529091e9e8652af140f0f1b3a91270"},"package":"9158d690bc62a3a57c3e45b85e4d50de2008b39345592c64efd79345c7e24be0"}
|
|
@ -1,5 +1,31 @@
|
|||
# Changelog
|
||||
|
||||
## v0.8.6 (March 18, 2019)
|
||||
- Added "did you mean" suggestions for unknown fields behind the `suggestions` flag [#60](https://github.com/TedDriggs/issues/60)
|
||||
- Added `Error::unknown_field_with_alts` to support the suggestion use-case.
|
||||
- Added `ast::Fields::len` and `ast::Fields::is_empty` methods.
|
||||
|
||||
## v0.8.5 (February 4, 2019)
|
||||
- Accept unquoted positive numeric literals [#52](https://github.com/TedDriggs/issues/52)
|
||||
- Add `FromMeta` to the `syn::Lit` enum and its variants
|
||||
- Improve error message for unexpected literal formats to not say "other"
|
||||
|
||||
## v0.8.4 (February 4, 2019)
|
||||
- Use `syn::Error` to provide precise errors before `proc_macro::Diagnostic` is available
|
||||
- Add `diagnostics` feature flag to toggle between stable and unstable error backends
|
||||
- Attach error information in more contexts
|
||||
- Add `allow_unknown_fields` to support parsing the same attribute multiple times for different macros [#51](https://github.com/darling/issues/51)
|
||||
- Proc-macro authors will now see better errors in `darling` attributes
|
||||
|
||||
## v0.8.3 (January 21, 2019)
|
||||
- Attach spans to errors in generated trait impls [#37](https://github.com/darling/issues/37)
|
||||
- Attach spans to errors for types with provided bespoke implementations
|
||||
- Deprecate `set_span` from 0.8.2, as spans should never be broadened after being initially set
|
||||
|
||||
## v0.8.2 (January 17, 2019)
|
||||
- Add spans to errors to make quality warnings and errors easy in darling. This is blocked on diagnostics stabilizing.
|
||||
- Add `darling::util::SpannedValue` so proc-macro authors can remember position information alongside parsed values.
|
||||
|
||||
## v0.8.0
|
||||
- Update dependency on `syn` to 0.15 [#44](https://github.com/darling/pull/44). Thanks to @hcpl
|
||||
|
||||
|
|
|
@ -12,18 +12,19 @@
|
|||
|
||||
[package]
|
||||
name = "darling"
|
||||
version = "0.8.1"
|
||||
version = "0.8.6"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
exclude = ["/.travis.yml", "/publish.sh"]
|
||||
description = "A proc-macro library for reading attributes into structs when\nimplementing custom derives.\n"
|
||||
documentation = "https://docs.rs/darling/0.8.1"
|
||||
documentation = "https://docs.rs/darling/0.8.6"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/TedDriggs/darling"
|
||||
[dependencies.darling_core]
|
||||
version = "=0.8.1"
|
||||
version = "=0.8.6"
|
||||
|
||||
[dependencies.darling_macro]
|
||||
version = "=0.8.1"
|
||||
version = "=0.8.6"
|
||||
[dev-dependencies.proc-macro2]
|
||||
version = "0.4"
|
||||
|
||||
|
@ -31,6 +32,10 @@ version = "0.4"
|
|||
version = "0.6"
|
||||
|
||||
[dev-dependencies.syn]
|
||||
version = "0.15"
|
||||
version = "0.15.26"
|
||||
|
||||
[features]
|
||||
diagnostics = ["darling_core/diagnostics"]
|
||||
suggestions = ["darling_core/suggestions"]
|
||||
[badges.travis-ci]
|
||||
repository = "TedDriggs/darling"
|
||||
|
|
|
@ -10,8 +10,9 @@ Darling
|
|||
`darling` provides a set of traits which can be derived or manually implemented.
|
||||
|
||||
1. `FromMeta` is used to extract values from a meta-item in an attribute. Implementations are likely reusable for many libraries, much like `FromStr` or `serde::Deserialize`. Trait implementations are provided for primitives, some std types, and some `syn` types.
|
||||
1. `FromDeriveInput` is implemented or derived by each proc-macro crate which depends on `darling`. This is the root for input parsing; it gets access to the identity, generics, and visibility of the target type, and can specify which attribute names should be parsed or forwarded from the input AST.
|
||||
1. `FromField` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity (if it exists), type, and visibility of the field.
|
||||
2. `FromDeriveInput` is implemented or derived by each proc-macro crate which depends on `darling`. This is the root for input parsing; it gets access to the identity, generics, and visibility of the target type, and can specify which attribute names should be parsed or forwarded from the input AST.
|
||||
3. `FromField` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity (if it exists), type, and visibility of the field.
|
||||
4. `FromVariant` is implemented or derived by each proc-macro crate which depends on `darling`. Structs deriving this trait will get access to the identity and contents of the variant, which can be transformed the same as any other `darling` input.
|
||||
|
||||
# Example
|
||||
|
||||
|
@ -46,6 +47,49 @@ The above code will then be able to parse this input:
|
|||
pub struct ConsumingType;
|
||||
```
|
||||
|
||||
# Attribute Macros
|
||||
Non-derive attribute macros are supported.
|
||||
To parse arguments for attribute macros, derive `FromMeta` on the argument receiver type, then pass `&syn::AttributeArgs` to the `from_list` method.
|
||||
This will produce a normal `darling::Result<T>` that can be used the same as a result from parsing a `DeriveInput`.
|
||||
|
||||
## Macro Code
|
||||
```rust,ignore
|
||||
use darling::FromMeta;
|
||||
use syn::{AttributeArgs, ItemFn};
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
#[derive(Debug, FromMeta)]
|
||||
pub struct MacroArgs {
|
||||
#[darling(default)]
|
||||
timeout_ms: Option<u16>,
|
||||
path: String,
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
fn your_attr(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
let attr_args = parse_macro_input!(args as AttributeArgs);
|
||||
let _input = parse_macro_input!(input as ItemFn);
|
||||
|
||||
let _args = match MacroArgs::from_list(&attr_args) {
|
||||
Ok(v) => v,
|
||||
Err(e) => { return e.write_errors(); }
|
||||
};
|
||||
|
||||
// do things with `args`
|
||||
unimplemented!()
|
||||
}
|
||||
```
|
||||
|
||||
## Consuming Code
|
||||
```rust,ignore
|
||||
use your_crate::your_attr;
|
||||
|
||||
#[your_attr(path = "hello", timeout_ms = 15)]
|
||||
fn do_stuff() {
|
||||
println!("Hello");
|
||||
}
|
||||
```
|
||||
|
||||
# Features
|
||||
Darling's features are built to work well for real-world projects.
|
||||
|
||||
|
@ -55,3 +99,4 @@ Darling's features are built to work well for real-world projects.
|
|||
* **Mapping function**: Use `#[darling(map="path")]` to specify a function that runs on the result of parsing a meta-item field. This can change the return type, which enables you to parse to an intermediate form and convert that to the type you need in your struct.
|
||||
* **Skip fields**: Use `#[darling(skip)]` to mark a field that shouldn't be read from attribute meta-items.
|
||||
* **Multiple-occurrence fields**: Use `#[darling(multiple)]` on a `Vec` field to allow that field to appear multiple times in the meta-item. Each occurrence will be pushed into the `Vec`.
|
||||
* **Span access**: Use `darling::util::SpannedValue` in a struct to get access to that meta item's source code span. This can be used to emit warnings that point at a specific field from your proc macro. In addition, you can use `darling::Error::write_errors` to automatically get precise error location details in most cases.
|
||||
|
|
|
@ -68,7 +68,8 @@ fn main() {
|
|||
Div(String)
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let parsed: SpeakingOptions<Phoneme, Volume> =
|
||||
FromDeriveInput::from_derive_input(&derive_input).unwrap();
|
||||
|
|
|
@ -64,7 +64,8 @@ impl ToTokens for MyInputReceiver {
|
|||
} = *self;
|
||||
|
||||
let (imp, ty, wher) = generics.split_for_impl();
|
||||
let fields = data.as_ref()
|
||||
let fields = data
|
||||
.as_ref()
|
||||
.take_struct()
|
||||
.expect("Should never be enum")
|
||||
.fields;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
//! 1. Using `darling::Result` as a carrier to preserve the error for later display
|
||||
//! 1. Using `Result<T, syn::Meta>` to attempt a recovery in imperative code
|
||||
//! 1. Using the `map` darling meta-item to post-process the receiver before returning.
|
||||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
|
@ -58,8 +57,8 @@ impl MyInputReceiver {
|
|||
};
|
||||
|
||||
Self {
|
||||
name: name,
|
||||
frequency: frequency,
|
||||
name,
|
||||
frequency,
|
||||
amplitude: Ok(amplitude),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,8 +37,7 @@ pub struct Foo {
|
|||
let parsed = syn::parse_str(good_input).unwrap();
|
||||
let receiver = Lorem::from_derive_input(&parsed).unwrap();
|
||||
let wrong_shape_parsed = syn::parse_str(bad_input).unwrap();
|
||||
let wrong_shape = Lorem::from_derive_input(&wrong_shape_parsed)
|
||||
.expect_err("Shape was wrong");
|
||||
let wrong_shape = Lorem::from_derive_input(&wrong_shape_parsed).expect_err("Shape was wrong");
|
||||
|
||||
println!(
|
||||
r#"
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
cargo publish --manifest-path core/Cargo.toml;
|
||||
cargo publish --manifest-path macro/Cargo.toml;
|
||||
cargo publish;
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::ast;
|
||||
use darling::FromDeriveInput;
|
||||
|
@ -30,17 +33,14 @@ struct LoremField {
|
|||
|
||||
#[test]
|
||||
fn bad_type_and_missing_fields() {
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(ipsum = true, dolor(amet = "Hi"))]
|
||||
pub struct NonConforming {
|
||||
foo: ()
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
let input = parse_quote! {
|
||||
#[accrue(ipsum = true, dolor(amet = "Hi"))]
|
||||
pub struct NonConforming {
|
||||
foo: ()
|
||||
}
|
||||
};
|
||||
|
||||
let s_result: ::darling::Error = Lorem::from_derive_input(&input).unwrap_err();
|
||||
//assert_eq!(3, s_result.len());
|
||||
let err = s_result.flatten();
|
||||
println!("{}", err);
|
||||
assert_eq!(3, err.len());
|
||||
|
@ -48,17 +48,15 @@ fn bad_type_and_missing_fields() {
|
|||
|
||||
#[test]
|
||||
fn body_only_issues() {
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(ipsum = "Hello", dolor(sit))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
let input = parse_quote! {
|
||||
#[accrue(ipsum = "Hello", dolor(sit))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
};
|
||||
|
||||
let s_err: ::darling::Error = Lorem::from_derive_input(&input).unwrap_err();
|
||||
let s_err = Lorem::from_derive_input(&input).unwrap_err();
|
||||
println!("{:?}", s_err);
|
||||
assert_eq!(2, s_err.len());
|
||||
}
|
||||
|
@ -78,17 +76,15 @@ struct Month {
|
|||
|
||||
#[test]
|
||||
fn error_in_enum_fields() {
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(schedule(tuesday(morning = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
let input = parse_quote! {
|
||||
#[accrue(schedule(tuesday(morning = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
};
|
||||
|
||||
let s_err: ::darling::Error = Month::from_derive_input(&input).unwrap_err();
|
||||
let s_err = Month::from_derive_input(&input).unwrap_err();
|
||||
assert_eq!(2, s_err.len());
|
||||
let err = s_err.flatten();
|
||||
// TODO add tests to check location path is correct
|
||||
|
@ -97,17 +93,15 @@ fn error_in_enum_fields() {
|
|||
|
||||
#[test]
|
||||
fn error_in_newtype_variant() {
|
||||
let input = syn::parse_str(
|
||||
r#"
|
||||
#[accrue(schedule(wednesday(sit = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
"#,
|
||||
).unwrap();
|
||||
let input = parse_quote! {
|
||||
#[accrue(schedule(wednesday(sit = "yes")))]
|
||||
pub struct NonConforming {
|
||||
foo: (),
|
||||
bar: bool,
|
||||
}
|
||||
};
|
||||
|
||||
let s_err: ::darling::Error = Month::from_derive_input(&input).unwrap_err();
|
||||
let s_err = Month::from_derive_input(&input).unwrap_err();
|
||||
assert_eq!(1, s_err.len());
|
||||
println!("{}", s_err);
|
||||
println!("{}", s_err.flatten());
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
extern crate syn;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, FromMeta)]
|
||||
#[darling(default)]
|
||||
pub struct Amet {
|
||||
|
@ -34,12 +36,10 @@ impl PartialEq<Lorem> for Holder {
|
|||
|
||||
#[test]
|
||||
fn bool_word() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem(ipsum))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Ipsum(true));
|
||||
|
@ -47,12 +47,10 @@ fn bool_word() {
|
|||
|
||||
#[test]
|
||||
fn bool_literal() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem(ipsum = false))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Ipsum(false));
|
||||
|
@ -60,12 +58,10 @@ fn bool_literal() {
|
|||
|
||||
#[test]
|
||||
fn string_literal() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem(dolor = "Hello"))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(pr, Lorem::Dolor("Hello".to_string()));
|
||||
|
@ -73,12 +69,10 @@ fn string_literal() {
|
|||
|
||||
#[test]
|
||||
fn struct_nested() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem(sit(world = "Hello", hello = false)))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Holder::from_derive_input(&di).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -93,12 +87,10 @@ fn struct_nested() {
|
|||
#[test]
|
||||
#[should_panic]
|
||||
fn format_mismatch() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem(dolor(world = "Hello", hello = false)))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
Holder::from_derive_input(&di).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
//! In case of bad input, parsing should fail. The error should have locations set in derived implementations.
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
|
@ -33,24 +36,20 @@ impl From<syn::Ident> for Lorem {
|
|||
|
||||
#[test]
|
||||
fn parsing_fail() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(ipsum(amet = "yes", world = false))]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
println!("{}", Lorem::from_derive_input(&di).unwrap_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_field() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(ipsum(amet = true))]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
println!("{}", Lorem::from_derive_input(&di).unwrap_err());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromTypeParam;
|
||||
use syn::{DeriveInput, GenericParam, Ident, TypeParam};
|
||||
|
@ -34,14 +37,13 @@ fn extract_type(param: &GenericParam) -> &TypeParam {
|
|||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di: DeriveInput = syn::parse_str(
|
||||
r#"
|
||||
let di: DeriveInput = parse_quote! {
|
||||
struct Baz<
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized
|
||||
>(T, U);
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let params = di.generics.params;
|
||||
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromTypeParam;
|
||||
use syn::{DeriveInput, GenericParam, TypeParam};
|
||||
|
@ -22,15 +25,13 @@ fn extract_type(param: &GenericParam) -> &TypeParam {
|
|||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di: DeriveInput = syn::parse_str(
|
||||
r#"
|
||||
let di: DeriveInput = parse_quote! {
|
||||
struct Baz<
|
||||
#[lorem(foo)] T,
|
||||
#[lorem(bar = "x")] U: Eq + ?Sized,
|
||||
#[lorem(foo = false)] V = (),
|
||||
>(T, U, V);
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
let params = di.generics.params;
|
||||
|
||||
{
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
|
@ -15,12 +18,10 @@ struct Foo<T> {
|
|||
|
||||
#[test]
|
||||
fn expansion() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(lorem = "Hello")]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let _parsed = Foo::<String>::from_derive_input(&di).unwrap();
|
||||
Foo::<String>::from_derive_input(&di).unwrap();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
|
@ -34,13 +33,11 @@ struct TraitCore {
|
|||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[derive(Foo)]
|
||||
#[darling_demo(lorem(ipsum))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
Core::from_derive_input(&di).unwrap(),
|
||||
|
@ -58,13 +55,11 @@ fn simple() {
|
|||
|
||||
#[test]
|
||||
fn trait_type() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[derive(Foo)]
|
||||
#[darling_demo(lorem(dolor = "hello"))]
|
||||
pub struct Bar;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
TraitCore::from_derive_input(&di).unwrap(),
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
|
@ -20,12 +23,10 @@ struct Ipsum {
|
|||
|
||||
#[test]
|
||||
fn expand_many() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[hello(ipsum(dolor = "Hello", dolor = "World"))]
|
||||
pub struct Baz;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let lorem: Lorem = Lorem::from_derive_input(&di).unwrap();
|
||||
assert_eq!(
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
|
@ -18,13 +20,11 @@ struct DemoContainer {
|
|||
|
||||
#[test]
|
||||
fn generated() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[derive(Baz)]
|
||||
#[newtype(lorem = false)]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let c = DemoContainer::from_derive_input(&di).unwrap();
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
|
@ -34,12 +37,10 @@ pub enum Sit {
|
|||
|
||||
#[test]
|
||||
fn verify_skipped_field_not_required() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[skip_test(ipsum = "Hello")]
|
||||
struct Baz;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
Lorem::from_derive_input(&di).unwrap(),
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use std::string::ToString;
|
||||
|
||||
|
@ -18,13 +21,11 @@ struct Lorem {
|
|||
|
||||
#[test]
|
||||
fn split_attributes_accrue_to_instance() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[split(foo = "Hello")]
|
||||
#[split(bar)]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let parsed = Lorem::from_derive_input(&di).unwrap();
|
||||
assert_eq!(
|
||||
|
@ -38,39 +39,37 @@ fn split_attributes_accrue_to_instance() {
|
|||
|
||||
#[test]
|
||||
fn duplicates_across_split_attrs_error() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[split(foo = "Hello")]
|
||||
#[split(foo = "World", bar)]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Lorem::from_derive_input(&di);
|
||||
let pr = Lorem::from_derive_input(&di).unwrap_err();
|
||||
assert!(pr.has_span());
|
||||
assert_eq!(
|
||||
pr.unwrap_err().to_string(),
|
||||
pr.to_string(),
|
||||
Error::duplicate_field("foo").to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_errors_accrue_to_instance() {
|
||||
let di = syn::parse_str(
|
||||
r#"
|
||||
let di = parse_quote! {
|
||||
#[split(foo = "Hello")]
|
||||
#[split(foo = "World")]
|
||||
pub struct Foo;
|
||||
"#,
|
||||
).unwrap();
|
||||
};
|
||||
|
||||
let pr = Lorem::from_derive_input(&di);
|
||||
let err: Error = pr.unwrap_err();
|
||||
assert_eq!(2, err.len());
|
||||
let mut errs = err.into_iter();
|
||||
let mut errs = err.into_iter().peekable();
|
||||
assert_eq!(
|
||||
errs.next().unwrap().to_string(),
|
||||
errs.peek().unwrap().to_string(),
|
||||
Error::duplicate_field("foo").to_string()
|
||||
);
|
||||
assert!(errs.next().unwrap().has_span());
|
||||
assert_eq!(
|
||||
errs.next().unwrap().to_string(),
|
||||
Error::missing_field("bar").to_string()
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#![cfg(feature = "suggestions")]
|
||||
|
||||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::FromDeriveInput;
|
||||
|
||||
#[derive(Debug, FromDeriveInput)]
|
||||
#[darling(attributes(suggest))]
|
||||
struct Lorem {
|
||||
ipsum: String,
|
||||
dolor: Dolor,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromMeta)]
|
||||
struct Dolor {
|
||||
sit: bool,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn suggest_dolor() {
|
||||
let input: syn::DeriveInput = parse_quote! {
|
||||
#[suggest(ipsum = "Hello", dolorr(sit))]
|
||||
pub struct Foo;
|
||||
};
|
||||
|
||||
let result = Lorem::from_derive_input(&input).unwrap_err();
|
||||
assert_eq!(2, result.len());
|
||||
assert!(format!("{}", result).contains("Did you mean"));
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
#[macro_use]
|
||||
extern crate darling;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
use darling::ast;
|
||||
use darling::FromDeriveInput;
|
||||
|
@ -28,41 +31,35 @@ mod source {
|
|||
use syn::{self, DeriveInput};
|
||||
|
||||
pub fn newtype_enum() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
enum Hello {
|
||||
World(bool),
|
||||
String(String),
|
||||
parse_quote!{
|
||||
enum Hello {
|
||||
World(bool),
|
||||
String(String),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
pub fn named_field_enum() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
enum Hello {
|
||||
Foo(u16),
|
||||
World {
|
||||
name: String
|
||||
},
|
||||
parse_quote! {
|
||||
enum Hello {
|
||||
Foo(u16),
|
||||
World {
|
||||
name: String
|
||||
},
|
||||
}
|
||||
}
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
pub fn named_struct() -> DeriveInput {
|
||||
syn::parse_str(
|
||||
r#"
|
||||
struct Hello {
|
||||
world: bool,
|
||||
parse_quote! {
|
||||
struct Hello {
|
||||
world: bool,
|
||||
}
|
||||
}
|
||||
"#,
|
||||
).unwrap()
|
||||
}
|
||||
|
||||
pub fn tuple_struct() -> DeriveInput {
|
||||
syn::parse_str("struct Hello(String, bool);").unwrap()
|
||||
parse_quote! { struct Hello(String, bool); }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "darling_core"
|
||||
version = "0.8.1"
|
||||
version = "0.8.6"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = "Helper crate for proc-macro library for reading attributes into structs when\nimplementing custom derives. Use https://crates.io/crates/darling in your code.\n"
|
||||
license = "MIT"
|
||||
|
@ -24,14 +24,20 @@ version = "1.0.6"
|
|||
version = "1.0.0"
|
||||
|
||||
[dependencies.proc-macro2]
|
||||
version = "0.4.2"
|
||||
version = "0.4.26"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.strsim]
|
||||
version = "0.8.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.15"
|
||||
version = "0.15.26"
|
||||
features = ["extra-traits"]
|
||||
|
||||
[features]
|
||||
default = ["syn/full"]
|
||||
diagnostics = []
|
||||
suggestions = ["strsim"]
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ted Driggs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,4 +1,4 @@
|
|||
use std::slice;
|
||||
use std::{slice, vec};
|
||||
|
||||
use syn;
|
||||
|
||||
|
@ -16,7 +16,7 @@ pub enum Data<V, F> {
|
|||
Struct(Fields<F>),
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.3", note = "this has been renamed to Data")]
|
||||
#[deprecated(since = "0.3.0", note = "this has been renamed to Data")]
|
||||
pub type Body<V, F> = Data<V, F>;
|
||||
|
||||
impl<V, F> Data<V, F> {
|
||||
|
@ -32,7 +32,7 @@ impl<V, F> Data<V, F> {
|
|||
/// Creates a new `Data<&'a V, &'a F>` instance from `Data<V, F>`.
|
||||
pub fn as_ref<'a>(&'a self) -> Data<&'a V, &'a F> {
|
||||
match *self {
|
||||
Data::Enum(ref variants) => Data::Enum(variants.into_iter().collect()),
|
||||
Data::Enum(ref variants) => Data::Enum(variants.iter().collect()),
|
||||
Data::Struct(ref data) => Data::Struct(data.as_ref()),
|
||||
}
|
||||
}
|
||||
|
@ -107,11 +107,7 @@ impl<V: FromVariant, F: FromField> Data<V, F> {
|
|||
syn::Data::Enum(ref data) => {
|
||||
let mut items = Vec::with_capacity(data.variants.len());
|
||||
let mut errors = Vec::new();
|
||||
for v_result in data.variants
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|v| FromVariant::from_variant(&v))
|
||||
{
|
||||
for v_result in data.variants.iter().map(FromVariant::from_variant) {
|
||||
match v_result {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(err),
|
||||
|
@ -156,13 +152,14 @@ impl<V: UsesLifetimes, F: UsesLifetimes> UsesLifetimes for Data<V, F> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Equivalent to `syn::Fields`, but replaces the AST element with a generic.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Fields<T> {
|
||||
pub style: Style,
|
||||
pub fields: Vec<T>,
|
||||
}
|
||||
|
||||
#[deprecated(since = "0.3", note = "this has been renamed to Fields")]
|
||||
#[deprecated(since = "0.3.0", note = "this has been renamed to Fields")]
|
||||
pub type VariantData<T> = Fields<T>;
|
||||
|
||||
impl<T> Fields<T> {
|
||||
|
@ -181,7 +178,7 @@ impl<T> Fields<T> {
|
|||
|
||||
/// Returns true if this variant's data makes it a newtype.
|
||||
pub fn is_newtype(&self) -> bool {
|
||||
self.style == Style::Tuple && self.fields.len() == 1
|
||||
self.style == Style::Tuple && self.len() == 1
|
||||
}
|
||||
|
||||
pub fn is_unit(&self) -> bool {
|
||||
|
@ -216,6 +213,16 @@ impl<T> Fields<T> {
|
|||
pub fn iter(&self) -> slice::Iter<T> {
|
||||
self.fields.iter()
|
||||
}
|
||||
|
||||
/// Returns the number of fields in the structure.
|
||||
pub fn len(&self) -> usize {
|
||||
self.fields.len()
|
||||
}
|
||||
|
||||
/// Returns `true` if the `Fields` contains no fields.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.fields.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FromField> Fields<F> {
|
||||
|
@ -226,8 +233,7 @@ impl<F: FromField> Fields<F> {
|
|||
let mut errors = Vec::new();
|
||||
|
||||
for field in &fields.named {
|
||||
let f_result = FromField::from_field(field);
|
||||
match f_result {
|
||||
match FromField::from_field(field) {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(if let Some(ref ident) = field.ident {
|
||||
err.at(ident)
|
||||
|
@ -244,8 +250,7 @@ impl<F: FromField> Fields<F> {
|
|||
let mut errors = Vec::new();
|
||||
|
||||
for field in &fields.unnamed {
|
||||
let f_result = FromField::from_field(field);
|
||||
match f_result {
|
||||
match FromField::from_field(field) {
|
||||
Ok(val) => items.push(val),
|
||||
Err(err) => errors.push(if let Some(ref ident) = field.ident {
|
||||
err.at(ident)
|
||||
|
@ -271,6 +276,15 @@ impl<F: FromField> Fields<F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> IntoIterator for Fields<T> {
|
||||
type Item = T;
|
||||
type IntoIter = vec::IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.fields.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Style> for Fields<T> {
|
||||
fn from(style: Style) -> Self {
|
||||
Fields {
|
||||
|
@ -314,16 +328,16 @@ pub enum Style {
|
|||
}
|
||||
|
||||
impl Style {
|
||||
pub fn is_unit(&self) -> bool {
|
||||
*self == Style::Unit
|
||||
pub fn is_unit(self) -> bool {
|
||||
self == Style::Unit
|
||||
}
|
||||
|
||||
pub fn is_tuple(&self) -> bool {
|
||||
*self == Style::Tuple
|
||||
pub fn is_tuple(self) -> bool {
|
||||
self == Style::Tuple
|
||||
}
|
||||
|
||||
pub fn is_struct(&self) -> bool {
|
||||
*self == Style::Struct
|
||||
pub fn is_struct(self) -> bool {
|
||||
self == Style::Struct
|
||||
}
|
||||
|
||||
/// Creates a new `Fields` of the specified style with the passed-in fields.
|
||||
|
|
|
@ -37,7 +37,8 @@ pub trait ExtractAttribute {
|
|||
let declarations = self.declarations();
|
||||
|
||||
let will_parse_any = !self.attr_names().is_empty();
|
||||
let will_fwd_any = self.forwarded_attrs()
|
||||
let will_fwd_any = self
|
||||
.forwarded_attrs()
|
||||
.map(|fa| !fa.is_empty())
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -56,7 +57,7 @@ pub trait ExtractAttribute {
|
|||
let core_loop = self.core_loop();
|
||||
quote!(
|
||||
#(#attr_names)|* => {
|
||||
if let Some(::syn::Meta::List(ref __data)) = __attr.interpret_meta() {
|
||||
if let ::darling::export::Some(::syn::Meta::List(ref __data)) = __attr.interpret_meta() {
|
||||
let __items = &__data.nested;
|
||||
|
||||
#core_loop
|
||||
|
@ -106,4 +107,4 @@ fn forwards_to_local(behavior: &ForwardAttrs) -> TokenStream {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,38 +2,27 @@ use proc_macro2::TokenStream;
|
|||
use quote::{TokenStreamExt, ToTokens};
|
||||
|
||||
/// Declares the local variable into which errors will be accumulated.
|
||||
#[derive(Default)]
|
||||
pub struct ErrorDeclaration {
|
||||
__hidden: (),
|
||||
}
|
||||
|
||||
impl ErrorDeclaration {
|
||||
pub fn new() -> Self {
|
||||
ErrorDeclaration { __hidden: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ErrorDeclaration {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.append_all(quote! {
|
||||
let mut __errors = Vec::new();
|
||||
let mut __errors = ::darling::export::Vec::new();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns early if attribute or body parsing has caused any errors.
|
||||
#[derive(Default)]
|
||||
pub struct ErrorCheck<'a> {
|
||||
location: Option<&'a str>,
|
||||
__hidden: (),
|
||||
}
|
||||
|
||||
impl<'a> ErrorCheck<'a> {
|
||||
pub fn new() -> Self {
|
||||
ErrorCheck {
|
||||
location: None,
|
||||
__hidden: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_location(location: &'a str) -> Self {
|
||||
ErrorCheck {
|
||||
location: Some(location),
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::{Ident, Path, Type};
|
||||
|
||||
use codegen::DefaultExpression;
|
||||
|
@ -11,7 +13,7 @@ use usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
|
|||
pub struct Field<'a> {
|
||||
/// The name presented to the user of the library. This will appear
|
||||
/// in error messages and will be looked when parsing names.
|
||||
pub name_in_attr: String,
|
||||
pub name_in_attr: Cow<'a, String>,
|
||||
|
||||
/// The name presented to the author of the library. This will appear
|
||||
/// in the setters or temporary variables which contain the values.
|
||||
|
@ -20,13 +22,17 @@ pub struct Field<'a> {
|
|||
/// The type of the field in the input.
|
||||
pub ty: &'a Type,
|
||||
pub default_expression: Option<DefaultExpression<'a>>,
|
||||
pub with_path: Path,
|
||||
pub with_path: Cow<'a, Path>,
|
||||
pub map: Option<&'a Path>,
|
||||
pub skip: bool,
|
||||
pub multiple: bool,
|
||||
}
|
||||
|
||||
impl<'a> Field<'a> {
|
||||
pub fn as_name(&'a self) -> &'a str {
|
||||
&self.name_in_attr
|
||||
}
|
||||
|
||||
pub fn as_declaration(&'a self) -> Declaration<'a> {
|
||||
Declaration(self, !self.skip)
|
||||
}
|
||||
|
@ -104,7 +110,12 @@ impl<'a> ToTokens for MatchArm<'a> {
|
|||
quote!(#name_str)
|
||||
};
|
||||
|
||||
let mut extractor = quote!(#with_path(__inner).map_err(|e| e.at(#location)));
|
||||
// Add the span immediately on extraction failure, so that it's as specific as possible.
|
||||
// The behavior of `with_span` makes this safe to do; if the child applied an
|
||||
// even-more-specific span, our attempt here will not overwrite that and will only cost
|
||||
// us one `if` check.
|
||||
let mut extractor =
|
||||
quote!(#with_path(__inner).map_err(|e| e.with_span(&__inner).at(#location)));
|
||||
if let Some(ref map) = field.map {
|
||||
extractor = quote!(#extractor.map(#map))
|
||||
}
|
||||
|
@ -116,10 +127,10 @@ impl<'a> ToTokens for MatchArm<'a> {
|
|||
// it for error reporting.
|
||||
let __len = #ident.len();
|
||||
match #extractor {
|
||||
Ok(__val) => {
|
||||
::darling::export::Ok(__val) => {
|
||||
#ident.push(__val)
|
||||
}
|
||||
Err(__err) => {
|
||||
::darling::export::Err(__err) => {
|
||||
__errors.push(__err)
|
||||
}
|
||||
}
|
||||
|
@ -130,16 +141,16 @@ impl<'a> ToTokens for MatchArm<'a> {
|
|||
#name_str => {
|
||||
if !#ident.0 {
|
||||
match #extractor {
|
||||
Ok(__val) => {
|
||||
::darling::export::Ok(__val) => {
|
||||
#ident = (true, ::darling::export::Some(__val));
|
||||
}
|
||||
Err(__err) => {
|
||||
::darling::export::Err(__err) => {
|
||||
#ident = (true, None);
|
||||
__errors.push(__err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
__errors.push(::darling::Error::duplicate_field(#name_str));
|
||||
__errors.push(::darling::Error::duplicate_field(#name_str).with_span(&__inner));
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -165,15 +176,13 @@ impl<'a> ToTokens for Initializer<'a> {
|
|||
} else {
|
||||
quote!(#ident: #ident)
|
||||
}
|
||||
} else if let Some(ref expr) = field.default_expression {
|
||||
quote!(#ident: match #ident.1 {
|
||||
::darling::export::Some(__val) => __val,
|
||||
::darling::export::None => #expr,
|
||||
})
|
||||
} else {
|
||||
if let Some(ref expr) = field.default_expression {
|
||||
quote!(#ident: match #ident.1 {
|
||||
::darling::export::Some(__val) => __val,
|
||||
::darling::export::None => #expr,
|
||||
})
|
||||
} else {
|
||||
quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked"))
|
||||
}
|
||||
quote!(#ident: #ident.1.expect("Uninitialized fields without defaults were already checked"))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
let ty_ident = base.ident;
|
||||
quote!(
|
||||
fn from_word() -> ::darling::Result<Self> {
|
||||
Ok(#ty_ident)
|
||||
::darling::export::Ok(#ty_ident)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -29,12 +29,13 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
ref fields,
|
||||
style: Style::Tuple,
|
||||
..
|
||||
}) if fields.len() == 1 =>
|
||||
{
|
||||
}) if fields.len() == 1 => {
|
||||
let ty_ident = base.ident;
|
||||
quote!(
|
||||
fn from_meta(__item: &::syn::Meta) -> ::darling::Result<Self> {
|
||||
Ok(#ty_ident(::darling::FromMeta::from_meta(__item)?))
|
||||
::darling::FromMeta::from_meta(__item)
|
||||
.map_err(|e| e.with_span(&__item))
|
||||
.map(#ty_ident)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -79,6 +80,17 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
let unit_arms = variants.iter().map(Variant::as_unit_match_arm);
|
||||
let struct_arms = variants.iter().map(Variant::as_data_match_arm);
|
||||
|
||||
let unknown_variant_err = if !variants.is_empty() {
|
||||
let names = variants.iter().map(Variant::as_name);
|
||||
quote! {
|
||||
unknown_field_with_alts(__other, &[#(#names),*])
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
unknown_field(__other)
|
||||
}
|
||||
};
|
||||
|
||||
quote!(
|
||||
fn from_list(__outer: &[::syn::NestedMeta]) -> ::darling::Result<Self> {
|
||||
// An enum must have exactly one value inside the parentheses if it's not a unit
|
||||
|
@ -89,7 +101,7 @@ impl<'a> ToTokens for FromMetaImpl<'a> {
|
|||
if let ::syn::NestedMeta::Meta(ref __nested) = __outer[0] {
|
||||
match __nested.name().to_string().as_ref() {
|
||||
#(#struct_arms)*
|
||||
__other => ::darling::export::Err(::darling::Error::unknown_value(__other))
|
||||
__other => ::darling::export::Err(::darling::Error::#unknown_variant_err.with_span(__nested))
|
||||
}
|
||||
} else {
|
||||
::darling::export::Err(::darling::Error::unsupported_format("literal"))
|
||||
|
|
|
@ -49,7 +49,7 @@ fn compute_impl_bounds(bound: Path, mut generics: Generics, applies_to: &IdentSe
|
|||
});
|
||||
|
||||
for mut param in generics.params.iter_mut() {
|
||||
if let &mut GenericParam::Type(ref mut typ) = param {
|
||||
if let GenericParam::Type(ref mut typ) = *param {
|
||||
if applies_to.contains(&typ.ident) {
|
||||
typ.bounds.push(added_bound.clone());
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ pub struct TraitImpl<'a> {
|
|||
pub default: Option<DefaultExpression<'a>>,
|
||||
pub map: Option<&'a Path>,
|
||||
pub bound: Option<&'a [WherePredicate]>,
|
||||
pub allow_unknown_fields: bool,
|
||||
}
|
||||
|
||||
impl<'a> TraitImpl<'a> {
|
||||
|
@ -44,17 +45,18 @@ impl<'a> TraitImpl<'a> {
|
|||
let declared = self.declared_type_params();
|
||||
match self.data {
|
||||
Data::Struct(ref v) => self.type_params_in_fields(v, &field_filter, &declared),
|
||||
Data::Enum(ref v) => v.iter().filter(variant_filter).fold(
|
||||
Default::default(),
|
||||
|mut state, variant| {
|
||||
state.extend(self.type_params_in_fields(
|
||||
&variant.data,
|
||||
&field_filter,
|
||||
&declared,
|
||||
));
|
||||
state
|
||||
},
|
||||
),
|
||||
Data::Enum(ref v) => {
|
||||
v.iter()
|
||||
.filter(variant_filter)
|
||||
.fold(Default::default(), |mut state, variant| {
|
||||
state.extend(self.type_params_in_fields(
|
||||
&variant.data,
|
||||
&field_filter,
|
||||
&declared,
|
||||
));
|
||||
state
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,12 +80,12 @@ impl<'a> TraitImpl<'a> {
|
|||
impl<'a> TraitImpl<'a> {
|
||||
/// Gets the `let` declaration for errors accumulated during parsing.
|
||||
pub fn declare_errors(&self) -> ErrorDeclaration {
|
||||
ErrorDeclaration::new()
|
||||
ErrorDeclaration::default()
|
||||
}
|
||||
|
||||
/// Gets the check which performs an early return if errors occurred during parsing.
|
||||
pub fn check_errors(&self) -> ErrorCheck {
|
||||
ErrorCheck::new()
|
||||
ErrorCheck::default()
|
||||
}
|
||||
|
||||
/// Generate local variable declarations for all fields.
|
||||
|
@ -129,21 +131,18 @@ impl<'a> TraitImpl<'a> {
|
|||
}
|
||||
|
||||
pub(in codegen) fn initializers(&self) -> TokenStream {
|
||||
let foo = match self.data {
|
||||
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
|
||||
Data::Struct(ref data) => FieldsGen(data),
|
||||
};
|
||||
|
||||
foo.initializers()
|
||||
self.make_field_ctx().initializers()
|
||||
}
|
||||
|
||||
/// Generate the loop which walks meta items looking for property matches.
|
||||
pub(in codegen) fn core_loop(&self) -> TokenStream {
|
||||
let foo = match self.data {
|
||||
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
|
||||
Data::Struct(ref data) => FieldsGen(data),
|
||||
};
|
||||
self.make_field_ctx().core_loop()
|
||||
}
|
||||
|
||||
foo.core_loop()
|
||||
fn make_field_ctx(&'a self) -> FieldsGen<'a> {
|
||||
match self.data {
|
||||
Data::Enum(_) => panic!("Core loop on enums isn't supported"),
|
||||
Data::Struct(ref data) => FieldsGen::new(data, self.allow_unknown_fields),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::Ident;
|
||||
|
||||
use ast::Fields;
|
||||
|
@ -7,11 +9,11 @@ use codegen::error::{ErrorCheck, ErrorDeclaration};
|
|||
use codegen::{Field, FieldsGen};
|
||||
use usage::{self, IdentRefSet, IdentSet, UsesTypeParams};
|
||||
|
||||
/// An enum variant.
|
||||
/// A variant of the enum which is deriving `FromMeta`.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Variant<'a> {
|
||||
/// The name which will appear in code passed to the `FromMeta` input.
|
||||
pub name_in_attr: String,
|
||||
pub name_in_attr: Cow<'a, String>,
|
||||
|
||||
/// The name of the variant which will be returned for a given `name_in_attr`.
|
||||
pub variant_ident: &'a Ident,
|
||||
|
@ -23,9 +25,15 @@ pub struct Variant<'a> {
|
|||
|
||||
/// Whether or not the variant should be skipped in the generated code.
|
||||
pub skip: bool,
|
||||
|
||||
pub allow_unknown_fields: bool,
|
||||
}
|
||||
|
||||
impl<'a> Variant<'a> {
|
||||
pub fn as_name(&'a self) -> &'a str {
|
||||
&self.name_in_attr
|
||||
}
|
||||
|
||||
pub fn as_unit_match_arm(&'a self) -> UnitMatchArm<'a> {
|
||||
UnitMatchArm(self)
|
||||
}
|
||||
|
@ -45,6 +53,9 @@ impl<'a> UsesTypeParams for Variant<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Code generator for an enum variant in a unit match position.
|
||||
/// This is placed in generated `from_string` calls for the parent enum.
|
||||
/// Value-carrying variants wrapped in this type will emit code to produce an "unsupported format" error.
|
||||
pub struct UnitMatchArm<'a>(&'a Variant<'a>);
|
||||
|
||||
impl<'a> ToTokens for UnitMatchArm<'a> {
|
||||
|
@ -72,6 +83,9 @@ impl<'a> ToTokens for UnitMatchArm<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Code generator for an enum variant in a data-carrying match position.
|
||||
/// This is placed in generated `from_list` calls for the parent enum.
|
||||
/// Unit variants wrapped in this type will emit code to produce an "unsupported format" error.
|
||||
pub struct DataMatchArm<'a>(&'a Variant<'a>);
|
||||
|
||||
impl<'a> ToTokens for DataMatchArm<'a> {
|
||||
|
@ -94,10 +108,10 @@ impl<'a> ToTokens for DataMatchArm<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
let vdg = FieldsGen(&val.data);
|
||||
let vdg = FieldsGen::new(&val.data, val.allow_unknown_fields);
|
||||
|
||||
if val.data.is_struct() {
|
||||
let declare_errors = ErrorDeclaration::new();
|
||||
let declare_errors = ErrorDeclaration::default();
|
||||
let check_errors = ErrorCheck::with_location(&name_in_attr);
|
||||
let require_fields = vdg.require_fields();
|
||||
let decls = vdg.declarations();
|
||||
|
|
|
@ -2,19 +2,29 @@ use proc_macro2::TokenStream;
|
|||
|
||||
use ast::Fields;
|
||||
use ast::Style;
|
||||
use codegen::field;
|
||||
use codegen::Field;
|
||||
|
||||
pub struct FieldsGen<'a>(pub &'a Fields<Field<'a>>);
|
||||
pub struct FieldsGen<'a> {
|
||||
fields: &'a Fields<Field<'a>>,
|
||||
allow_unknown_fields: bool,
|
||||
}
|
||||
|
||||
impl<'a> FieldsGen<'a> {
|
||||
pub fn new(fields: &'a Fields<Field<'a>>, allow_unknown_fields: bool) -> Self {
|
||||
Self {
|
||||
fields,
|
||||
allow_unknown_fields,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create declarations for all the fields in the struct.
|
||||
pub(in codegen) fn declarations(&self) -> TokenStream {
|
||||
match *self.0 {
|
||||
match *self.fields {
|
||||
Fields {
|
||||
style: Style::Struct,
|
||||
ref fields,
|
||||
} => {
|
||||
let vdr = fields.into_iter().map(Field::as_declaration);
|
||||
let vdr = fields.iter().map(Field::as_declaration);
|
||||
quote!(#(#vdr)*)
|
||||
}
|
||||
_ => panic!("FieldsGen doesn't support tuples yet"),
|
||||
|
@ -23,7 +33,26 @@ impl<'a> FieldsGen<'a> {
|
|||
|
||||
/// Generate the loop which walks meta items looking for property matches.
|
||||
pub(in codegen) fn core_loop(&self) -> TokenStream {
|
||||
let arms: Vec<field::MatchArm> = self.0.as_ref().map(Field::as_match).fields;
|
||||
let arms = self.fields.as_ref().map(Field::as_match);
|
||||
|
||||
// If we're allowing unknown fields, then handling one is a no-op.
|
||||
// Otherwise, we're going to push a new spanned error pointing at the field.
|
||||
let handle_unknown = if self.allow_unknown_fields {
|
||||
quote!()
|
||||
} else {
|
||||
// We can't call `unknown_field_with_alts` with an empty slice, or else it fails to
|
||||
// infer the type of the slice item.
|
||||
let err_fn = if arms.is_empty() {
|
||||
quote!(unknown_field(__other))
|
||||
} else {
|
||||
let names = self.fields.as_ref().map(Field::as_name);
|
||||
quote!(unknown_field_with_alts(__other, &[#(#names),*]))
|
||||
};
|
||||
|
||||
quote! {
|
||||
__errors.push(::darling::Error::#err_fn.with_span(__inner));
|
||||
}
|
||||
};
|
||||
|
||||
quote!(
|
||||
for __item in __items {
|
||||
|
@ -31,7 +60,7 @@ impl<'a> FieldsGen<'a> {
|
|||
let __name = __inner.name().to_string();
|
||||
match __name.as_str() {
|
||||
#(#arms)*
|
||||
__other => { __errors.push(::darling::Error::unknown_field(__other)); }
|
||||
__other => { #handle_unknown }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,12 +68,12 @@ impl<'a> FieldsGen<'a> {
|
|||
}
|
||||
|
||||
pub fn require_fields(&self) -> TokenStream {
|
||||
match *self.0 {
|
||||
match *self.fields {
|
||||
Fields {
|
||||
style: Style::Struct,
|
||||
ref fields,
|
||||
} => {
|
||||
let checks = fields.into_iter().map(Field::as_presence_check);
|
||||
let checks = fields.iter().map(Field::as_presence_check);
|
||||
quote!(#(#checks)*)
|
||||
}
|
||||
_ => panic!("FieldsGen doesn't support tuples for requirement checks"),
|
||||
|
@ -52,7 +81,7 @@ impl<'a> FieldsGen<'a> {
|
|||
}
|
||||
|
||||
pub(in codegen) fn initializers(&self) -> TokenStream {
|
||||
let inits: Vec<_> = self.0.as_ref().map(Field::as_initializer).fields;
|
||||
let inits = self.fields.as_ref().map(Field::as_initializer);
|
||||
|
||||
quote!(#(#inits),*)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
//! Functions to derive `darling`'s traits from well-formed input, without directly depending
|
||||
//! on `proc_macro`.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::DeriveInput;
|
||||
|
||||
use options;
|
||||
|
||||
/// Run an expression which returns a `darling::Result`, then either return the tokenized
|
||||
/// representation of the `Ok` value, or the tokens of the compiler errors in the `Err` case.
|
||||
macro_rules! emit_impl_or_error {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
Ok(val) => val.into_token_stream(),
|
||||
Err(err) => err.write_errors(),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Create tokens for a `darling::FromMeta` impl from a `DeriveInput`. If
|
||||
/// the input cannot produce a valid impl, the returned tokens will contain
|
||||
/// compile errors instead.
|
||||
pub fn from_meta(input: &DeriveInput) -> TokenStream {
|
||||
emit_impl_or_error!(options::FromMetaOptions::new(input))
|
||||
}
|
||||
|
||||
/// Create tokens for a `darling::FromDeriveInput` impl from a `DeriveInput`. If
|
||||
/// the input cannot produce a valid impl, the returned tokens will contain
|
||||
/// compile errors instead.
|
||||
pub fn from_derive_input(input: &DeriveInput) -> TokenStream {
|
||||
emit_impl_or_error!(options::FdiOptions::new(&input))
|
||||
}
|
||||
|
||||
/// Create tokens for a `darling::FromField` impl from a `DeriveInput`. If
|
||||
/// the input cannot produce a valid impl, the returned tokens will contain
|
||||
/// compile errors instead.
|
||||
pub fn from_field(input: &DeriveInput) -> TokenStream {
|
||||
emit_impl_or_error!(options::FromFieldOptions::new(input))
|
||||
}
|
||||
|
||||
/// Create tokens for a `darling::FromTypeParam` impl from a `DeriveInput`. If
|
||||
/// the input cannot produce a valid impl, the returned tokens will contain
|
||||
/// compile errors instead.
|
||||
pub fn from_type_param(input: &DeriveInput) -> TokenStream {
|
||||
emit_impl_or_error!(options::FromTypeParamOptions::new(input))
|
||||
}
|
||||
|
||||
/// Create tokens for a `darling::FromVariant` impl from a `DeriveInput`. If
|
||||
/// the input cannot produce a valid impl, the returned tokens will contain
|
||||
/// compile errors instead.
|
||||
pub fn from_variant(input: &DeriveInput) -> TokenStream {
|
||||
emit_impl_or_error!(options::FromVariantOptions::new(input))
|
||||
}
|
|
@ -1,373 +0,0 @@
|
|||
//! Types for working with darling errors and results.
|
||||
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::iter::{self, Iterator};
|
||||
use std::string::ToString;
|
||||
use std::vec;
|
||||
|
||||
/// An alias of `Result` specific to attribute parsing.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// An error encountered during attribute parsing.
|
||||
///
|
||||
/// Given that most errors darling encounters represent code bugs in dependent crates,
|
||||
/// the internal structure of the error is deliberately opaque.
|
||||
#[derive(Debug)]
|
||||
// Don't want to publicly commit to Error supporting equality yet, but
|
||||
// not having it makes testing very difficult.
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
locations: Vec<String>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn new(kind: ErrorKind) -> Self {
|
||||
Error {
|
||||
kind: kind,
|
||||
locations: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new error with a custom message.
|
||||
pub fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error::new(ErrorKind::Custom(msg.to_string()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field that appears twice in the input.
|
||||
pub fn duplicate_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::DuplicateField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a non-optional field that does not appear in the input.
|
||||
pub fn missing_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::MissingField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field name that appears in the input but does not correspond
|
||||
/// to a known field.
|
||||
pub fn unknown_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::UnknownField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a struct or variant that does not adhere to the supported shape.
|
||||
pub fn unsupported_shape(shape: &str) -> Self {
|
||||
Error::new(ErrorKind::UnsupportedShape(shape.into()))
|
||||
}
|
||||
|
||||
pub fn unsupported_format(format: &str) -> Self {
|
||||
Error::new(ErrorKind::UnexpectedFormat(format.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field which has an unexpected literal type.
|
||||
pub fn unexpected_type(ty: &str) -> Self {
|
||||
Error::new(ErrorKind::UnexpectedType(ty.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a value which doesn't match a set of expected literals.
|
||||
pub fn unknown_value(value: &str) -> Self {
|
||||
Error::new(ErrorKind::UnknownValue(value.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a list which did not get enough items to proceed.
|
||||
pub fn too_few_items(min: usize) -> Self {
|
||||
Error::new(ErrorKind::TooFewItems(min))
|
||||
}
|
||||
|
||||
/// Creates a new error when a list got more items than it supports. The `max` argument
|
||||
/// is the largest number of items the receiver could accept.
|
||||
pub fn too_many_items(max: usize) -> Self {
|
||||
Error::new(ErrorKind::TooManyItems(max))
|
||||
}
|
||||
|
||||
/// Bundle a set of multiple errors into a single `Error` instance.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function will panic if `errors.is_empty() == true`.
|
||||
pub fn multiple(mut errors: Vec<Error>) -> Self {
|
||||
if errors.len() > 1 {
|
||||
Error::new(ErrorKind::Multiple(errors))
|
||||
} else if errors.len() == 1 {
|
||||
errors
|
||||
.pop()
|
||||
.expect("Error array of length 1 has a first item")
|
||||
} else {
|
||||
panic!("Can't deal with 0 errors")
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively converts a tree of errors to a flattened list.
|
||||
pub fn flatten(self) -> Self {
|
||||
Error::multiple(self.to_vec())
|
||||
}
|
||||
|
||||
fn to_vec(self) -> Vec<Self> {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
let mut flat = Vec::new();
|
||||
for error in errors {
|
||||
flat.extend(error.prepend_at(self.locations.clone()).to_vec());
|
||||
}
|
||||
|
||||
flat
|
||||
} else {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a location to the error, such as a field or variant.
|
||||
/// Locations must be added in reverse order of specificity.
|
||||
pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
|
||||
self.locations.insert(0, location.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the number of individual errors in this error.
|
||||
///
|
||||
/// This function should never return `0`, as it shouldn't be possible to construct
|
||||
/// a multi-error from an empty `Vec`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.kind.len()
|
||||
}
|
||||
|
||||
/// Adds a location chain to the head of the error's existing locations.
|
||||
fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
|
||||
if !locations.is_empty() {
|
||||
locations.extend(self.locations);
|
||||
self.locations = locations;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the location slice.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn location(&self) -> Vec<&str> {
|
||||
self.locations.iter().map(|i| i.as_str()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.kind.description()
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.kind)?;
|
||||
if !self.locations.is_empty() {
|
||||
write!(f, " at {}", self.locations.join("/"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for Error {
|
||||
type Item = Error;
|
||||
type IntoIter = IntoIter;
|
||||
|
||||
fn into_iter(self) -> IntoIter {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Multiple(errors.into_iter()),
|
||||
}
|
||||
} else {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Single(iter::once(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum IntoIterEnum {
|
||||
Single(iter::Once<Error>),
|
||||
Multiple(vec::IntoIter<Error>),
|
||||
}
|
||||
|
||||
impl Iterator for IntoIterEnum {
|
||||
type Item = Error;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match *self {
|
||||
IntoIterEnum::Single(ref mut content) => content.next(),
|
||||
IntoIterEnum::Multiple(ref mut content) => content.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that moves out of an `Error`.
|
||||
pub struct IntoIter {
|
||||
inner: IntoIterEnum,
|
||||
}
|
||||
|
||||
impl Iterator for IntoIter {
|
||||
type Item = Error;
|
||||
|
||||
fn next(&mut self) -> Option<Error> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
type DeriveInputShape = String;
|
||||
type FieldName = String;
|
||||
type MetaFormat = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
// Don't want to publicly commit to ErrorKind supporting equality yet, but
|
||||
// not having it makes testing very difficult.
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
enum ErrorKind {
|
||||
/// An arbitrary error message.
|
||||
Custom(String),
|
||||
DuplicateField(FieldName),
|
||||
MissingField(FieldName),
|
||||
UnsupportedShape(DeriveInputShape),
|
||||
UnknownField(FieldName),
|
||||
UnexpectedFormat(MetaFormat),
|
||||
UnexpectedType(String),
|
||||
UnknownValue(String),
|
||||
TooFewItems(usize),
|
||||
TooManyItems(usize),
|
||||
/// A set of errors.
|
||||
Multiple(Vec<Error>),
|
||||
|
||||
// TODO make this variant take `!` so it can't exist
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn description(&self) -> &str {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match *self {
|
||||
Custom(ref s) => s,
|
||||
DuplicateField(_) => "Duplicate field",
|
||||
MissingField(_) => "Missing field",
|
||||
UnknownField(_) => "Unexpected field",
|
||||
UnsupportedShape(_) => "Unsupported shape",
|
||||
UnexpectedFormat(_) => "Unexpected meta-item format",
|
||||
UnexpectedType(_) => "Unexpected literal type",
|
||||
UnknownValue(_) => "Unknown literal value",
|
||||
TooFewItems(_) => "Too few items",
|
||||
TooManyItems(_) => "Too many items",
|
||||
Multiple(_) => "Multiple errors",
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deeply counts the number of errors this item represents.
|
||||
pub fn len(&self) -> usize {
|
||||
if let ErrorKind::Multiple(ref items) = *self {
|
||||
items.iter().map(Error::len).sum()
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match *self {
|
||||
Custom(ref s) => s.fmt(f),
|
||||
DuplicateField(ref field) => write!(f, "Duplicate field `{}`", field),
|
||||
MissingField(ref field) => write!(f, "Missing field `{}`", field),
|
||||
UnknownField(ref field) => write!(f, "Unexpected field `{}`", field),
|
||||
UnsupportedShape(ref shape) => write!(f, "Unsupported shape `{}`", shape),
|
||||
UnexpectedFormat(ref format) => write!(f, "Unexpected meta-item format `{}`", format),
|
||||
UnexpectedType(ref ty) => write!(f, "Unexpected literal type `{}`", ty),
|
||||
UnknownValue(ref val) => write!(f, "Unknown literal value `{}`", val),
|
||||
TooFewItems(ref min) => write!(f, "Too few items: Expected at least {}", min),
|
||||
TooManyItems(ref max) => write!(f, "Too many items: Expected no more than {}", max),
|
||||
Multiple(ref items) if items.len() == 1 => items[0].fmt(f),
|
||||
Multiple(ref items) => {
|
||||
write!(f, "Multiple errors: (")?;
|
||||
let mut first = true;
|
||||
for item in items {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
item.fmt(f)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn flatten_noop() {
|
||||
let err = Error::duplicate_field("hello").at("world");
|
||||
assert_eq!(err.clone().flatten(), err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_simple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::unknown_field("hello").at("world"),
|
||||
Error::missing_field("hell_no").at("world"),
|
||||
]).at("foo")
|
||||
.flatten();
|
||||
|
||||
assert!(err.location().is_empty());
|
||||
|
||||
let mut err_iter = err.into_iter();
|
||||
|
||||
let first = err_iter.next();
|
||||
assert!(first.is_some());
|
||||
assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
|
||||
|
||||
let second = err_iter.next();
|
||||
assert!(second.is_some());
|
||||
|
||||
assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
|
||||
|
||||
assert!(err_iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_single() {
|
||||
let err = Error::duplicate_field("hello");
|
||||
assert_eq!(1, err.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_multiple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::duplicate_field("hello"),
|
||||
Error::missing_field("hell_no"),
|
||||
]);
|
||||
assert_eq!(2, err.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_nested() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::duplicate_field("hello"),
|
||||
Error::multiple(vec![
|
||||
Error::duplicate_field("hi"),
|
||||
Error::missing_field("bye"),
|
||||
Error::multiple(vec![Error::duplicate_field("whatsup")]),
|
||||
]),
|
||||
]);
|
||||
|
||||
assert_eq!(4, err.len());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
use std::fmt;
|
||||
|
||||
use error::Error;
|
||||
|
||||
type DeriveInputShape = String;
|
||||
type FieldName = String;
|
||||
type MetaFormat = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
// Don't want to publicly commit to ErrorKind supporting equality yet, but
|
||||
// not having it makes testing very difficult.
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
pub(in error) enum ErrorKind {
|
||||
/// An arbitrary error message.
|
||||
Custom(String),
|
||||
DuplicateField(FieldName),
|
||||
MissingField(FieldName),
|
||||
UnsupportedShape(DeriveInputShape),
|
||||
UnknownField(ErrorUnknownField),
|
||||
UnexpectedFormat(MetaFormat),
|
||||
UnexpectedType(String),
|
||||
UnknownValue(String),
|
||||
TooFewItems(usize),
|
||||
TooManyItems(usize),
|
||||
/// A set of errors.
|
||||
Multiple(Vec<Error>),
|
||||
|
||||
// TODO make this variant take `!` so it can't exist
|
||||
#[doc(hidden)]
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl ErrorKind {
|
||||
pub fn description(&self) -> &str {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match *self {
|
||||
Custom(ref s) => s,
|
||||
DuplicateField(_) => "Duplicate field",
|
||||
MissingField(_) => "Missing field",
|
||||
UnknownField(_) => "Unexpected field",
|
||||
UnsupportedShape(_) => "Unsupported shape",
|
||||
UnexpectedFormat(_) => "Unexpected meta-item format",
|
||||
UnexpectedType(_) => "Unexpected literal type",
|
||||
UnknownValue(_) => "Unknown literal value",
|
||||
TooFewItems(_) => "Too few items",
|
||||
TooManyItems(_) => "Too many items",
|
||||
Multiple(_) => "Multiple errors",
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Deeply counts the number of errors this item represents.
|
||||
pub fn len(&self) -> usize {
|
||||
if let ErrorKind::Multiple(ref items) = *self {
|
||||
items.iter().map(Error::len).sum()
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match *self {
|
||||
Custom(ref s) => s.fmt(f),
|
||||
DuplicateField(ref field) => write!(f, "Duplicate field `{}`", field),
|
||||
MissingField(ref field) => write!(f, "Missing field `{}`", field),
|
||||
UnknownField(ref field) => field.fmt(f),
|
||||
UnsupportedShape(ref shape) => write!(f, "Unsupported shape `{}`", shape),
|
||||
UnexpectedFormat(ref format) => write!(f, "Unexpected meta-item format `{}`", format),
|
||||
UnexpectedType(ref ty) => write!(f, "Unexpected literal type `{}`", ty),
|
||||
UnknownValue(ref val) => write!(f, "Unknown literal value `{}`", val),
|
||||
TooFewItems(ref min) => write!(f, "Too few items: Expected at least {}", min),
|
||||
TooManyItems(ref max) => write!(f, "Too many items: Expected no more than {}", max),
|
||||
Multiple(ref items) if items.len() == 1 => items[0].fmt(f),
|
||||
Multiple(ref items) => {
|
||||
write!(f, "Multiple errors: (")?;
|
||||
let mut first = true;
|
||||
for item in items {
|
||||
if !first {
|
||||
write!(f, ", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
|
||||
item.fmt(f)?;
|
||||
}
|
||||
|
||||
write!(f, ")")
|
||||
}
|
||||
__NonExhaustive => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ErrorUnknownField> for ErrorKind {
|
||||
fn from(err: ErrorUnknownField) -> Self {
|
||||
ErrorKind::UnknownField(err)
|
||||
}
|
||||
}
|
||||
|
||||
/// An error for an unknown field, with a possible "did-you-mean" suggestion to get
|
||||
/// the user back on the right track.
|
||||
#[derive(Debug)]
|
||||
// Don't want to publicly commit to ErrorKind supporting equality yet, but
|
||||
// not having it makes testing very difficult.
|
||||
#[cfg_attr(test, derive(Clone, PartialEq, Eq))]
|
||||
pub(in error) struct ErrorUnknownField {
|
||||
name: String,
|
||||
did_you_mean: Option<String>,
|
||||
}
|
||||
|
||||
impl ErrorUnknownField {
|
||||
pub fn new<I: Into<String>>(name: I, did_you_mean: Option<String>) -> Self {
|
||||
ErrorUnknownField {
|
||||
name: name.into(),
|
||||
did_you_mean,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_alts<'a, T, I>(field: &str, alternates: I) -> Self
|
||||
where
|
||||
T: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
ErrorUnknownField::new(field, did_you_mean(field, alternates))
|
||||
}
|
||||
|
||||
#[cfg(feature = "diagnostics")]
|
||||
pub fn to_diagnostic(self, span: Option<::proc_macro2::Span>) -> ::proc_macro::Diagnostic {
|
||||
let base = span
|
||||
.unwrap_or_else(::proc_macro2::Span::call_site)
|
||||
.unwrap()
|
||||
.error(self.top_line());
|
||||
match self.did_you_mean {
|
||||
Some(alt_name) => base.help(format!("did you mean `{}`?", alt_name)),
|
||||
None => base,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diagnostics")]
|
||||
fn top_line(&self) -> String {
|
||||
format!("Unknown field: `{}`", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ErrorUnknownField {
|
||||
fn from(name: String) -> Self {
|
||||
ErrorUnknownField::new(name, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ErrorUnknownField {
|
||||
fn from(name: &'a str) -> Self {
|
||||
ErrorUnknownField::new(name, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ErrorUnknownField {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Unknown field: `{}`", self.name)?;
|
||||
|
||||
if let Some(ref did_you_mean) = self.did_you_mean {
|
||||
write!(f, ". Did you mean `{}`?", did_you_mean)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "suggestions")]
|
||||
fn did_you_mean<'a, T, I>(field: &str, alternates: I) -> Option<String>
|
||||
where
|
||||
T: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
let mut candidate: Option<(f64, &str)> = None;
|
||||
for pv in alternates {
|
||||
let confidence = ::strsim::jaro_winkler(field, pv.as_ref());
|
||||
if confidence > 0.8 && (candidate.is_none() || (candidate.as_ref().unwrap().0 < confidence))
|
||||
{
|
||||
candidate = Some((confidence, pv.as_ref()));
|
||||
}
|
||||
}
|
||||
match candidate {
|
||||
None => None,
|
||||
Some((_, candidate)) => Some(candidate.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "suggestions"))]
|
||||
fn did_you_mean<'a, T, I>(_field: &str, _alternates: I) -> Option<String>
|
||||
where
|
||||
T: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
None
|
||||
}
|
|
@ -0,0 +1,505 @@
|
|||
//! The `darling::Error` type and its internals.
|
||||
//!
|
||||
//! Error handling is one of the core values of `darling`; creating great errors is hard and
|
||||
//! never the reason that a proc-macro author started writing their crate. As a result, the
|
||||
//! `Error` type in `darling` tries to make adding span information, suggestions, and other
|
||||
//! help content easy when manually implementing `darling` traits, and automatic when deriving
|
||||
//! them.
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt;
|
||||
use std::iter::{self, Iterator};
|
||||
use std::string::ToString;
|
||||
use std::vec;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{Lit, LitStr};
|
||||
|
||||
mod kind;
|
||||
|
||||
use self::kind::{ErrorKind, ErrorUnknownField};
|
||||
|
||||
/// An alias of `Result` specific to attribute parsing.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// An error encountered during attribute parsing.
|
||||
///
|
||||
/// Given that most errors darling encounters represent code bugs in dependent crates,
|
||||
/// the internal structure of the error is deliberately opaque.
|
||||
#[derive(Debug)]
|
||||
#[cfg_attr(test, derive(Clone))]
|
||||
pub struct Error {
|
||||
kind: ErrorKind,
|
||||
locations: Vec<String>,
|
||||
/// The span to highlight in the emitted diagnostic.
|
||||
span: Option<Span>,
|
||||
}
|
||||
|
||||
/// Error creation functions
|
||||
impl Error {
|
||||
pub(in error) fn new(kind: ErrorKind) -> Self {
|
||||
Error {
|
||||
kind,
|
||||
locations: Vec::new(),
|
||||
span: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new error with a custom message.
|
||||
pub fn custom<T: fmt::Display>(msg: T) -> Self {
|
||||
Error::new(ErrorKind::Custom(msg.to_string()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field that appears twice in the input.
|
||||
pub fn duplicate_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::DuplicateField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a non-optional field that does not appear in the input.
|
||||
pub fn missing_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::MissingField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field name that appears in the input but does not correspond
|
||||
/// to a known field.
|
||||
pub fn unknown_field(name: &str) -> Self {
|
||||
Error::new(ErrorKind::UnknownField(name.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field name that appears in the input but does not correspond to
|
||||
/// a known attribute. The second argument is the list of known attributes; if a similar name
|
||||
/// is found that will be shown in the emitted error message.
|
||||
pub fn unknown_field_with_alts<'a, T, I>(field: &str, alternates: I) -> Self
|
||||
where
|
||||
T: AsRef<str> + 'a,
|
||||
I: IntoIterator<Item = &'a T>,
|
||||
{
|
||||
Error::new(ErrorUnknownField::with_alts(field, alternates).into())
|
||||
}
|
||||
|
||||
/// Creates a new error for a struct or variant that does not adhere to the supported shape.
|
||||
pub fn unsupported_shape(shape: &str) -> Self {
|
||||
Error::new(ErrorKind::UnsupportedShape(shape.into()))
|
||||
}
|
||||
|
||||
pub fn unsupported_format(format: &str) -> Self {
|
||||
Error::new(ErrorKind::UnexpectedFormat(format.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field which has an unexpected literal type.
|
||||
pub fn unexpected_type(ty: &str) -> Self {
|
||||
Error::new(ErrorKind::UnexpectedType(ty.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a field which has an unexpected literal type. This will automatically
|
||||
/// extract the literal type name from the passed-in `Lit` and set the span to encompass only the
|
||||
/// literal value.
|
||||
///
|
||||
/// # Usage
|
||||
/// This is most frequently used in overrides of the `FromMeta::from_value` method.
|
||||
///
|
||||
/// ```rust
|
||||
/// # // pretend darling_core is darling so the doc example looks correct.
|
||||
/// # extern crate darling_core as darling;
|
||||
/// # extern crate syn;
|
||||
///
|
||||
/// use darling::{FromMeta, Error, Result};
|
||||
/// use syn::{Lit, LitStr};
|
||||
///
|
||||
/// pub struct Foo(String);
|
||||
///
|
||||
/// impl FromMeta for Foo {
|
||||
/// fn from_value(value: &Lit) -> Result<Self> {
|
||||
/// if let Lit::Str(ref lit_str) = value {
|
||||
/// Ok(Foo(lit_str.value()))
|
||||
/// } else {
|
||||
/// Err(Error::unexpected_lit_type(value))
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub fn unexpected_lit_type(lit: &Lit) -> Self {
|
||||
Error::unexpected_type(match *lit {
|
||||
Lit::Str(_) => "string",
|
||||
Lit::ByteStr(_) => "byte string",
|
||||
Lit::Byte(_) => "byte",
|
||||
Lit::Char(_) => "char",
|
||||
Lit::Int(_) => "int",
|
||||
Lit::Float(_) => "float",
|
||||
Lit::Bool(_) => "bool",
|
||||
Lit::Verbatim(_) => "verbatim",
|
||||
})
|
||||
.with_span(lit)
|
||||
}
|
||||
|
||||
/// Creates a new error for a value which doesn't match a set of expected literals.
|
||||
pub fn unknown_value(value: &str) -> Self {
|
||||
Error::new(ErrorKind::UnknownValue(value.into()))
|
||||
}
|
||||
|
||||
/// Creates a new error for a list which did not get enough items to proceed.
|
||||
pub fn too_few_items(min: usize) -> Self {
|
||||
Error::new(ErrorKind::TooFewItems(min))
|
||||
}
|
||||
|
||||
/// Creates a new error when a list got more items than it supports. The `max` argument
|
||||
/// is the largest number of items the receiver could accept.
|
||||
pub fn too_many_items(max: usize) -> Self {
|
||||
Error::new(ErrorKind::TooManyItems(max))
|
||||
}
|
||||
|
||||
/// Bundle a set of multiple errors into a single `Error` instance.
|
||||
///
|
||||
/// # Panics
|
||||
/// This function will panic if `errors.is_empty() == true`.
|
||||
pub fn multiple(mut errors: Vec<Error>) -> Self {
|
||||
if errors.len() > 1 {
|
||||
Error::new(ErrorKind::Multiple(errors))
|
||||
} else if errors.len() == 1 {
|
||||
errors
|
||||
.pop()
|
||||
.expect("Error array of length 1 has a first item")
|
||||
} else {
|
||||
panic!("Can't deal with 0 errors")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Create a new error about a literal string that doesn't match a set of known
|
||||
/// or permissible values. This function can be made public if the API proves useful
|
||||
/// beyond impls for `syn` types.
|
||||
pub(crate) fn unknown_lit_str_value(value: &LitStr) -> Self {
|
||||
Error::unknown_value(&value.value()).with_span(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Error instance methods
|
||||
impl Error {
|
||||
/// Check if this error is associated with a span in the token stream.
|
||||
pub fn has_span(&self) -> bool {
|
||||
self.span.is_some()
|
||||
}
|
||||
|
||||
/// Override the source code location of this error with a new one.
|
||||
#[deprecated(
|
||||
since = "0.8.3",
|
||||
note = "Callers should not broaden error spans. Use with_span instead."
|
||||
)]
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
self.span = Some(span)
|
||||
}
|
||||
|
||||
/// Tie a span to the error if none is already present. This is used in `darling::FromMeta`
|
||||
/// and other traits to attach errors to the most specific possible location in the input
|
||||
/// source code.
|
||||
///
|
||||
/// All `darling`-built impls, either from the crate or from the proc macro, will call this
|
||||
/// when appropriate during parsing, so it should not be necessary to call this unless you have
|
||||
/// overridden:
|
||||
///
|
||||
/// * `FromMeta::from_meta`
|
||||
/// * `FromMeta::from_nested_meta`
|
||||
/// * `FromMeta::from_value`
|
||||
pub fn with_span<T: Spanned>(mut self, node: &T) -> Self {
|
||||
if !self.has_span() {
|
||||
self.span = Some(node.span());
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Recursively converts a tree of errors to a flattened list.
|
||||
pub fn flatten(self) -> Self {
|
||||
Error::multiple(self.into_vec())
|
||||
}
|
||||
|
||||
fn into_vec(self) -> Vec<Self> {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
let mut flat = Vec::new();
|
||||
for error in errors {
|
||||
flat.extend(error.prepend_at(self.locations.clone()).into_vec());
|
||||
}
|
||||
|
||||
flat
|
||||
} else {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds a location to the error, such as a field or variant.
|
||||
/// Locations must be added in reverse order of specificity.
|
||||
pub fn at<T: fmt::Display>(mut self, location: T) -> Self {
|
||||
self.locations.insert(0, location.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the number of individual errors in this error.
|
||||
///
|
||||
/// This function never returns `0`, as it's impossible to construct
|
||||
/// a multi-error from an empty `Vec`.
|
||||
pub fn len(&self) -> usize {
|
||||
self.kind.len()
|
||||
}
|
||||
|
||||
/// Adds a location chain to the head of the error's existing locations.
|
||||
fn prepend_at(mut self, mut locations: Vec<String>) -> Self {
|
||||
if !locations.is_empty() {
|
||||
locations.extend(self.locations);
|
||||
self.locations = locations;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Gets the location slice.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn location(&self) -> Vec<&str> {
|
||||
self.locations.iter().map(|i| i.as_str()).collect()
|
||||
}
|
||||
|
||||
/// Write this error and any children as compile errors into a `TokenStream` to
|
||||
/// be returned by the proc-macro.
|
||||
///
|
||||
/// The behavior of this method will be slightly different if the `diagnostics` feature
|
||||
/// is enabled: In that case, the diagnostics will be emitted immediately by this call,
|
||||
/// and an empty `TokenStream` will be returned.
|
||||
///
|
||||
/// Return these tokens unmodified to avoid disturbing the attached span information.
|
||||
///
|
||||
/// # Usage
|
||||
/// ```rust,ignore
|
||||
/// // in your proc-macro function
|
||||
/// let opts = match MyOptions::from_derive_input(&ast) {
|
||||
/// Ok(val) => val,
|
||||
/// Err(err) => {
|
||||
/// return err.write_errors();
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn write_errors(self) -> TokenStream {
|
||||
#[cfg(feature = "diagnostics")]
|
||||
{
|
||||
self.emit();
|
||||
quote!()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "diagnostics"))]
|
||||
{
|
||||
self.flatten()
|
||||
.into_iter()
|
||||
.map(|e| e.single_to_syn_error().to_compile_error())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "diagnostics"))]
|
||||
fn single_to_syn_error(self) -> ::syn::Error {
|
||||
match self.span {
|
||||
Some(span) => ::syn::Error::new(span, self.kind),
|
||||
None => ::syn::Error::new(Span::call_site(), self),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "diagnostics")]
|
||||
fn single_to_diagnostic(self) -> ::proc_macro::Diagnostic {
|
||||
use proc_macro::{Diagnostic, Level};
|
||||
|
||||
// Delegate to dedicated error formatters when applicable.
|
||||
//
|
||||
// If span information is available, don't include the error property path
|
||||
// since it's redundant and not consistent with native compiler diagnostics.
|
||||
match self.kind {
|
||||
ErrorKind::UnknownField(euf) => euf.to_diagnostic(self.span),
|
||||
_ => match self.span {
|
||||
Some(span) => span.unwrap().error(self.kind.to_string()),
|
||||
None => Diagnostic::new(Level::Error, self.to_string()),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform this error and its children into a list of compiler diagnostics
|
||||
/// and emit them. If the `Error` has associated span information, the diagnostics
|
||||
/// will identify the correct location in source code automatically.
|
||||
///
|
||||
/// # Stability
|
||||
/// This is only available on `nightly` until the compiler `proc_macro_diagnostic`
|
||||
/// feature stabilizes. Until then, it may break at any time.
|
||||
#[cfg(feature = "diagnostics")]
|
||||
pub fn emit(self) {
|
||||
for error in self.flatten() {
|
||||
error.single_to_diagnostic().emit()
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform the error into a compiler diagnostic and - if the diagnostic points to
|
||||
/// a specific code location - add a spanned help child diagnostic that points to the
|
||||
/// parent derived trait.
|
||||
///
|
||||
/// This is experimental and therefore not exposed outside the crate.
|
||||
#[cfg(feature = "diagnostics")]
|
||||
#[allow(dead_code)]
|
||||
fn emit_with_macro_help_span(self) {
|
||||
use proc_macro::Diagnostic;
|
||||
|
||||
for error in self.flatten() {
|
||||
let needs_help = error.has_span();
|
||||
let diagnostic = error.single_to_diagnostic();
|
||||
Diagnostic::emit(if needs_help {
|
||||
diagnostic.span_help(
|
||||
Span::call_site().unwrap(),
|
||||
"Encountered as part of this derive-mode-macro",
|
||||
)
|
||||
} else {
|
||||
diagnostic
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
fn description(&self) -> &str {
|
||||
&self.kind.description()
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&StdError> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.kind)?;
|
||||
if !self.locations.is_empty() {
|
||||
write!(f, " at {}", self.locations.join("/"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Don't want to publicly commit to Error supporting equality yet, but
|
||||
// not having it makes testing very difficult. Note that spans are not
|
||||
// considered for equality since that would break testing in most cases.
|
||||
#[cfg(test)]
|
||||
impl PartialEq for Error {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.kind == other.kind && self.locations == other.locations
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Eq for Error {}
|
||||
|
||||
impl IntoIterator for Error {
|
||||
type Item = Error;
|
||||
type IntoIter = IntoIter;
|
||||
|
||||
fn into_iter(self) -> IntoIter {
|
||||
if let ErrorKind::Multiple(errors) = self.kind {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Multiple(errors.into_iter()),
|
||||
}
|
||||
} else {
|
||||
IntoIter {
|
||||
inner: IntoIterEnum::Single(iter::once(self)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum IntoIterEnum {
|
||||
Single(iter::Once<Error>),
|
||||
Multiple(vec::IntoIter<Error>),
|
||||
}
|
||||
|
||||
impl Iterator for IntoIterEnum {
|
||||
type Item = Error;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match *self {
|
||||
IntoIterEnum::Single(ref mut content) => content.next(),
|
||||
IntoIterEnum::Multiple(ref mut content) => content.next(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that moves out of an `Error`.
|
||||
pub struct IntoIter {
|
||||
inner: IntoIterEnum,
|
||||
}
|
||||
|
||||
impl Iterator for IntoIter {
|
||||
type Item = Error;
|
||||
|
||||
fn next(&mut self) -> Option<Error> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Error;
|
||||
|
||||
#[test]
|
||||
fn flatten_noop() {
|
||||
let err = Error::duplicate_field("hello").at("world");
|
||||
assert_eq!(err.clone().flatten(), err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_simple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::unknown_field("hello").at("world"),
|
||||
Error::missing_field("hell_no").at("world"),
|
||||
])
|
||||
.at("foo")
|
||||
.flatten();
|
||||
|
||||
assert!(err.location().is_empty());
|
||||
|
||||
let mut err_iter = err.into_iter();
|
||||
|
||||
let first = err_iter.next();
|
||||
assert!(first.is_some());
|
||||
assert_eq!(first.unwrap().location(), vec!["foo", "world"]);
|
||||
|
||||
let second = err_iter.next();
|
||||
assert!(second.is_some());
|
||||
|
||||
assert_eq!(second.unwrap().location(), vec!["foo", "world"]);
|
||||
|
||||
assert!(err_iter.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_single() {
|
||||
let err = Error::duplicate_field("hello");
|
||||
assert_eq!(1, err.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_multiple() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::duplicate_field("hello"),
|
||||
Error::missing_field("hell_no"),
|
||||
]);
|
||||
assert_eq!(2, err.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn len_nested() {
|
||||
let err = Error::multiple(vec![
|
||||
Error::duplicate_field("hello"),
|
||||
Error::multiple(vec![
|
||||
Error::duplicate_field("hi"),
|
||||
Error::missing_field("bye"),
|
||||
Error::multiple(vec![Error::duplicate_field("whatsup")]),
|
||||
]),
|
||||
]);
|
||||
|
||||
assert_eq!(4, err.len());
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
use std::hash::BuildHasher;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
@ -26,6 +27,10 @@ use {Error, Result};
|
|||
/// * As a string literal, e.g. `foo = "hello"`.
|
||||
/// * As a raw string literal, e.g. `foo = r#"hello "world""#`.
|
||||
///
|
||||
/// ## Number
|
||||
/// * As a string literal, e.g. `foo = "-25"`.
|
||||
/// * As an unquoted positive value, e.g. `foo = 404`. Negative numbers must be in quotation marks.
|
||||
///
|
||||
/// ## ()
|
||||
/// * Word with no value specified, e.g. `foo`. This is best used with `Option`.
|
||||
/// See `darling::util::Flag` for a more strongly-typed alternative.
|
||||
|
@ -38,26 +43,34 @@ use {Error, Result};
|
|||
/// parse attempt.
|
||||
pub trait FromMeta: Sized {
|
||||
fn from_nested_meta(item: &NestedMeta) -> Result<Self> {
|
||||
match *item {
|
||||
(match *item {
|
||||
NestedMeta::Literal(ref lit) => Self::from_value(lit),
|
||||
NestedMeta::Meta(ref mi) => Self::from_meta(mi),
|
||||
}
|
||||
})
|
||||
.map_err(|e| e.with_span(item))
|
||||
}
|
||||
|
||||
/// Create an instance from a `syn::Meta` by dispatching to the format-appropriate
|
||||
/// trait function. This generally should not be overridden by implementers.
|
||||
///
|
||||
/// # Error Spans
|
||||
/// If this method is overridden and can introduce errors that weren't passed up from
|
||||
/// other `from_meta` calls, the override must call `with_span` on the error using the
|
||||
/// `item` to make sure that the emitted diagnostic points to the correct location in
|
||||
/// source code.
|
||||
fn from_meta(item: &Meta) -> Result<Self> {
|
||||
match *item {
|
||||
(match *item {
|
||||
Meta::Word(_) => Self::from_word(),
|
||||
Meta::List(ref value) => Self::from_list(
|
||||
&value
|
||||
.nested
|
||||
.clone()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<Vec<syn::NestedMeta>>()[..],
|
||||
),
|
||||
Meta::NameValue(ref value) => Self::from_value(&value.lit),
|
||||
}
|
||||
})
|
||||
.map_err(|e| e.with_span(item))
|
||||
}
|
||||
|
||||
/// Create an instance from the presence of the word in the attribute with no
|
||||
|
@ -75,12 +88,17 @@ pub trait FromMeta: Sized {
|
|||
/// Create an instance from a literal value of either `foo = "bar"` or `foo("bar")`.
|
||||
/// This dispatches to the appropriate method based on the type of literal encountered,
|
||||
/// and generally should not be overridden by implementers.
|
||||
///
|
||||
/// # Error Spans
|
||||
/// If this method is overridden, the override must make sure to add `value`'s span
|
||||
/// information to the returned error by calling `with_span(value)` on the `Error` instance.
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
match *value {
|
||||
(match *value {
|
||||
Lit::Bool(ref b) => Self::from_bool(b.value),
|
||||
Lit::Str(ref s) => Self::from_string(&s.value()),
|
||||
ref _other => Err(Error::unexpected_type("other")),
|
||||
}
|
||||
_ => Err(Error::unexpected_lit_type(value)),
|
||||
})
|
||||
.map_err(|e| e.with_span(value))
|
||||
}
|
||||
|
||||
/// Create an instance from a char literal in a value position.
|
||||
|
@ -126,7 +144,9 @@ impl FromMeta for bool {
|
|||
|
||||
impl FromMeta for AtomicBool {
|
||||
fn from_meta(mi: &Meta) -> Result<Self> {
|
||||
FromMeta::from_meta(mi).map(AtomicBool::new)
|
||||
FromMeta::from_meta(mi)
|
||||
.map(AtomicBool::new)
|
||||
.map_err(|e| e.with_span(mi))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,84 +156,127 @@ impl FromMeta for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromMeta for u8 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
/// Generate an impl of `FromMeta` that will accept strings which parse to numbers or
|
||||
/// integer literals.
|
||||
macro_rules! from_meta_num {
|
||||
($ty:ident) => {
|
||||
impl FromMeta for $ty {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
(match *value {
|
||||
Lit::Str(ref s) => Self::from_string(&s.value()),
|
||||
Lit::Int(ref s) => Ok(s.value() as $ty),
|
||||
_ => Err(Error::unexpected_lit_type(value)),
|
||||
})
|
||||
.map_err(|e| e.with_span(value))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl FromMeta for u16 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
from_meta_num!(u8);
|
||||
from_meta_num!(u16);
|
||||
from_meta_num!(u32);
|
||||
from_meta_num!(u64);
|
||||
from_meta_num!(usize);
|
||||
from_meta_num!(i8);
|
||||
from_meta_num!(i16);
|
||||
from_meta_num!(i32);
|
||||
from_meta_num!(i64);
|
||||
from_meta_num!(isize);
|
||||
|
||||
/// Generate an impl of `FromMeta` that will accept strings which parse to floats or
|
||||
/// float literals.
|
||||
macro_rules! from_meta_float {
|
||||
($ty:ident) => {
|
||||
impl FromMeta for $ty {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
(match *value {
|
||||
Lit::Str(ref s) => Self::from_string(&s.value()),
|
||||
Lit::Float(ref s) => Ok(s.value() as $ty),
|
||||
_ => Err(Error::unexpected_lit_type(value)),
|
||||
})
|
||||
.map_err(|e| e.with_span(value))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl FromMeta for u32 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for u64 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for usize {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for i8 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for i16 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for i32 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for i64 {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromMeta for isize {
|
||||
fn from_string(s: &str) -> Result<Self> {
|
||||
s.parse().map_err(|_| Error::unknown_value(s))
|
||||
}
|
||||
}
|
||||
from_meta_float!(f32);
|
||||
from_meta_float!(f64);
|
||||
|
||||
/// Parsing support for identifiers. This attempts to preserve span information
|
||||
/// when available, but also supports parsing strings with the call site as the
|
||||
/// emitted span.
|
||||
impl FromMeta for syn::Ident {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
Ok(syn::Ident::new(value, ::proc_macro2::Span::call_site()))
|
||||
}
|
||||
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
if let Lit::Str(ref ident) = *value {
|
||||
ident
|
||||
.parse()
|
||||
.map_err(|_| Error::unknown_lit_str_value(ident))
|
||||
} else {
|
||||
Err(Error::unexpected_lit_type(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing support for paths. This attempts to preserve span information when available,
|
||||
/// but also supports parsing strings with the call site as the emitted span.
|
||||
impl FromMeta for syn::Path {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
syn::parse_str(value).map_err(|_| Error::unknown_value(value))
|
||||
}
|
||||
}
|
||||
/*
|
||||
impl FromMeta for syn::TypeParamBound {
|
||||
fn from_string(value: &str) -> Result<Self> {
|
||||
Ok(syn::TypeParamBound::from(value))
|
||||
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
if let Lit::Str(ref path_str) = *value {
|
||||
path_str
|
||||
.parse()
|
||||
.map_err(|_| Error::unknown_lit_str_value(path_str))
|
||||
} else {
|
||||
Err(Error::unexpected_lit_type(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
impl FromMeta for syn::Lit {
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
Ok(value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_meta_lit {
|
||||
($impl_ty:path, $lit_variant:path) => {
|
||||
impl FromMeta for $impl_ty {
|
||||
fn from_value(value: &Lit) -> Result<Self> {
|
||||
if let $lit_variant(ref value) = *value {
|
||||
Ok(value.clone())
|
||||
} else {
|
||||
Err(Error::unexpected_lit_type(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
from_meta_lit!(syn::LitInt, Lit::Int);
|
||||
from_meta_lit!(syn::LitFloat, Lit::Float);
|
||||
from_meta_lit!(syn::LitStr, Lit::Str);
|
||||
from_meta_lit!(syn::LitByte, Lit::Byte);
|
||||
from_meta_lit!(syn::LitByteStr, Lit::ByteStr);
|
||||
from_meta_lit!(syn::LitChar, Lit::Char);
|
||||
from_meta_lit!(syn::LitBool, Lit::Bool);
|
||||
from_meta_lit!(syn::LitVerbatim, Lit::Verbatim);
|
||||
|
||||
impl FromMeta for syn::Meta {
|
||||
fn from_meta(value: &syn::Meta) -> Result<Self> {
|
||||
|
@ -286,14 +349,20 @@ impl<T: FromMeta> FromMeta for RefCell<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<V: FromMeta> FromMeta for HashMap<String, V> {
|
||||
impl<V: FromMeta, S: BuildHasher + Default> FromMeta for HashMap<String, V, S> {
|
||||
fn from_list(nested: &[syn::NestedMeta]) -> Result<Self> {
|
||||
let mut map = HashMap::with_capacity(nested.len());
|
||||
let mut map = HashMap::with_capacity_and_hasher(nested.len(), Default::default());
|
||||
for item in nested {
|
||||
if let syn::NestedMeta::Meta(ref inner) = *item {
|
||||
match map.entry(inner.name().to_string()) {
|
||||
Entry::Occupied(_) => return Err(Error::duplicate_field(&inner.name().to_string())),
|
||||
Entry::Occupied(_) => {
|
||||
return Err(
|
||||
Error::duplicate_field(&inner.name().to_string()).with_span(inner)
|
||||
);
|
||||
}
|
||||
Entry::Vacant(entry) => {
|
||||
// In the error case, extend the error's path, but assume the inner `from_meta`
|
||||
// set the span, and that subsequently we don't have to.
|
||||
entry.insert(FromMeta::from_meta(inner).map_err(|e| e.at(inner.name()))?);
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +380,7 @@ mod tests {
|
|||
use proc_macro2::TokenStream;
|
||||
use syn;
|
||||
|
||||
use {FromMeta, Result};
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
|
@ -356,6 +425,24 @@ mod tests {
|
|||
fn number_succeeds() {
|
||||
assert_eq!(fm::<u8>(quote!(ignore = "2")), 2u8);
|
||||
assert_eq!(fm::<i16>(quote!(ignore = "-25")), -25i16);
|
||||
assert_eq!(fm::<f64>(quote!(ignore = "1.4e10")), 1.4e10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_without_quotes() {
|
||||
assert_eq!(fm::<u8>(quote!(ignore = 2)), 2u8);
|
||||
assert_eq!(fm::<u16>(quote!(ignore = 255)), 255u16);
|
||||
assert_eq!(fm::<u32>(quote!(ignore = 5000)), 5000u32);
|
||||
|
||||
// Check that we aren't tripped up by incorrect suffixes
|
||||
assert_eq!(fm::<u32>(quote!(ignore = 5000i32)), 5000u32);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_without_quotes() {
|
||||
assert_eq!(fm::<f32>(quote!(ignore = 2.)), 2.0f32);
|
||||
assert_eq!(fm::<f32>(quote!(ignore = 2.0)), 2.0f32);
|
||||
assert_eq!(fm::<f64>(quote!(ignore = 1.4e10)), 1.4e10f64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -386,6 +473,21 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// Check that a `HashMap` cannot have duplicate keys, and that the generated error
|
||||
/// is assigned a span to correctly target the diagnostic message.
|
||||
#[test]
|
||||
fn hash_map_duplicate() {
|
||||
use std::collections::HashMap;
|
||||
|
||||
let err: Result<HashMap<String, bool>> =
|
||||
FromMeta::from_meta(&pm(quote!(ignore(hello, hello = false))).unwrap());
|
||||
|
||||
let err = err.expect_err("Duplicate keys in HashMap should error");
|
||||
|
||||
assert!(err.has_span());
|
||||
assert_eq!(err.to_string(), Error::duplicate_field("hello").to_string());
|
||||
}
|
||||
|
||||
/// Tests that fallible parsing will always produce an outer `Ok` (from `fm`),
|
||||
/// and will accurately preserve the inner contents.
|
||||
#[test]
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#![recursion_limit = "256"]
|
||||
#![cfg_attr(feature = "diagnostics", feature(proc_macro_diagnostic))]
|
||||
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
extern crate proc_macro2;
|
||||
|
||||
extern crate fnv;
|
||||
extern crate ident_case;
|
||||
#[cfg(feature = "diagnostics")]
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
#[cfg(feature = "suggestions")]
|
||||
extern crate strsim;
|
||||
|
||||
#[macro_use]
|
||||
mod macros_private;
|
||||
|
@ -17,6 +20,7 @@ mod macros_public;
|
|||
|
||||
pub mod ast;
|
||||
pub mod codegen;
|
||||
pub mod derive;
|
||||
pub mod error;
|
||||
mod from_derive_input;
|
||||
mod from_field;
|
||||
|
@ -40,4 +44,4 @@ pub use from_variant::FromVariant;
|
|||
|
||||
// Re-export tokenizer
|
||||
#[doc(hidden)]
|
||||
pub use quote::ToTokens;
|
||||
pub use quote::ToTokens;
|
||||
|
|
|
@ -4,6 +4,7 @@ use syn;
|
|||
use ast::{Data, Fields, Style};
|
||||
use codegen;
|
||||
use options::{DefaultExpression, InputField, InputVariant, ParseAttribute, ParseData};
|
||||
use util::Flag;
|
||||
use {Error, FromMeta, Result};
|
||||
|
||||
/// A struct or enum which should have `FromMeta` or `FromDeriveInput` implementations
|
||||
|
@ -33,6 +34,9 @@ pub struct Core {
|
|||
|
||||
/// The custom bound to apply to the generated impl
|
||||
pub bound: Option<Vec<syn::WherePredicate>>,
|
||||
|
||||
/// Whether or not unknown fields should produce an error at compilation time.
|
||||
pub allow_unknown_fields: Flag,
|
||||
}
|
||||
|
||||
impl Core {
|
||||
|
@ -52,6 +56,7 @@ impl Core {
|
|||
},
|
||||
map: Default::default(),
|
||||
bound: Default::default(),
|
||||
allow_unknown_fields: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,7 +75,7 @@ impl ParseAttribute for Core {
|
|||
match mi.name().to_string().as_str() {
|
||||
"default" => {
|
||||
if self.default.is_some() {
|
||||
Err(Error::duplicate_field("default"))
|
||||
Err(Error::duplicate_field("default").with_span(mi))
|
||||
} else {
|
||||
self.default = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
|
@ -84,7 +89,7 @@ impl ParseAttribute for Core {
|
|||
}
|
||||
"map" => {
|
||||
if self.map.is_some() {
|
||||
Err(Error::duplicate_field("map"))
|
||||
Err(Error::duplicate_field("map").with_span(mi))
|
||||
} else {
|
||||
self.map = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
|
@ -94,7 +99,15 @@ impl ParseAttribute for Core {
|
|||
self.bound = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n.as_ref())),
|
||||
"allow_unknown_fields" => {
|
||||
if self.allow_unknown_fields.is_some() {
|
||||
Err(Error::duplicate_field("allow_unknown_fields").with_span(mi))
|
||||
} else {
|
||||
self.allow_unknown_fields = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
n => Err(Error::unknown_field(n).with_span(mi)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,13 +146,15 @@ impl<'a> From<&'a Core> for codegen::TraitImpl<'a> {
|
|||
codegen::TraitImpl {
|
||||
ident: &v.ident,
|
||||
generics: &v.generics,
|
||||
data: v.data
|
||||
data: v
|
||||
.data
|
||||
.as_ref()
|
||||
.map_struct_fields(InputField::as_codegen_field)
|
||||
.map_enum_variants(|variant| variant.as_codegen_variant(&v.ident)),
|
||||
default: v.as_codegen_default(),
|
||||
map: v.map.as_ref(),
|
||||
bound: v.bound.as_ref().map(|i| i.as_slice()),
|
||||
allow_unknown_fields: v.allow_unknown_fields.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen;
|
||||
use codegen::FromDeriveInputImpl;
|
||||
use options::{OuterFrom, ParseAttribute, ParseData, Shape};
|
||||
use {FromMeta, Result};
|
||||
|
||||
|
@ -27,8 +29,9 @@ impl FdiOptions {
|
|||
generics: Default::default(),
|
||||
data: Default::default(),
|
||||
supports: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
})
|
||||
.parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,9 +77,9 @@ impl ParseData for FdiOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a FdiOptions> for codegen::FromDeriveInputImpl<'a> {
|
||||
impl<'a> From<&'a FdiOptions> for FromDeriveInputImpl<'a> {
|
||||
fn from(v: &'a FdiOptions) -> Self {
|
||||
codegen::FromDeriveInputImpl {
|
||||
FromDeriveInputImpl {
|
||||
base: (&v.base.container).into(),
|
||||
attr_names: &v.base.attr_names,
|
||||
from_ident: v.base.from_ident,
|
||||
|
@ -90,3 +93,9 @@ impl<'a> From<&'a FdiOptions> for codegen::FromDeriveInputImpl<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FdiOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
FromDeriveInputImpl::from(self).to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen::FromFieldImpl;
|
||||
|
@ -17,8 +19,9 @@ impl FromFieldOptions {
|
|||
base: OuterFrom::start(di),
|
||||
vis: Default::default(),
|
||||
ty: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
})
|
||||
.parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,3 +71,9 @@ impl<'a> From<&'a FromFieldOptions> for FromFieldImpl<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FromFieldOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
FromFieldImpl::from(self).to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn;
|
||||
|
||||
use codegen;
|
||||
use codegen::FromMetaImpl;
|
||||
use options::{Core, ParseAttribute, ParseData};
|
||||
use Result;
|
||||
|
||||
|
@ -12,8 +14,9 @@ impl FromMetaOptions {
|
|||
pub fn new(di: &syn::DeriveInput) -> Result<Self> {
|
||||
(FromMetaOptions {
|
||||
base: Core::start(di),
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
})
|
||||
.parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,10 +36,16 @@ impl ParseData for FromMetaOptions {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a FromMetaOptions> for codegen::FromMetaImpl<'a> {
|
||||
impl<'a> From<&'a FromMetaOptions> for FromMetaImpl<'a> {
|
||||
fn from(v: &'a FromMetaOptions) -> Self {
|
||||
codegen::FromMetaImpl {
|
||||
FromMetaImpl {
|
||||
base: (&v.base).into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FromMetaOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
FromMetaImpl::from(self).to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{self, Ident};
|
||||
|
||||
use codegen::FromTypeParamImpl;
|
||||
|
@ -17,8 +19,9 @@ impl FromTypeParamOptions {
|
|||
base: OuterFrom::start(di),
|
||||
bounds: None,
|
||||
default: None,
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
})
|
||||
.parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,3 +71,9 @@ impl<'a> From<&'a FromTypeParamOptions> for FromTypeParamImpl<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FromTypeParamOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
FromTypeParamImpl::from(self).to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{DeriveInput, Field, Ident, Meta};
|
||||
|
||||
use codegen::FromVariantImpl;
|
||||
|
@ -17,8 +19,9 @@ impl FromVariantOptions {
|
|||
base: OuterFrom::start(di),
|
||||
fields: Default::default(),
|
||||
supports: Default::default(),
|
||||
}).parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
})
|
||||
.parse_attributes(&di.attrs)?
|
||||
.parse_body(&di.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,3 +69,9 @@ impl ParseData for FromVariantOptions {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for FromVariantOptions {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
FromVariantImpl::from(self).to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use syn;
|
||||
|
||||
use codegen;
|
||||
|
@ -24,14 +26,16 @@ impl InputField {
|
|||
pub fn as_codegen_field<'a>(&'a self) -> codegen::Field<'a> {
|
||||
codegen::Field {
|
||||
ident: &self.ident,
|
||||
name_in_attr: self.attr_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.ident.to_string()),
|
||||
name_in_attr: self
|
||||
.attr_name
|
||||
.as_ref()
|
||||
.map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
|
||||
ty: &self.ty,
|
||||
default_expression: self.as_codegen_default(),
|
||||
with_path: self.with
|
||||
.clone()
|
||||
.unwrap_or_else(|| parse_quote!(::darling::FromMeta::from_meta)),
|
||||
with_path: self.with.as_ref().map_or_else(
|
||||
|| Cow::Owned(parse_quote!(::darling::FromMeta::from_meta)),
|
||||
Cow::Borrowed,
|
||||
),
|
||||
skip: self.skip,
|
||||
map: self.map.as_ref(),
|
||||
multiple: self.multiple,
|
||||
|
@ -62,10 +66,10 @@ impl InputField {
|
|||
}
|
||||
|
||||
pub fn from_field(f: &syn::Field, parent: Option<&Core>) -> Result<Self> {
|
||||
let ident = f.ident.clone().unwrap_or_else(|| syn::Ident::new(
|
||||
"__unnamed",
|
||||
::proc_macro2::Span::call_site(),
|
||||
));
|
||||
let ident = f
|
||||
.ident
|
||||
.clone()
|
||||
.unwrap_or_else(|| syn::Ident::new("__unnamed", ::proc_macro2::Span::call_site()));
|
||||
let ty = f.ty.clone();
|
||||
let base = Self::new(ident, ty).parse_attributes(&f.attrs)?;
|
||||
|
||||
|
@ -137,7 +141,7 @@ impl ParseAttribute for InputField {
|
|||
self.multiple = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n)),
|
||||
n => Err(Error::unknown_field(n).with_span(mi)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use syn;
|
||||
|
||||
use ast::{Fields, Style};
|
||||
use ast::Fields;
|
||||
use codegen;
|
||||
use options::{Core, InputField, ParseAttribute};
|
||||
use {Error, FromMeta, Result};
|
||||
|
@ -11,6 +13,8 @@ pub struct InputVariant {
|
|||
attr_name: Option<String>,
|
||||
data: Fields<InputField>,
|
||||
skip: bool,
|
||||
/// Whether or not unknown fields are acceptable in this
|
||||
allow_unknown_fields: Option<bool>,
|
||||
}
|
||||
|
||||
impl InputVariant {
|
||||
|
@ -18,11 +22,13 @@ impl InputVariant {
|
|||
codegen::Variant {
|
||||
ty_ident,
|
||||
variant_ident: &self.ident,
|
||||
name_in_attr: self.attr_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| self.ident.to_string()),
|
||||
name_in_attr: self
|
||||
.attr_name
|
||||
.as_ref()
|
||||
.map_or_else(|| Cow::Owned(self.ident.to_string()), Cow::Borrowed),
|
||||
data: self.data.as_ref().map(InputField::as_codegen_field),
|
||||
skip: self.skip,
|
||||
allow_unknown_fields: self.allow_unknown_fields.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,20 +38,19 @@ impl InputVariant {
|
|||
attr_name: Default::default(),
|
||||
data: Fields::empty_from(&v.fields),
|
||||
skip: Default::default(),
|
||||
}).parse_attributes(&v.attrs)?;
|
||||
allow_unknown_fields: None,
|
||||
})
|
||||
.parse_attributes(&v.attrs)?;
|
||||
|
||||
starter.data = match v.fields {
|
||||
syn::Fields::Unit => Style::Unit.into(),
|
||||
starter.data.fields = match v.fields {
|
||||
syn::Fields::Unit => vec![],
|
||||
syn::Fields::Unnamed(ref fields) => {
|
||||
let mut items = Vec::with_capacity(fields.unnamed.len());
|
||||
for item in &fields.unnamed {
|
||||
items.push(InputField::from_field(item, parent)?);
|
||||
}
|
||||
|
||||
Fields {
|
||||
style: v.fields.clone().into(),
|
||||
fields: items,
|
||||
}
|
||||
items
|
||||
}
|
||||
syn::Fields::Named(ref fields) => {
|
||||
let mut items = Vec::with_capacity(fields.named.len());
|
||||
|
@ -53,10 +58,7 @@ impl InputVariant {
|
|||
items.push(InputField::from_field(item, parent)?);
|
||||
}
|
||||
|
||||
Fields {
|
||||
style: v.fields.clone().into(),
|
||||
fields: items,
|
||||
}
|
||||
items
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -72,6 +74,10 @@ impl InputVariant {
|
|||
self.attr_name = Some(parent.rename_rule.apply_to_variant(self.ident.to_string()));
|
||||
}
|
||||
|
||||
if self.allow_unknown_fields.is_none() {
|
||||
self.allow_unknown_fields = Some(parent.allow_unknown_fields.is_some());
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +94,7 @@ impl ParseAttribute for InputVariant {
|
|||
self.skip = FromMeta::from_meta(mi)?;
|
||||
Ok(())
|
||||
}
|
||||
n => Err(Error::unknown_field(n)),
|
||||
n => Err(Error::unknown_field(n).with_span(mi)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,23 +42,37 @@ impl FromMeta for DefaultExpression {
|
|||
Ok(DefaultExpression::Trait)
|
||||
}
|
||||
|
||||
fn from_string(lit: &str) -> Result<Self> {
|
||||
Ok(DefaultExpression::Explicit(
|
||||
syn::parse_str(lit).map_err(|_| Error::unknown_value(lit))?
|
||||
))
|
||||
fn from_value(value: &syn::Lit) -> Result<Self> {
|
||||
syn::Path::from_value(value).map(DefaultExpression::Explicit)
|
||||
}
|
||||
}
|
||||
|
||||
/// Middleware for extracting attribute values.
|
||||
/// Run a parsing task, and if it produces an error push it into `$errors`
|
||||
macro_rules! collect_error {
|
||||
($errors:ident, $task:expr) => {
|
||||
if let Err(e) = $task {
|
||||
$errors.push(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Middleware for extracting attribute values. Implementers are expected to override
|
||||
/// `parse_nested` so they can apply individual items to themselves, while `parse_attributes`
|
||||
/// is responsible for looping through distinct outer attributes and collecting errors.
|
||||
pub trait ParseAttribute: Sized {
|
||||
fn parse_attributes(mut self, attrs: &[syn::Attribute]) -> Result<Self> {
|
||||
let mut errors = Vec::new();
|
||||
for attr in attrs {
|
||||
if attr.path == parse_quote!(darling) {
|
||||
parse_attr(attr, &mut self)?;
|
||||
collect_error!(errors, parse_attr(attr, &mut self));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
if !errors.is_empty() {
|
||||
Err(Error::multiple(errors))
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a meta-item, and apply its values to the current instance.
|
||||
|
@ -66,66 +80,75 @@ pub trait ParseAttribute: Sized {
|
|||
}
|
||||
|
||||
fn parse_attr<T: ParseAttribute>(attr: &syn::Attribute, target: &mut T) -> Result<()> {
|
||||
match attr.interpret_meta() {
|
||||
let mut errors = Vec::new();
|
||||
match attr.parse_meta().ok() {
|
||||
Some(syn::Meta::List(data)) => {
|
||||
for item in data.nested {
|
||||
if let syn::NestedMeta::Meta(ref mi) = item {
|
||||
target.parse_nested(mi)?;
|
||||
collect_error!(errors, target.parse_nested(mi));
|
||||
} else {
|
||||
panic!("Wasn't able to parse: `{:?}`", item);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
if !errors.is_empty() {
|
||||
Err(Error::multiple(errors))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Some(ref item) => panic!("Wasn't able to parse: `{:?}`", item),
|
||||
None => panic!("Unable to parse {:?}", attr),
|
||||
}
|
||||
}
|
||||
|
||||
/// Middleware for extracting values from the body of the derive input. Implementers are
|
||||
/// expected to override `parse_field` or `parse_variant` as appropriate for their use-case,
|
||||
/// while `parse_body` dispatches to the appropriate methods and handles error collection.
|
||||
pub trait ParseData: Sized {
|
||||
fn parse_body(mut self, body: &syn::Data) -> Result<Self> {
|
||||
use syn::{Data, Fields};
|
||||
|
||||
let mut errors = Vec::new();
|
||||
|
||||
match *body {
|
||||
Data::Struct(ref data) => match data.fields {
|
||||
Fields::Unit => Ok(self),
|
||||
Fields::Unit => {}
|
||||
Fields::Named(ref fields) => {
|
||||
for field in &fields.named {
|
||||
self.parse_field(field)?;
|
||||
collect_error!(errors, self.parse_field(field));
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
Fields::Unnamed(ref fields) => {
|
||||
for field in &fields.unnamed {
|
||||
self.parse_field(field)?;
|
||||
collect_error!(errors, self.parse_field(field));
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
},
|
||||
Data::Enum(ref data) => {
|
||||
for variant in &data.variants {
|
||||
self.parse_variant(variant)?;
|
||||
collect_error!(errors, self.parse_variant(variant));
|
||||
}
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
Data::Union(_) => unreachable!(),
|
||||
};
|
||||
|
||||
if !errors.is_empty() {
|
||||
Err(Error::multiple(errors))
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the next found variant to the object, returning an error
|
||||
/// if parsing goes wrong.
|
||||
#[allow(unused_variables)]
|
||||
fn parse_variant(&mut self, variant: &syn::Variant) -> Result<()> {
|
||||
Err(Error::unsupported_format("enum variant"))
|
||||
Err(Error::unsupported_format("enum variant").with_span(variant))
|
||||
}
|
||||
|
||||
/// Apply the next found struct field to the object, returning an error
|
||||
/// if parsing goes wrong.
|
||||
#[allow(unused_variables)]
|
||||
fn parse_field(&mut self, field: &syn::Field) -> Result<()> {
|
||||
Err(Error::unsupported_format("struct field"))
|
||||
Err(Error::unsupported_format("struct field").with_span(field))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{TokenStreamExt, ToTokens};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::{Meta, NestedMeta};
|
||||
|
||||
use {Error, FromMeta, Result};
|
||||
|
@ -40,14 +40,18 @@ impl FromMeta for Shape {
|
|||
if word == "any" {
|
||||
new.any = true;
|
||||
} else if word.starts_with("enum_") {
|
||||
new.enum_values.set_word(word)?;
|
||||
new.enum_values
|
||||
.set_word(word)
|
||||
.map_err(|e| e.with_span(ident))?;
|
||||
} else if word.starts_with("struct_") {
|
||||
new.struct_values.set_word(word)?;
|
||||
new.struct_values
|
||||
.set_word(word)
|
||||
.map_err(|e| e.with_span(ident))?;
|
||||
} else {
|
||||
return Err(Error::unknown_value(word));
|
||||
return Err(Error::unknown_value(word).with_span(ident));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::unsupported_format("non-word"));
|
||||
return Err(Error::unsupported_format("non-word").with_span(item));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,7 +61,7 @@ impl FromMeta for Shape {
|
|||
|
||||
impl ToTokens for Shape {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let fn_body = if self.any == true {
|
||||
let fn_body = if self.any {
|
||||
quote!(::darling::export::Ok(()))
|
||||
} else {
|
||||
let en = &self.enum_values;
|
||||
|
@ -84,24 +88,27 @@ impl ToTokens for Shape {
|
|||
}
|
||||
};
|
||||
|
||||
// FIXME: Remove the &[]
|
||||
tokens.append_all(&[quote!{
|
||||
tokens.append_all(quote! {
|
||||
#[allow(unused_variables)]
|
||||
fn __validate_body(__body: &::syn::Data) -> ::darling::Result<()> {
|
||||
#fn_body
|
||||
}
|
||||
}]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct DataShape {
|
||||
/// The kind of shape being described. This can be `struct_` or `enum_`.
|
||||
prefix: &'static str,
|
||||
newtype: bool,
|
||||
named: bool,
|
||||
tuple: bool,
|
||||
unit: bool,
|
||||
any: bool,
|
||||
/// Control whether the emitted code should be inside a function or not.
|
||||
/// This is `true` when creating a `Shape` for `FromDeriveInput`, but false
|
||||
/// when deriving `FromVariant`.
|
||||
embedded: bool,
|
||||
}
|
||||
|
||||
|
@ -147,16 +154,24 @@ impl DataShape {
|
|||
|
||||
impl FromMeta for DataShape {
|
||||
fn from_list(items: &[NestedMeta]) -> Result<Self> {
|
||||
let mut errors = Vec::new();
|
||||
let mut new = DataShape::default();
|
||||
|
||||
for item in items {
|
||||
if let NestedMeta::Meta(Meta::Word(ref ident)) = *item {
|
||||
new.set_word(ident.to_string().as_str())?;
|
||||
if let Err(e) = new.set_word(&ident.to_string()) {
|
||||
errors.push(e.with_span(ident));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::unsupported_format("non-word"));
|
||||
errors.push(Error::unsupported_format("non-word").with_span(item));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(new)
|
||||
if !errors.is_empty() {
|
||||
Err(Error::multiple(errors))
|
||||
} else {
|
||||
Ok(new)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,15 +198,13 @@ impl ToTokens for DataShape {
|
|||
};
|
||||
|
||||
if self.embedded {
|
||||
// FIXME: Remove the &[]
|
||||
tokens.append_all(&[body]);
|
||||
body.to_tokens(tokens);
|
||||
} else {
|
||||
// FIXME: Remove the &[]
|
||||
tokens.append_all(&[quote! {
|
||||
tokens.append_all(quote! {
|
||||
fn __validate_data(data: &::syn::Fields) -> ::darling::Result<()> {
|
||||
#body
|
||||
}
|
||||
}]);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +228,7 @@ mod tests {
|
|||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.interpret_meta().ok_or("Unable to parse".into())
|
||||
attribute.parse_meta().or(Err("Unable to parse".into()))
|
||||
}
|
||||
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
|
|
|
@ -99,10 +99,9 @@ impl<T: UsesLifetimes> UsesLifetimes for Option<T> {
|
|||
options: &Options,
|
||||
lifetimes: &'a LifetimeSet,
|
||||
) -> LifetimeRefSet<'a> {
|
||||
self.as_ref().map_or_else(
|
||||
|| Default::default(),
|
||||
|v| v.uses_lifetimes(options, lifetimes),
|
||||
)
|
||||
self.as_ref()
|
||||
.map(|v| v.uses_lifetimes(options, lifetimes))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -288,13 +287,14 @@ mod tests {
|
|||
use usage::GenericsExt;
|
||||
use usage::Purpose::*;
|
||||
|
||||
fn parse(src: &str) -> DeriveInput {
|
||||
syn::parse_str(src).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_named() {
|
||||
let input = parse("struct Foo<'a, 'b: 'a> { parent: &'b Bar, child: &'a Baz, }");
|
||||
let input: DeriveInput = parse_quote! {
|
||||
struct Foo<'a, 'b: 'a> {
|
||||
parent: &'b Bar,
|
||||
child: &'a Baz,
|
||||
}
|
||||
};
|
||||
let omitted = syn::Lifetime::new("'c", Span::call_site());
|
||||
|
||||
let lifetimes = {
|
||||
|
@ -309,9 +309,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn qself() {
|
||||
let input = parse(
|
||||
"struct Foo<'a, 'b: 'a> { parent: &'b Bar, child: <Bar<'a> as MyIterator>::Item, }",
|
||||
);
|
||||
let input: DeriveInput = parse_quote! {
|
||||
struct Foo<'a, 'b: 'a> {
|
||||
parent: &'b Bar,
|
||||
child: <Bar<'a> as MyIterator>::Item,
|
||||
}
|
||||
};
|
||||
let lifetimes = input.generics.declared_lifetimes();
|
||||
let matches = input.data.uses_lifetimes(&BoundImpl.into(), &lifetimes);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
|
|
@ -246,16 +246,12 @@ impl UsesTypeParams for syn::TypeParamBound {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use proc_macro2::Span;
|
||||
use syn::{self, Ident};
|
||||
use syn::{DeriveInput, Ident};
|
||||
|
||||
use super::UsesTypeParams;
|
||||
use usage::IdentSet;
|
||||
use usage::Purpose::*;
|
||||
|
||||
fn given_src(src: &str) -> syn::DeriveInput {
|
||||
syn::parse_str(src).unwrap()
|
||||
}
|
||||
|
||||
fn ident_set(idents: Vec<&str>) -> IdentSet {
|
||||
idents
|
||||
.into_iter()
|
||||
|
@ -265,7 +261,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn finds_simple() {
|
||||
let input = given_src("struct Foo<T, U>(T, i32, A, U);");
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T, U>(T, i32, A, U); };
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 2);
|
||||
|
@ -277,14 +273,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn finds_named() {
|
||||
let input = given_src(
|
||||
r#"
|
||||
struct Foo<T, U = usize> {
|
||||
bar: T,
|
||||
world: U,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let input: DeriveInput = parse_quote! {
|
||||
struct Foo<T, U = usize> {
|
||||
bar: T,
|
||||
world: U,
|
||||
}
|
||||
};
|
||||
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
|
||||
|
@ -299,14 +293,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn finds_as_type_arg() {
|
||||
let input = given_src(
|
||||
r#"
|
||||
struct Foo<T, U> {
|
||||
bar: T,
|
||||
world: Vec<U>,
|
||||
}
|
||||
"#,
|
||||
);
|
||||
let input: DeriveInput = parse_quote! {
|
||||
struct Foo<T, U> {
|
||||
bar: T,
|
||||
world: Vec<U>,
|
||||
}
|
||||
};
|
||||
|
||||
let generics = ident_set(vec!["T", "U", "X"]);
|
||||
|
||||
|
@ -321,7 +313,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn associated_type() {
|
||||
let input = given_src("struct Foo<'a, T> where T: Iterator { peek: T::Item }");
|
||||
let input: DeriveInput =
|
||||
parse_quote! { struct Foo<'a, T> where T: Iterator { peek: T::Item } };
|
||||
let generics = ident_set(vec!["T", "INTO"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
@ -329,7 +322,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn box_fn_output() {
|
||||
let input = given_src("struct Foo<T>(Box<Fn() -> T>);");
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<Fn() -> T>); };
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
@ -338,7 +331,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn box_fn_input() {
|
||||
let input = given_src("struct Foo<T>(Box<Fn(&T) -> ()>);");
|
||||
let input: DeriveInput = parse_quote! { struct Foo<T>(Box<Fn(&T) -> ()>); };
|
||||
let generics = ident_set(vec!["T"]);
|
||||
let matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
assert_eq!(matches.len(), 1);
|
||||
|
@ -349,7 +342,8 @@ mod tests {
|
|||
/// search can execute in.
|
||||
#[test]
|
||||
fn qself_vec() {
|
||||
let input = given_src("struct Foo<T>(<Vec<T> as a::b::Trait>::AssociatedItem);");
|
||||
let input: DeriveInput =
|
||||
parse_quote! { struct Foo<T>(<Vec<T> as a::b::Trait>::AssociatedItem); };
|
||||
let generics = ident_set(vec!["T", "U"]);
|
||||
|
||||
let bound_matches = input.data.uses_type_params(&BoundImpl.into(), &generics);
|
||||
|
|
|
@ -52,10 +52,50 @@ impl FromMeta for IdentList {
|
|||
if let NestedMeta::Meta(Meta::Word(ref ident)) = *nmi {
|
||||
idents.push(ident.clone());
|
||||
} else {
|
||||
return Err(Error::unexpected_type("non-word"));
|
||||
return Err(Error::unexpected_type("non-word").with_span(nmi));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(IdentList(idents))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::IdentList;
|
||||
use proc_macro2::TokenStream;
|
||||
use FromMeta;
|
||||
|
||||
/// parse a string as a syn::Meta instance.
|
||||
fn pm(tokens: TokenStream) -> ::std::result::Result<syn::Meta, String> {
|
||||
let attribute: syn::Attribute = parse_quote!(#[#tokens]);
|
||||
attribute.interpret_meta().ok_or("Unable to parse".into())
|
||||
}
|
||||
|
||||
fn fm<T: FromMeta>(tokens: TokenStream) -> T {
|
||||
FromMeta::from_meta(&pm(tokens).expect("Tests should pass well-formed input"))
|
||||
.expect("Tests should pass valid input")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn succeeds() {
|
||||
let idents = fm::<IdentList>(quote!(ignore(Debug, Clone, Eq)));
|
||||
assert_eq!(
|
||||
idents.to_strings(),
|
||||
vec![
|
||||
String::from("Debug"),
|
||||
String::from("Clone"),
|
||||
String::from("Eq")
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/// Check that the parser rejects non-word members of the list, and that the error
|
||||
/// has an associated span.
|
||||
#[test]
|
||||
fn fails_non_word() {
|
||||
let input = IdentList::from_meta(&pm(quote!(ignore(Debug, Clone = false))).unwrap());
|
||||
let err = input.unwrap_err();
|
||||
assert!(err.has_span());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
|
@ -9,7 +10,7 @@ use {FromMeta, Result};
|
|||
/// A wrapper for an `Ident` which also keeps the value as a string.
|
||||
///
|
||||
/// This struct can be used to perform string comparisons and operations.
|
||||
#[derive(Clone, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialOrd, Ord)]
|
||||
pub struct IdentString {
|
||||
ident: Ident,
|
||||
string: String,
|
||||
|
@ -104,6 +105,12 @@ impl<'a> PartialEq<&'a str> for IdentString {
|
|||
}
|
||||
}
|
||||
|
||||
impl Hash for IdentString {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.ident.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for IdentString {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.ident.to_tokens(tokens);
|
||||
|
|
|
@ -10,11 +10,13 @@ mod ident_string;
|
|||
mod ignored;
|
||||
mod over_ride;
|
||||
mod with_original;
|
||||
mod spanned_value;
|
||||
|
||||
pub use self::ident_list::IdentList;
|
||||
pub use self::ident_string::IdentString;
|
||||
pub use self::ignored::Ignored;
|
||||
pub use self::over_ride::Override;
|
||||
pub use self::spanned_value::SpannedValue;
|
||||
pub use self::with_original::WithOriginal;
|
||||
|
||||
/// Marker type equivalent to `Option<()>` for use in attribute parsing.
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
use proc_macro2::Span;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use syn;
|
||||
use syn::spanned::Spanned;
|
||||
use {
|
||||
FromDeriveInput, FromField, FromGenericParam, FromGenerics, FromMeta, FromTypeParam,
|
||||
FromVariant, Result,
|
||||
};
|
||||
|
||||
/// A value and an associated position in source code. The main use case for this is
|
||||
/// to preserve position information to emit warnings from proc macros. You can use
|
||||
/// a `SpannedValue<T>` as a field in any struct that implements or derives any of
|
||||
/// `darling`'s core traits.
|
||||
///
|
||||
/// To access the underlying value, use the struct's `Deref` implementation.
|
||||
///
|
||||
/// # Defaulting
|
||||
/// This type is meant to be used in conjunction with attribute-extracted options,
|
||||
/// but the user may not always explicitly set those options in their source code.
|
||||
/// In this case, using `Default::default()` will create an instance which points
|
||||
/// to `Span::call_site()`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SpannedValue<T> {
|
||||
value: T,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl<T> SpannedValue<T> {
|
||||
pub fn new(value: T, span: Span) -> Self {
|
||||
SpannedValue { value, span }
|
||||
}
|
||||
|
||||
/// Get the source code location referenced by this struct.
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for SpannedValue<T> {
|
||||
fn default() -> Self {
|
||||
SpannedValue::new(Default::default(), Span::call_site())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for SpannedValue<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for SpannedValue<T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for SpannedValue<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! spanned {
|
||||
($trayt:ident, $method:ident, $syn:path) => {
|
||||
impl<T: $trayt> $trayt for SpannedValue<T> {
|
||||
fn $method(value: &$syn) -> Result<Self> {
|
||||
Ok(SpannedValue::new(
|
||||
$trayt::$method(value).map_err(|e| e.with_span(value))?,
|
||||
value.span(),
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
spanned!(FromGenericParam, from_generic_param, syn::GenericParam);
|
||||
spanned!(FromGenerics, from_generics, syn::Generics);
|
||||
spanned!(FromTypeParam, from_type_param, syn::TypeParam);
|
||||
spanned!(FromMeta, from_meta, syn::Meta);
|
||||
spanned!(FromDeriveInput, from_derive_input, syn::DeriveInput);
|
||||
spanned!(FromField, from_field, syn::Field);
|
||||
spanned!(FromVariant, from_variant, syn::Variant);
|
||||
|
||||
impl<T: Spanned> From<T> for SpannedValue<T> {
|
||||
fn from(value: T) -> Self {
|
||||
let span = value.span();
|
||||
SpannedValue::new(value, span)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proc_macro2::Span;
|
||||
|
||||
/// Make sure that `SpannedValue` can be seamlessly used as its underlying type.
|
||||
#[test]
|
||||
fn deref() {
|
||||
let test = SpannedValue::new("hello", Span::call_site());
|
||||
assert_eq!("hello", test.trim());
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"a062c325e8c632ab4da20f1f5918efb2b8ebdcaa45c7cc23fad00d546fa8fc46","src/lib.rs":"8343bf4500faeaf72e0cafb9e2c378be176bfe41529ba8f0d5e315336de422bf"},"package":"99c4eff4bcbeaf6a22578012ff79c95910338668278d1901e528bd34a22f575d"}
|
||||
{"files":{"Cargo.toml":"7f87d565ab1cb6661d23c51bca2b37b15f0f97456cdace142b54ef39f1f17d0a","LICENSE":"8ea93490d74a5a1b1af3ff71d786271b3f1e5f0bea79ac16e02ec533cef040d6","src/lib.rs":"88141a58dc13b9001a83ca7559b6213ab44641510b6d642a3280c244ad735cec"},"package":"244e8987bd4e174385240cde20a3657f607fb0797563c28255c353b5819a07b1"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "darling_macro"
|
||||
version = "0.8.1"
|
||||
version = "0.8.6"
|
||||
authors = ["Ted Driggs <ted.driggs@outlook.com>"]
|
||||
description = "Internal support for a proc-macro library for reading attributes into structs when\nimplementing custom derives. Use https://crates.io/crates/darling in your code.\n"
|
||||
license = "MIT"
|
||||
|
@ -21,10 +21,10 @@ repository = "https://github.com/TedDriggs/darling"
|
|||
[lib]
|
||||
proc-macro = true
|
||||
[dependencies.darling_core]
|
||||
version = "=0.8.1"
|
||||
version = "=0.8.6"
|
||||
|
||||
[dependencies.quote]
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.syn]
|
||||
version = "0.15"
|
||||
version = "0.15.26"
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Ted Driggs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -1,72 +1,41 @@
|
|||
extern crate proc_macro;
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
|
||||
extern crate darling_core;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use darling_core::{codegen, options};
|
||||
use darling_core::{derive, Error};
|
||||
|
||||
#[proc_macro_derive(FromMeta, attributes(darling))]
|
||||
pub fn derive_from_meta(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let container = options::FromMetaOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromMetaImpl::from(&container);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
derive::from_meta(&parse_macro_input!(input)).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromMetaItem, attributes(darling))]
|
||||
pub fn derive_from_meta_item(_input: TokenStream) -> TokenStream {
|
||||
panic!("darling::FromMetaItem has been replaced by darling::FromMeta");
|
||||
Error::custom("darling::FromMetaItem has been replaced by darling::FromMeta")
|
||||
.write_errors()
|
||||
.into()
|
||||
}
|
||||
|
||||
|
||||
#[proc_macro_derive(FromDeriveInput, attributes(darling))]
|
||||
pub fn derive_from_input(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let container = options::FdiOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromDeriveInputImpl::from(&container);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
derive::from_derive_input(&parse_macro_input!(input)).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromField, attributes(darling))]
|
||||
pub fn derive_field(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromFieldOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromFieldImpl::from(&fdic);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
derive::from_field(&parse_macro_input!(input)).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromTypeParam, attributes(darling))]
|
||||
pub fn derive_type_param(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromTypeParamOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromTypeParamImpl::from(&fdic);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
derive::from_type_param(&parse_macro_input!(input)).into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromVariant, attributes(darling))]
|
||||
pub fn derive_variant(input: TokenStream) -> TokenStream {
|
||||
let ast = parse_macro_input!(input as syn::DeriveInput);
|
||||
|
||||
let fdic = options::FromVariantOptions::new(&ast).unwrap();
|
||||
let trait_impl = codegen::FromVariantImpl::from(&fdic);
|
||||
let result = quote!(#trait_impl);
|
||||
|
||||
result.into()
|
||||
derive::from_variant(&parse_macro_input!(input)).into()
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"f020c87cba7dd2260861239307b2cb93e16c2bed6e2ef6c9178642b1dfcc43a3","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"538fd635d385b6a90ef4cc1e361aad717162a139e932a6192212cad8407aa8e1","build.rs":"7698abdd3087e0f3308916c37ade3349b6b000165186b80913013af18d36ecb6","src/lib.rs":"c5c276236d828189a5151c890a66f2b7d1c02beca98f08f2d9c01166df441eb2","src/stable.rs":"a1f29e850e5fc4c602ee1204847124e266087175695d77ec448016db910acb6b","src/strnom.rs":"807c377bdb49b8b1c67d013089b8ff33fe93ffd3fa36b6440dbb1d6fe8cd9c17","src/unstable.rs":"0b7f86862d8254104330d14837ea6ec89e7b3bf2ffe910b73629269f2bc282de","tests/marker.rs":"0227d07bbc7f2e2ad34662a6acb65668b7dc2f79141c4faa672703a04e27bea0","tests/test.rs":"427821bab498926aa56bfcea7d28c36fb24a7d63d7f59d3e7e097bcfc77fe95b"},"package":"77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"}
|
||||
{"files":{"Cargo.toml":"b523856472549844b4bf20eca0473d955a7e5eeb95c70eddd31a05ac455427bb","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"89857eaaa305afe540abcf56fabae0194dfb4e7906a8098b7206acb23ed11ce8","build.rs":"36fa668f3bf309f243d0e977e8428446cc424303139c1f63410b3c2e30445aec","src/fallback.rs":"e4d1bcb1e92383a2285e6c947dd74b0e34144904948db68127faea627f5dd6ff","src/lib.rs":"896a1d212e30902ff051313808007406ca4471c27880a6ef19508f0ebb8333ee","src/strnom.rs":"60f5380106dbe568cca7abd09877e133c874fbee95d502e4830425c4613a640d","src/wrapper.rs":"0d7fe28ab2b7ee02b8eb8c5a636da364c60f6704b23e7db0a1ddd57c742f54b1","tests/marker.rs":"0227d07bbc7f2e2ad34662a6acb65668b7dc2f79141c4faa672703a04e27bea0","tests/test.rs":"166d35835355bdaa85bcf69de4dfb56ccddd8acf2e1a8cbc506782632b151674"},"package":"4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.24"
|
||||
version = "0.4.27"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||
build = "build.rs"
|
||||
description = "A stable implementation of the upcoming new `proc_macro` API. Comes with an\noption, off by default, to also reimplement itself in terms of the upstream\nunstable API.\n"
|
||||
|
@ -32,7 +32,8 @@ version = "0.6"
|
|||
|
||||
[features]
|
||||
default = ["proc-macro"]
|
||||
nightly = ["proc-macro"]
|
||||
nightly = []
|
||||
proc-macro = []
|
||||
span-locations = []
|
||||
[badges.travis-ci]
|
||||
repository = "alexcrichton/proc-macro2"
|
||||
|
|
|
@ -1,60 +1,76 @@
|
|||
# proc-macro2
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/alexcrichton/proc-macro2.svg?branch=master)](https://travis-ci.org/alexcrichton/proc-macro2)
|
||||
[![Build Status](https://api.travis-ci.com/alexcrichton/proc-macro2.svg?branch=master)](https://travis-ci.com/alexcrichton/proc-macro2)
|
||||
[![Latest Version](https://img.shields.io/crates/v/proc-macro2.svg)](https://crates.io/crates/proc-macro2)
|
||||
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/proc-macro2)
|
||||
|
||||
A small shim over the `proc_macro` crate in the compiler intended to multiplex
|
||||
the stable interface as of 1.15.0 and the interface as of 1.30.0.
|
||||
A wrapper around the procedural macro API of the compiler's `proc_macro` crate.
|
||||
This library serves three purposes:
|
||||
|
||||
New features added in Rust 1.30.0 include:
|
||||
- **Bring proc-macro-like functionality to other contexts like build.rs and
|
||||
main.rs.** Types from `proc_macro` are entirely specific to procedural macros
|
||||
and cannot ever exist in code outside of a procedural macro. Meanwhile
|
||||
`proc_macro2` types may exist anywhere including non-macro code. By developing
|
||||
foundational libraries like [syn] and [quote] against `proc_macro2` rather
|
||||
than `proc_macro`, the procedural macro ecosystem becomes easily applicable to
|
||||
many other use cases and we avoid reimplementing non-macro equivalents of
|
||||
those libraries.
|
||||
|
||||
* Span information on tokens
|
||||
* No need to go in/out through strings
|
||||
* Structured input/output
|
||||
- **Make procedural macros unit testable.** As a consequence of being specific
|
||||
to procedural macros, nothing that uses `proc_macro` can be executed from a
|
||||
unit test. In order for helper libraries or components of a macro to be
|
||||
testable in isolation, they must be implemented using `proc_macro2`.
|
||||
|
||||
Libraries ported to `proc_macro2` can retain support for older compilers while
|
||||
continuing to get all the nice benefits of using a 1.30.0+ compiler.
|
||||
- **Provide the latest and greatest APIs across all compiler versions.**
|
||||
Procedural macros were first introduced to Rust in 1.15.0 with an extremely
|
||||
minimal interface. Since then, many improvements have landed to make macros
|
||||
more flexible and easier to write. This library tracks the procedural macro
|
||||
API of the most recent stable compiler but employs a polyfill to provide that
|
||||
API consistently across any compiler since 1.15.0.
|
||||
|
||||
[syn]: https://github.com/dtolnay/syn
|
||||
[quote]: https://github.com/dtolnay/quote
|
||||
|
||||
## Usage
|
||||
|
||||
This crate compiles on all 1.15.0+ stable compilers and usage looks like:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
proc-macro2 = "0.4"
|
||||
```
|
||||
|
||||
followed by
|
||||
The skeleton of a typical procedural macro typically looks like this:
|
||||
|
||||
```rust
|
||||
extern crate proc_macro;
|
||||
extern crate proc_macro2;
|
||||
|
||||
#[proc_macro_derive(MyDerive)]
|
||||
pub fn my_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input: proc_macro2::TokenStream = input.into();
|
||||
let input = proc_macro2::TokenStream::from(input);
|
||||
|
||||
let output: proc_macro2::TokenStream = {
|
||||
/* transform input */
|
||||
};
|
||||
|
||||
output.into()
|
||||
proc_macro::TokenStream::from(output)
|
||||
}
|
||||
```
|
||||
|
||||
The 1.30.0 compiler is automatically detected and its interfaces are used when
|
||||
available.
|
||||
If parsing with [Syn], you'll use [`parse_macro_input!`] instead to propagate
|
||||
parse errors correctly back to the compiler when parsing fails.
|
||||
|
||||
## Unstable Features
|
||||
[`parse_macro_input!`]: https://docs.rs/syn/0.15/syn/macro.parse_macro_input.html
|
||||
|
||||
`proc-macro2` supports exporting some methods from `proc_macro` which are
|
||||
currently highly unstable, and are not stabilized in the first pass of
|
||||
`proc_macro` stabilizations. These features are not exported by default. Minor
|
||||
versions of `proc-macro2` may make breaking changes to them at any time.
|
||||
## Unstable features
|
||||
|
||||
To enable these features, the `procmacro2_semver_exempt` config flag must be
|
||||
passed to rustc.
|
||||
The default feature set of proc-macro2 tracks the most recent stable compiler
|
||||
API. Functionality in `proc_macro` that is not yet stable is not exposed by
|
||||
proc-macro2 by default.
|
||||
|
||||
To opt into the additional APIs available in the most recent nightly compiler,
|
||||
the `procmacro2_semver_exempt` config flag must be passed to rustc. As usual, we
|
||||
will polyfill those nightly-only APIs all the way back to Rust 1.15.0. As these
|
||||
are unstable APIs that track the nightly compiler, minor versions of proc-macro2
|
||||
may make breaking changes to them at any time.
|
||||
|
||||
```
|
||||
RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build
|
||||
|
@ -64,6 +80,8 @@ Note that this must not only be done for your crate, but for any crate that
|
|||
depends on your crate. This infectious nature is intentional, as it serves as a
|
||||
reminder that you are outside of the normal semver guarantees.
|
||||
|
||||
Semver exempt methods are marked as such in the proc-macro2 documentation.
|
||||
|
||||
# License
|
||||
|
||||
This project is licensed under either of
|
||||
|
|
|
@ -1,3 +1,43 @@
|
|||
// rustc-cfg emitted by the build script:
|
||||
//
|
||||
// "u128"
|
||||
// Include u128 and i128 constructors for proc_macro2::Literal. Enabled on
|
||||
// any compiler 1.26+.
|
||||
//
|
||||
// "use_proc_macro"
|
||||
// Link to extern crate proc_macro. Available on any compiler and any target
|
||||
// except wasm32. Requires "proc-macro" Cargo cfg to be enabled (default is
|
||||
// enabled). On wasm32 we never link to proc_macro even if "proc-macro" cfg
|
||||
// is enabled.
|
||||
//
|
||||
// "wrap_proc_macro"
|
||||
// Wrap types from libproc_macro rather than polyfilling the whole API.
|
||||
// Enabled on rustc 1.29+ as long as procmacro2_semver_exempt is not set,
|
||||
// because we can't emulate the unstable API without emulating everything
|
||||
// else. Also enabled unconditionally on nightly, in which case the
|
||||
// procmacro2_semver_exempt surface area is implemented by using the
|
||||
// nightly-only proc_macro API.
|
||||
//
|
||||
// "slow_extend"
|
||||
// Fallback when `impl Extend for TokenStream` is not available. These impls
|
||||
// were added one version later than the rest of the proc_macro token API.
|
||||
// Enabled on rustc 1.29 only.
|
||||
//
|
||||
// "nightly"
|
||||
// Enable the Span::unwrap method. This is to support proc_macro_span and
|
||||
// proc_macro_diagnostic use on the nightly channel without requiring the
|
||||
// semver exemption opt-in. Enabled when building with nightly.
|
||||
//
|
||||
// "super_unstable"
|
||||
// Implement the semver exempt API in terms of the nightly-only proc_macro
|
||||
// API. Enabled when using procmacro2_semver_exempt on a nightly compiler.
|
||||
//
|
||||
// "span_locations"
|
||||
// Provide methods Span::start and Span::end which give the line/column
|
||||
// location of a token. Enabled by procmacro2_semver_exempt or the
|
||||
// "span-locations" Cargo cfg. This is behind a cfg because tracking
|
||||
// location inside spans is a performance hit.
|
||||
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
use std::str;
|
||||
|
@ -7,34 +47,47 @@ fn main() {
|
|||
|
||||
let target = env::var("TARGET").unwrap();
|
||||
|
||||
let minor = match rustc_minor_version() {
|
||||
Some(n) => n,
|
||||
let version = match rustc_version() {
|
||||
Some(version) => version,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if minor >= 26 {
|
||||
if version.minor >= 26 {
|
||||
println!("cargo:rustc-cfg=u128");
|
||||
}
|
||||
|
||||
let semver_exempt = cfg!(procmacro2_semver_exempt);
|
||||
if semver_exempt {
|
||||
// https://github.com/alexcrichton/proc-macro2/issues/147
|
||||
println!("cargo:rustc-cfg=procmacro2_semver_exempt");
|
||||
}
|
||||
|
||||
if semver_exempt || cfg!(feature = "span-locations") {
|
||||
println!("cargo:rustc-cfg=span_locations");
|
||||
}
|
||||
|
||||
if !enable_use_proc_macro(&target) {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("cargo:rustc-cfg=use_proc_macro");
|
||||
|
||||
// Rust 1.29 stabilized the necessary APIs in the `proc_macro` crate
|
||||
if (minor >= 29 && !cfg!(procmacro2_semver_exempt)) || cfg!(feature = "nightly") {
|
||||
if version.nightly || version.minor >= 29 && !semver_exempt {
|
||||
println!("cargo:rustc-cfg=wrap_proc_macro");
|
||||
|
||||
if cfg!(procmacro2_semver_exempt) {
|
||||
println!("cargo:rustc-cfg=super_unstable");
|
||||
// https://github.com/alexcrichton/proc-macro2/issues/147
|
||||
println!("cargo:rustc-cfg=procmacro2_semver_exempt");
|
||||
}
|
||||
}
|
||||
|
||||
if minor == 29 {
|
||||
if version.minor == 29 {
|
||||
println!("cargo:rustc-cfg=slow_extend");
|
||||
}
|
||||
|
||||
if version.nightly {
|
||||
println!("cargo:rustc-cfg=nightly");
|
||||
}
|
||||
|
||||
if semver_exempt && version.nightly {
|
||||
println!("cargo:rustc-cfg=super_unstable");
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_use_proc_macro(target: &str) -> bool {
|
||||
|
@ -47,7 +100,12 @@ fn enable_use_proc_macro(target: &str) -> bool {
|
|||
cfg!(feature = "proc-macro")
|
||||
}
|
||||
|
||||
fn rustc_minor_version() -> Option<u32> {
|
||||
struct RustcVersion {
|
||||
minor: u32,
|
||||
nightly: bool,
|
||||
}
|
||||
|
||||
fn rustc_version() -> Option<RustcVersion> {
|
||||
macro_rules! otry {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
|
@ -56,12 +114,20 @@ fn rustc_minor_version() -> Option<u32> {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
let rustc = otry!(env::var_os("RUSTC"));
|
||||
let output = otry!(Command::new(rustc).arg("--version").output().ok());
|
||||
let version = otry!(str::from_utf8(&output.stdout).ok());
|
||||
let nightly = version.contains("nightly");
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return None;
|
||||
}
|
||||
otry!(pieces.next()).parse().ok()
|
||||
let minor = otry!(pieces.next());
|
||||
let minor = otry!(minor.parse().ok());
|
||||
|
||||
Some(RustcVersion {
|
||||
minor: minor,
|
||||
nightly: nightly,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#![cfg_attr(not(procmacro2_semver_exempt), allow(dead_code))]
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
use std::cell::RefCell;
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
use std::cmp;
|
||||
|
@ -35,7 +33,7 @@ impl TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
fn get_cursor(src: &str) -> Cursor {
|
||||
// Create a dummy file & add it to the codemap
|
||||
CODEMAP.with(|cm| {
|
||||
|
@ -49,7 +47,7 @@ fn get_cursor(src: &str) -> Cursor {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
#[cfg(not(span_locations))]
|
||||
fn get_cursor(src: &str) -> Cursor {
|
||||
Cursor { rest: src }
|
||||
}
|
||||
|
@ -225,27 +223,41 @@ pub struct LineColumn {
|
|||
pub column: usize,
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
thread_local! {
|
||||
static CODEMAP: RefCell<Codemap> = RefCell::new(Codemap {
|
||||
// NOTE: We start with a single dummy file which all call_site() and
|
||||
// def_site() spans reference.
|
||||
files: vec![FileInfo {
|
||||
name: "<unspecified>".to_owned(),
|
||||
span: Span { lo: 0, hi: 0 },
|
||||
lines: vec![0],
|
||||
files: vec![{
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
{
|
||||
FileInfo {
|
||||
name: "<unspecified>".to_owned(),
|
||||
span: Span { lo: 0, hi: 0 },
|
||||
lines: vec![0],
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
{
|
||||
FileInfo {
|
||||
span: Span { lo: 0, hi: 0 },
|
||||
lines: vec![0],
|
||||
}
|
||||
}
|
||||
}],
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
struct FileInfo {
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
name: String,
|
||||
span: Span,
|
||||
lines: Vec<usize>,
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
impl FileInfo {
|
||||
fn offset_line_column(&self, offset: usize) -> LineColumn {
|
||||
assert!(self.span_within(Span {
|
||||
|
@ -271,7 +283,7 @@ impl FileInfo {
|
|||
}
|
||||
|
||||
/// Computesthe offsets of each line in the given source string.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
fn lines_offsets(s: &str) -> Vec<usize> {
|
||||
let mut lines = vec![0];
|
||||
let mut prev = 0;
|
||||
|
@ -282,12 +294,12 @@ fn lines_offsets(s: &str) -> Vec<usize> {
|
|||
lines
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
struct Codemap {
|
||||
files: Vec<FileInfo>,
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
impl Codemap {
|
||||
fn next_start_pos(&self) -> u32 {
|
||||
// Add 1 so there's always space between files.
|
||||
|
@ -306,12 +318,20 @@ impl Codemap {
|
|||
hi: lo + (src.len() as u32),
|
||||
};
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
self.files.push(FileInfo {
|
||||
name: name.to_owned(),
|
||||
span: span,
|
||||
lines: lines,
|
||||
});
|
||||
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
self.files.push(FileInfo {
|
||||
span: span,
|
||||
lines: lines,
|
||||
});
|
||||
let _ = name;
|
||||
|
||||
span
|
||||
}
|
||||
|
||||
|
@ -327,27 +347,29 @@ impl Codemap {
|
|||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
lo: u32,
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
hi: u32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
#[cfg(not(span_locations))]
|
||||
pub fn call_site() -> Span {
|
||||
Span {}
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub fn call_site() -> Span {
|
||||
Span { lo: 0, hi: 0 }
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn def_site() -> Span {
|
||||
Span::call_site()
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn resolved_at(&self, _other: Span) -> Span {
|
||||
// Stable spans consist only of line/column information, so
|
||||
// `resolved_at` and `located_at` only select which span the
|
||||
|
@ -355,6 +377,7 @@ impl Span {
|
|||
*self
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn located_at(&self, other: Span) -> Span {
|
||||
other
|
||||
}
|
||||
|
@ -370,7 +393,7 @@ impl Span {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub fn start(&self) -> LineColumn {
|
||||
CODEMAP.with(|cm| {
|
||||
let cm = cm.borrow();
|
||||
|
@ -379,7 +402,7 @@ impl Span {
|
|||
})
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub fn end(&self) -> LineColumn {
|
||||
CODEMAP.with(|cm| {
|
||||
let cm = cm.borrow();
|
||||
|
@ -414,6 +437,12 @@ impl fmt::Debug for Span {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn debug_span_field_if_nontrivial(debug: &mut fmt::DebugStruct, span: Span) {
|
||||
if cfg!(procmacro2_semver_exempt) {
|
||||
debug.field("span", &span);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group {
|
||||
delimiter: Delimiter,
|
||||
|
@ -442,10 +471,12 @@ impl Group {
|
|||
self.span
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn span_open(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
pub fn span_close(&self) -> Span {
|
||||
self.span
|
||||
}
|
||||
|
@ -782,16 +813,16 @@ fn token_stream(mut input: Cursor) -> PResult<TokenStream> {
|
|||
Ok((input, TokenStream { inner: trees }))
|
||||
}
|
||||
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
#[cfg(not(span_locations))]
|
||||
fn spanned<'a, T>(
|
||||
input: Cursor<'a>,
|
||||
f: fn(Cursor<'a>) -> PResult<'a, T>,
|
||||
) -> PResult<'a, (T, ::Span)> {
|
||||
let (a, b) = f(skip_whitespace(input))?;
|
||||
Ok((a, ((b, ::Span::_new_stable(Span {})))))
|
||||
Ok((a, ((b, ::Span::_new_stable(Span::call_site())))))
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
fn spanned<'a, T>(
|
||||
input: Cursor<'a>,
|
||||
f: fn(Cursor<'a>) -> PResult<'a, T>,
|
|
@ -1,35 +1,72 @@
|
|||
//! A "shim crate" intended to multiplex the [`proc_macro`] API on to stable
|
||||
//! Rust.
|
||||
//! A wrapper around the procedural macro API of the compiler's [`proc_macro`]
|
||||
//! crate. This library serves three purposes:
|
||||
//!
|
||||
//! Procedural macros in Rust operate over the upstream
|
||||
//! [`proc_macro::TokenStream`][ts] type. This type currently is quite
|
||||
//! conservative and exposed no internal implementation details. Nightly
|
||||
//! compilers, however, contain a much richer interface. This richer interface
|
||||
//! allows fine-grained inspection of the token stream which avoids
|
||||
//! stringification/re-lexing and also preserves span information.
|
||||
//! [`proc_macro`]: https://doc.rust-lang.org/proc_macro/
|
||||
//!
|
||||
//! The upcoming APIs added to [`proc_macro`] upstream are the foundation for
|
||||
//! productive procedural macros in the ecosystem. To help prepare the ecosystem
|
||||
//! for using them this crate serves to both compile on stable and nightly and
|
||||
//! mirrors the API-to-be. The intention is that procedural macros which switch
|
||||
//! to use this crate will be trivially able to switch to the upstream
|
||||
//! `proc_macro` crate once its API stabilizes.
|
||||
//! - **Bring proc-macro-like functionality to other contexts like build.rs and
|
||||
//! main.rs.** Types from `proc_macro` are entirely specific to procedural
|
||||
//! macros and cannot ever exist in code outside of a procedural macro.
|
||||
//! Meanwhile `proc_macro2` types may exist anywhere including non-macro code.
|
||||
//! By developing foundational libraries like [syn] and [quote] against
|
||||
//! `proc_macro2` rather than `proc_macro`, the procedural macro ecosystem
|
||||
//! becomes easily applicable to many other use cases and we avoid
|
||||
//! reimplementing non-macro equivalents of those libraries.
|
||||
//!
|
||||
//! In the meantime this crate also has a `nightly` Cargo feature which
|
||||
//! enables it to reimplement itself with the unstable API of [`proc_macro`].
|
||||
//! This'll allow immediate usage of the beneficial upstream API, particularly
|
||||
//! around preserving span information.
|
||||
//! - **Make procedural macros unit testable.** As a consequence of being
|
||||
//! specific to procedural macros, nothing that uses `proc_macro` can be
|
||||
//! executed from a unit test. In order for helper libraries or components of
|
||||
//! a macro to be testable in isolation, they must be implemented using
|
||||
//! `proc_macro2`.
|
||||
//!
|
||||
//! # Unstable Features
|
||||
//! - **Provide the latest and greatest APIs across all compiler versions.**
|
||||
//! Procedural macros were first introduced to Rust in 1.15.0 with an
|
||||
//! extremely minimal interface. Since then, many improvements have landed to
|
||||
//! make macros more flexible and easier to write. This library tracks the
|
||||
//! procedural macro API of the most recent stable compiler but employs a
|
||||
//! polyfill to provide that API consistently across any compiler since
|
||||
//! 1.15.0.
|
||||
//!
|
||||
//! `proc-macro2` supports exporting some methods from `proc_macro` which are
|
||||
//! currently highly unstable, and may not be stabilized in the first pass of
|
||||
//! `proc_macro` stabilizations. These features are not exported by default.
|
||||
//! Minor versions of `proc-macro2` may make breaking changes to them at any
|
||||
//! time.
|
||||
//! [syn]: https://github.com/dtolnay/syn
|
||||
//! [quote]: https://github.com/dtolnay/quote
|
||||
//!
|
||||
//! To enable these features, the `procmacro2_semver_exempt` config flag must be
|
||||
//! passed to rustc.
|
||||
//! # Usage
|
||||
//!
|
||||
//! The skeleton of a typical procedural macro typically looks like this:
|
||||
//!
|
||||
//! ```edition2018
|
||||
//! extern crate proc_macro;
|
||||
//!
|
||||
//! # const IGNORE: &str = stringify! {
|
||||
//! #[proc_macro_derive(MyDerive)]
|
||||
//! # };
|
||||
//! pub fn my_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
//! let input = proc_macro2::TokenStream::from(input);
|
||||
//!
|
||||
//! let output: proc_macro2::TokenStream = {
|
||||
//! /* transform input */
|
||||
//! # input
|
||||
//! };
|
||||
//!
|
||||
//! proc_macro::TokenStream::from(output)
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! If parsing with [Syn], you'll use [`parse_macro_input!`] instead to
|
||||
//! propagate parse errors correctly back to the compiler when parsing fails.
|
||||
//!
|
||||
//! [`parse_macro_input!`]: https://docs.rs/syn/0.15/syn/macro.parse_macro_input.html
|
||||
//!
|
||||
//! # Unstable features
|
||||
//!
|
||||
//! The default feature set of proc-macro2 tracks the most recent stable
|
||||
//! compiler API. Functionality in `proc_macro` that is not yet stable is not
|
||||
//! exposed by proc-macro2 by default.
|
||||
//!
|
||||
//! To opt into the additional APIs available in the most recent nightly
|
||||
//! compiler, the `procmacro2_semver_exempt` config flag must be passed to
|
||||
//! rustc. As usual, we will polyfill those nightly-only APIs all the way back
|
||||
//! to Rust 1.15.0. As these are unstable APIs that track the nightly compiler,
|
||||
//! minor versions of proc-macro2 may make breaking changes to them at any time.
|
||||
//!
|
||||
//! ```sh
|
||||
//! RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build
|
||||
|
@ -39,15 +76,12 @@
|
|||
//! depends on your crate. This infectious nature is intentional, as it serves
|
||||
//! as a reminder that you are outside of the normal semver guarantees.
|
||||
//!
|
||||
//! [`proc_macro`]: https://doc.rust-lang.org/proc_macro/
|
||||
//! [ts]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html
|
||||
//! Semver exempt methods are marked as such in the proc-macro2 documentation.
|
||||
|
||||
// Proc-macro2 types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/proc-macro2/0.4.24")]
|
||||
#![cfg_attr(
|
||||
super_unstable,
|
||||
feature(proc_macro_raw_ident, proc_macro_span, proc_macro_def_site)
|
||||
)]
|
||||
#![doc(html_root_url = "https://docs.rs/proc-macro2/0.4.27")]
|
||||
#![cfg_attr(nightly, feature(proc_macro_span))]
|
||||
#![cfg_attr(super_unstable, feature(proc_macro_raw_ident, proc_macro_def_site))]
|
||||
|
||||
#[cfg(use_proc_macro)]
|
||||
extern crate proc_macro;
|
||||
|
@ -65,11 +99,11 @@ use std::str::FromStr;
|
|||
|
||||
#[macro_use]
|
||||
mod strnom;
|
||||
mod stable;
|
||||
mod fallback;
|
||||
|
||||
#[cfg(not(wrap_proc_macro))]
|
||||
use stable as imp;
|
||||
#[path = "unstable.rs"]
|
||||
use fallback as imp;
|
||||
#[path = "wrapper.rs"]
|
||||
#[cfg(wrap_proc_macro)]
|
||||
mod imp;
|
||||
|
||||
|
@ -100,7 +134,7 @@ impl TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
fn _new_stable(inner: stable::TokenStream) -> TokenStream {
|
||||
fn _new_stable(inner: fallback::TokenStream) -> TokenStream {
|
||||
TokenStream {
|
||||
inner: inner.into(),
|
||||
_marker: marker::PhantomData,
|
||||
|
@ -266,7 +300,7 @@ impl fmt::Debug for SourceFile {
|
|||
/// A line-column pair representing the start or end of a `Span`.
|
||||
///
|
||||
/// This type is semver exempt and not exposed by default.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub struct LineColumn {
|
||||
/// The 1-indexed line in the source file on which the span starts or ends
|
||||
/// (inclusive).
|
||||
|
@ -291,7 +325,7 @@ impl Span {
|
|||
}
|
||||
}
|
||||
|
||||
fn _new_stable(inner: stable::Span) -> Span {
|
||||
fn _new_stable(inner: fallback::Span) -> Span {
|
||||
Span {
|
||||
inner: inner.into(),
|
||||
_marker: marker::PhantomData,
|
||||
|
@ -333,11 +367,26 @@ impl Span {
|
|||
Span::_new(self.inner.located_at(other.inner))
|
||||
}
|
||||
|
||||
/// This method is only available when the `"nightly"` feature is enabled.
|
||||
/// Convert `proc_macro2::Span` to `proc_macro::Span`.
|
||||
///
|
||||
/// This method is available when building with a nightly compiler, or when
|
||||
/// building with rustc 1.29+ *without* semver exempt features.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if called from outside of a procedural macro. Unlike
|
||||
/// `proc_macro2::Span`, the `proc_macro::Span` type can only exist within
|
||||
/// the context of a procedural macro invocation.
|
||||
#[cfg(wrap_proc_macro)]
|
||||
pub fn unwrap(self) -> proc_macro::Span {
|
||||
self.inner.unwrap()
|
||||
}
|
||||
|
||||
// Soft deprecated. Please use Span::unwrap.
|
||||
#[cfg(wrap_proc_macro)]
|
||||
#[doc(hidden)]
|
||||
#[cfg(any(feature = "nightly", super_unstable))]
|
||||
pub fn unstable(self) -> proc_macro::Span {
|
||||
self.inner.unstable()
|
||||
self.unwrap()
|
||||
}
|
||||
|
||||
/// The original source file into which this span points.
|
||||
|
@ -350,8 +399,8 @@ impl Span {
|
|||
|
||||
/// Get the starting line/column in the source file for this span.
|
||||
///
|
||||
/// This method is semver exempt and not exposed by default.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
/// This method requires the `"span-locations"` feature to be enabled.
|
||||
#[cfg(span_locations)]
|
||||
pub fn start(&self) -> LineColumn {
|
||||
let imp::LineColumn { line, column } = self.inner.start();
|
||||
LineColumn {
|
||||
|
@ -362,8 +411,8 @@ impl Span {
|
|||
|
||||
/// Get the ending line/column in the source file for this span.
|
||||
///
|
||||
/// This method is semver exempt and not exposed by default.
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
/// This method requires the `"span-locations"` feature to be enabled.
|
||||
#[cfg(span_locations)]
|
||||
pub fn end(&self) -> LineColumn {
|
||||
let imp::LineColumn { line, column } = self.inner.end();
|
||||
LineColumn {
|
||||
|
@ -487,8 +536,7 @@ impl fmt::Debug for TokenTree {
|
|||
TokenTree::Ident(ref t) => {
|
||||
let mut debug = f.debug_struct("Ident");
|
||||
debug.field("sym", &format_args!("{}", t));
|
||||
#[cfg(any(feature = "nightly", procmacro2_semver_exempt))]
|
||||
debug.field("span", &t.span());
|
||||
imp::debug_span_field_if_nontrivial(&mut debug, t.span().inner);
|
||||
debug.finish()
|
||||
}
|
||||
TokenTree::Punct(ref t) => t.fmt(f),
|
||||
|
@ -527,12 +575,10 @@ pub enum Delimiter {
|
|||
|
||||
impl Group {
|
||||
fn _new(inner: imp::Group) -> Self {
|
||||
Group {
|
||||
inner: inner,
|
||||
}
|
||||
Group { inner: inner }
|
||||
}
|
||||
|
||||
fn _new_stable(inner: stable::Group) -> Self {
|
||||
fn _new_stable(inner: fallback::Group) -> Self {
|
||||
Group {
|
||||
inner: inner.into(),
|
||||
}
|
||||
|
@ -699,8 +745,7 @@ impl fmt::Debug for Punct {
|
|||
let mut debug = fmt.debug_struct("Punct");
|
||||
debug.field("op", &self.op);
|
||||
debug.field("spacing", &self.spacing);
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
debug.field("span", &self.span);
|
||||
imp::debug_span_field_if_nontrivial(&mut debug, self.span.inner);
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
@ -727,9 +772,7 @@ impl fmt::Debug for Punct {
|
|||
/// A span must be provided explicitly which governs the name resolution
|
||||
/// behavior of the resulting identifier.
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate proc_macro2;
|
||||
///
|
||||
/// ```edition2018
|
||||
/// use proc_macro2::{Ident, Span};
|
||||
///
|
||||
/// fn main() {
|
||||
|
@ -741,13 +784,9 @@ impl fmt::Debug for Punct {
|
|||
///
|
||||
/// An ident can be interpolated into a token stream using the `quote!` macro.
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use]
|
||||
/// extern crate quote;
|
||||
///
|
||||
/// extern crate proc_macro2;
|
||||
///
|
||||
/// ```edition2018
|
||||
/// use proc_macro2::{Ident, Span};
|
||||
/// use quote::quote;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let ident = Ident::new("demo", Span::call_site());
|
||||
|
@ -764,9 +803,7 @@ impl fmt::Debug for Punct {
|
|||
/// A string representation of the ident is available through the `to_string()`
|
||||
/// method.
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate proc_macro2;
|
||||
/// #
|
||||
/// ```edition2018
|
||||
/// # use proc_macro2::{Ident, Span};
|
||||
/// #
|
||||
/// # let ident = Ident::new("another_identifier", Span::call_site());
|
||||
|
@ -954,7 +991,7 @@ impl Literal {
|
|||
}
|
||||
}
|
||||
|
||||
fn _new_stable(inner: stable::Literal) -> Literal {
|
||||
fn _new_stable(inner: fallback::Literal) -> Literal {
|
||||
Literal {
|
||||
inner: inner.into(),
|
||||
_marker: marker::PhantomData,
|
||||
|
|
|
@ -4,23 +4,23 @@ use std::str::{Bytes, CharIndices, Chars};
|
|||
|
||||
use unicode_xid::UnicodeXID;
|
||||
|
||||
use stable::LexError;
|
||||
use fallback::LexError;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Cursor<'a> {
|
||||
pub rest: &'a str,
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub off: u32,
|
||||
}
|
||||
|
||||
impl<'a> Cursor<'a> {
|
||||
#[cfg(not(procmacro2_semver_exempt))]
|
||||
#[cfg(not(span_locations))]
|
||||
pub fn advance(&self, amt: usize) -> Cursor<'a> {
|
||||
Cursor {
|
||||
rest: &self.rest[amt..],
|
||||
}
|
||||
}
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
pub fn advance(&self, amt: usize) -> Cursor<'a> {
|
||||
Cursor {
|
||||
rest: &self.rest[amt..],
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#![cfg_attr(not(super_unstable), allow(dead_code))]
|
||||
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use std::panic::{self, PanicInfo};
|
||||
|
@ -7,20 +5,20 @@ use std::panic::{self, PanicInfo};
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use fallback;
|
||||
use proc_macro;
|
||||
use stable;
|
||||
|
||||
use {Delimiter, Punct, Spacing, TokenTree};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TokenStream {
|
||||
Nightly(proc_macro::TokenStream),
|
||||
Stable(stable::TokenStream),
|
||||
Compiler(proc_macro::TokenStream),
|
||||
Fallback(fallback::TokenStream),
|
||||
}
|
||||
|
||||
pub enum LexError {
|
||||
Nightly(proc_macro::LexError),
|
||||
Stable(stable::LexError),
|
||||
Compiler(proc_macro::LexError),
|
||||
Fallback(fallback::LexError),
|
||||
}
|
||||
|
||||
fn nightly_works() -> bool {
|
||||
|
@ -87,30 +85,30 @@ fn mismatch() -> ! {
|
|||
impl TokenStream {
|
||||
pub fn new() -> TokenStream {
|
||||
if nightly_works() {
|
||||
TokenStream::Nightly(proc_macro::TokenStream::new())
|
||||
TokenStream::Compiler(proc_macro::TokenStream::new())
|
||||
} else {
|
||||
TokenStream::Stable(stable::TokenStream::new())
|
||||
TokenStream::Fallback(fallback::TokenStream::new())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => tts.is_empty(),
|
||||
TokenStream::Stable(tts) => tts.is_empty(),
|
||||
TokenStream::Compiler(tts) => tts.is_empty(),
|
||||
TokenStream::Fallback(tts) => tts.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_nightly(self) -> proc_macro::TokenStream {
|
||||
match self {
|
||||
TokenStream::Nightly(s) => s,
|
||||
TokenStream::Stable(_) => mismatch(),
|
||||
TokenStream::Compiler(s) => s,
|
||||
TokenStream::Fallback(_) => mismatch(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_stable(self) -> stable::TokenStream {
|
||||
fn unwrap_stable(self) -> fallback::TokenStream {
|
||||
match self {
|
||||
TokenStream::Nightly(_) => mismatch(),
|
||||
TokenStream::Stable(s) => s,
|
||||
TokenStream::Compiler(_) => mismatch(),
|
||||
TokenStream::Fallback(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,9 +118,9 @@ impl FromStr for TokenStream {
|
|||
|
||||
fn from_str(src: &str) -> Result<TokenStream, LexError> {
|
||||
if nightly_works() {
|
||||
Ok(TokenStream::Nightly(src.parse()?))
|
||||
Ok(TokenStream::Compiler(src.parse()?))
|
||||
} else {
|
||||
Ok(TokenStream::Stable(src.parse()?))
|
||||
Ok(TokenStream::Fallback(src.parse()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -130,37 +128,37 @@ impl FromStr for TokenStream {
|
|||
impl fmt::Display for TokenStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => tts.fmt(f),
|
||||
TokenStream::Stable(tts) => tts.fmt(f),
|
||||
TokenStream::Compiler(tts) => tts.fmt(f),
|
||||
TokenStream::Fallback(tts) => tts.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proc_macro::TokenStream> for TokenStream {
|
||||
fn from(inner: proc_macro::TokenStream) -> TokenStream {
|
||||
TokenStream::Nightly(inner)
|
||||
TokenStream::Compiler(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenStream> for proc_macro::TokenStream {
|
||||
fn from(inner: TokenStream) -> proc_macro::TokenStream {
|
||||
match inner {
|
||||
TokenStream::Nightly(inner) => inner,
|
||||
TokenStream::Stable(inner) => inner.to_string().parse().unwrap(),
|
||||
TokenStream::Compiler(inner) => inner,
|
||||
TokenStream::Fallback(inner) => inner.to_string().parse().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stable::TokenStream> for TokenStream {
|
||||
fn from(inner: stable::TokenStream) -> TokenStream {
|
||||
TokenStream::Stable(inner)
|
||||
impl From<fallback::TokenStream> for TokenStream {
|
||||
fn from(inner: fallback::TokenStream) -> TokenStream {
|
||||
TokenStream::Fallback(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TokenTree> for TokenStream {
|
||||
fn from(token: TokenTree) -> TokenStream {
|
||||
if !nightly_works() {
|
||||
return TokenStream::Stable(token.into());
|
||||
return TokenStream::Fallback(token.into());
|
||||
}
|
||||
let tt: proc_macro::TokenTree = match token {
|
||||
TokenTree::Group(tt) => tt.inner.unwrap_nightly().into(),
|
||||
|
@ -176,7 +174,7 @@ impl From<TokenTree> for TokenStream {
|
|||
TokenTree::Ident(tt) => tt.inner.unwrap_nightly().into(),
|
||||
TokenTree::Literal(tt) => tt.inner.unwrap_nightly().into(),
|
||||
};
|
||||
TokenStream::Nightly(tt.into())
|
||||
TokenStream::Compiler(tt.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,12 +185,12 @@ impl iter::FromIterator<TokenTree> for TokenStream {
|
|||
.into_iter()
|
||||
.map(TokenStream::from)
|
||||
.flat_map(|t| match t {
|
||||
TokenStream::Nightly(s) => s,
|
||||
TokenStream::Stable(_) => mismatch(),
|
||||
TokenStream::Compiler(s) => s,
|
||||
TokenStream::Fallback(_) => mismatch(),
|
||||
});
|
||||
TokenStream::Nightly(trees.collect())
|
||||
TokenStream::Compiler(trees.collect())
|
||||
} else {
|
||||
TokenStream::Stable(trees.into_iter().collect())
|
||||
TokenStream::Fallback(trees.into_iter().collect())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,36 +200,31 @@ impl iter::FromIterator<TokenStream> for TokenStream {
|
|||
let mut streams = streams.into_iter();
|
||||
match streams.next() {
|
||||
#[cfg(slow_extend)]
|
||||
Some(TokenStream::Nightly(first)) => {
|
||||
let stream = iter::once(first).chain(streams.map(|s| {
|
||||
match s {
|
||||
TokenStream::Nightly(s) => s,
|
||||
TokenStream::Stable(_) => mismatch(),
|
||||
}
|
||||
})).collect();
|
||||
TokenStream::Nightly(stream)
|
||||
Some(TokenStream::Compiler(first)) => {
|
||||
let stream = iter::once(first)
|
||||
.chain(streams.map(|s| match s {
|
||||
TokenStream::Compiler(s) => s,
|
||||
TokenStream::Fallback(_) => mismatch(),
|
||||
}))
|
||||
.collect();
|
||||
TokenStream::Compiler(stream)
|
||||
}
|
||||
#[cfg(not(slow_extend))]
|
||||
Some(TokenStream::Nightly(mut first)) => {
|
||||
first.extend(streams.map(|s| {
|
||||
match s {
|
||||
TokenStream::Nightly(s) => s,
|
||||
TokenStream::Stable(_) => mismatch(),
|
||||
}
|
||||
Some(TokenStream::Compiler(mut first)) => {
|
||||
first.extend(streams.map(|s| match s {
|
||||
TokenStream::Compiler(s) => s,
|
||||
TokenStream::Fallback(_) => mismatch(),
|
||||
}));
|
||||
TokenStream::Nightly(first)
|
||||
TokenStream::Compiler(first)
|
||||
}
|
||||
Some(TokenStream::Stable(mut first)) => {
|
||||
first.extend(streams.map(|s| {
|
||||
match s {
|
||||
TokenStream::Stable(s) => s,
|
||||
TokenStream::Nightly(_) => mismatch(),
|
||||
}
|
||||
Some(TokenStream::Fallback(mut first)) => {
|
||||
first.extend(streams.map(|s| match s {
|
||||
TokenStream::Fallback(s) => s,
|
||||
TokenStream::Compiler(_) => mismatch(),
|
||||
}));
|
||||
TokenStream::Stable(first)
|
||||
TokenStream::Fallback(first)
|
||||
}
|
||||
None => TokenStream::new(),
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +232,7 @@ impl iter::FromIterator<TokenStream> for TokenStream {
|
|||
impl Extend<TokenTree> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, streams: I) {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => {
|
||||
TokenStream::Compiler(tts) => {
|
||||
#[cfg(not(slow_extend))]
|
||||
{
|
||||
tts.extend(
|
||||
|
@ -250,21 +243,19 @@ impl Extend<TokenTree> for TokenStream {
|
|||
}
|
||||
#[cfg(slow_extend)]
|
||||
{
|
||||
*tts = tts
|
||||
.clone()
|
||||
.into_iter()
|
||||
.chain(
|
||||
streams
|
||||
.into_iter()
|
||||
.map(TokenStream::from)
|
||||
.flat_map(|t| match t {
|
||||
TokenStream::Nightly(tts) => tts.into_iter(),
|
||||
*tts =
|
||||
tts.clone()
|
||||
.into_iter()
|
||||
.chain(streams.into_iter().map(TokenStream::from).flat_map(
|
||||
|t| match t {
|
||||
TokenStream::Compiler(tts) => tts.into_iter(),
|
||||
_ => mismatch(),
|
||||
}),
|
||||
).collect();
|
||||
},
|
||||
))
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
TokenStream::Stable(tts) => tts.extend(streams),
|
||||
TokenStream::Fallback(tts) => tts.extend(streams),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +263,7 @@ impl Extend<TokenTree> for TokenStream {
|
|||
impl Extend<TokenStream> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => {
|
||||
TokenStream::Compiler(tts) => {
|
||||
#[cfg(not(slow_extend))]
|
||||
{
|
||||
tts.extend(streams.into_iter().map(|stream| stream.unwrap_nightly()));
|
||||
|
@ -282,17 +273,14 @@ impl Extend<TokenStream> for TokenStream {
|
|||
*tts = tts
|
||||
.clone()
|
||||
.into_iter()
|
||||
.chain(
|
||||
streams
|
||||
.into_iter()
|
||||
.flat_map(|t| match t {
|
||||
TokenStream::Nightly(tts) => tts.into_iter(),
|
||||
_ => mismatch(),
|
||||
}),
|
||||
).collect();
|
||||
.chain(streams.into_iter().flat_map(|t| match t {
|
||||
TokenStream::Compiler(tts) => tts.into_iter(),
|
||||
_ => mismatch(),
|
||||
}))
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
TokenStream::Stable(tts) => {
|
||||
TokenStream::Fallback(tts) => {
|
||||
tts.extend(streams.into_iter().map(|stream| stream.unwrap_stable()))
|
||||
}
|
||||
}
|
||||
|
@ -302,36 +290,36 @@ impl Extend<TokenStream> for TokenStream {
|
|||
impl fmt::Debug for TokenStream {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => tts.fmt(f),
|
||||
TokenStream::Stable(tts) => tts.fmt(f),
|
||||
TokenStream::Compiler(tts) => tts.fmt(f),
|
||||
TokenStream::Fallback(tts) => tts.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proc_macro::LexError> for LexError {
|
||||
fn from(e: proc_macro::LexError) -> LexError {
|
||||
LexError::Nightly(e)
|
||||
LexError::Compiler(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stable::LexError> for LexError {
|
||||
fn from(e: stable::LexError) -> LexError {
|
||||
LexError::Stable(e)
|
||||
impl From<fallback::LexError> for LexError {
|
||||
fn from(e: fallback::LexError) -> LexError {
|
||||
LexError::Fallback(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for LexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
LexError::Nightly(e) => e.fmt(f),
|
||||
LexError::Stable(e) => e.fmt(f),
|
||||
LexError::Compiler(e) => e.fmt(f),
|
||||
LexError::Fallback(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TokenTreeIter {
|
||||
Nightly(proc_macro::token_stream::IntoIter),
|
||||
Stable(stable::TokenTreeIter),
|
||||
Compiler(proc_macro::token_stream::IntoIter),
|
||||
Fallback(fallback::TokenTreeIter),
|
||||
}
|
||||
|
||||
impl IntoIterator for TokenStream {
|
||||
|
@ -340,8 +328,8 @@ impl IntoIterator for TokenStream {
|
|||
|
||||
fn into_iter(self) -> TokenTreeIter {
|
||||
match self {
|
||||
TokenStream::Nightly(tts) => TokenTreeIter::Nightly(tts.into_iter()),
|
||||
TokenStream::Stable(tts) => TokenTreeIter::Stable(tts.into_iter()),
|
||||
TokenStream::Compiler(tts) => TokenTreeIter::Compiler(tts.into_iter()),
|
||||
TokenStream::Fallback(tts) => TokenTreeIter::Fallback(tts.into_iter()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,29 +339,29 @@ impl Iterator for TokenTreeIter {
|
|||
|
||||
fn next(&mut self) -> Option<TokenTree> {
|
||||
let token = match self {
|
||||
TokenTreeIter::Nightly(iter) => iter.next()?,
|
||||
TokenTreeIter::Stable(iter) => return iter.next(),
|
||||
TokenTreeIter::Compiler(iter) => iter.next()?,
|
||||
TokenTreeIter::Fallback(iter) => return iter.next(),
|
||||
};
|
||||
Some(match token {
|
||||
proc_macro::TokenTree::Group(tt) => ::Group::_new(Group::Nightly(tt)).into(),
|
||||
proc_macro::TokenTree::Group(tt) => ::Group::_new(Group::Compiler(tt)).into(),
|
||||
proc_macro::TokenTree::Punct(tt) => {
|
||||
let spacing = match tt.spacing() {
|
||||
proc_macro::Spacing::Joint => Spacing::Joint,
|
||||
proc_macro::Spacing::Alone => Spacing::Alone,
|
||||
};
|
||||
let mut o = Punct::new(tt.as_char(), spacing);
|
||||
o.set_span(::Span::_new(Span::Nightly(tt.span())));
|
||||
o.set_span(::Span::_new(Span::Compiler(tt.span())));
|
||||
o.into()
|
||||
}
|
||||
proc_macro::TokenTree::Ident(s) => ::Ident::_new(Ident::Nightly(s)).into(),
|
||||
proc_macro::TokenTree::Literal(l) => ::Literal::_new(Literal::Nightly(l)).into(),
|
||||
proc_macro::TokenTree::Ident(s) => ::Ident::_new(Ident::Compiler(s)).into(),
|
||||
proc_macro::TokenTree::Literal(l) => ::Literal::_new(Literal::Compiler(l)).into(),
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
TokenTreeIter::Nightly(tts) => tts.size_hint(),
|
||||
TokenTreeIter::Stable(tts) => tts.size_hint(),
|
||||
TokenTreeIter::Compiler(tts) => tts.size_hint(),
|
||||
TokenTreeIter::Fallback(tts) => tts.size_hint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -387,28 +375,28 @@ impl fmt::Debug for TokenTreeIter {
|
|||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg(super_unstable)]
|
||||
pub enum SourceFile {
|
||||
Nightly(proc_macro::SourceFile),
|
||||
Stable(stable::SourceFile),
|
||||
Compiler(proc_macro::SourceFile),
|
||||
Fallback(fallback::SourceFile),
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
impl SourceFile {
|
||||
fn nightly(sf: proc_macro::SourceFile) -> Self {
|
||||
SourceFile::Nightly(sf)
|
||||
SourceFile::Compiler(sf)
|
||||
}
|
||||
|
||||
/// Get the path to this source file as a string.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
match self {
|
||||
SourceFile::Nightly(a) => a.path(),
|
||||
SourceFile::Stable(a) => a.path(),
|
||||
SourceFile::Compiler(a) => a.path(),
|
||||
SourceFile::Fallback(a) => a.path(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_real(&self) -> bool {
|
||||
match self {
|
||||
SourceFile::Nightly(a) => a.is_real(),
|
||||
SourceFile::Stable(a) => a.is_real(),
|
||||
SourceFile::Compiler(a) => a.is_real(),
|
||||
SourceFile::Fallback(a) => a.is_real(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -417,12 +405,13 @@ impl SourceFile {
|
|||
impl fmt::Debug for SourceFile {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
SourceFile::Nightly(a) => a.fmt(f),
|
||||
SourceFile::Stable(a) => a.fmt(f),
|
||||
SourceFile::Compiler(a) => a.fmt(f),
|
||||
SourceFile::Fallback(a) => a.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(super_unstable, feature = "span-locations"))]
|
||||
pub struct LineColumn {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
|
@ -430,33 +419,33 @@ pub struct LineColumn {
|
|||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Span {
|
||||
Nightly(proc_macro::Span),
|
||||
Stable(stable::Span),
|
||||
Compiler(proc_macro::Span),
|
||||
Fallback(fallback::Span),
|
||||
}
|
||||
|
||||
impl Span {
|
||||
pub fn call_site() -> Span {
|
||||
if nightly_works() {
|
||||
Span::Nightly(proc_macro::Span::call_site())
|
||||
Span::Compiler(proc_macro::Span::call_site())
|
||||
} else {
|
||||
Span::Stable(stable::Span::call_site())
|
||||
Span::Fallback(fallback::Span::call_site())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub fn def_site() -> Span {
|
||||
if nightly_works() {
|
||||
Span::Nightly(proc_macro::Span::def_site())
|
||||
Span::Compiler(proc_macro::Span::def_site())
|
||||
} else {
|
||||
Span::Stable(stable::Span::def_site())
|
||||
Span::Fallback(fallback::Span::def_site())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub fn resolved_at(&self, other: Span) -> Span {
|
||||
match (self, other) {
|
||||
(Span::Nightly(a), Span::Nightly(b)) => Span::Nightly(a.resolved_at(b)),
|
||||
(Span::Stable(a), Span::Stable(b)) => Span::Stable(a.resolved_at(b)),
|
||||
(Span::Compiler(a), Span::Compiler(b)) => Span::Compiler(a.resolved_at(b)),
|
||||
(Span::Fallback(a), Span::Fallback(b)) => Span::Fallback(a.resolved_at(b)),
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
@ -464,50 +453,56 @@ impl Span {
|
|||
#[cfg(super_unstable)]
|
||||
pub fn located_at(&self, other: Span) -> Span {
|
||||
match (self, other) {
|
||||
(Span::Nightly(a), Span::Nightly(b)) => Span::Nightly(a.located_at(b)),
|
||||
(Span::Stable(a), Span::Stable(b)) => Span::Stable(a.located_at(b)),
|
||||
(Span::Compiler(a), Span::Compiler(b)) => Span::Compiler(a.located_at(b)),
|
||||
(Span::Fallback(a), Span::Fallback(b)) => Span::Fallback(a.located_at(b)),
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unstable(self) -> proc_macro::Span {
|
||||
pub fn unwrap(self) -> proc_macro::Span {
|
||||
match self {
|
||||
Span::Nightly(s) => s,
|
||||
Span::Stable(_) => mismatch(),
|
||||
Span::Compiler(s) => s,
|
||||
Span::Fallback(_) => panic!("proc_macro::Span is only available in procedural macros"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub fn source_file(&self) -> SourceFile {
|
||||
match self {
|
||||
Span::Nightly(s) => SourceFile::nightly(s.source_file()),
|
||||
Span::Stable(s) => SourceFile::Stable(s.source_file()),
|
||||
Span::Compiler(s) => SourceFile::nightly(s.source_file()),
|
||||
Span::Fallback(s) => SourceFile::Fallback(s.source_file()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
#[cfg(any(super_unstable, feature = "span-locations"))]
|
||||
pub fn start(&self) -> LineColumn {
|
||||
match self {
|
||||
Span::Nightly(s) => {
|
||||
#[cfg(nightly)]
|
||||
Span::Compiler(s) => {
|
||||
let proc_macro::LineColumn { line, column } = s.start();
|
||||
LineColumn { line, column }
|
||||
}
|
||||
Span::Stable(s) => {
|
||||
let stable::LineColumn { line, column } = s.start();
|
||||
#[cfg(not(nightly))]
|
||||
Span::Compiler(_) => LineColumn { line: 0, column: 0 },
|
||||
Span::Fallback(s) => {
|
||||
let fallback::LineColumn { line, column } = s.start();
|
||||
LineColumn { line, column }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
#[cfg(any(super_unstable, feature = "span-locations"))]
|
||||
pub fn end(&self) -> LineColumn {
|
||||
match self {
|
||||
Span::Nightly(s) => {
|
||||
#[cfg(nightly)]
|
||||
Span::Compiler(s) => {
|
||||
let proc_macro::LineColumn { line, column } = s.end();
|
||||
LineColumn { line, column }
|
||||
}
|
||||
Span::Stable(s) => {
|
||||
let stable::LineColumn { line, column } = s.end();
|
||||
#[cfg(not(nightly))]
|
||||
Span::Compiler(_) => LineColumn { line: 0, column: 0 },
|
||||
Span::Fallback(s) => {
|
||||
let fallback::LineColumn { line, column } = s.end();
|
||||
LineColumn { line, column }
|
||||
}
|
||||
}
|
||||
|
@ -516,8 +511,8 @@ impl Span {
|
|||
#[cfg(super_unstable)]
|
||||
pub fn join(&self, other: Span) -> Option<Span> {
|
||||
let ret = match (self, other) {
|
||||
(Span::Nightly(a), Span::Nightly(b)) => Span::Nightly(a.join(b)?),
|
||||
(Span::Stable(a), Span::Stable(b)) => Span::Stable(a.join(b)?),
|
||||
(Span::Compiler(a), Span::Compiler(b)) => Span::Compiler(a.join(b)?),
|
||||
(Span::Fallback(a), Span::Fallback(b)) => Span::Fallback(a.join(b)?),
|
||||
_ => return None,
|
||||
};
|
||||
Some(ret)
|
||||
|
@ -526,134 +521,143 @@ impl Span {
|
|||
#[cfg(super_unstable)]
|
||||
pub fn eq(&self, other: &Span) -> bool {
|
||||
match (self, other) {
|
||||
(Span::Nightly(a), Span::Nightly(b)) => a.eq(b),
|
||||
(Span::Stable(a), Span::Stable(b)) => a.eq(b),
|
||||
(Span::Compiler(a), Span::Compiler(b)) => a.eq(b),
|
||||
(Span::Fallback(a), Span::Fallback(b)) => a.eq(b),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_nightly(self) -> proc_macro::Span {
|
||||
match self {
|
||||
Span::Nightly(s) => s,
|
||||
Span::Stable(_) => mismatch(),
|
||||
Span::Compiler(s) => s,
|
||||
Span::Fallback(_) => mismatch(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<proc_macro::Span> for ::Span {
|
||||
fn from(proc_span: proc_macro::Span) -> ::Span {
|
||||
::Span::_new(Span::Nightly(proc_span))
|
||||
::Span::_new(Span::Compiler(proc_span))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stable::Span> for Span {
|
||||
fn from(inner: stable::Span) -> Span {
|
||||
Span::Stable(inner)
|
||||
impl From<fallback::Span> for Span {
|
||||
fn from(inner: fallback::Span) -> Span {
|
||||
Span::Fallback(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Span::Nightly(s) => s.fmt(f),
|
||||
Span::Stable(s) => s.fmt(f),
|
||||
Span::Compiler(s) => s.fmt(f),
|
||||
Span::Fallback(s) => s.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn debug_span_field_if_nontrivial(debug: &mut fmt::DebugStruct, span: Span) {
|
||||
match span {
|
||||
Span::Compiler(s) => {
|
||||
debug.field("span", &s);
|
||||
}
|
||||
Span::Fallback(s) => fallback::debug_span_field_if_nontrivial(debug, s),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Group {
|
||||
Nightly(proc_macro::Group),
|
||||
Stable(stable::Group),
|
||||
Compiler(proc_macro::Group),
|
||||
Fallback(fallback::Group),
|
||||
}
|
||||
|
||||
impl Group {
|
||||
pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group {
|
||||
match stream {
|
||||
TokenStream::Nightly(stream) => {
|
||||
TokenStream::Compiler(stream) => {
|
||||
let delimiter = match delimiter {
|
||||
Delimiter::Parenthesis => proc_macro::Delimiter::Parenthesis,
|
||||
Delimiter::Bracket => proc_macro::Delimiter::Bracket,
|
||||
Delimiter::Brace => proc_macro::Delimiter::Brace,
|
||||
Delimiter::None => proc_macro::Delimiter::None,
|
||||
};
|
||||
Group::Nightly(proc_macro::Group::new(delimiter, stream))
|
||||
Group::Compiler(proc_macro::Group::new(delimiter, stream))
|
||||
}
|
||||
TokenStream::Stable(stream) => {
|
||||
Group::Stable(stable::Group::new(delimiter, stream))
|
||||
TokenStream::Fallback(stream) => {
|
||||
Group::Fallback(fallback::Group::new(delimiter, stream))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delimiter(&self) -> Delimiter {
|
||||
match self {
|
||||
Group::Nightly(g) => match g.delimiter() {
|
||||
Group::Compiler(g) => match g.delimiter() {
|
||||
proc_macro::Delimiter::Parenthesis => Delimiter::Parenthesis,
|
||||
proc_macro::Delimiter::Bracket => Delimiter::Bracket,
|
||||
proc_macro::Delimiter::Brace => Delimiter::Brace,
|
||||
proc_macro::Delimiter::None => Delimiter::None,
|
||||
}
|
||||
Group::Stable(g) => g.delimiter(),
|
||||
},
|
||||
Group::Fallback(g) => g.delimiter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stream(&self) -> TokenStream {
|
||||
match self {
|
||||
Group::Nightly(g) => TokenStream::Nightly(g.stream()),
|
||||
Group::Stable(g) => TokenStream::Stable(g.stream()),
|
||||
Group::Compiler(g) => TokenStream::Compiler(g.stream()),
|
||||
Group::Fallback(g) => TokenStream::Fallback(g.stream()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Group::Nightly(g) => Span::Nightly(g.span()),
|
||||
Group::Stable(g) => Span::Stable(g.span()),
|
||||
Group::Compiler(g) => Span::Compiler(g.span()),
|
||||
Group::Fallback(g) => Span::Fallback(g.span()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub fn span_open(&self) -> Span {
|
||||
match self {
|
||||
Group::Nightly(g) => Span::Nightly(g.span_open()),
|
||||
Group::Stable(g) => Span::Stable(g.span_open()),
|
||||
Group::Compiler(g) => Span::Compiler(g.span_open()),
|
||||
Group::Fallback(g) => Span::Fallback(g.span_open()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(super_unstable)]
|
||||
pub fn span_close(&self) -> Span {
|
||||
match self {
|
||||
Group::Nightly(g) => Span::Nightly(g.span_close()),
|
||||
Group::Stable(g) => Span::Stable(g.span_close()),
|
||||
Group::Compiler(g) => Span::Compiler(g.span_close()),
|
||||
Group::Fallback(g) => Span::Fallback(g.span_close()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
match (self, span) {
|
||||
(Group::Nightly(g), Span::Nightly(s)) => g.set_span(s),
|
||||
(Group::Stable(g), Span::Stable(s)) => g.set_span(s),
|
||||
(Group::Compiler(g), Span::Compiler(s)) => g.set_span(s),
|
||||
(Group::Fallback(g), Span::Fallback(s)) => g.set_span(s),
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_nightly(self) -> proc_macro::Group {
|
||||
match self {
|
||||
Group::Nightly(g) => g,
|
||||
Group::Stable(_) => mismatch(),
|
||||
Group::Compiler(g) => g,
|
||||
Group::Fallback(_) => mismatch(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stable::Group> for Group {
|
||||
fn from(g: stable::Group) -> Self {
|
||||
Group::Stable(g)
|
||||
impl From<fallback::Group> for Group {
|
||||
fn from(g: fallback::Group) -> Self {
|
||||
Group::Fallback(g)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Group {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Group::Nightly(group) => group.fmt(formatter),
|
||||
Group::Stable(group) => group.fmt(formatter),
|
||||
Group::Compiler(group) => group.fmt(formatter),
|
||||
Group::Fallback(group) => group.fmt(formatter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -661,29 +665,29 @@ impl fmt::Display for Group {
|
|||
impl fmt::Debug for Group {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Group::Nightly(group) => group.fmt(formatter),
|
||||
Group::Stable(group) => group.fmt(formatter),
|
||||
Group::Compiler(group) => group.fmt(formatter),
|
||||
Group::Fallback(group) => group.fmt(formatter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Ident {
|
||||
Nightly(proc_macro::Ident),
|
||||
Stable(stable::Ident),
|
||||
Compiler(proc_macro::Ident),
|
||||
Fallback(fallback::Ident),
|
||||
}
|
||||
|
||||
impl Ident {
|
||||
pub fn new(string: &str, span: Span) -> Ident {
|
||||
match span {
|
||||
Span::Nightly(s) => Ident::Nightly(proc_macro::Ident::new(string, s)),
|
||||
Span::Stable(s) => Ident::Stable(stable::Ident::new(string, s)),
|
||||
Span::Compiler(s) => Ident::Compiler(proc_macro::Ident::new(string, s)),
|
||||
Span::Fallback(s) => Ident::Fallback(fallback::Ident::new(string, s)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_raw(string: &str, span: Span) -> Ident {
|
||||
match span {
|
||||
Span::Nightly(s) => {
|
||||
Span::Compiler(s) => {
|
||||
let p: proc_macro::TokenStream = string.parse().unwrap();
|
||||
let ident = match p.into_iter().next() {
|
||||
Some(proc_macro::TokenTree::Ident(mut i)) => {
|
||||
|
@ -692,31 +696,31 @@ impl Ident {
|
|||
}
|
||||
_ => panic!(),
|
||||
};
|
||||
Ident::Nightly(ident)
|
||||
Ident::Compiler(ident)
|
||||
}
|
||||
Span::Stable(s) => Ident::Stable(stable::Ident::new_raw(string, s)),
|
||||
Span::Fallback(s) => Ident::Fallback(fallback::Ident::new_raw(string, s)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Ident::Nightly(t) => Span::Nightly(t.span()),
|
||||
Ident::Stable(t) => Span::Stable(t.span()),
|
||||
Ident::Compiler(t) => Span::Compiler(t.span()),
|
||||
Ident::Fallback(t) => Span::Fallback(t.span()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
match (self, span) {
|
||||
(Ident::Nightly(t), Span::Nightly(s)) => t.set_span(s),
|
||||
(Ident::Stable(t), Span::Stable(s)) => t.set_span(s),
|
||||
(Ident::Compiler(t), Span::Compiler(s)) => t.set_span(s),
|
||||
(Ident::Fallback(t), Span::Fallback(s)) => t.set_span(s),
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_nightly(self) -> proc_macro::Ident {
|
||||
match self {
|
||||
Ident::Nightly(s) => s,
|
||||
Ident::Stable(_) => mismatch(),
|
||||
Ident::Compiler(s) => s,
|
||||
Ident::Fallback(_) => mismatch(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -724,8 +728,8 @@ impl Ident {
|
|||
impl PartialEq for Ident {
|
||||
fn eq(&self, other: &Ident) -> bool {
|
||||
match (self, other) {
|
||||
(Ident::Nightly(t), Ident::Nightly(o)) => t.to_string() == o.to_string(),
|
||||
(Ident::Stable(t), Ident::Stable(o)) => t == o,
|
||||
(Ident::Compiler(t), Ident::Compiler(o)) => t.to_string() == o.to_string(),
|
||||
(Ident::Fallback(t), Ident::Fallback(o)) => t == o,
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
@ -738,8 +742,8 @@ where
|
|||
fn eq(&self, other: &T) -> bool {
|
||||
let other = other.as_ref();
|
||||
match self {
|
||||
Ident::Nightly(t) => t.to_string() == other,
|
||||
Ident::Stable(t) => t == other,
|
||||
Ident::Compiler(t) => t.to_string() == other,
|
||||
Ident::Fallback(t) => t == other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -747,8 +751,8 @@ where
|
|||
impl fmt::Display for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Ident::Nightly(t) => t.fmt(f),
|
||||
Ident::Stable(t) => t.fmt(f),
|
||||
Ident::Compiler(t) => t.fmt(f),
|
||||
Ident::Fallback(t) => t.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -756,25 +760,25 @@ impl fmt::Display for Ident {
|
|||
impl fmt::Debug for Ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Ident::Nightly(t) => t.fmt(f),
|
||||
Ident::Stable(t) => t.fmt(f),
|
||||
Ident::Compiler(t) => t.fmt(f),
|
||||
Ident::Fallback(t) => t.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Literal {
|
||||
Nightly(proc_macro::Literal),
|
||||
Stable(stable::Literal),
|
||||
Compiler(proc_macro::Literal),
|
||||
Fallback(fallback::Literal),
|
||||
}
|
||||
|
||||
macro_rules! suffixed_numbers {
|
||||
($($name:ident => $kind:ident,)*) => ($(
|
||||
pub fn $name(n: $kind) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::$name(n))
|
||||
Literal::Compiler(proc_macro::Literal::$name(n))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::$name(n))
|
||||
Literal::Fallback(fallback::Literal::$name(n))
|
||||
}
|
||||
}
|
||||
)*)
|
||||
|
@ -784,9 +788,9 @@ macro_rules! unsuffixed_integers {
|
|||
($($name:ident => $kind:ident,)*) => ($(
|
||||
pub fn $name(n: $kind) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::$name(n))
|
||||
Literal::Compiler(proc_macro::Literal::$name(n))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::$name(n))
|
||||
Literal::Fallback(fallback::Literal::$name(n))
|
||||
}
|
||||
}
|
||||
)*)
|
||||
|
@ -836,78 +840,78 @@ impl Literal {
|
|||
|
||||
pub fn f32_unsuffixed(f: f32) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::f32_unsuffixed(f))
|
||||
Literal::Compiler(proc_macro::Literal::f32_unsuffixed(f))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::f32_unsuffixed(f))
|
||||
Literal::Fallback(fallback::Literal::f32_unsuffixed(f))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn f64_unsuffixed(f: f64) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::f64_unsuffixed(f))
|
||||
Literal::Compiler(proc_macro::Literal::f64_unsuffixed(f))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::f64_unsuffixed(f))
|
||||
Literal::Fallback(fallback::Literal::f64_unsuffixed(f))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn string(t: &str) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::string(t))
|
||||
Literal::Compiler(proc_macro::Literal::string(t))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::string(t))
|
||||
Literal::Fallback(fallback::Literal::string(t))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn character(t: char) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::character(t))
|
||||
Literal::Compiler(proc_macro::Literal::character(t))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::character(t))
|
||||
Literal::Fallback(fallback::Literal::character(t))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn byte_string(bytes: &[u8]) -> Literal {
|
||||
if nightly_works() {
|
||||
Literal::Nightly(proc_macro::Literal::byte_string(bytes))
|
||||
Literal::Compiler(proc_macro::Literal::byte_string(bytes))
|
||||
} else {
|
||||
Literal::Stable(stable::Literal::byte_string(bytes))
|
||||
Literal::Fallback(fallback::Literal::byte_string(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Literal::Nightly(lit) => Span::Nightly(lit.span()),
|
||||
Literal::Stable(lit) => Span::Stable(lit.span()),
|
||||
Literal::Compiler(lit) => Span::Compiler(lit.span()),
|
||||
Literal::Fallback(lit) => Span::Fallback(lit.span()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_span(&mut self, span: Span) {
|
||||
match (self, span) {
|
||||
(Literal::Nightly(lit), Span::Nightly(s)) => lit.set_span(s),
|
||||
(Literal::Stable(lit), Span::Stable(s)) => lit.set_span(s),
|
||||
(Literal::Compiler(lit), Span::Compiler(s)) => lit.set_span(s),
|
||||
(Literal::Fallback(lit), Span::Fallback(s)) => lit.set_span(s),
|
||||
_ => mismatch(),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_nightly(self) -> proc_macro::Literal {
|
||||
match self {
|
||||
Literal::Nightly(s) => s,
|
||||
Literal::Stable(_) => mismatch(),
|
||||
Literal::Compiler(s) => s,
|
||||
Literal::Fallback(_) => mismatch(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<stable::Literal> for Literal {
|
||||
fn from(s: stable::Literal) -> Literal {
|
||||
Literal::Stable(s)
|
||||
impl From<fallback::Literal> for Literal {
|
||||
fn from(s: fallback::Literal) -> Literal {
|
||||
Literal::Fallback(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Literal::Nightly(t) => t.fmt(f),
|
||||
Literal::Stable(t) => t.fmt(f),
|
||||
Literal::Compiler(t) => t.fmt(f),
|
||||
Literal::Fallback(t) => t.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -915,8 +919,8 @@ impl fmt::Display for Literal {
|
|||
impl fmt::Debug for Literal {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Literal::Nightly(t) => t.fmt(f),
|
||||
Literal::Stable(t) => t.fmt(f),
|
||||
Literal::Compiler(t) => t.fmt(f),
|
||||
Literal::Fallback(t) => t.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -137,7 +137,7 @@ fn fail() {
|
|||
fail("r#_");
|
||||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(span_locations)]
|
||||
#[test]
|
||||
fn span_test() {
|
||||
use proc_macro2::TokenTree;
|
||||
|
@ -193,7 +193,7 @@ testing 123
|
|||
}
|
||||
|
||||
#[cfg(procmacro2_semver_exempt)]
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[cfg(not(nightly))]
|
||||
#[test]
|
||||
fn default_span() {
|
||||
let start = Span::call_site().start();
|
||||
|
@ -329,7 +329,6 @@ fn test_debug_ident() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
fn test_debug_tokenstream() {
|
||||
let tts = TokenStream::from_str("[a + 1]").unwrap();
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"44cf9d3a28be1b21f4247572b6ca4d38dc3fd42fa84c4a4e0e5632aa27bee083","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"b43ef9b9c61628f8de7036271e61322cba23d878d056748e571f4f6cf9fba1b1","src/ext.rs":"a2def0b0f24c822b3f936a781c347e5f6fdc75120f85874c94f5e7eb708168c2","src/lib.rs":"f1ba768690c57252e8135ee474a20bdd513fd0bd0664e9e9b697800163f39d08","src/to_tokens.rs":"86c419a72017846ef33a0acc53caee7312c750c90b3f1d3b58e33f20efcb94f4","tests/conditional/integer128.rs":"d83e21a91efbaa801a82ae499111bdda2d31edaa620e78c0199eba42d69c9ee6","tests/test.rs":"810013d7fd77b738abd0ace90ce2f2f3e219c757652eabab29bc1c0ce4a73b24"},"package":"53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"}
|
||||
{"files":{"Cargo.toml":"68f4dc89836a05a2347086addab1849567ef8073c552ec0dfca8f96fd20550f9","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"c9a75f18b9ab2927829a208fc6aa2cf4e63b8420887ba29cdb265d6619ae82d5","README.md":"d9392d4c7af3bf9714f0a95801d64de46ffd4558cdfeea0eb85b414e555abb72","src/ext.rs":"03919239a20f8393288783a21bf6fdee12e405d13d162c9faa6f8f5ce54b003b","src/lib.rs":"5345b4d2e6f923724cec35c62d7397e6f04d5503d2d813bff7bbaa7ffc39a9cf","src/to_tokens.rs":"0dcd15cba2aa83abeb47b9a1babce7a29643b5efa2fe620b070cb37bb21a84f1","tests/conditional/integer128.rs":"d83e21a91efbaa801a82ae499111bdda2d31edaa620e78c0199eba42d69c9ee6","tests/test.rs":"810013d7fd77b738abd0ace90ce2f2f3e219c757652eabab29bc1c0ce4a73b24"},"package":"cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"}
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
[package]
|
||||
name = "quote"
|
||||
version = "0.6.10"
|
||||
version = "0.6.11"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
include = ["Cargo.toml", "src/**/*.rs", "tests/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
description = "Quasi-quoting macro quote!(...)"
|
||||
|
|
|
@ -38,18 +38,11 @@ first support for procedural macros in Rust 1.15.0.*
|
|||
quote = "0.6"
|
||||
```
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate quote;
|
||||
```
|
||||
|
||||
## Syntax
|
||||
|
||||
The quote crate provides a [`quote!`] macro within which you can write Rust code
|
||||
that gets packaged into a [`TokenStream`] and can be treated as data. You should
|
||||
think of `TokenStream` as representing a fragment of Rust source code. This type
|
||||
can be returned directly back to the compiler by a procedural macro to get
|
||||
compiled into the caller's crate.
|
||||
think of `TokenStream` as representing a fragment of Rust source code.
|
||||
|
||||
[`TokenStream`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.TokenStream.html
|
||||
|
||||
|
@ -64,7 +57,7 @@ most Rust primitive types as well as most of the syntax tree types from [`syn`].
|
|||
let tokens = quote! {
|
||||
struct SerializeWith #generics #where_clause {
|
||||
value: &'a #field_ty,
|
||||
phantom: ::std::marker::PhantomData<#item_ty>,
|
||||
phantom: core::marker::PhantomData<#item_ty>,
|
||||
}
|
||||
|
||||
impl #generics serde::Serialize for SerializeWith #generics #where_clause {
|
||||
|
@ -78,7 +71,7 @@ let tokens = quote! {
|
|||
|
||||
SerializeWith {
|
||||
value: #value,
|
||||
phantom: ::std::marker::PhantomData::<#item_ty>,
|
||||
phantom: core::marker::PhantomData::<#item_ty>,
|
||||
}
|
||||
};
|
||||
```
|
||||
|
@ -100,6 +93,107 @@ Note that there is a difference between `#(#var ,)*` and `#(#var),*`—the latte
|
|||
does not produce a trailing comma. This matches the behavior of delimiters in
|
||||
`macro_rules!`.
|
||||
|
||||
## Returning tokens to the compiler
|
||||
|
||||
The `quote!` macro evaluates to an expression of type
|
||||
`proc_macro2::TokenStream`. Meanwhile Rust procedural macros are expected to
|
||||
return the type `proc_macro::TokenStream`.
|
||||
|
||||
The difference between the two types is that `proc_macro` types are entirely
|
||||
specific to procedural macros and cannot ever exist in code outside of a
|
||||
procedural macro, while `proc_macro2` types may exist anywhere including tests
|
||||
and non-macro code like main.rs and build.rs. This is why even the procedural
|
||||
macro ecosystem is largely built around `proc_macro2`, because that ensures the
|
||||
libraries are unit testable and accessible in non-macro contexts.
|
||||
|
||||
There is a [`From`]-conversion in both directions so returning the output of
|
||||
`quote!` from a procedural macro usually looks like `tokens.into()` or
|
||||
`proc_macro::TokenStream::from(tokens)`.
|
||||
|
||||
[`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
|
||||
|
||||
## Examples
|
||||
|
||||
### Combining quoted fragments
|
||||
|
||||
Usually you don't end up constructing an entire final `TokenStream` in one
|
||||
piece. Different parts may come from different helper functions. The tokens
|
||||
produced by `quote!` themselves implement `ToTokens` and so can be interpolated
|
||||
into later `quote!` invocations to build up a final result.
|
||||
|
||||
```rust
|
||||
let type_definition = quote! {...};
|
||||
let methods = quote! {...};
|
||||
|
||||
let tokens = quote! {
|
||||
#type_definition
|
||||
#methods
|
||||
};
|
||||
```
|
||||
|
||||
### Constructing identifiers
|
||||
|
||||
Suppose we have an identifier `ident` which came from somewhere in a macro
|
||||
input and we need to modify it in some way for the macro output. Let's consider
|
||||
prepending the identifier with an underscore.
|
||||
|
||||
Simply interpolating the identifier next to an underscore will not have the
|
||||
behavior of concatenating them. The underscore and the identifier will continue
|
||||
to be two separate tokens as if you had written `_ x`.
|
||||
|
||||
```rust
|
||||
// incorrect
|
||||
quote! {
|
||||
let mut _#ident = 0;
|
||||
}
|
||||
```
|
||||
|
||||
The solution is to perform token-level manipulations using the APIs provided by
|
||||
Syn and proc-macro2.
|
||||
|
||||
```rust
|
||||
let concatenated = format!("_{}", ident);
|
||||
let varname = syn::Ident::new(&concatenated, ident.span());
|
||||
quote! {
|
||||
let mut #varname = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### Making method calls
|
||||
|
||||
Let's say our macro requires some type specified in the macro input to have a
|
||||
constructor called `new`. We have the type in a variable called `field_type` of
|
||||
type `syn::Type` and want to invoke the constructor.
|
||||
|
||||
```rust
|
||||
// incorrect
|
||||
quote! {
|
||||
let value = #field_type::new();
|
||||
}
|
||||
```
|
||||
|
||||
This works only sometimes. If `field_type` is `String`, the expanded code
|
||||
contains `String::new()` which is fine. But if `field_type` is something like
|
||||
`Vec<i32>` then the expanded code is `Vec<i32>::new()` which is invalid syntax.
|
||||
Ordinarily in handwritten Rust we would write `Vec::<i32>::new()` but for macros
|
||||
often the following is more convenient.
|
||||
|
||||
```rust
|
||||
quote! {
|
||||
let value = <#field_type>::new();
|
||||
}
|
||||
```
|
||||
|
||||
This expands to `<Vec<i32>>::new()` which behaves correctly.
|
||||
|
||||
A similar pattern is appropriate for trait methods.
|
||||
|
||||
```rust
|
||||
quote! {
|
||||
let value = <#field_type as core::default::Default>::default();
|
||||
}
|
||||
```
|
||||
|
||||
## Hygiene
|
||||
|
||||
Any interpolated tokens preserve the `Span` information provided by their
|
||||
|
|
|
@ -8,47 +8,19 @@ use proc_macro2::{TokenStream, TokenTree};
|
|||
///
|
||||
/// This trait is sealed and cannot be implemented outside of the `quote` crate.
|
||||
pub trait TokenStreamExt: private::Sealed {
|
||||
fn append<U>(&mut self, token: U)
|
||||
where
|
||||
U: Into<TokenTree>;
|
||||
|
||||
fn append_all<T, I>(&mut self, iter: I)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>;
|
||||
|
||||
fn append_separated<T, I, U>(&mut self, iter: I, op: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>,
|
||||
U: ToTokens;
|
||||
|
||||
fn append_terminated<T, I, U>(&mut self, iter: I, term: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>,
|
||||
U: ToTokens;
|
||||
}
|
||||
|
||||
impl TokenStreamExt for TokenStream {
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// Appends the token specified to this list of tokens.
|
||||
fn append<U>(&mut self, token: U)
|
||||
where
|
||||
U: Into<TokenTree>,
|
||||
{
|
||||
self.extend(iter::once(token.into()));
|
||||
}
|
||||
U: Into<TokenTree>;
|
||||
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use] extern crate quote;
|
||||
/// # extern crate proc_macro2;
|
||||
/// # use quote::{TokenStreamExt, ToTokens};
|
||||
/// ```edition2018
|
||||
/// # use quote::{quote, TokenStreamExt, ToTokens};
|
||||
/// # use proc_macro2::TokenStream;
|
||||
/// # fn main() {
|
||||
/// #
|
||||
/// struct X;
|
||||
///
|
||||
/// impl ToTokens for X {
|
||||
|
@ -59,8 +31,41 @@ impl TokenStreamExt for TokenStream {
|
|||
///
|
||||
/// let tokens = quote!(#X);
|
||||
/// assert_eq!(tokens.to_string(), "true false");
|
||||
/// # }
|
||||
/// ```
|
||||
fn append_all<T, I>(&mut self, iter: I)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>;
|
||||
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// Appends all of the items in the iterator `I`, separated by the tokens
|
||||
/// `U`.
|
||||
fn append_separated<T, I, U>(&mut self, iter: I, op: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>,
|
||||
U: ToTokens;
|
||||
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// Appends all tokens in the iterator `I`, appending `U` after each
|
||||
/// element, including after the last element of the iterator.
|
||||
fn append_terminated<T, I, U>(&mut self, iter: I, term: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
I: IntoIterator<Item = T>,
|
||||
U: ToTokens;
|
||||
}
|
||||
|
||||
impl TokenStreamExt for TokenStream {
|
||||
fn append<U>(&mut self, token: U)
|
||||
where
|
||||
U: Into<TokenTree>,
|
||||
{
|
||||
self.extend(iter::once(token.into()));
|
||||
}
|
||||
|
||||
fn append_all<T, I>(&mut self, iter: I)
|
||||
where
|
||||
T: ToTokens,
|
||||
|
@ -71,10 +76,6 @@ impl TokenStreamExt for TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// Appends all of the items in the iterator `I`, separated by the tokens
|
||||
/// `U`.
|
||||
fn append_separated<T, I, U>(&mut self, iter: I, op: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
|
@ -89,10 +90,6 @@ impl TokenStreamExt for TokenStream {
|
|||
}
|
||||
}
|
||||
|
||||
/// For use by `ToTokens` implementations.
|
||||
///
|
||||
/// Appends all tokens in the iterator `I`, appending `U` after each
|
||||
/// element, including after the last element of the iterator.
|
||||
fn append_terminated<T, I, U>(&mut self, iter: I, term: U)
|
||||
where
|
||||
T: ToTokens,
|
||||
|
|
|
@ -29,13 +29,6 @@
|
|||
//! quote = "0.6"
|
||||
//! ```
|
||||
//!
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate quote;
|
||||
//! #
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! The following quasi-quoted block of code is something you might find in [a]
|
||||
|
@ -48,22 +41,20 @@
|
|||
//! [a]: https://serde.rs/
|
||||
//! [`quote_spanned!`]: macro.quote_spanned.html
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use]
|
||||
//! # extern crate quote;
|
||||
//! ```edition2018
|
||||
//! # use quote::quote;
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! # let generics = "";
|
||||
//! # let where_clause = "";
|
||||
//! # let field_ty = "";
|
||||
//! # let item_ty = "";
|
||||
//! # let path = "";
|
||||
//! # let value = "";
|
||||
//! # let generics = "";
|
||||
//! # let where_clause = "";
|
||||
//! # let field_ty = "";
|
||||
//! # let item_ty = "";
|
||||
//! # let path = "";
|
||||
//! # let value = "";
|
||||
//! #
|
||||
//! let tokens = quote! {
|
||||
//! struct SerializeWith #generics #where_clause {
|
||||
//! value: &'a #field_ty,
|
||||
//! phantom: ::std::marker::PhantomData<#item_ty>,
|
||||
//! phantom: core::marker::PhantomData<#item_ty>,
|
||||
//! }
|
||||
//!
|
||||
//! impl #generics serde::Serialize for SerializeWith #generics #where_clause {
|
||||
|
@ -77,14 +68,12 @@
|
|||
//!
|
||||
//! SerializeWith {
|
||||
//! value: #value,
|
||||
//! phantom: ::std::marker::PhantomData::<#item_ty>,
|
||||
//! phantom: core::marker::PhantomData::<#item_ty>,
|
||||
//! }
|
||||
//! };
|
||||
//! #
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Recursion limit
|
||||
//! # Recursion limit
|
||||
//!
|
||||
//! The `quote!` macro relies on deep recursion so some large invocations may
|
||||
//! fail with "recursion limit reached" when you compile. If it fails, bump up
|
||||
|
@ -92,7 +81,7 @@
|
|||
//! An even higher limit may be necessary for especially large invocations.
|
||||
|
||||
// Quote types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/quote/0.6.10")]
|
||||
#![doc(html_root_url = "https://docs.rs/quote/0.6.11")]
|
||||
|
||||
#[cfg(all(
|
||||
not(all(target_arch = "wasm32", target_os = "unknown")),
|
||||
|
@ -277,17 +266,43 @@ pub mod __rt {
|
|||
///
|
||||
/// [`quote_spanned!`]: macro.quote_spanned.html
|
||||
///
|
||||
/// # Example
|
||||
/// # Return type
|
||||
///
|
||||
/// ```
|
||||
/// The macro evaluates to an expression of type `proc_macro2::TokenStream`.
|
||||
/// Meanwhile Rust procedural macros are expected to return the type
|
||||
/// `proc_macro::TokenStream`.
|
||||
///
|
||||
/// The difference between the two types is that `proc_macro` types are entirely
|
||||
/// specific to procedural macros and cannot ever exist in code outside of a
|
||||
/// procedural macro, while `proc_macro2` types may exist anywhere including
|
||||
/// tests and non-macro code like main.rs and build.rs. This is why even the
|
||||
/// procedural macro ecosystem is largely built around `proc_macro2`, because
|
||||
/// that ensures the libraries are unit testable and accessible in non-macro
|
||||
/// contexts.
|
||||
///
|
||||
/// There is a [`From`]-conversion in both directions so returning the output of
|
||||
/// `quote!` from a procedural macro usually looks like `tokens.into()` or
|
||||
/// `proc_macro::TokenStream::from(tokens)`.
|
||||
///
|
||||
/// [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ## Procedural macro
|
||||
///
|
||||
/// The structure of a basic procedural macro is as follows. Refer to the [Syn]
|
||||
/// crate for further useful guidance on using `quote!` as part of a procedural
|
||||
/// macro.
|
||||
///
|
||||
/// [Syn]: https://github.com/dtolnay/syn
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # #[cfg(any())]
|
||||
/// extern crate proc_macro;
|
||||
/// # extern crate proc_macro2 as proc_macro;
|
||||
///
|
||||
/// #[macro_use]
|
||||
/// extern crate quote;
|
||||
/// # use proc_macro2 as proc_macro;
|
||||
///
|
||||
/// use proc_macro::TokenStream;
|
||||
/// use quote::quote;
|
||||
///
|
||||
/// # const IGNORE_TOKENS: &'static str = stringify! {
|
||||
/// #[proc_macro_derive(HeapSize)]
|
||||
|
@ -304,7 +319,7 @@ pub mod __rt {
|
|||
///
|
||||
/// let expanded = quote! {
|
||||
/// // The generated impl.
|
||||
/// impl ::heapsize::HeapSize for #name {
|
||||
/// impl heapsize::HeapSize for #name {
|
||||
/// fn heap_size_of_children(&self) -> usize {
|
||||
/// #expr
|
||||
/// }
|
||||
|
@ -312,10 +327,117 @@ pub mod __rt {
|
|||
/// };
|
||||
///
|
||||
/// // Hand the output tokens back to the compiler.
|
||||
/// expanded.into()
|
||||
/// TokenStream::from(expanded)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Combining quoted fragments
|
||||
///
|
||||
/// Usually you don't end up constructing an entire final `TokenStream` in one
|
||||
/// piece. Different parts may come from different helper functions. The tokens
|
||||
/// produced by `quote!` themselves implement `ToTokens` and so can be
|
||||
/// interpolated into later `quote!` invocations to build up a final result.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// let type_definition = quote! {...};
|
||||
/// let methods = quote! {...};
|
||||
///
|
||||
/// let tokens = quote! {
|
||||
/// #type_definition
|
||||
/// #methods
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// ## Constructing identifiers
|
||||
///
|
||||
/// Suppose we have an identifier `ident` which came from somewhere in a macro
|
||||
/// input and we need to modify it in some way for the macro output. Let's
|
||||
/// consider prepending the identifier with an underscore.
|
||||
///
|
||||
/// Simply interpolating the identifier next to an underscore will not have the
|
||||
/// behavior of concatenating them. The underscore and the identifier will
|
||||
/// continue to be two separate tokens as if you had written `_ x`.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use proc_macro2::{self as syn, Span};
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # let ident = syn::Ident::new("i", Span::call_site());
|
||||
/// #
|
||||
/// // incorrect
|
||||
/// quote! {
|
||||
/// let mut _#ident = 0;
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// The solution is to perform token-level manipulations using the APIs provided
|
||||
/// by Syn and proc-macro2.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use proc_macro2::{self as syn, Span};
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # let ident = syn::Ident::new("i", Span::call_site());
|
||||
/// #
|
||||
/// let concatenated = format!("_{}", ident);
|
||||
/// let varname = syn::Ident::new(&concatenated, ident.span());
|
||||
/// quote! {
|
||||
/// let mut #varname = 0;
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// ## Making method calls
|
||||
///
|
||||
/// Let's say our macro requires some type specified in the macro input to have
|
||||
/// a constructor called `new`. We have the type in a variable called
|
||||
/// `field_type` of type `syn::Type` and want to invoke the constructor.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # let field_type = quote!(...);
|
||||
/// #
|
||||
/// // incorrect
|
||||
/// quote! {
|
||||
/// let value = #field_type::new();
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// This works only sometimes. If `field_type` is `String`, the expanded code
|
||||
/// contains `String::new()` which is fine. But if `field_type` is something
|
||||
/// like `Vec<i32>` then the expanded code is `Vec<i32>::new()` which is invalid
|
||||
/// syntax. Ordinarily in handwritten Rust we would write `Vec::<i32>::new()`
|
||||
/// but for macros often the following is more convenient.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # let field_type = quote!(...);
|
||||
/// #
|
||||
/// quote! {
|
||||
/// let value = <#field_type>::new();
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// This expands to `<Vec<i32>>::new()` which behaves correctly.
|
||||
///
|
||||
/// A similar pattern is appropriate for trait methods.
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use quote::quote;
|
||||
/// #
|
||||
/// # let field_type = quote!(...);
|
||||
/// #
|
||||
/// quote! {
|
||||
/// let value = <#field_type as core::default::Default>::default();
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! quote {
|
||||
|
@ -333,14 +455,10 @@ macro_rules! quote {
|
|||
///
|
||||
/// [`Span`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use]
|
||||
/// # extern crate quote;
|
||||
/// # extern crate proc_macro2;
|
||||
/// #
|
||||
/// ```edition2018
|
||||
/// # use proc_macro2::Span;
|
||||
/// # use quote::quote_spanned;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # const IGNORE_TOKENS: &'static str = stringify! {
|
||||
/// let span = /* ... */;
|
||||
/// # };
|
||||
|
@ -354,7 +472,6 @@ macro_rules! quote {
|
|||
/// let tokens = quote_spanned! {span=>
|
||||
/// Box::into_raw(Box::new(#init))
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// The lack of space before the `=>` should look jarring to Rust programmers
|
||||
|
@ -377,12 +494,8 @@ macro_rules! quote {
|
|||
///
|
||||
/// [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
///
|
||||
/// ```
|
||||
/// # #[macro_use]
|
||||
/// # extern crate quote;
|
||||
/// # extern crate proc_macro2;
|
||||
/// #
|
||||
/// # use quote::{TokenStreamExt, ToTokens};
|
||||
/// ```edition2018
|
||||
/// # use quote::{quote_spanned, TokenStreamExt, ToTokens};
|
||||
/// # use proc_macro2::{Span, TokenStream};
|
||||
/// #
|
||||
/// # struct Type;
|
||||
|
@ -397,7 +510,6 @@ macro_rules! quote {
|
|||
/// # fn to_tokens(&self, _tokens: &mut TokenStream) {}
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let ty = Type;
|
||||
/// # let call_site = Span::call_site();
|
||||
/// #
|
||||
|
@ -405,7 +517,6 @@ macro_rules! quote {
|
|||
/// let assert_sync = quote_spanned! {ty_span=>
|
||||
/// struct _AssertSync where #ty: Sync;
|
||||
/// };
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// If the assertion fails, the user will see an error like the following. The
|
||||
|
|
|
@ -21,12 +21,9 @@ pub trait ToTokens {
|
|||
/// Example implementation for a struct representing Rust paths like
|
||||
/// `std::cmp::PartialEq`:
|
||||
///
|
||||
/// ```
|
||||
/// extern crate quote;
|
||||
/// use quote::{TokenStreamExt, ToTokens};
|
||||
///
|
||||
/// extern crate proc_macro2;
|
||||
/// ```edition2018
|
||||
/// use proc_macro2::{TokenTree, Spacing, Span, Punct, TokenStream};
|
||||
/// use quote::{TokenStreamExt, ToTokens};
|
||||
///
|
||||
/// pub struct Path {
|
||||
/// pub global: bool,
|
||||
|
@ -53,8 +50,6 @@ pub trait ToTokens {
|
|||
/// # unimplemented!()
|
||||
/// # }
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
fn to_tokens(&self, tokens: &mut TokenStream);
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"fdac1ee690fa3d33b906be9d09a8551741db11ddba7f755cb75c8d74fd2d918b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"3f3d105c0f1bae3bdb5ed3cc32a8e5a02f3de6f62a9f17f5ba03af3f813d0881","src/attr.rs":"9210a8dc5fea0ee3004b14a9e2923142932c53986b56685d62d9aa115fe093b0","src/buffer.rs":"fac77febe022ab4982078c73ca502f9d698a402b3eca0f8e9c7a2796af01e5f4","src/data.rs":"54ee54c3c650bc5e200d4bea947d6e89093a39f2444cd43e8861f5852975e0bc","src/derive.rs":"eb041b47a73bace73c2872cd9a8e190de7e2b3b57cd57213770f088ec19ab3c6","src/error.rs":"0bcf09b0892c6d5f5f31f7f758866ded1e47463833cad60694329348ac1fb44a","src/export.rs":"1f7e017dac9426e91c796445e733c390c408c808ba9c21cb7df7799b67352fde","src/expr.rs":"3fe98f69af9be770d4d549d869d35136f3dea66452e4cb4b9e387982e3b7aea2","src/ext.rs":"1881179e634681cdd472ecac502192b5a5a7759056d1c49497706749fdab1bdf","src/file.rs":"abb9f5e71a8a6b52649c15da21d3f99e25a727d87c2f87d2744ac3970c1d683f","src/gen/fold.rs":"7f7ab907e3c17b503a72707e2f8d9fc29d9139269d810ea9b6511af9de7d3080","src/gen/visit.rs":"0a4543ac9f82d8ab7ccf02c55e8325ff9b859e36ea08d3e187a4836f470eef1c","src/gen/visit_mut.rs":"66441522f544056cd464740e8ba614688a5f417857c649550eeba2b589ef6096","src/gen_helper.rs":"644b1d31987919c0239891d8f90c09f3bf541a71fb85e744438e4814cc6dff89","src/generics.rs":"6ee5bba532b95f7de7c6bbe8caadabc6a71c45e7f8d7636d823075ff27f28937","src/group.rs":"03487f75d0abd302f06e50eb09d14ab83fb60c67e1f2602be53ca3f28a833b90","src/ident.rs":"61534c48949ebfa03e948874ef64174e1c8111c3b953edd58f79748fe9e00507","src/item.rs":"6799adb332bedaa68c3801055e71a5665cc7b9c4ba53960f6b91487408e7e10c","src/keyword.rs":"7dde0b4a0d70527921be16f50f8d88e4b5ad7e5fd70e9badd2bb351fd796dfb8","src/lib.rs":"254744712dae7c30ce7f97ce4685fc6256cf125e7872397d3dd9e32632273262","src/lifetime.rs":"7912a4c77ee805e912fb28c7f434836ea82540263d549877cd5edfbe32d1bf95","src/lit.rs":"b6aef4f2787201edbeb85529fc0c333bd8083d697a08f28c812b6b2f765939f5","src/lookahead.rs":"5b3c55ae8c1b1d0ed813c296dc6fa586379a99e7792a3cb0d634ae6ca74f54b5","src/mac.rs":"a91623ed9c1de7b18ef752db79a242002e95156497a52a1790a75069915d22ee","src/macros.rs":"2f91e07a1aec4b385986c0a0f66274e8de1c1aa81f95d398a5cd364b3c451bb4","src/op.rs":"01edb1e07b6d60b266797ca4b30788b0a511452228e04073a11f0b61f106a0e7","src/parse.rs":"d907b9822943bafbcb1e005f09a145e46c162e7702fce703b57f9b7ccbdf85a2","src/parse_macro_input.rs":"8df7b4c1b361171f3fefb0490dec570ad29c024c04e35184b296725f97f2002c","src/parse_quote.rs":"d5e613fbba06900d882f2aaa042f10c1bee1b1dffaa1d9ee9a73d1e504a08fad","src/path.rs":"d6a319db75e4b34783912aed0ddfad92cdec05798d8d378f2f23231437cab3e1","src/print.rs":"7ebb68123898f2ebbae12abf028747c05bea7b08f1e96b17164f1dcebdab7355","src/punctuated.rs":"5ad6885e602cb1c79b49f11e1c739bdb7c33ecfa1ca9c709d711b0778ae48085","src/span.rs":"748c51c6feb223c26d3b1701f5bb98aee823666c775c98106cfa24fe29d8cec1","src/spanned.rs":"83b4ab1e2138ac9340eaa8234ad1d9f7468b450ddf3a852e574cac18e4f766b8","src/synom.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/thread.rs":"798dd0a6cca7807f1d55c1f79cec967314a75c4e8e2cfdb5644499c22618b307","src/token.rs":"8fa7ffb89da61d5187ab0ff40de7b3b1135ace6cf770c8d84fce8371854698a9","src/tt.rs":"b3d99cbd68cd50749f26f4afa138e6366d327099ed566b30c315ccb58fa26ded","src/ty.rs":"4ac9d1b84f9bf269516348e1b923b1c8e3f7562b98ec7ef66174c31fffb8dce5"},"package":"734ecc29cd36e8123850d9bf21dfd62ef8300aaa8f879aabaa899721808be37c"}
|
||||
{"files":{"Cargo.toml":"ebf2c860a726b1210648f946b92b56a54414f2a009043d8affefdb1a0e4bd234","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"3f3d105c0f1bae3bdb5ed3cc32a8e5a02f3de6f62a9f17f5ba03af3f813d0881","build.rs":"9f3b0dc6ae4c0065c3cf001a40a2ff9c5a86cac5edf66fa0e80d0bcd37c0d4fc","src/attr.rs":"9210a8dc5fea0ee3004b14a9e2923142932c53986b56685d62d9aa115fe093b0","src/buffer.rs":"fac77febe022ab4982078c73ca502f9d698a402b3eca0f8e9c7a2796af01e5f4","src/data.rs":"54ee54c3c650bc5e200d4bea947d6e89093a39f2444cd43e8861f5852975e0bc","src/derive.rs":"eb041b47a73bace73c2872cd9a8e190de7e2b3b57cd57213770f088ec19ab3c6","src/error.rs":"0bcf09b0892c6d5f5f31f7f758866ded1e47463833cad60694329348ac1fb44a","src/export.rs":"1f7e017dac9426e91c796445e733c390c408c808ba9c21cb7df7799b67352fde","src/expr.rs":"c9000092a57412dcbef401cd51195b816ebfae96736deeebcece5bdc73471cdc","src/ext.rs":"1881179e634681cdd472ecac502192b5a5a7759056d1c49497706749fdab1bdf","src/file.rs":"abb9f5e71a8a6b52649c15da21d3f99e25a727d87c2f87d2744ac3970c1d683f","src/gen/fold.rs":"5507dde16c41e4f11b8903938a5f442fff9f94468afccbf0a37a4fa14f2eddde","src/gen/visit.rs":"4c8f499d1fd988b9c9ff820ef76c0c93b1e5ee91e6a94e0892298f5e6f424893","src/gen/visit_mut.rs":"d1f905b2b5afd685ace07532b6cbc67175e2d104508c7ed86ed4e9d1567c7c7b","src/gen_helper.rs":"644b1d31987919c0239891d8f90c09f3bf541a71fb85e744438e4814cc6dff89","src/generics.rs":"6ee5bba532b95f7de7c6bbe8caadabc6a71c45e7f8d7636d823075ff27f28937","src/group.rs":"03487f75d0abd302f06e50eb09d14ab83fb60c67e1f2602be53ca3f28a833b90","src/ident.rs":"bd7884de0031d942c556f929905532dd8799f8ca2450e65b8e4e8dfa78cf4fee","src/item.rs":"92b5ceca78590d9236dbdd008b97d817d051a16b10cb4dac8614199a09bda03e","src/keyword.rs":"aaca18ec0226f454ed5eecf8d99ef01afb8dd2457c9cae4f91b262accc0c0c41","src/lib.rs":"d36df1838b3454cd63a4ca214044c2be9281a33b533581f5da622bc66e2446d2","src/lifetime.rs":"7912a4c77ee805e912fb28c7f434836ea82540263d549877cd5edfbe32d1bf95","src/lit.rs":"bc034e4443a32f2066b41f62e05946143f60397fbbd05016c0020c4818e0a0f8","src/lookahead.rs":"5b3c55ae8c1b1d0ed813c296dc6fa586379a99e7792a3cb0d634ae6ca74f54b5","src/mac.rs":"a91623ed9c1de7b18ef752db79a242002e95156497a52a1790a75069915d22ee","src/macros.rs":"2f91e07a1aec4b385986c0a0f66274e8de1c1aa81f95d398a5cd364b3c451bb4","src/op.rs":"01edb1e07b6d60b266797ca4b30788b0a511452228e04073a11f0b61f106a0e7","src/parse.rs":"d907b9822943bafbcb1e005f09a145e46c162e7702fce703b57f9b7ccbdf85a2","src/parse_macro_input.rs":"8df7b4c1b361171f3fefb0490dec570ad29c024c04e35184b296725f97f2002c","src/parse_quote.rs":"d5e613fbba06900d882f2aaa042f10c1bee1b1dffaa1d9ee9a73d1e504a08fad","src/path.rs":"18b5c17b5acb7814a63517bbc473e1120895b4f7ff8f856279f31493663cc7fa","src/print.rs":"7ebb68123898f2ebbae12abf028747c05bea7b08f1e96b17164f1dcebdab7355","src/punctuated.rs":"3abae461aa47b71f00ede80e42691da5617a21c6f4864c40b2f984bb34a9a277","src/span.rs":"748c51c6feb223c26d3b1701f5bb98aee823666c775c98106cfa24fe29d8cec1","src/spanned.rs":"83b4ab1e2138ac9340eaa8234ad1d9f7468b450ddf3a852e574cac18e4f766b8","src/thread.rs":"ac3f4aa972b0dee5b9ae5202c5cd6bef46823fc91ff83787a3fe1bdfb8f79135","src/token.rs":"20868cd459ac8eea83e8891cf0b5d7c9dc179b0ff76953d5d0a2a29f17c0c7af","src/tt.rs":"b3d99cbd68cd50749f26f4afa138e6366d327099ed566b30c315ccb58fa26ded","src/ty.rs":"4ac9d1b84f9bf269516348e1b923b1c8e3f7562b98ec7ef66174c31fffb8dce5"},"package":"66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2"}
|
|
@ -3,7 +3,7 @@
|
|||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
|
@ -12,9 +12,9 @@
|
|||
|
||||
[package]
|
||||
name = "syn"
|
||||
version = "0.15.24"
|
||||
version = "0.15.30"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
include = ["/Cargo.toml", "/src/**/*.rs", "/README.md", "/LICENSE-APACHE", "/LICENSE-MIT"]
|
||||
include = ["/build.rs", "/Cargo.toml", "/LICENSE-APACHE", "/LICENSE-MIT", "/README.md", "/src/**/*.rs"]
|
||||
description = "Parser for Rust source code"
|
||||
documentation = "https://docs.rs/syn"
|
||||
readme = "README.md"
|
||||
|
@ -29,11 +29,6 @@ all-features = true
|
|||
|
||||
[lib]
|
||||
name = "syn"
|
||||
|
||||
[[example]]
|
||||
name = "dump-syntax"
|
||||
path = "examples/dump-syntax/main.rs"
|
||||
required-features = ["full", "parsing", "extra-traits"]
|
||||
[dependencies.proc-macro2]
|
||||
version = "0.4.4"
|
||||
default-features = false
|
||||
|
@ -45,6 +40,12 @@ default-features = false
|
|||
|
||||
[dependencies.unicode-xid]
|
||||
version = "0.1"
|
||||
[dev-dependencies.colored]
|
||||
version = "1.7"
|
||||
|
||||
[dev-dependencies.insta]
|
||||
version = "0.7"
|
||||
|
||||
[dev-dependencies.rayon]
|
||||
version = "1.0"
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
use std::env;
|
||||
use std::process::Command;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
// The rustc-cfg strings below are *not* public API. Please let us know by
|
||||
// opening a GitHub issue if your build environment requires some way to enable
|
||||
// these cfgs other than by executing our build script.
|
||||
fn main() {
|
||||
let compiler = match rustc_version() {
|
||||
Some(compiler) => compiler,
|
||||
None => return,
|
||||
};
|
||||
|
||||
if compiler.minor >= 19 {
|
||||
println!("cargo:rustc-cfg=syn_can_use_thread_id");
|
||||
}
|
||||
|
||||
// Macro modularization allows re-exporting the `quote!` macro in 1.30+.
|
||||
if compiler.minor >= 30 {
|
||||
println!("cargo:rustc-cfg=syn_can_call_macro_by_path");
|
||||
}
|
||||
|
||||
if !compiler.nightly {
|
||||
println!("cargo:rustc-cfg=syn_disable_nightly_tests");
|
||||
}
|
||||
}
|
||||
|
||||
struct Compiler {
|
||||
minor: u32,
|
||||
nightly: bool,
|
||||
}
|
||||
|
||||
fn rustc_version() -> Option<Compiler> {
|
||||
let rustc = match env::var_os("RUSTC") {
|
||||
Some(rustc) => rustc,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let output = match Command::new(rustc).arg("--version").output() {
|
||||
Ok(output) => output,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let version = match str::from_utf8(&output.stdout) {
|
||||
Ok(version) => version,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let next = match pieces.next() {
|
||||
Some(next) => next,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let minor = match u32::from_str(next) {
|
||||
Ok(minor) => minor,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
Some(Compiler {
|
||||
minor: minor,
|
||||
nightly: version.contains("nightly"),
|
||||
})
|
||||
}
|
|
@ -799,8 +799,8 @@ ast_enum_of_structs! {
|
|||
/// A path pattern like `Color::Red`, optionally qualified with a
|
||||
/// self-type.
|
||||
///
|
||||
/// Unquailfied path patterns can legally refer to variants, structs,
|
||||
/// constants or associated constants. Quailfied path patterns like
|
||||
/// Unqualified path patterns can legally refer to variants, structs,
|
||||
/// constants or associated constants. Qualified path patterns like
|
||||
/// `<A>::B::C` and `<A as Trait>::B::C` can only legally refer to
|
||||
/// associated constants.
|
||||
///
|
||||
|
@ -1508,7 +1508,7 @@ pub mod parsing {
|
|||
} else if input.peek(Token![loop]) {
|
||||
input.call(expr_loop).map(Expr::Loop)
|
||||
} else if input.peek(Token![match]) {
|
||||
input.call(expr_match).map(Expr::Match)
|
||||
input.parse().map(Expr::Match)
|
||||
} else if input.peek(Token![yield]) {
|
||||
input.call(expr_yield).map(Expr::Yield)
|
||||
} else if input.peek(Token![unsafe]) {
|
||||
|
@ -1703,7 +1703,7 @@ pub mod parsing {
|
|||
} else if input.peek(Token![loop]) {
|
||||
Expr::Loop(input.call(expr_loop)?)
|
||||
} else if input.peek(Token![match]) {
|
||||
Expr::Match(input.call(expr_match)?)
|
||||
Expr::Match(input.parse()?)
|
||||
} else if input.peek(Token![try]) && input.peek2(token::Brace) {
|
||||
Expr::TryBlock(input.call(expr_try_block)?)
|
||||
} else if input.peek(Token![unsafe]) {
|
||||
|
@ -1775,6 +1775,7 @@ pub mod parsing {
|
|||
let_token: input.parse()?,
|
||||
pats: {
|
||||
let mut pats = Punctuated::new();
|
||||
input.parse::<Option<Token![|]>>()?;
|
||||
let value: Pat = input.parse()?;
|
||||
pats.push_value(value);
|
||||
while input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) {
|
||||
|
@ -1876,26 +1877,28 @@ pub mod parsing {
|
|||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
fn expr_match(input: ParseStream) -> Result<ExprMatch> {
|
||||
let match_token: Token![match] = input.parse()?;
|
||||
let expr = expr_no_struct(input)?;
|
||||
impl Parse for ExprMatch {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let match_token: Token![match] = input.parse()?;
|
||||
let expr = expr_no_struct(input)?;
|
||||
|
||||
let content;
|
||||
let brace_token = braced!(content in input);
|
||||
let inner_attrs = content.call(Attribute::parse_inner)?;
|
||||
let content;
|
||||
let brace_token = braced!(content in input);
|
||||
let inner_attrs = content.call(Attribute::parse_inner)?;
|
||||
|
||||
let mut arms = Vec::new();
|
||||
while !content.is_empty() {
|
||||
arms.push(content.call(Arm::parse)?);
|
||||
let mut arms = Vec::new();
|
||||
while !content.is_empty() {
|
||||
arms.push(content.call(Arm::parse)?);
|
||||
}
|
||||
|
||||
Ok(ExprMatch {
|
||||
attrs: inner_attrs,
|
||||
match_token: match_token,
|
||||
expr: Box::new(expr),
|
||||
brace_token: brace_token,
|
||||
arms: arms,
|
||||
})
|
||||
}
|
||||
|
||||
Ok(ExprMatch {
|
||||
attrs: inner_attrs,
|
||||
match_token: match_token,
|
||||
expr: Box::new(expr),
|
||||
brace_token: brace_token,
|
||||
arms: arms,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -81,6 +81,6 @@ ident_from_token!(extern);
|
|||
|
||||
impl From<Token![_]> for Ident {
|
||||
fn from(token: Token![_]) -> Ident {
|
||||
Ident::new("_", token.spans[0])
|
||||
Ident::new("_", token.span)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1092,6 +1092,13 @@ pub mod parsing {
|
|||
let content;
|
||||
let paren_token = parenthesized!(content in input);
|
||||
let inputs = content.parse_terminated(FnArg::parse)?;
|
||||
let variadic: Option<Token![...]> = match inputs.last() {
|
||||
Some(punctuated::Pair::End(&FnArg::Captured(ArgCaptured {
|
||||
ty: Type::Verbatim(TypeVerbatim { ref tts }),
|
||||
..
|
||||
}))) => parse2(tts.clone()).ok(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let output: ReturnType = input.parse()?;
|
||||
let where_clause: Option<WhereClause> = input.parse()?;
|
||||
|
@ -1114,7 +1121,7 @@ pub mod parsing {
|
|||
paren_token: paren_token,
|
||||
inputs: inputs,
|
||||
output: output,
|
||||
variadic: None,
|
||||
variadic: variadic,
|
||||
generics: Generics {
|
||||
where_clause: where_clause,
|
||||
..generics
|
||||
|
@ -1179,7 +1186,23 @@ pub mod parsing {
|
|||
Ok(ArgCaptured {
|
||||
pat: input.parse()?,
|
||||
colon_token: input.parse()?,
|
||||
ty: input.parse()?,
|
||||
ty: match input.parse::<Token![...]>() {
|
||||
Ok(dot3) => {
|
||||
let mut args = vec![
|
||||
TokenTree::Punct(Punct::new('.', Spacing::Joint)),
|
||||
TokenTree::Punct(Punct::new('.', Spacing::Joint)),
|
||||
TokenTree::Punct(Punct::new('.', Spacing::Alone)),
|
||||
];
|
||||
let tokens = TokenStream::from_iter(args.into_iter().zip(&dot3.spans).map(
|
||||
|(mut arg, span)| {
|
||||
arg.set_span(*span);
|
||||
arg
|
||||
},
|
||||
));
|
||||
Type::Verbatim(TypeVerbatim { tts: tokens })
|
||||
}
|
||||
Err(_) => input.parse()?,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
/// [Peeking]: parse/struct.ParseBuffer.html#method.peek
|
||||
/// [Parsing]: parse/struct.ParseBuffer.html#method.parse
|
||||
/// [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
|
||||
/// [`Span`]: struct.Span.html
|
||||
/// [`Span`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
|
@ -89,6 +89,7 @@
|
|||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! custom_keyword {
|
||||
($ident:ident) => {
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $ident {
|
||||
pub span: $crate::export::Span,
|
||||
}
|
||||
|
|
|
@ -222,7 +222,7 @@
|
|||
//! dynamic library libproc_macro from rustc toolchain.
|
||||
|
||||
// Syn types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/syn/0.15.24")]
|
||||
#![doc(html_root_url = "https://docs.rs/syn/0.15.30")]
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Ignored clippy lints.
|
||||
|
@ -230,8 +230,8 @@
|
|||
feature = "cargo-clippy",
|
||||
allow(
|
||||
block_in_if_condition_stmt,
|
||||
cognitive_complexity,
|
||||
const_static_lifetime,
|
||||
cyclomatic_complexity,
|
||||
deprecated_cfg_attr,
|
||||
doc_markdown,
|
||||
eval_order_dependence,
|
||||
|
@ -672,7 +672,8 @@ pub fn parse_str<T: parse::Parse>(s: &str) -> Result<T> {
|
|||
///
|
||||
/// If present, either of these would be an error using `from_str`.
|
||||
///
|
||||
/// *This function is available if Syn is built with the `"parsing"` and `"full"` features.*
|
||||
/// *This function is available if Syn is built with the `"parsing"` and
|
||||
/// `"full"` features.*
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
@ -6,8 +6,6 @@ use proc_macro2::Ident;
|
|||
|
||||
#[cfg(feature = "parsing")]
|
||||
use proc_macro2::TokenStream;
|
||||
#[cfg(feature = "parsing")]
|
||||
use Error;
|
||||
|
||||
use proc_macro2::TokenTree;
|
||||
|
||||
|
@ -17,7 +15,7 @@ use std::hash::{Hash, Hasher};
|
|||
#[cfg(feature = "parsing")]
|
||||
use lookahead;
|
||||
#[cfg(feature = "parsing")]
|
||||
use parse::Parse;
|
||||
use parse::{Parse, Parser, Result};
|
||||
|
||||
ast_enum_of_structs! {
|
||||
/// A Rust literal such as a string or integer or boolean.
|
||||
|
@ -140,23 +138,44 @@ impl LitStr {
|
|||
/// lit_str.parse().map(Some)
|
||||
/// }
|
||||
/// _ => {
|
||||
/// let error_span = attr.bracket_token.span;
|
||||
/// let message = "expected #[path = \"...\"]";
|
||||
/// Err(Error::new(error_span, message))
|
||||
/// Err(Error::new_spanned(attr, message))
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "parsing")]
|
||||
pub fn parse<T: Parse>(&self) -> Result<T, Error> {
|
||||
use proc_macro2::Group;
|
||||
pub fn parse<T: Parse>(&self) -> Result<T> {
|
||||
self.parse_with(T::parse)
|
||||
}
|
||||
|
||||
// Parse string literal into a token stream with every span equal to the
|
||||
// original literal's span.
|
||||
fn spanned_tokens(s: &LitStr) -> Result<TokenStream, Error> {
|
||||
let stream = ::parse_str(&s.value())?;
|
||||
Ok(respan_token_stream(stream, s.span()))
|
||||
}
|
||||
/// Invoke parser on the content of this string literal.
|
||||
///
|
||||
/// All spans in the syntax tree will point to the span of this `LitStr`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```edition2018
|
||||
/// # use proc_macro2::Span;
|
||||
/// # use syn::{LitStr, Result};
|
||||
/// #
|
||||
/// # fn main() -> Result<()> {
|
||||
/// # let lit_str = LitStr::new("a::b::c", Span::call_site());
|
||||
/// #
|
||||
/// # const IGNORE: &str = stringify! {
|
||||
/// let lit_str: LitStr = /* ... */;
|
||||
/// # };
|
||||
///
|
||||
/// // Parse a string literal like "a::b::c" into a Path, not allowing
|
||||
/// // generic arguments on any of the path segments.
|
||||
/// let basic_path = lit_str.parse_with(syn::Path::parse_mod_style)?;
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(feature = "parsing")]
|
||||
pub fn parse_with<F: Parser>(&self, parser: F) -> Result<F::Output> {
|
||||
use proc_macro2::Group;
|
||||
|
||||
// Token stream with every span replaced by the given one.
|
||||
fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
|
||||
|
@ -179,7 +198,12 @@ impl LitStr {
|
|||
token
|
||||
}
|
||||
|
||||
spanned_tokens(self).and_then(::parse2)
|
||||
// Parse string literal into a token stream with every span equal to the
|
||||
// original literal's span.
|
||||
let mut tokens = ::parse_str(&self.value())?;
|
||||
tokens = respan_token_stream(tokens, self.span());
|
||||
|
||||
parser.parse2(tokens)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
|
|
|
@ -86,6 +86,7 @@ impl PathArguments {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
fn is_none(&self) -> bool {
|
||||
match *self {
|
||||
PathArguments::None => true,
|
||||
|
@ -427,6 +428,9 @@ pub mod parsing {
|
|||
/// - the first path segment has no angle bracketed or parenthesized
|
||||
/// path arguments
|
||||
/// - and the ident of the first path segment is equal to the given one.
|
||||
///
|
||||
/// *This function is available if Syn is built with the `"parsing"`
|
||||
/// feature.*
|
||||
pub fn is_ident<I>(&self, ident: I) -> bool
|
||||
where
|
||||
Ident: PartialEq<I>,
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
//!
|
||||
//! - The fields of a struct are `Punctuated<Field, Token![,]>`.
|
||||
//! - The segments of a path are `Punctuated<PathSegment, Token![::]>`.
|
||||
//! - The bounds on a generic parameter are `Punctuated<TypeParamBound, Token![+]>`.
|
||||
//! - The bounds on a generic parameter are `Punctuated<TypeParamBound,
|
||||
//! Token![+]>`.
|
||||
//! - The arguments to a function call are `Punctuated<Expr, Token![,]>`.
|
||||
//!
|
||||
//! This module provides a common representation for these punctuated sequences
|
||||
|
@ -104,7 +105,7 @@ impl<T, P> Punctuated<T, P> {
|
|||
Iter {
|
||||
inner: Box::new(PrivateIter {
|
||||
inner: self.inner.iter(),
|
||||
last: self.last.as_ref().map(|t| t.as_ref()).into_iter(),
|
||||
last: self.last.as_ref().map(Box::as_ref).into_iter(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -115,7 +116,7 @@ impl<T, P> Punctuated<T, P> {
|
|||
IterMut {
|
||||
inner: Box::new(PrivateIterMut {
|
||||
inner: self.inner.iter_mut(),
|
||||
last: self.last.as_mut().map(|t| t.as_mut()).into_iter(),
|
||||
last: self.last.as_mut().map(Box::as_mut).into_iter(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +126,7 @@ impl<T, P> Punctuated<T, P> {
|
|||
pub fn pairs(&self) -> Pairs<T, P> {
|
||||
Pairs {
|
||||
inner: self.inner.iter(),
|
||||
last: self.last.as_ref().map(|t| t.as_ref()).into_iter(),
|
||||
last: self.last.as_ref().map(Box::as_ref).into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -134,7 +135,7 @@ impl<T, P> Punctuated<T, P> {
|
|||
pub fn pairs_mut(&mut self) -> PairsMut<T, P> {
|
||||
PairsMut {
|
||||
inner: self.inner.iter_mut(),
|
||||
last: self.last.as_mut().map(|t| t.as_mut()).into_iter(),
|
||||
last: self.last.as_mut().map(Box::as_mut).into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,10 +54,12 @@ mod thread_id {
|
|||
|
||||
#[cfg(not(syn_can_use_thread_id))]
|
||||
mod thread_id {
|
||||
#[allow(deprecated)]
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
|
||||
thread_local! {
|
||||
static THREAD_ID: usize = {
|
||||
#[allow(deprecated)]
|
||||
static NEXT_THREAD_ID: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
|
||||
// Ordering::Relaxed because our only requirement for the ids is
|
||||
|
|
|
@ -68,6 +68,25 @@
|
|||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # Other operations
|
||||
//!
|
||||
//! Every keyword and punctuation token supports the following operations.
|
||||
//!
|
||||
//! - [Peeking] — `input.peek(Token![...])`
|
||||
//!
|
||||
//! - [Parsing] — `input.parse::<Token![...]>()?`
|
||||
//!
|
||||
//! - [Printing] — `quote!( ... #the_token ... )`
|
||||
//!
|
||||
//! - Construction from a [`Span`] — `let the_token = Token![...](sp)`
|
||||
//!
|
||||
//! - Field access to its span — `let sp = the_token.span`
|
||||
//!
|
||||
//! [Peeking]: ../parse/struct.ParseBuffer.html#method.peek
|
||||
//! [Parsing]: ../parse/struct.ParseBuffer.html#method.parse
|
||||
//! [Printing]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
|
||||
//! [`Span`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html
|
||||
|
||||
use std;
|
||||
#[cfg(feature = "extra-traits")]
|
||||
|
@ -76,6 +95,7 @@ use std::cmp;
|
|||
use std::fmt::{self, Debug};
|
||||
#[cfg(feature = "extra-traits")]
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
use proc_macro2::Delimiter;
|
||||
|
@ -87,6 +107,7 @@ use proc_macro2::TokenStream;
|
|||
#[cfg(feature = "printing")]
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
|
||||
use self::private::WithSpan;
|
||||
#[cfg(feature = "parsing")]
|
||||
use buffer::Cursor;
|
||||
#[cfg(feature = "parsing")]
|
||||
|
@ -117,9 +138,18 @@ pub trait Token: private::Sealed {
|
|||
fn display() -> &'static str;
|
||||
}
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
mod private {
|
||||
use proc_macro2::Span;
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
pub trait Sealed {}
|
||||
|
||||
/// Support writing `token.span` rather than `token.spans[0]` on tokens that
|
||||
/// hold a single span.
|
||||
#[repr(C)]
|
||||
pub struct WithSpan {
|
||||
pub span: Span,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "parsing")]
|
||||
|
@ -285,10 +315,31 @@ macro_rules! define_keywords {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_deref_if_len_is_1 {
|
||||
($name:ident/1) => {
|
||||
impl Deref for $name {
|
||||
type Target = WithSpan;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(self as *const Self as *const WithSpan) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for $name {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *(self as *mut Self as *mut WithSpan) }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
($name:ident/$len:tt) => {};
|
||||
}
|
||||
|
||||
macro_rules! define_punctuation_structs {
|
||||
($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
|
||||
$(
|
||||
#[cfg_attr(feature = "clone-impls", derive(Copy, Clone))]
|
||||
#[repr(C)]
|
||||
#[$doc]
|
||||
///
|
||||
/// Don't try to remember the name of this type -- use the [`Token!`]
|
||||
|
@ -336,6 +387,8 @@ macro_rules! define_punctuation_structs {
|
|||
impl Hash for $name {
|
||||
fn hash<H: Hasher>(&self, _state: &mut H) {}
|
||||
}
|
||||
|
||||
impl_deref_if_len_is_1!($name/$len);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
@ -450,7 +503,7 @@ define_punctuation_structs! {
|
|||
#[cfg(feature = "printing")]
|
||||
impl ToTokens for Underscore {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.append(Ident::new("_", self.spans[0]));
|
||||
tokens.append(Ident::new("_", self.span));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче