Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ParsedMethod, ParsedSignal and ParsedInheritedMethod #1054

Merged
merged 9 commits into from
Sep 2, 2024
2 changes: 1 addition & 1 deletion crates/cxx-qt-gen/src/generator/cpp/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub fn generate(
return {base_class}::{func_ident}(args...);
}}"#,
mutability = if method.mutable { "" } else { " const" },
func_ident = method.ident.cxx_unqualified(),
func_ident = method.name.cxx_unqualified(),
wrapper_ident = method.wrapper_ident(),
return_type = return_type.unwrap_or_else(|| "void".to_string()),
base_class = base_class
Expand Down
5 changes: 5 additions & 0 deletions crates/cxx-qt-gen/src/generator/rust/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub fn generate(
quote! { #ident: #ty }
})
.collect::<Vec<TokenStream>>();

let ident = &method.method.sig.ident;
let cxx_name_string = &method.wrapper_ident().to_string();
let self_param = if method.mutable {
Expand All @@ -44,12 +45,16 @@ pub fn generate(
if method.safe {
std::mem::swap(&mut unsafe_call, &mut unsafe_block);
}

let attrs = &method.method.attrs;
let doc_comments = &method.docs;

syn::parse2(quote_spanned! {
method.method.span() =>
#unsafe_block extern "C++" {
#(#attrs)*
#[cxx_name = #cxx_name_string]
#(#doc_comments)*
#unsafe_call fn #ident(#self_param, #(#parameters),*) #return_type;
}
})
Expand Down
13 changes: 6 additions & 7 deletions crates/cxx-qt-gen/src/generator/rust/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ pub fn generate_rust_methods(

let return_type = &invokable.method.sig.output;

let mut unsafe_block = None;
let mut unsafe_call = Some(quote! { unsafe });
if invokable.safe {
std::mem::swap(&mut unsafe_call, &mut unsafe_block);
std::mem::swap(&mut unsafe_call, &mut None);
}

let fragment = RustFragmentPair {
Expand All @@ -51,8 +50,8 @@ pub fn generate_rust_methods(
// Note that we are exposing a Rust method on the C++ type to C++
//
// CXX ends up generating the source, then we generate the matching header.
#[doc(hidden)]
#[cxx_name = #wrapper_ident_cpp]
#[doc(hidden)]
// TODO: Add #[namespace] of the QObject
#unsafe_call fn #invokable_ident_rust(#parameter_signatures) #return_type;
}
Expand Down Expand Up @@ -179,8 +178,8 @@ mod tests {
&generated.cxx_mod_contents[0],
quote! {
extern "Rust" {
#[doc(hidden)]
LeonMatthesKDAB marked this conversation as resolved.
Show resolved Hide resolved
#[cxx_name = "voidInvokableWrapper"]
#[doc(hidden)]
fn void_invokable(self: &MyObject);
}
},
Expand All @@ -191,8 +190,8 @@ mod tests {
&generated.cxx_mod_contents[1],
quote! {
extern "Rust" {
#[doc(hidden)]
#[cxx_name = "trivialInvokableWrapper"]
#[doc(hidden)]
fn trivial_invokable(self: &MyObject, param: i32) -> i32;
}
},
Expand All @@ -203,8 +202,8 @@ mod tests {
&generated.cxx_mod_contents[2],
quote! {
extern "Rust" {
#[doc(hidden)]
#[cxx_name = "opaqueInvokableWrapper"]
#[doc(hidden)]
fn opaque_invokable(self: Pin<&mut MyObject>, param: &QColor) -> UniquePtr<QColor>;
}
},
Expand All @@ -215,8 +214,8 @@ mod tests {
&generated.cxx_mod_contents[3],
quote! {
extern "Rust" {
#[doc(hidden)]
#[cxx_name = "unsafeInvokableWrapper"]
#[doc(hidden)]
unsafe fn unsafe_invokable(self:&MyObject, param: *mut T) -> *mut T;
}
},
Expand Down
25 changes: 25 additions & 0 deletions crates/cxx-qt-gen/src/generator/structuring/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,31 @@ mod tests {
.is_err());
}

#[test]
fn test_inherited_lookup() {
let module = parse_quote! {
#[cxx_qt::bridge]
mod ffi {
extern "RustQt" {
#[qobject]
type MyObject = super::MyObjectRust;
}

unsafe extern "RustQt" {
#[qinvokable]
#[inherit]
fn test_fn(self: Pin<&mut MyObject>);
}
}
};

let parser = Parser::from(module).unwrap();
let structures = Structures::new(&parser.cxx_qt_data).unwrap();

let qobject = structures.qobjects.first().unwrap();
assert!(qobject.method_lookup(&format_ident!("test_fn")).is_ok());
}

#[test]
fn test_structures() {
let module = parse_quote! {
Expand Down
24 changes: 13 additions & 11 deletions crates/cxx-qt-gen/src/generator/structuring/qobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ pub struct StructuredQObject<'a> {
pub threading: bool,
}

fn lookup<T>(invokables: &[T], id: &Ident, name_getter: impl Fn(&T) -> &Name) -> Option<Name> {
invokables
.iter()
.map(name_getter)
.find(|name| name.rust_unqualified() == id)
.cloned()
}

impl<'a> StructuredQObject<'a> {
pub fn has_qobject_name(&self, ident: &Ident) -> bool {
self.declaration.name.rust_unqualified() == ident
Expand All @@ -44,22 +52,16 @@ impl<'a> StructuredQObject<'a> {
}
}

/// Returns the name of the method with the provided Rust ident if it exists, or an error
pub fn method_lookup(&self, id: &Ident) -> Result<Name> {
// TODO account for inherited methods too since those are in a different vector
self.methods
.iter()
.map(|method| &method.name)
.find(|name| name.rust_unqualified() == id)
.cloned()
lookup(&self.methods, id, |method| &method.name)
.or_else(|| lookup(&self.inherited_methods, id, |inherited| &inherited.name)) // fallback to searching inherited methods
.ok_or_else(|| Error::new_spanned(id, format!("Method with name '{id}' not found!")))
}

/// Returns the name of the signal with the provided Rust ident if it exists, or an error
pub fn signal_lookup(&self, id: &Ident) -> Result<Name> {
self.signals
.iter()
.map(|signal| &signal.name)
.find(|name| name.rust_unqualified() == id)
.cloned()
lookup(&self.signals, id, |signal| &signal.name)
.ok_or_else(|| Error::new_spanned(id, format!("Signal with name '{id}' not found!")))
}

Expand Down
59 changes: 26 additions & 33 deletions crates/cxx-qt-gen/src/parser/inherit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
//
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::parser::method::MethodFields;
use crate::parser::{check_safety, separate_docs};
use crate::{
naming::Name,
parser::parameter::ParsedFunctionParameter,
syntax::{
attribute::attribute_take_path, expr::expr_to_string, foreignmod, safety::Safety, types,
},
syntax::{attribute::attribute_take_path, safety::Safety},
};
use quote::format_ident;
use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result};
use syn::{Attribute, ForeignItemFn, Ident, Result};

/// Describes a method found in an extern "RustQt" with #[inherit]
pub struct ParsedInheritedMethod {
Expand All @@ -26,46 +26,39 @@ pub struct ParsedInheritedMethod {
/// the parameters of the method, without the `self` argument
pub parameters: Vec<ParsedFunctionParameter>,
/// the name of the function in Rust, as well as C++
pub ident: Name,
pub name: Name,
/// All the docs (each line) of the inherited method
pub docs: Vec<Attribute>,
}

impl ParsedInheritedMethod {
pub fn parse(mut method: ForeignItemFn, safety: Safety) -> Result<Self> {
if safety == Safety::Unsafe && method.sig.unsafety.is_none() {
return Err(Error::new(
method.span(),
"Inherited methods must be marked as unsafe or wrapped in an `unsafe extern \"RustQt\"` block!",
));
}

let self_receiver = foreignmod::self_type_from_foreign_fn(&method.sig)?;
let (qobject_ident, mutability) = types::extract_qobject_ident(&self_receiver.ty)?;
let mutable = mutability.is_some();

let parameters = ParsedFunctionParameter::parse_all_ignoring_receiver(&method.sig)?;
check_safety(&method, &safety)?;

let mut ident =
Name::from_rust_ident_and_attrs(&method.sig.ident, &method.attrs, None, None)?;
let docs = separate_docs(&mut method);
let invokable_fields = MethodFields::parse(&method, docs)?;

if let Some(attr) = attribute_take_path(&mut method.attrs, &["cxx_name"]) {
ident = ident.with_cxx_name(expr_to_string(&attr.meta.require_name_value()?.value)?);
}
// This block seems unnecessary but since attrs are passed through on generator/rust/inherit.rs a duplicate attr would occur without it
attribute_take_path(&mut method.attrs, &["cxx_name"]);

let safe = method.sig.unsafety.is_none();
Ok(Self::from_invokable_fields(invokable_fields, method))
}

Ok(Self {
fn from_invokable_fields(fields: MethodFields, method: ForeignItemFn) -> Self {
Self {
method,
qobject_ident,
mutable,
parameters,
ident,
safe,
})
qobject_ident: fields.qobject_ident,
mutable: fields.mutable,
safe: fields.safe,
parameters: fields.parameters,
name: fields.name,
docs: fields.docs,
}
}

/// the name of the wrapper function in C++
pub fn wrapper_ident(&self) -> Ident {
format_ident!("{}CxxQtInherit", self.ident.cxx_unqualified())
format_ident!("{}CxxQtInherit", self.name.cxx_unqualified())
}
}

Expand Down Expand Up @@ -146,10 +139,10 @@ mod tests {
assert_eq!(parsed.qobject_ident, format_ident!("T"));
assert_eq!(parsed.parameters.len(), 2);
assert_eq!(
parsed.ident.rust_unqualified().to_string(),
parsed.name.rust_unqualified().to_string(),
String::from("test")
);
assert_eq!(parsed.ident.cxx_unqualified(), String::from("testFunction"));
assert_eq!(parsed.name.cxx_unqualified(), String::from("testFunction"));
assert_eq!(
parsed.wrapper_ident(),
format_ident!("testFunctionCxxQtInherit")
Expand Down
Loading
Loading