Skip to content

Commit

Permalink
cxx-qt-gen: syn 2 supports unsafety in ItemForeignMod
Browse files Browse the repository at this point in the history
We do not need to consider verbatim code paths for parsing
unsafe extern "ABI" anymore, this is supportted in syn 2.

eg compare ItemForeignMod and notice Option<Unsafe>
syn 1 https://docs.rs/syn/1.0.109/syn/struct.ItemForeignMod.html
syn 2 https://docs.rs/syn/2.0.0/syn/struct.ItemForeignMod.html
  • Loading branch information
ahayzen-kdab committed Jun 7, 2023
1 parent 4bedcd2 commit 433a932
Show file tree
Hide file tree
Showing 4 changed files with 10 additions and 146 deletions.
41 changes: 5 additions & 36 deletions crates/cxx-qt-gen/src/parser/cxxqtdata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::syntax::foreignmod::{foreign_mod_to_foreign_item_types, verbatim_to_foreign_mod};
use crate::syntax::foreignmod::foreign_mod_to_foreign_item_types;
use crate::syntax::{attribute::attribute_find_path, path::path_to_single_ident};
use crate::{
parser::{
inherit::{InheritMethods, MaybeInheritMethods, ParsedInheritedMethod},
inherit::{InheritMethods, ParsedInheritedMethod},
qobject::ParsedQObject,
signals::{MaybeSignalMethods, ParsedSignal, SignalMethods},
signals::{ParsedSignal, SignalMethods},
},
syntax::expr::expr_to_string,
};
Expand Down Expand Up @@ -111,16 +111,8 @@ impl ParsedCxxQtData {
_others => {}
}

// Extract the foreign mod (extern "ABI" { ... })
let foreign_mod = match item {
Item::ForeignMod(foreign_mod) => Some(foreign_mod.clone()),
// Could be Verbatim TokenStream when it's an unsafe block, the remainder of the blocks are a normal ForeignMod though
Item::Verbatim(tokens) => verbatim_to_foreign_mod(tokens)?,
_others => None,
};

// If there is a foreign mod then process it
if let Some(foreign_mod) = &foreign_mod {
if let Item::ForeignMod(foreign_mod) = &item {
// Retrieve a namespace from the mod or the bridge
let block_namespace =
if let Some(index) = attribute_find_path(&foreign_mod.attrs, &["namespace"]) {
Expand Down Expand Up @@ -187,34 +179,11 @@ impl ParsedCxxQtData {
self.uses.push(item);
Ok(None)
}
Item::Verbatim(tokens) => self.try_parse_verbatim(tokens),
Item::ForeignMod(foreign_mod) => self.parse_foreign_mod(foreign_mod),
_ => Ok(Some(item)),
}
}

fn try_parse_verbatim(&mut self, tokens: TokenStream) -> Result<Option<Item>> {
let try_parse: MaybeInheritMethods = syn::parse2(tokens.clone())?;

match try_parse {
MaybeInheritMethods::Found(inherited) => {
self.add_inherited_methods(inherited)?;
Ok(None)
}
MaybeInheritMethods::PassThrough(_item) => {
let try_parse: MaybeSignalMethods = syn::parse2(tokens)?;

match try_parse {
MaybeSignalMethods::Found(signals) => {
self.add_signal_methods(signals)?;
Ok(None)
}
MaybeSignalMethods::PassThrough(item) => Ok(Some(item)),
}
}
}
}

fn parse_inherit_mod(&mut self, tokens: TokenStream) -> Result<()> {
let inherited: InheritMethods = syn::parse2(tokens)?;

Expand Down Expand Up @@ -594,7 +563,7 @@ mod tests {
}

#[test]
fn test_cxx_mappings_cxx_name_verbatim() {
fn test_cxx_mappings_cxx_name_unsafe() {
let mut cxx_qt_data = create_parsed_cxx_qt_data();

let item: Item = parse_quote! {
Expand Down
42 changes: 4 additions & 38 deletions crates/cxx-qt-gen/src/parser/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,9 @@ use quote::format_ident;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Attribute, Error, ForeignItem, ForeignItemFn, Ident, Item, ItemForeignMod, LitStr, Result,
Token,
Attribute, Error, ForeignItem, ForeignItemFn, Ident, ItemForeignMod, LitStr, Result, Token,
};

/// Used when parsing a syn::Item::Verbatim, that we suspect may be a `#[cxx_qt::inherit]` block,
/// but we don't yet know whether this is actually the case.
/// This is the case if `#[cxx_qt::inherit]` is used with `unsafe extern "C++"`.
pub enum MaybeInheritMethods {
/// We found a `#[cxx_qt::inherit]` block
Found(InheritMethods),
/// `#[cxx_qt::inherit]` block not found, pass this Item through to outside code!
PassThrough(Item),
}

impl Parse for MaybeInheritMethods {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.fork();
if let Ok(attribute) = lookahead.call(Attribute::parse_outer) {
if attribute_find_path(attribute.as_slice(), &["cxx_qt", "inherit"]).is_some() {
input.call(Attribute::parse_outer)?;
let methods = input.parse::<InheritMethods>()?;
return Ok(Self::Found(methods));
}
}

Ok(Self::PassThrough(input.parse()?))
}
}

/// This type is used when parsing the `#[cxx_qt::inherit]` macro contents into raw ForeignItemFn items
pub struct InheritMethods {
pub safety: Safety,
Expand Down Expand Up @@ -185,22 +159,14 @@ mod tests {
#[test]
fn test_parse_safe_mod() {
let module = quote! {
#[cxx_qt::inherit]
unsafe extern "C++" {
fn test(self: &qobject::T);
unsafe fn test2(self: &qobject::T);
}
};
let parsed: MaybeInheritMethods = syn::parse2(module).unwrap();
match parsed {
MaybeInheritMethods::Found(inherit) => {
assert_eq!(inherit.base_functions.len(), 2);
assert_eq!(inherit.safety, Safety::Safe);
}
MaybeInheritMethods::PassThrough(item) => {
panic!("Expected InheritMethods, got {item:?}");
}
}
let parsed: InheritMethods = syn::parse2(module).unwrap();
assert_eq!(parsed.base_functions.len(), 2);
assert_eq!(parsed.safety, Safety::Safe);
}

#[test]
Expand Down
27 changes: 1 addition & 26 deletions crates/cxx-qt-gen/src/parser/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,9 @@ use syn::Attribute;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Error, ForeignItem, ForeignItemFn, Ident, Item, ItemForeignMod, LitStr, Result, Token,
Error, ForeignItem, ForeignItemFn, Ident, ItemForeignMod, LitStr, Result, Token,
};

/// Used when parsing a syn::Item::Verbatim, that we suspect may be a `#[cxx_qt::qsignals]` block,
/// but we don't yet know whether this is actually the case.
/// This is the case if `#[cxx_qt::qsignals]` is used with `unsafe extern "C++"`.
pub enum MaybeSignalMethods {
/// We found a `#[cxx_qt::qsignals]` block
Found(SignalMethods),
/// `#[cxx_qt::qsignals]` block not found, pass this Item through to outside code!
PassThrough(Item),
}

impl Parse for MaybeSignalMethods {
fn parse(input: ParseStream) -> Result<Self> {
let lookahead = input.fork();
if let Ok(attribute) = lookahead.call(Attribute::parse_outer) {
if attribute_find_path(attribute.as_slice(), &["cxx_qt", "qsignals"]).is_some() {
input.call(Attribute::parse_outer)?;
let methods = input.parse::<SignalMethods>()?;
return Ok(Self::Found(methods));
}
}

Ok(Self::PassThrough(input.parse()?))
}
}

/// This type is used when parsing the `#[cxx_qt::qsignals]` macro contents into raw ForeignItemFn items
pub struct SignalMethods {
pub safety: Safety,
Expand Down
46 changes: 0 additions & 46 deletions crates/cxx-qt-gen/src/syntax/foreignmod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,38 +42,6 @@ pub(crate) fn foreign_mod_to_foreign_item_types(
.collect::<Result<Vec<ForeignItemType>>>()
}

/// For a given verbatim [proc_macro2::TokenStream] return a [syn::ItemForegnMod] if there is one
///
/// And ignore any unsafe token before the extern block
pub(crate) fn verbatim_to_foreign_mod(tokens: &TokenStream) -> Result<Option<ItemForeignMod>> {
|input: ParseStream| -> Result<Option<ItemForeignMod>> {
// Parse any namespace attributes on the outside of the unsafe extern block
let mut attrs = input.call(Attribute::parse_outer)?;

// If we are an unsafe then extern block try to parse it
if input.peek(Token![unsafe]) && input.peek2(Token![extern]) {
input.parse::<Token![unsafe]>()?;
let mut foreign_mod = input.parse::<ItemForeignMod>()?;
// Inject the attributes from the outside of the unsafe block into the foreign mod
attrs.append(&mut foreign_mod.attrs);
foreign_mod.attrs = attrs;
Ok(Some(foreign_mod))
} else {
// Move the cursor past all remaining tokens, otherwise parse2 fails
input.step(|cursor| {
let mut rest = *cursor;
while let Some((_, next)) = rest.token_tree() {
rest = next;
}
Ok(((), rest))
})?;

Ok(None)
}
}
.parse2(tokens.clone())
}

/// For a given verbatim [proc_macro2::TokenStream] return the [syn::ForeignItemType] if there is one
///
/// And ignore any extra syntax after the = in type A = ...
Expand Down Expand Up @@ -178,20 +146,6 @@ mod tests {
assert_eq!(result[1].ident, "B");
}

#[test]
fn test_verbatim_to_foreign_mod() {
let tokens = quote! {
#[namespace = "a"]
unsafe extern "C++" {
type A;
}
};
let result = verbatim_to_foreign_mod(&tokens).unwrap();
let result = result.unwrap();
assert_eq!(result.attrs.len(), 1);
assert_eq!(result.items.len(), 1);
}

#[test]
fn test_foreign_fn_self() {
let foreign_fn: ForeignItemFn = parse_quote! {
Expand Down

0 comments on commit 433a932

Please sign in to comment.