From 05ef6ffa28719132df8967edc80ba45907166318 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 11:27:05 -0700 Subject: [PATCH 1/9] Data structure to represent possibly unsafe module --- macro/src/expand.rs | 17 ++++++++-------- macro/src/lib.rs | 5 +++-- syntax/file.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++ syntax/mod.rs | 1 + 4 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 syntax/file.rs diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 0869c2c58..48ef6a32b 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,21 +1,20 @@ use crate::syntax::atom::Atom::{self, *}; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, }; -use proc_macro2::{Ident, Span, TokenStream}; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; -use syn::{parse_quote, Error, ItemMod, Result, Token}; +use std::mem; +use syn::{parse_quote, Result, Token}; -pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { +pub fn bridge(namespace: &Namespace, mut ffi: Module) -> Result { let ref mut errors = Errors::new(); - let content = ffi.content.take().ok_or(Error::new( - Span::call_site(), - "#[cxx::bridge] module must have inline contents", - ))?; - let ref apis = syntax::parse_items(errors, content.1); + let content = mem::take(&mut ffi.content); + let ref apis = syntax::parse_items(errors, content); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); @@ -24,7 +23,7 @@ pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result { Ok(expand(namespace, ffi, apis, types)) } -fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream { +fn expand(namespace: &Namespace, ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); diff --git a/macro/src/lib.rs b/macro/src/lib.rs index bc8d19bbc..b8a592b6f 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -13,9 +13,10 @@ mod expand; mod syntax; mod type_id; +use crate::syntax::file::Module; use crate::syntax::namespace::Namespace; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemMod, LitStr}; +use syn::{parse_macro_input, LitStr}; /// `#[cxx::bridge] mod ffi { ... }` /// @@ -39,7 +40,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = parse_macro_input!(args as Namespace); - let ffi = parse_macro_input!(input as ItemMod); + let ffi = parse_macro_input!(input as Module); expand::bridge(&namespace, ffi) .unwrap_or_else(|err| err.to_compile_error()) diff --git a/syntax/file.rs b/syntax/file.rs new file mode 100644 index 000000000..132a98621 --- /dev/null +++ b/syntax/file.rs @@ -0,0 +1,47 @@ +use proc_macro2::Span; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; + +pub struct Module { + pub attrs: Vec, + pub vis: Visibility, + // TODO: unsafety + pub mod_token: Token![mod], + pub ident: Ident, + pub brace_token: token::Brace, + pub content: Vec, +} + +impl Parse for Module { + fn parse(input: ParseStream) -> Result { + let mut attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let mod_token: Token![mod] = input.parse()?; + let ident: Ident = input.parse()?; + + if input.peek(Token![;]) { + return Err(Error::new( + Span::call_site(), + "#[cxx::bridge] module must have inline contents", + ))?; + } + + let content; + let brace_token = braced!(content in input); + attrs.extend(content.call(Attribute::parse_inner)?); + + let mut items = Vec::new(); + while !content.is_empty() { + items.push(content.parse()?); + } + + Ok(Module { + attrs, + vis, + mod_token, + ident, + brace_token, + content: items, + }) + } +} diff --git a/syntax/mod.rs b/syntax/mod.rs index cd6f2359e..ae37802de 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -7,6 +7,7 @@ mod derive; mod discriminant; mod doc; pub mod error; +pub mod file; pub mod ident; mod impls; pub mod mangle; From fcd8f463a0fbba06ee1e3b06d017e5a8b0fcc6a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 12:13:09 -0700 Subject: [PATCH 2/9] Data structure for source file --- gen/src/file.rs | 20 ++++++++++++++++++++ gen/src/find.rs | 4 ++-- gen/src/mod.rs | 4 +++- 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 gen/src/file.rs diff --git a/gen/src/file.rs b/gen/src/file.rs new file mode 100644 index 000000000..3b45fe750 --- /dev/null +++ b/gen/src/file.rs @@ -0,0 +1,20 @@ +use syn::parse::{Parse, ParseStream, Result}; +use syn::{Attribute, Item}; + +pub struct File { + pub attrs: Vec, + pub items: Vec, +} + +impl Parse for File { + fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_inner)?; + + let mut items = Vec::new(); + while !input.is_empty() { + items.push(input.parse()?); + } + + Ok(File { attrs, items }) + } +} diff --git a/gen/src/find.rs b/gen/src/find.rs index d897debce..d4608c8dc 100644 --- a/gen/src/find.rs +++ b/gen/src/find.rs @@ -1,6 +1,6 @@ -use crate::gen::{Error, Input, Result}; +use crate::gen::{Error, File, Input, Result}; use crate::syntax::namespace::Namespace; -use syn::{Attribute, File, Item}; +use syn::{Attribute, Item}; pub(super) fn find_bridge_mod(syntax: File) -> Result { match scan(syntax.items)? { diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 710bca224..8df3404fb 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -2,6 +2,7 @@ // the cxxbridge CLI command. mod error; +mod file; mod find; pub(super) mod include; pub(super) mod out; @@ -11,6 +12,7 @@ mod write; mod tests; use self::error::{format_err, Error, Result}; +use self::file::File; use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; @@ -56,7 +58,7 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { fn generate(source: &str, opt: Opt, header: bool) -> Result> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); - let syntax = syn::parse_file(&source)?; + let syntax: File = syn::parse_str(source)?; let bridge = find::find_bridge_mod(syntax)?; let ref namespace = bridge.namespace; let ref apis = syntax::parse_items(errors, bridge.module); From 17c3230d08481f23e7031ee8bd2ff59bba117837 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 12:21:16 -0700 Subject: [PATCH 3/9] Skip past shebang in source file --- gen/src/mod.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 8df3404fb..0aa149f37 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -49,9 +49,14 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec { Ok(source) => source, Err(err) => format_err(path, "", Error::Io(err)), }; - match generate(&source, opt, header) { + let mut source = source.as_str(); + if source.starts_with("#!") && !source.starts_with("#![") { + let shebang_end = source.find('\n').unwrap_or(source.len()); + source = &source[shebang_end..]; + } + match generate(source, opt, header) { Ok(out) => out, - Err(err) => format_err(path, &source, err), + Err(err) => format_err(path, source, err), } } From 3c64a4e144a4b7f476345e038212747fd6feb5c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 14:07:38 -0700 Subject: [PATCH 4/9] Parse full file using the new Module parser --- gen/src/error.rs | 2 -- gen/src/file.rs | 69 +++++++++++++++++++++++++++++++++++++++------ gen/src/find.rs | 47 ------------------------------ gen/src/mod.rs | 16 ++++------- macro/src/expand.rs | 8 ++++-- macro/src/lib.rs | 5 ++-- syntax/file.rs | 14 ++++++--- 7 files changed, 84 insertions(+), 77 deletions(-) delete mode 100644 gen/src/find.rs diff --git a/gen/src/error.rs b/gen/src/error.rs index 51dbbe711..537351f08 100644 --- a/gen/src/error.rs +++ b/gen/src/error.rs @@ -16,7 +16,6 @@ pub(super) type Result = std::result::Result; #[derive(Debug)] pub(super) enum Error { NoBridgeMod, - OutOfLineMod, Io(io::Error), Syn(syn::Error), } @@ -25,7 +24,6 @@ impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"), - Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"), Error::Io(err) => err.fmt(f), Error::Syn(err) => err.fmt(f), } diff --git a/gen/src/file.rs b/gen/src/file.rs index 3b45fe750..a2f86e69b 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -1,20 +1,71 @@ -use syn::parse::{Parse, ParseStream, Result}; -use syn::{Attribute, Item}; +use crate::syntax::file::Module; +use crate::syntax::namespace::Namespace; +use syn::parse::discouraged::Speculative; +use syn::parse::{Error, Parse, ParseStream, Result}; +use syn::{braced, Attribute, Ident, Item, Token, Visibility}; pub struct File { - pub attrs: Vec, - pub items: Vec, + pub modules: Vec, } impl Parse for File { fn parse(input: ParseStream) -> Result { - let attrs = input.call(Attribute::parse_inner)?; + let mut modules = Vec::new(); + input.call(Attribute::parse_inner)?; + parse(input, &mut modules)?; + Ok(File { modules }) + } +} + +fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { + while !input.is_empty() { + let mut cxx_bridge = false; + let mut namespace = Namespace::none(); + let attrs = input.call(Attribute::parse_outer)?; + for attr in &attrs { + let path = &attr.path.segments; + if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { + cxx_bridge = true; + namespace = parse_args(attr)?; + break; + } + } - let mut items = Vec::new(); - while !input.is_empty() { - items.push(input.parse()?); + let ahead = input.fork(); + ahead.parse::()?; + ahead.parse::>()?; + if !ahead.peek(Token![mod]) { + let item: Item = input.parse()?; + if cxx_bridge { + return Err(Error::new_spanned(item, "expected a module")); + } + continue; } - Ok(File { attrs, items }) + if cxx_bridge { + let mut module: Module = input.parse()?; + module.namespace = namespace; + module.attrs = attrs; + modules.push(module); + } else { + input.advance_to(&ahead); + input.parse::()?; + input.parse::()?; + let semi: Option = input.parse()?; + if semi.is_none() { + let content; + braced!(content in input); + parse(&content, modules)?; + } + } + } + Ok(()) +} + +fn parse_args(attr: &Attribute) -> Result { + if attr.tokens.is_empty() { + Ok(Namespace::none()) + } else { + attr.parse_args() } } diff --git a/gen/src/find.rs b/gen/src/find.rs deleted file mode 100644 index d4608c8dc..000000000 --- a/gen/src/find.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::gen::{Error, File, Input, Result}; -use crate::syntax::namespace::Namespace; -use syn::{Attribute, Item}; - -pub(super) fn find_bridge_mod(syntax: File) -> Result { - match scan(syntax.items)? { - Some(input) => Ok(input), - None => Err(Error::NoBridgeMod), - } -} - -fn scan(items: Vec) -> Result> { - for item in items { - if let Item::Mod(item) = item { - for attr in &item.attrs { - let path = &attr.path.segments; - if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { - let module = match item.content { - Some(module) => module.1, - None => { - return Err(Error::Syn(syn::Error::new_spanned( - item, - Error::OutOfLineMod, - ))); - } - }; - let namespace = parse_args(attr)?; - return Ok(Some(Input { namespace, module })); - } - } - if let Some(module) = item.content { - if let Some(input) = scan(module.1)? { - return Ok(Some(input)); - } - } - } - } - Ok(None) -} - -fn parse_args(attr: &Attribute) -> syn::Result { - if attr.tokens.is_empty() { - Ok(Namespace::none()) - } else { - attr.parse_args() - } -} diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 0aa149f37..fa9e4073e 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -3,7 +3,6 @@ mod error; mod file; -mod find; pub(super) mod include; pub(super) mod out; mod write; @@ -13,17 +12,10 @@ mod tests; use self::error::{format_err, Error, Result}; use self::file::File; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::{self, check, Types}; use std::fs; use std::path::Path; -use syn::Item; - -struct Input { - namespace: Namespace, - module: Vec, -} #[derive(Default)] pub(super) struct Opt { @@ -64,9 +56,13 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result> { proc_macro2::fallback::force(); let ref mut errors = Errors::new(); let syntax: File = syn::parse_str(source)?; - let bridge = find::find_bridge_mod(syntax)?; + let bridge = syntax + .modules + .into_iter() + .next() + .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; - let ref apis = syntax::parse_items(errors, bridge.module); + let ref apis = syntax::parse_items(errors, bridge.content); let ref types = Types::collect(errors, apis); errors.propagate()?; check::typecheck(errors, namespace, apis, types); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 48ef6a32b..aecec7528 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -11,21 +11,23 @@ use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::mem; use syn::{parse_quote, Result, Token}; -pub fn bridge(namespace: &Namespace, mut ffi: Module) -> Result { +pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let ref apis = syntax::parse_items(errors, content); let ref types = Types::collect(errors, apis); errors.propagate()?; + let namespace = &ffi.namespace; check::typecheck(errors, namespace, apis, types); errors.propagate()?; - Ok(expand(namespace, ffi, apis, types)) + Ok(expand(ffi, apis, types)) } -fn expand(namespace: &Namespace, ffi: Module, apis: &[Api], types: &Types) -> TokenStream { +fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); + let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b8a592b6f..c2ad38b09 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -40,9 +40,10 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { let _ = syntax::error::ERRORS; let namespace = parse_macro_input!(args as Namespace); - let ffi = parse_macro_input!(input as Module); + let mut ffi = parse_macro_input!(input as Module); + ffi.namespace = namespace; - expand::bridge(&namespace, ffi) + expand::bridge(ffi) .unwrap_or_else(|err| err.to_compile_error()) .into() } diff --git a/syntax/file.rs b/syntax/file.rs index 132a98621..dc7c2950c 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -1,8 +1,10 @@ -use proc_macro2::Span; +use crate::syntax::namespace::Namespace; +use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; pub struct Module { + pub namespace: Namespace, pub attrs: Vec, pub vis: Visibility, // TODO: unsafety @@ -14,14 +16,17 @@ pub struct Module { impl Parse for Module { fn parse(input: ParseStream) -> Result { + let namespace = Namespace::none(); let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let mod_token: Token![mod] = input.parse()?; let ident: Ident = input.parse()?; - if input.peek(Token![;]) { - return Err(Error::new( - Span::call_site(), + let semi: Option = input.parse()?; + if let Some(semi) = semi { + let span = quote!(#vis #mod_token #semi); + return Err(Error::new_spanned( + span, "#[cxx::bridge] module must have inline contents", ))?; } @@ -36,6 +41,7 @@ impl Parse for Module { } Ok(Module { + namespace, attrs, vis, mod_token, From 633f5669fa03e72917720eb44ec89811ea998907 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 14:47:45 -0700 Subject: [PATCH 5/9] Parse unsafety on module --- syntax/file.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/syntax/file.rs b/syntax/file.rs index dc7c2950c..22432356d 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -7,7 +7,7 @@ pub struct Module { pub namespace: Namespace, pub attrs: Vec, pub vis: Visibility, - // TODO: unsafety + pub unsafety: Option, pub mod_token: Token![mod], pub ident: Ident, pub brace_token: token::Brace, @@ -19,6 +19,7 @@ impl Parse for Module { let namespace = Namespace::none(); let mut attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; + let unsafety: Option = input.parse()?; let mod_token: Token![mod] = input.parse()?; let ident: Ident = input.parse()?; @@ -44,6 +45,7 @@ impl Parse for Module { namespace, attrs, vis, + unsafety, mod_token, ident, brace_token, From 00a83852cd9362c2e8bb6c4889801e3980d402c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 15:06:16 -0700 Subject: [PATCH 6/9] Data structure for parsed contents of bridge module --- syntax/file.rs | 26 +++++++++++++++++++++++++- syntax/parse.rs | 5 +++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/syntax/file.rs b/syntax/file.rs index 22432356d..f22790035 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -1,7 +1,10 @@ use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; -use syn::{braced, token, Attribute, Ident, Item, Token, Visibility}; +use syn::{ + braced, token, Attribute, Ident, Item as RustItem, ItemEnum, ItemForeignMod, ItemStruct, + ItemUse, Token, Visibility, +}; pub struct Module { pub namespace: Namespace, @@ -14,6 +17,14 @@ pub struct Module { pub content: Vec, } +pub enum Item { + Struct(ItemStruct), + Enum(ItemEnum), + ForeignMod(ItemForeignMod), + Use(ItemUse), + Other(RustItem), +} + impl Parse for Module { fn parse(input: ParseStream) -> Result { let namespace = Namespace::none(); @@ -53,3 +64,16 @@ impl Parse for Module { }) } } + +impl Parse for Item { + fn parse(input: ParseStream) -> Result { + let item = input.parse()?; + match item { + RustItem::Struct(item) => Ok(Item::Struct(item)), + RustItem::Enum(item) => Ok(Item::Enum(item)), + RustItem::ForeignMod(item) => Ok(Item::ForeignMod(item)), + RustItem::Use(item) => Ok(Item::Use(item)), + other => Ok(Item::Other(other)), + } + } +} diff --git a/syntax/parse.rs b/syntax/parse.rs index e195081f4..117d02588 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,4 +1,5 @@ use crate::syntax::discriminant::DiscriminantSet; +use crate::syntax::file::Item; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -11,7 +12,7 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, Item, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, + GenericArgument, Ident, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; @@ -33,7 +34,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec) -> Vec { }, Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis), Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED), - _ => cx.error(item, "unsupported item"), + Item::Other(item) => cx.error(item, "unsupported item"), } } apis From c598a279025c8a89b029b2899fccfe6f06f3663a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 15:10:18 -0700 Subject: [PATCH 7/9] Represent the unsafety on foreign module --- syntax/file.rs | 18 ++++++++++++++++-- syntax/parse.rs | 6 +++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/syntax/file.rs b/syntax/file.rs index f22790035..dfe8e0d18 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -2,7 +2,7 @@ use crate::syntax::namespace::Namespace; use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ - braced, token, Attribute, Ident, Item as RustItem, ItemEnum, ItemForeignMod, ItemStruct, + braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, ItemUse, Token, Visibility, }; @@ -25,6 +25,14 @@ pub enum Item { Other(RustItem), } +pub struct ItemForeignMod { + pub attrs: Vec, + pub unsafety: Option, + pub abi: Abi, + pub brace_token: token::Brace, + pub items: Vec, +} + impl Parse for Module { fn parse(input: ParseStream) -> Result { let namespace = Namespace::none(); @@ -71,7 +79,13 @@ impl Parse for Item { match item { RustItem::Struct(item) => Ok(Item::Struct(item)), RustItem::Enum(item) => Ok(Item::Enum(item)), - RustItem::ForeignMod(item) => Ok(Item::ForeignMod(item)), + RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { + attrs: item.attrs, + unsafety: None, + abi: item.abi, + brace_token: item.brace_token, + items: item.items, + })), RustItem::Use(item) => Ok(Item::Use(item)), other => Ok(Item::Other(other)), } diff --git a/syntax/parse.rs b/syntax/parse.rs index 117d02588..c23ee8f93 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -1,5 +1,5 @@ use crate::syntax::discriminant::DiscriminantSet; -use crate::syntax::file::Item; +use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ @@ -12,8 +12,8 @@ use syn::parse::{ParseStream, Parser}; use syn::punctuated::Punctuated; use syn::{ Abi, Attribute, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, - GenericArgument, Ident, ItemEnum, ItemForeignMod, ItemStruct, LitStr, Pat, PathArguments, - Result, ReturnType, Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, + GenericArgument, Ident, ItemEnum, ItemStruct, LitStr, Pat, PathArguments, Result, ReturnType, + Token, Type as RustType, TypeBareFn, TypePath, TypeReference, TypeSlice, }; pub mod kw { From 02550c04a0cb3b3def55f2c16fa9f55ccf7bc0f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 15:20:50 -0700 Subject: [PATCH 8/9] Preserve inner attrs inside bridge module --- gen/src/file.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gen/src/file.rs b/gen/src/file.rs index a2f86e69b..c69640711 100644 --- a/gen/src/file.rs +++ b/gen/src/file.rs @@ -21,7 +21,7 @@ fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { while !input.is_empty() { let mut cxx_bridge = false; let mut namespace = Namespace::none(); - let attrs = input.call(Attribute::parse_outer)?; + let mut attrs = input.call(Attribute::parse_outer)?; for attr in &attrs { let path = &attr.path.segments; if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" { @@ -45,6 +45,7 @@ fn parse(input: ParseStream, modules: &mut Vec) -> Result<()> { if cxx_bridge { let mut module: Module = input.parse()?; module.namespace = namespace; + attrs.extend(module.attrs); module.attrs = attrs; modules.push(module); } else { From 0c0cfee26499acee4563eb281809cbc543c2584c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 29 Aug 2020 15:29:25 -0700 Subject: [PATCH 9/9] Parse unsafety on extern blocks --- syntax/file.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/syntax/file.rs b/syntax/file.rs index dfe8e0d18..2141c1746 100644 --- a/syntax/file.rs +++ b/syntax/file.rs @@ -3,7 +3,7 @@ use quote::quote; use syn::parse::{Error, Parse, ParseStream, Result}; use syn::{ braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct, - ItemUse, Token, Visibility, + ItemUse, LitStr, Token, Visibility, }; pub struct Module { @@ -75,18 +75,31 @@ impl Parse for Module { impl Parse for Item { fn parse(input: ParseStream) -> Result { + let attrs = input.call(Attribute::parse_outer)?; + + let ahead = input.fork(); + let unsafety = if ahead.parse::>()?.is_some() + && ahead.parse::>()?.is_some() + && ahead.parse::>().is_ok() + && ahead.peek(token::Brace) + { + Some(input.parse()?) + } else { + None + }; + let item = input.parse()?; match item { - RustItem::Struct(item) => Ok(Item::Struct(item)), - RustItem::Enum(item) => Ok(Item::Enum(item)), + RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })), + RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })), RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod { attrs: item.attrs, - unsafety: None, + unsafety, abi: item.abi, brace_token: item.brace_token, items: item.items, })), - RustItem::Use(item) => Ok(Item::Use(item)), + RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })), other => Ok(Item::Other(other)), } }