mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-06-12 12:31:22 +00:00
Move the macro support code into its own crate (#529)
This commit is contained in:
22
crates/macro-support/Cargo.toml
Normal file
22
crates/macro-support/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.15"
|
||||
authors = ["The wasm-bindgen Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support"
|
||||
homepage = "https://rustwasm.github.io/wasm-bindgen/"
|
||||
documentation = "https://docs.rs/wasm-bindgen"
|
||||
description = """
|
||||
The part of the implementation of the `#[wasm_bindgen]` attribute that is not in the shared backend crate
|
||||
"""
|
||||
|
||||
[features]
|
||||
spans = ["proc-macro2/nightly", "wasm-bindgen-backend/spans"]
|
||||
extra-traits = ["syn/extra-traits"]
|
||||
|
||||
[dependencies]
|
||||
syn = { version = '0.14', features = ['full'] }
|
||||
quote = '0.6'
|
||||
proc-macro2 = "0.4.9"
|
||||
wasm-bindgen-backend = { path = "../backend", version = "=0.2.15" }
|
||||
wasm-bindgen-shared = { path = "../shared", version = "=0.2.15" }
|
201
crates/macro-support/LICENSE-APACHE
Normal file
201
crates/macro-support/LICENSE-APACHE
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
25
crates/macro-support/LICENSE-MIT
Normal file
25
crates/macro-support/LICENSE-MIT
Normal file
@ -0,0 +1,25 @@
|
||||
Copyright (c) 2014 Alex Crichton
|
||||
|
||||
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.
|
26
crates/macro-support/src/lib.rs
Executable file
26
crates/macro-support/src/lib.rs
Executable file
@ -0,0 +1,26 @@
|
||||
//! This crate contains the part of the implementation of the `#[wasm_bindgen]` optsibute that is
|
||||
//! not in the shared backend crate.
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/wasm-bindgen-macro-support/0.2")]
|
||||
|
||||
extern crate proc_macro2;
|
||||
extern crate quote;
|
||||
#[macro_use]
|
||||
extern crate syn;
|
||||
extern crate wasm_bindgen_backend as backend;
|
||||
extern crate wasm_bindgen_shared as shared;
|
||||
|
||||
pub use parser::BindgenAttrs;
|
||||
use parser::MacroParse;
|
||||
use quote::ToTokens;
|
||||
|
||||
mod parser;
|
||||
|
||||
/// Takes the parsed input from a `#[wasm_bindgen]` macro and returns the generated bindings
|
||||
pub fn expand(item: syn::Item, opts: parser::BindgenAttrs) -> proc_macro2::TokenStream {
|
||||
let mut tokens = proc_macro2::TokenStream::new();
|
||||
let mut program = backend::ast::Program::default();
|
||||
item.macro_parse(&mut program, (Some(opts), &mut tokens));
|
||||
program.to_tokens(&mut tokens);
|
||||
tokens
|
||||
}
|
920
crates/macro-support/src/parser.rs
Normal file
920
crates/macro-support/src/parser.rs
Normal file
@ -0,0 +1,920 @@
|
||||
use backend::ast;
|
||||
use backend::util::{ident_ty, ShortHash};
|
||||
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use shared;
|
||||
use syn;
|
||||
|
||||
/// Parsed attributes from a `#[wasm_bindgen(..)]`.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
#[derive(Default)]
|
||||
pub struct BindgenAttrs {
|
||||
/// List of parsed attributes
|
||||
pub attrs: Vec<BindgenAttr>,
|
||||
}
|
||||
|
||||
impl BindgenAttrs {
|
||||
/// Find and parse the wasm_bindgen attributes.
|
||||
fn find(attrs: &mut Vec<syn::Attribute>) -> BindgenAttrs {
|
||||
let pos = attrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|&(_, ref m)| m.path.segments[0].ident == "wasm_bindgen")
|
||||
.map(|a| a.0);
|
||||
let pos = match pos {
|
||||
Some(i) => i,
|
||||
None => return BindgenAttrs::default(),
|
||||
};
|
||||
let mut tts = attrs.remove(pos).tts.into_iter();
|
||||
let tt = match tts.next() {
|
||||
Some(TokenTree::Group(d)) => d.stream(),
|
||||
Some(_) => panic!("malformed #[wasm_bindgen] attribute"),
|
||||
None => return BindgenAttrs::default(),
|
||||
};
|
||||
if tts.next().is_some() {
|
||||
panic!("malformed #[wasm_bindgen] attribute");
|
||||
}
|
||||
syn::parse(tt.into()).expect("malformed #[wasm_bindgen] attribute")
|
||||
}
|
||||
|
||||
/// Get the first module attribute
|
||||
fn module(&self) -> Option<&str> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::Module(s) => Some(&s[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Get the first version attribute
|
||||
fn version(&self) -> Option<&str> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::Version(s) => Some(&s[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Whether the catch attribute is present
|
||||
fn catch(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match a {
|
||||
BindgenAttr::Catch => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the constructor attribute is present
|
||||
fn constructor(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match a {
|
||||
BindgenAttr::Constructor => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the first static_method_of attribute
|
||||
fn static_method_of(&self) -> Option<&Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::StaticMethodOf(c) => Some(c),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Whether the method attributes is present
|
||||
fn method(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match a {
|
||||
BindgenAttr::Method => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the first js_namespace attribute
|
||||
fn js_namespace(&self) -> Option<&Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::JsNamespace(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Get the first getter attribute
|
||||
fn getter(&self) -> Option<Option<Ident>> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::Getter(g) => Some(g.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Get the first setter attribute
|
||||
fn setter(&self) -> Option<Option<Ident>> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::Setter(s) => Some(s.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Whether the structural attributes is present
|
||||
fn structural(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Structural => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Whether the readonly attributes is present
|
||||
fn readonly(&self) -> bool {
|
||||
self.attrs.iter().any(|a| match *a {
|
||||
BindgenAttr::Readonly => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the first js_name attribute
|
||||
fn js_name(&self) -> Option<&Ident> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::JsName(s) => Some(s),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// Get the first js_name attribute
|
||||
fn js_class(&self) -> Option<&str> {
|
||||
self.attrs
|
||||
.iter()
|
||||
.filter_map(|a| match a {
|
||||
BindgenAttr::JsClass(s) => Some(&s[..]),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttrs {
|
||||
named!(parse -> Self, alt!(
|
||||
do_parse!(
|
||||
opts: call!(
|
||||
syn::punctuated::Punctuated::<_, syn::token::Comma>::parse_terminated
|
||||
) >>
|
||||
(BindgenAttrs {
|
||||
attrs: opts.into_iter().collect(),
|
||||
})
|
||||
) => { |s| s }
|
||||
|
|
||||
epsilon!() => { |_| BindgenAttrs { attrs: Vec::new() } }
|
||||
));
|
||||
}
|
||||
|
||||
/// The possible attributes in the `#[wasm_bindgen]`.
|
||||
#[cfg_attr(feature = "extra-traits", derive(Debug, PartialEq, Eq))]
|
||||
pub enum BindgenAttr {
|
||||
Catch,
|
||||
Constructor,
|
||||
Method,
|
||||
StaticMethodOf(Ident),
|
||||
JsNamespace(Ident),
|
||||
Module(String),
|
||||
Version(String),
|
||||
Getter(Option<Ident>),
|
||||
Setter(Option<Ident>),
|
||||
Structural,
|
||||
Readonly,
|
||||
JsName(Ident),
|
||||
JsClass(String),
|
||||
}
|
||||
|
||||
impl syn::synom::Synom for BindgenAttr {
|
||||
named!(parse -> Self, alt!(
|
||||
call!(term, "catch") => { |_| BindgenAttr::Catch }
|
||||
|
|
||||
call!(term, "constructor") => { |_| BindgenAttr::Constructor }
|
||||
|
|
||||
call!(term, "method") => { |_| BindgenAttr::Method }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "static_method_of") >>
|
||||
punct!(=) >>
|
||||
cls: call!(term2ident) >>
|
||||
(cls)
|
||||
)=> { BindgenAttr::StaticMethodOf }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "getter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: call!(term2ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Getter }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "setter") >>
|
||||
val: option!(do_parse!(
|
||||
punct!(=) >>
|
||||
s: call!(term2ident) >>
|
||||
(s)
|
||||
)) >>
|
||||
(val)
|
||||
)=> { BindgenAttr::Setter }
|
||||
|
|
||||
call!(term, "structural") => { |_| BindgenAttr::Structural }
|
||||
|
|
||||
call!(term, "readonly") => { |_| BindgenAttr::Readonly }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_namespace") >>
|
||||
punct!(=) >>
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsNamespace }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "module") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Module }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "version") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::Version }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_name") >>
|
||||
punct!(=) >>
|
||||
ns: call!(term2ident) >>
|
||||
(ns)
|
||||
)=> { BindgenAttr::JsName }
|
||||
|
|
||||
do_parse!(
|
||||
call!(term, "js_class") >>
|
||||
punct!(=) >>
|
||||
s: syn!(syn::LitStr) >>
|
||||
(s.value())
|
||||
)=> { BindgenAttr::JsClass }
|
||||
));
|
||||
}
|
||||
|
||||
/// Consumes a `Ident` with the given name
|
||||
fn term<'a>(cursor: syn::buffer::Cursor<'a>, name: &str) -> syn::synom::PResult<'a, ()> {
|
||||
if let Some((ident, next)) = cursor.ident() {
|
||||
if ident == name {
|
||||
return Ok(((), next));
|
||||
}
|
||||
}
|
||||
syn::parse_error()
|
||||
}
|
||||
|
||||
/// Consumes a `Ident` and returns it.
|
||||
fn term2ident<'a>(cursor: syn::buffer::Cursor<'a>) -> syn::synom::PResult<'a, Ident> {
|
||||
match cursor.ident() {
|
||||
Some(pair) => Ok(pair),
|
||||
None => syn::parse_error(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Conversion trait with context.
|
||||
///
|
||||
/// Used to convert syn tokens into an AST, that we can then use to generate glue code. The context
|
||||
/// (`Ctx`) is used to pass in the attributes from the `#[wasm_bindgen]`, if needed.
|
||||
trait ConvertToAst<Ctx> {
|
||||
/// What we are converting to.
|
||||
type Target;
|
||||
/// Convert into our target.
|
||||
///
|
||||
/// Since this is used in a procedural macro, use panic to fail.
|
||||
fn convert(self, context: Ctx) -> Self::Target;
|
||||
}
|
||||
|
||||
impl<'a> ConvertToAst<()> for &'a mut syn::ItemStruct {
|
||||
type Target = ast::Struct;
|
||||
|
||||
fn convert(self, (): ()) -> Self::Target {
|
||||
if self.generics.params.len() > 0 {
|
||||
panic!(
|
||||
"structs with #[wasm_bindgen] cannot have lifetime or \
|
||||
type parameters currently"
|
||||
);
|
||||
}
|
||||
let mut fields = Vec::new();
|
||||
if let syn::Fields::Named(names) = &mut self.fields {
|
||||
for field in names.named.iter_mut() {
|
||||
match field.vis {
|
||||
syn::Visibility::Public(..) => {}
|
||||
_ => continue,
|
||||
}
|
||||
let name = match &field.ident {
|
||||
Some(n) => n,
|
||||
None => continue,
|
||||
};
|
||||
let ident = self.ident.to_string();
|
||||
let name_str = name.to_string();
|
||||
let getter = shared::struct_field_get(&ident, &name_str);
|
||||
let setter = shared::struct_field_set(&ident, &name_str);
|
||||
let opts = BindgenAttrs::find(&mut field.attrs);
|
||||
let comments = extract_doc_comments(&field.attrs);
|
||||
fields.push(ast::StructField {
|
||||
name: name.clone(),
|
||||
struct_name: self.ident.clone(),
|
||||
readonly: opts.readonly(),
|
||||
ty: field.ty.clone(),
|
||||
getter: Ident::new(&getter, Span::call_site()),
|
||||
setter: Ident::new(&setter, Span::call_site()),
|
||||
comments,
|
||||
});
|
||||
}
|
||||
}
|
||||
let comments: Vec<String> = extract_doc_comments(&self.attrs);
|
||||
ast::Struct {
|
||||
name: self.ident.clone(),
|
||||
fields,
|
||||
comments,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvertToAst<BindgenAttrs> for syn::ForeignItemFn {
|
||||
type Target = ast::ImportKind;
|
||||
|
||||
fn convert(self, opts: BindgenAttrs) -> Self::Target {
|
||||
let js_name = opts.js_name().unwrap_or(&self.ident).clone();
|
||||
let wasm = function_from_decl(&js_name, self.decl, self.attrs, self.vis, false).0;
|
||||
let catch = opts.catch();
|
||||
let js_ret = if catch {
|
||||
// TODO: this assumes a whole bunch:
|
||||
//
|
||||
// * The outer type is actually a `Result`
|
||||
// * The error type is a `JsValue`
|
||||
// * The actual type is the first type parameter
|
||||
//
|
||||
// should probably fix this one day...
|
||||
extract_first_ty_param(wasm.ret.as_ref())
|
||||
.expect("can't `catch` without returning a Result")
|
||||
} else {
|
||||
wasm.ret.clone()
|
||||
};
|
||||
|
||||
let mut operation_kind = ast::OperationKind::Regular;
|
||||
if let Some(g) = opts.getter() {
|
||||
operation_kind = ast::OperationKind::Getter(g);
|
||||
}
|
||||
if let Some(s) = opts.setter() {
|
||||
operation_kind = ast::OperationKind::Setter(s);
|
||||
}
|
||||
|
||||
let kind = if opts.method() {
|
||||
let class = wasm
|
||||
.arguments
|
||||
.get(0)
|
||||
.expect("methods must have at least one argument");
|
||||
let class = match class.ty {
|
||||
syn::Type::Reference(syn::TypeReference {
|
||||
mutability: None,
|
||||
ref elem,
|
||||
..
|
||||
}) => &**elem,
|
||||
_ => panic!("first argument of method must be a shared reference"),
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
let class_name = opts
|
||||
.js_class()
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|| class_name.to_string());
|
||||
|
||||
let kind = ast::MethodKind::Operation(ast::Operation {
|
||||
is_static: false,
|
||||
kind: operation_kind,
|
||||
});
|
||||
|
||||
ast::ImportFunctionKind::Method {
|
||||
class: class_name,
|
||||
ty: class.clone(),
|
||||
kind,
|
||||
}
|
||||
} else if let Some(cls) = opts.static_method_of() {
|
||||
let class = cls.to_string();
|
||||
let ty = ident_ty(cls.clone());
|
||||
|
||||
let kind = ast::MethodKind::Operation(ast::Operation {
|
||||
is_static: true,
|
||||
kind: operation_kind,
|
||||
});
|
||||
|
||||
ast::ImportFunctionKind::Method { class, ty, kind }
|
||||
} else if opts.constructor() {
|
||||
let class = match wasm.ret {
|
||||
Some(ref ty) => ty,
|
||||
_ => panic!("constructor returns must be bare types"),
|
||||
};
|
||||
let class_name = match *class {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => panic!("first argument of method must be a path"),
|
||||
};
|
||||
let class_name = extract_path_ident(class_name)
|
||||
.expect("first argument of method must be a bare type");
|
||||
|
||||
ast::ImportFunctionKind::Method {
|
||||
class: class_name.to_string(),
|
||||
ty: class.clone(),
|
||||
kind: ast::MethodKind::Constructor,
|
||||
}
|
||||
} else {
|
||||
ast::ImportFunctionKind::Normal
|
||||
};
|
||||
|
||||
let shim = {
|
||||
let ns = match kind {
|
||||
ast::ImportFunctionKind::Normal => (0, "n"),
|
||||
ast::ImportFunctionKind::Method { ref class, .. } => (1, &class[..]),
|
||||
};
|
||||
let data = (ns, &self.ident);
|
||||
format!("__wbg_{}_{}", js_name, ShortHash(data))
|
||||
};
|
||||
ast::ImportKind::Function(ast::ImportFunction {
|
||||
function: wasm,
|
||||
kind,
|
||||
js_ret,
|
||||
catch,
|
||||
structural: opts.structural(),
|
||||
rust_name: self.ident.clone(),
|
||||
shim: Ident::new(&shim, Span::call_site()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvertToAst<()> for syn::ForeignItemType {
|
||||
type Target = ast::ImportKind;
|
||||
|
||||
fn convert(self, (): ()) -> Self::Target {
|
||||
ast::ImportKind::Type(ast::ImportType {
|
||||
vis: self.vis,
|
||||
name: self.ident,
|
||||
attrs: self.attrs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvertToAst<BindgenAttrs> for syn::ForeignItemStatic {
|
||||
type Target = ast::ImportKind;
|
||||
|
||||
fn convert(self, opts: BindgenAttrs) -> Self::Target {
|
||||
if self.mutability.is_some() {
|
||||
panic!("cannot import mutable globals yet")
|
||||
}
|
||||
let js_name = opts.js_name().unwrap_or(&self.ident);
|
||||
let shim = format!("__wbg_static_accessor_{}_{}", js_name, self.ident);
|
||||
ast::ImportKind::Static(ast::ImportStatic {
|
||||
ty: *self.ty,
|
||||
vis: self.vis,
|
||||
rust_name: self.ident.clone(),
|
||||
js_name: js_name.clone(),
|
||||
shim: Ident::new(&shim, Span::call_site()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvertToAst<BindgenAttrs> for syn::ItemFn {
|
||||
type Target = ast::Function;
|
||||
|
||||
fn convert(self, attrs: BindgenAttrs) -> Self::Target {
|
||||
match self.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("can only bindgen public functions"),
|
||||
}
|
||||
if self.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if self.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
let name = attrs.js_name().unwrap_or(&self.ident);
|
||||
function_from_decl(name, self.decl, self.attrs, self.vis, false).0
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a function (and gets the self type if appropriate) for our AST from a syn function.
|
||||
fn function_from_decl(
|
||||
name: &Ident,
|
||||
mut decl: Box<syn::FnDecl>,
|
||||
attrs: Vec<syn::Attribute>,
|
||||
vis: syn::Visibility,
|
||||
allow_self: bool,
|
||||
) -> (ast::Function, Option<ast::MethodSelf>) {
|
||||
if decl.variadic.is_some() {
|
||||
panic!("can't bindgen variadic functions")
|
||||
}
|
||||
if decl.generics.params.len() > 0 {
|
||||
panic!("can't bindgen functions with lifetime or type parameters")
|
||||
}
|
||||
|
||||
assert_no_lifetimes(&mut decl);
|
||||
|
||||
let syn::FnDecl { inputs, output, .. } = { *decl };
|
||||
|
||||
let mut method_self = None;
|
||||
let arguments = inputs
|
||||
.into_iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Captured(c) => Some(c),
|
||||
syn::FnArg::SelfValue(_) => {
|
||||
assert!(method_self.is_none());
|
||||
method_self = Some(ast::MethodSelf::ByValue);
|
||||
None
|
||||
}
|
||||
syn::FnArg::SelfRef(ref a) if allow_self => {
|
||||
assert!(method_self.is_none());
|
||||
if a.mutability.is_some() {
|
||||
method_self = Some(ast::MethodSelf::RefMutable);
|
||||
} else {
|
||||
method_self = Some(ast::MethodSelf::RefShared);
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => panic!("arguments cannot be `self` or ignored"),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let ret = match output {
|
||||
syn::ReturnType::Default => None,
|
||||
syn::ReturnType::Type(_, ty) => Some(*ty),
|
||||
};
|
||||
|
||||
(
|
||||
ast::Function {
|
||||
name: name.clone(),
|
||||
arguments,
|
||||
ret,
|
||||
rust_vis: vis,
|
||||
rust_attrs: attrs,
|
||||
},
|
||||
method_self,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) trait MacroParse<Ctx> {
|
||||
/// Parse the contents of an object into our AST, with a context if necessary.
|
||||
///
|
||||
/// The context is used to have access to the attributes on `#[wasm_bindgen]`, and to allow
|
||||
/// writing to the output `TokenStream`.
|
||||
fn macro_parse(self, program: &mut ast::Program, context: Ctx);
|
||||
}
|
||||
|
||||
impl<'a> MacroParse<(Option<BindgenAttrs>, &'a mut TokenStream)> for syn::Item {
|
||||
fn macro_parse(
|
||||
self,
|
||||
program: &mut ast::Program,
|
||||
(opts, tokens): (Option<BindgenAttrs>, &'a mut TokenStream),
|
||||
) {
|
||||
match self {
|
||||
syn::Item::Fn(mut f) => {
|
||||
let no_mangle = f
|
||||
.attrs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, m)| m.interpret_meta().map(|m| (i, m)))
|
||||
.find(|&(_, ref m)| m.name() == "no_mangle");
|
||||
match no_mangle {
|
||||
Some((i, _)) => {
|
||||
f.attrs.remove(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let comments = extract_doc_comments(&f.attrs);
|
||||
f.to_tokens(tokens);
|
||||
program.exports.push(ast::Export {
|
||||
class: None,
|
||||
method_self: None,
|
||||
constructor: None,
|
||||
comments,
|
||||
rust_name: f.ident.clone(),
|
||||
function: f.convert(opts.unwrap_or_default()),
|
||||
});
|
||||
}
|
||||
syn::Item::Struct(mut s) => {
|
||||
program.structs.push((&mut s).convert(()));
|
||||
s.to_tokens(tokens);
|
||||
}
|
||||
syn::Item::Impl(mut i) => {
|
||||
(&mut i).macro_parse(program, ());
|
||||
i.to_tokens(tokens);
|
||||
}
|
||||
syn::Item::ForeignMod(mut f) => {
|
||||
let opts = opts.unwrap_or_else(|| BindgenAttrs::find(&mut f.attrs));
|
||||
f.macro_parse(program, opts);
|
||||
}
|
||||
syn::Item::Enum(e) => {
|
||||
e.to_tokens(tokens);
|
||||
e.macro_parse(program, ());
|
||||
}
|
||||
_ => panic!(
|
||||
"#[wasm_bindgen] can only be applied to a function, \
|
||||
struct, enum, impl, or extern block"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MacroParse<()> for &'a mut syn::ItemImpl {
|
||||
fn macro_parse(self, program: &mut ast::Program, (): ()) {
|
||||
if self.defaultness.is_some() {
|
||||
panic!("default impls are not supported");
|
||||
}
|
||||
if self.unsafety.is_some() {
|
||||
panic!("unsafe impls are not supported");
|
||||
}
|
||||
if self.trait_.is_some() {
|
||||
panic!("trait impls are not supported");
|
||||
}
|
||||
if self.generics.params.len() > 0 {
|
||||
panic!("generic impls aren't supported");
|
||||
}
|
||||
let name = match *self.self_ty {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => match extract_path_ident(path) {
|
||||
Some(ident) => ident,
|
||||
None => panic!("unsupported self type in impl"),
|
||||
},
|
||||
_ => panic!("unsupported self type in impl"),
|
||||
};
|
||||
for item in self.items.iter_mut() {
|
||||
(&name, item).macro_parse(program, ())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> MacroParse<()> for (&'a Ident, &'b mut syn::ImplItem) {
|
||||
fn macro_parse(self, program: &mut ast::Program, (): ()) {
|
||||
let (class, item) = self;
|
||||
replace_self(class, item);
|
||||
let method = match item {
|
||||
syn::ImplItem::Const(_) => panic!("const definitions aren't supported"),
|
||||
syn::ImplItem::Type(_) => panic!("type definitions in impls aren't supported"),
|
||||
syn::ImplItem::Method(ref mut m) => m,
|
||||
syn::ImplItem::Macro(_) => panic!("macros in impls aren't supported"),
|
||||
syn::ImplItem::Verbatim(_) => panic!("unparsed impl item?"),
|
||||
};
|
||||
match method.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => return,
|
||||
}
|
||||
if method.defaultness.is_some() {
|
||||
panic!("default methods are not supported");
|
||||
}
|
||||
if method.sig.constness.is_some() {
|
||||
panic!("can only bindgen non-const functions");
|
||||
}
|
||||
if method.sig.unsafety.is_some() {
|
||||
panic!("can only bindgen safe functions");
|
||||
}
|
||||
|
||||
let opts = BindgenAttrs::find(&mut method.attrs);
|
||||
let comments = extract_doc_comments(&method.attrs);
|
||||
let is_constructor = opts.constructor();
|
||||
let constructor = if is_constructor {
|
||||
Some(method.sig.ident.to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (function, method_self) = function_from_decl(
|
||||
opts.js_name().unwrap_or(&method.sig.ident),
|
||||
Box::new(method.sig.decl.clone()),
|
||||
method.attrs.clone(),
|
||||
method.vis.clone(),
|
||||
true,
|
||||
);
|
||||
|
||||
program.exports.push(ast::Export {
|
||||
class: Some(class.clone()),
|
||||
method_self,
|
||||
constructor,
|
||||
function,
|
||||
comments,
|
||||
rust_name: method.sig.ident.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroParse<()> for syn::ItemEnum {
|
||||
fn macro_parse(self, program: &mut ast::Program, (): ()) {
|
||||
match self.vis {
|
||||
syn::Visibility::Public(_) => {}
|
||||
_ => panic!("only public enums are allowed"),
|
||||
}
|
||||
|
||||
let variants = self
|
||||
.variants
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, v)| {
|
||||
match v.fields {
|
||||
syn::Fields::Unit => (),
|
||||
_ => panic!("Only C-Style enums allowed"),
|
||||
}
|
||||
let value = match v.discriminant {
|
||||
Some((
|
||||
_,
|
||||
syn::Expr::Lit(syn::ExprLit {
|
||||
attrs: _,
|
||||
lit: syn::Lit::Int(ref int_lit),
|
||||
}),
|
||||
)) => {
|
||||
if int_lit.value() > <u32>::max_value() as u64 {
|
||||
panic!("Enums can only support numbers that can be represented as u32");
|
||||
}
|
||||
int_lit.value() as u32
|
||||
}
|
||||
None => i as u32,
|
||||
_ => panic!("Enums may only have number literal values"),
|
||||
};
|
||||
|
||||
ast::Variant {
|
||||
name: v.ident.clone(),
|
||||
value,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let comments = extract_doc_comments(&self.attrs);
|
||||
program.enums.push(ast::Enum {
|
||||
name: self.ident,
|
||||
variants,
|
||||
comments,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl MacroParse<BindgenAttrs> for syn::ItemForeignMod {
|
||||
fn macro_parse(self, program: &mut ast::Program, opts: BindgenAttrs) {
|
||||
match self.abi.name {
|
||||
Some(ref l) if l.value() == "C" => {}
|
||||
None => {}
|
||||
_ => panic!("only foreign mods with the `C` ABI are allowed"),
|
||||
}
|
||||
for mut item in self.items.into_iter() {
|
||||
let item_opts = {
|
||||
let attrs = match item {
|
||||
syn::ForeignItem::Fn(ref mut f) => &mut f.attrs,
|
||||
syn::ForeignItem::Type(ref mut t) => &mut t.attrs,
|
||||
syn::ForeignItem::Static(ref mut s) => &mut s.attrs,
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
BindgenAttrs::find(attrs)
|
||||
};
|
||||
let module = item_opts.module().or(opts.module()).map(|s| s.to_string());
|
||||
let version = item_opts
|
||||
.version()
|
||||
.or(opts.version())
|
||||
.map(|s| s.to_string());
|
||||
let js_namespace = item_opts.js_namespace().or(opts.js_namespace()).cloned();
|
||||
let mut kind = match item {
|
||||
syn::ForeignItem::Fn(f) => f.convert(item_opts),
|
||||
syn::ForeignItem::Type(t) => t.convert(()),
|
||||
syn::ForeignItem::Static(s) => s.convert(item_opts),
|
||||
_ => panic!("only foreign functions/types allowed for now"),
|
||||
};
|
||||
|
||||
program.imports.push(ast::Import {
|
||||
module,
|
||||
version,
|
||||
js_namespace,
|
||||
kind,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the first type parameter of a generic type, errors on incorrect input.
|
||||
fn extract_first_ty_param(ty: Option<&syn::Type>) -> Result<Option<syn::Type>, ()> {
|
||||
let t = match ty {
|
||||
Some(t) => t,
|
||||
None => return Ok(None),
|
||||
};
|
||||
let path = match *t {
|
||||
syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => path,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let seg = path.segments.last().ok_or(())?.into_value();
|
||||
let generics = match seg.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref t) => t,
|
||||
_ => return Err(()),
|
||||
};
|
||||
let ty = match *generics.args.first().ok_or(())?.into_value() {
|
||||
syn::GenericArgument::Type(ref t) => t,
|
||||
_ => return Err(()),
|
||||
};
|
||||
match *ty {
|
||||
syn::Type::Tuple(ref t) if t.elems.len() == 0 => return Ok(None),
|
||||
_ => {}
|
||||
}
|
||||
Ok(Some(ty.clone()))
|
||||
}
|
||||
|
||||
/// Replace `Self` with the given name in `item`.
|
||||
fn replace_self(name: &Ident, item: &mut syn::ImplItem) {
|
||||
struct Walk<'a>(&'a Ident);
|
||||
|
||||
impl<'a> syn::visit_mut::VisitMut for Walk<'a> {
|
||||
fn visit_ident_mut(&mut self, i: &mut Ident) {
|
||||
if i == "Self" {
|
||||
*i = self.0.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syn::visit_mut::VisitMut::visit_impl_item_mut(&mut Walk(name), item);
|
||||
}
|
||||
|
||||
/// Extract the documentation comments from a Vec of attributes
|
||||
fn extract_doc_comments(attrs: &[syn::Attribute]) -> Vec<String> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter_map(|a| {
|
||||
// if the path segments include an ident of "doc" we know this
|
||||
// this is a doc comment
|
||||
if a.path.segments.iter().any(|s| s.ident.to_string() == "doc") {
|
||||
Some(
|
||||
// We want to filter out any Puncts so just grab the Literals
|
||||
a.tts.clone().into_iter().filter_map(|t| match t {
|
||||
TokenTree::Literal(lit) => {
|
||||
// this will always return the quoted string, we deal with
|
||||
// that in the cli when we read in the comments
|
||||
Some(lit.to_string())
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
//Fold up the [[String]] iter we created into Vec<String>
|
||||
.fold(vec![], |mut acc, a| {acc.extend(a); acc})
|
||||
}
|
||||
|
||||
/// Check there are no lifetimes on the function.
|
||||
fn assert_no_lifetimes(decl: &mut syn::FnDecl) {
|
||||
struct Walk;
|
||||
|
||||
impl<'ast> syn::visit_mut::VisitMut for Walk {
|
||||
fn visit_lifetime_mut(&mut self, _i: &mut syn::Lifetime) {
|
||||
panic!(
|
||||
"it is currently not sound to use lifetimes in function \
|
||||
signatures"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
syn::visit_mut::VisitMut::visit_fn_decl_mut(&mut Walk, decl);
|
||||
}
|
||||
|
||||
/// If the path is a single ident, return it.
|
||||
fn extract_path_ident(path: &syn::Path) -> Option<Ident> {
|
||||
if path.leading_colon.is_some() {
|
||||
return None;
|
||||
}
|
||||
if path.segments.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
match path.segments.first().unwrap().value().arguments {
|
||||
syn::PathArguments::None => {}
|
||||
_ => return None,
|
||||
}
|
||||
path.segments.first().map(|v| v.value().ident.clone())
|
||||
}
|
Reference in New Issue
Block a user