Skip to content

Commit 3c01ac2

Browse files
authored
Merge pull request #270 from dtolnay/parse
Parse unsafe modules and unsafe extern blocks
2 parents 5e668bc + 0c0cfee commit 3c01ac2

File tree

9 files changed

+215
-78
lines changed

9 files changed

+215
-78
lines changed

gen/src/error.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
1616
#[derive(Debug)]
1717
pub(super) enum Error {
1818
NoBridgeMod,
19-
OutOfLineMod,
2019
Io(io::Error),
2120
Syn(syn::Error),
2221
}
@@ -25,7 +24,6 @@ impl Display for Error {
2524
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2625
match self {
2726
Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
28-
Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"),
2927
Error::Io(err) => err.fmt(f),
3028
Error::Syn(err) => err.fmt(f),
3129
}

gen/src/file.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use crate::syntax::file::Module;
2+
use crate::syntax::namespace::Namespace;
3+
use syn::parse::discouraged::Speculative;
4+
use syn::parse::{Error, Parse, ParseStream, Result};
5+
use syn::{braced, Attribute, Ident, Item, Token, Visibility};
6+
7+
pub struct File {
8+
pub modules: Vec<Module>,
9+
}
10+
11+
impl Parse for File {
12+
fn parse(input: ParseStream) -> Result<Self> {
13+
let mut modules = Vec::new();
14+
input.call(Attribute::parse_inner)?;
15+
parse(input, &mut modules)?;
16+
Ok(File { modules })
17+
}
18+
}
19+
20+
fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
21+
while !input.is_empty() {
22+
let mut cxx_bridge = false;
23+
let mut namespace = Namespace::none();
24+
let mut attrs = input.call(Attribute::parse_outer)?;
25+
for attr in &attrs {
26+
let path = &attr.path.segments;
27+
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
28+
cxx_bridge = true;
29+
namespace = parse_args(attr)?;
30+
break;
31+
}
32+
}
33+
34+
let ahead = input.fork();
35+
ahead.parse::<Visibility>()?;
36+
ahead.parse::<Option<Token![unsafe]>>()?;
37+
if !ahead.peek(Token![mod]) {
38+
let item: Item = input.parse()?;
39+
if cxx_bridge {
40+
return Err(Error::new_spanned(item, "expected a module"));
41+
}
42+
continue;
43+
}
44+
45+
if cxx_bridge {
46+
let mut module: Module = input.parse()?;
47+
module.namespace = namespace;
48+
attrs.extend(module.attrs);
49+
module.attrs = attrs;
50+
modules.push(module);
51+
} else {
52+
input.advance_to(&ahead);
53+
input.parse::<Token![mod]>()?;
54+
input.parse::<Ident>()?;
55+
let semi: Option<Token![;]> = input.parse()?;
56+
if semi.is_none() {
57+
let content;
58+
braced!(content in input);
59+
parse(&content, modules)?;
60+
}
61+
}
62+
}
63+
Ok(())
64+
}
65+
66+
fn parse_args(attr: &Attribute) -> Result<Namespace> {
67+
if attr.tokens.is_empty() {
68+
Ok(Namespace::none())
69+
} else {
70+
attr.parse_args()
71+
}
72+
}

gen/src/find.rs

Lines changed: 0 additions & 47 deletions
This file was deleted.

gen/src/mod.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// the cxxbridge CLI command.
33

44
mod error;
5-
mod find;
5+
mod file;
66
pub(super) mod include;
77
pub(super) mod out;
88
mod write;
@@ -11,17 +11,11 @@ mod write;
1111
mod tests;
1212

1313
use self::error::{format_err, Error, Result};
14-
use crate::syntax::namespace::Namespace;
14+
use self::file::File;
1515
use crate::syntax::report::Errors;
1616
use crate::syntax::{self, check, Types};
1717
use std::fs;
1818
use std::path::Path;
19-
use syn::Item;
20-
21-
struct Input {
22-
namespace: Namespace,
23-
module: Vec<Item>,
24-
}
2519

2620
#[derive(Default)]
2721
pub(super) struct Opt {
@@ -47,19 +41,28 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
4741
Ok(source) => source,
4842
Err(err) => format_err(path, "", Error::Io(err)),
4943
};
50-
match generate(&source, opt, header) {
44+
let mut source = source.as_str();
45+
if source.starts_with("#!") && !source.starts_with("#![") {
46+
let shebang_end = source.find('\n').unwrap_or(source.len());
47+
source = &source[shebang_end..];
48+
}
49+
match generate(source, opt, header) {
5150
Ok(out) => out,
52-
Err(err) => format_err(path, &source, err),
51+
Err(err) => format_err(path, source, err),
5352
}
5453
}
5554

5655
fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
5756
proc_macro2::fallback::force();
5857
let ref mut errors = Errors::new();
59-
let syntax = syn::parse_file(&source)?;
60-
let bridge = find::find_bridge_mod(syntax)?;
58+
let syntax: File = syn::parse_str(source)?;
59+
let bridge = syntax
60+
.modules
61+
.into_iter()
62+
.next()
63+
.ok_or(Error::NoBridgeMod)?;
6164
let ref namespace = bridge.namespace;
62-
let ref apis = syntax::parse_items(errors, bridge.module);
65+
let ref apis = syntax::parse_items(errors, bridge.content);
6366
let ref types = Types::collect(errors, apis);
6467
errors.propagate()?;
6568
check::typecheck(errors, namespace, apis, types);

macro/src/expand.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
use crate::syntax::atom::Atom::{self, *};
2+
use crate::syntax::file::Module;
23
use crate::syntax::namespace::Namespace;
34
use crate::syntax::report::Errors;
45
use crate::syntax::symbol::Symbol;
56
use crate::syntax::{
67
self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types,
78
};
8-
use proc_macro2::{Ident, Span, TokenStream};
9+
use proc_macro2::{Ident, TokenStream};
910
use quote::{format_ident, quote, quote_spanned, ToTokens};
10-
use syn::{parse_quote, Error, ItemMod, Result, Token};
11+
use std::mem;
12+
use syn::{parse_quote, Result, Token};
1113

12-
pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result<TokenStream> {
14+
pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
1315
let ref mut errors = Errors::new();
14-
let content = ffi.content.take().ok_or(Error::new(
15-
Span::call_site(),
16-
"#[cxx::bridge] module must have inline contents",
17-
))?;
18-
let ref apis = syntax::parse_items(errors, content.1);
16+
let content = mem::take(&mut ffi.content);
17+
let ref apis = syntax::parse_items(errors, content);
1918
let ref types = Types::collect(errors, apis);
2019
errors.propagate()?;
20+
let namespace = &ffi.namespace;
2121
check::typecheck(errors, namespace, apis, types);
2222
errors.propagate()?;
2323

24-
Ok(expand(namespace, ffi, apis, types))
24+
Ok(expand(ffi, apis, types))
2525
}
2626

27-
fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream {
27+
fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
2828
let mut expanded = TokenStream::new();
2929
let mut hidden = TokenStream::new();
30+
let namespace = &ffi.namespace;
3031

3132
for api in apis {
3233
if let Api::RustType(ety) = api {

macro/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ mod expand;
1313
mod syntax;
1414
mod type_id;
1515

16+
use crate::syntax::file::Module;
1617
use crate::syntax::namespace::Namespace;
1718
use proc_macro::TokenStream;
18-
use syn::{parse_macro_input, ItemMod, LitStr};
19+
use syn::{parse_macro_input, LitStr};
1920

2021
/// `#[cxx::bridge] mod ffi { ... }`
2122
///
@@ -39,9 +40,10 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
3940
let _ = syntax::error::ERRORS;
4041

4142
let namespace = parse_macro_input!(args as Namespace);
42-
let ffi = parse_macro_input!(input as ItemMod);
43+
let mut ffi = parse_macro_input!(input as Module);
44+
ffi.namespace = namespace;
4345

44-
expand::bridge(&namespace, ffi)
46+
expand::bridge(ffi)
4547
.unwrap_or_else(|err| err.to_compile_error())
4648
.into()
4749
}

syntax/file.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use crate::syntax::namespace::Namespace;
2+
use quote::quote;
3+
use syn::parse::{Error, Parse, ParseStream, Result};
4+
use syn::{
5+
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
6+
ItemUse, LitStr, Token, Visibility,
7+
};
8+
9+
pub struct Module {
10+
pub namespace: Namespace,
11+
pub attrs: Vec<Attribute>,
12+
pub vis: Visibility,
13+
pub unsafety: Option<Token![unsafe]>,
14+
pub mod_token: Token![mod],
15+
pub ident: Ident,
16+
pub brace_token: token::Brace,
17+
pub content: Vec<Item>,
18+
}
19+
20+
pub enum Item {
21+
Struct(ItemStruct),
22+
Enum(ItemEnum),
23+
ForeignMod(ItemForeignMod),
24+
Use(ItemUse),
25+
Other(RustItem),
26+
}
27+
28+
pub struct ItemForeignMod {
29+
pub attrs: Vec<Attribute>,
30+
pub unsafety: Option<Token![unsafe]>,
31+
pub abi: Abi,
32+
pub brace_token: token::Brace,
33+
pub items: Vec<ForeignItem>,
34+
}
35+
36+
impl Parse for Module {
37+
fn parse(input: ParseStream) -> Result<Self> {
38+
let namespace = Namespace::none();
39+
let mut attrs = input.call(Attribute::parse_outer)?;
40+
let vis: Visibility = input.parse()?;
41+
let unsafety: Option<Token![unsafe]> = input.parse()?;
42+
let mod_token: Token![mod] = input.parse()?;
43+
let ident: Ident = input.parse()?;
44+
45+
let semi: Option<Token![;]> = input.parse()?;
46+
if let Some(semi) = semi {
47+
let span = quote!(#vis #mod_token #semi);
48+
return Err(Error::new_spanned(
49+
span,
50+
"#[cxx::bridge] module must have inline contents",
51+
))?;
52+
}
53+
54+
let content;
55+
let brace_token = braced!(content in input);
56+
attrs.extend(content.call(Attribute::parse_inner)?);
57+
58+
let mut items = Vec::new();
59+
while !content.is_empty() {
60+
items.push(content.parse()?);
61+
}
62+
63+
Ok(Module {
64+
namespace,
65+
attrs,
66+
vis,
67+
unsafety,
68+
mod_token,
69+
ident,
70+
brace_token,
71+
content: items,
72+
})
73+
}
74+
}
75+
76+
impl Parse for Item {
77+
fn parse(input: ParseStream) -> Result<Self> {
78+
let attrs = input.call(Attribute::parse_outer)?;
79+
80+
let ahead = input.fork();
81+
let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
82+
&& ahead.parse::<Option<Token![extern]>>()?.is_some()
83+
&& ahead.parse::<Option<LitStr>>().is_ok()
84+
&& ahead.peek(token::Brace)
85+
{
86+
Some(input.parse()?)
87+
} else {
88+
None
89+
};
90+
91+
let item = input.parse()?;
92+
match item {
93+
RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })),
94+
RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })),
95+
RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod {
96+
attrs: item.attrs,
97+
unsafety,
98+
abi: item.abi,
99+
brace_token: item.brace_token,
100+
items: item.items,
101+
})),
102+
RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
103+
other => Ok(Item::Other(other)),
104+
}
105+
}
106+
}

syntax/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod derive;
77
mod discriminant;
88
mod doc;
99
pub mod error;
10+
pub mod file;
1011
pub mod ident;
1112
mod impls;
1213
pub mod mangle;

0 commit comments

Comments
 (0)