6
6
use super :: qnamespace:: ParsedQNamespace ;
7
7
use super :: trait_impl:: TraitImpl ;
8
8
use crate :: naming:: cpp:: err_unsupported_item;
9
+ use crate :: parser:: method:: MethodFields ;
9
10
use crate :: parser:: CaseConversion ;
10
11
use crate :: {
11
12
parser:: {
@@ -17,6 +18,8 @@ use crate::{
17
18
path:: path_compare_str,
18
19
} ,
19
20
} ;
21
+ use quote:: format_ident;
22
+ use std:: ops:: DerefMut ;
20
23
use syn:: {
21
24
spanned:: Spanned , Error , ForeignItem , Ident , Item , ItemEnum , ItemForeignMod , ItemImpl ,
22
25
ItemMacro , Meta , Result ,
@@ -65,6 +68,33 @@ impl ParsedCxxQtData {
65
68
}
66
69
}
67
70
71
+ fn try_inline_self_types (
72
+ inline : bool ,
73
+ type_to_inline : & Option < Ident > ,
74
+ invokables : & mut [ impl DerefMut < Target = MethodFields > ] ,
75
+ ) -> Result < ( ) > {
76
+ for method in invokables. iter_mut ( ) {
77
+ if method. self_unresolved {
78
+ if inline {
79
+ if let Some ( inline_type) = type_to_inline. clone ( ) {
80
+ method. qobject_ident = inline_type;
81
+ } else {
82
+ return Err ( Error :: new (
83
+ method. method . span ( ) ,
84
+ "Expected a type to inline, no `qobject` typename was passed!" ,
85
+ ) ) ;
86
+ }
87
+ } else {
88
+ return Err ( Error :: new (
89
+ method. method . span ( ) ,
90
+ "`Self` type can only be inferred if the extern block contains only one `qobject`." ,
91
+ ) ) ;
92
+ }
93
+ }
94
+ }
95
+ Ok ( ( ) )
96
+ }
97
+
68
98
/// Determine if the given [syn::Item] is a CXX-Qt related item
69
99
/// If it is then add the [syn::Item] into qobjects BTreeMap
70
100
/// Otherwise return the [syn::Item] to pass through to CXX
@@ -139,6 +169,12 @@ impl ParsedCxxQtData {
139
169
140
170
let auto_case = CaseConversion :: from_attrs ( & attrs) ?;
141
171
172
+ let mut qobjects = vec ! [ ] ;
173
+
174
+ let mut methods = vec ! [ ] ;
175
+ let mut signals = vec ! [ ] ;
176
+ let mut inherited = vec ! [ ] ;
177
+
142
178
let namespace = attrs
143
179
. get ( "namespace" )
144
180
. map ( |attr| expr_to_string ( & attr. meta . require_name_value ( ) ?. value ) )
@@ -159,7 +195,7 @@ impl ParsedCxxQtData {
159
195
return Err ( Error :: new ( foreign_fn. span ( ) , "block must be declared `unsafe extern \" RustQt\" ` if it contains any safe-to-call #[inherit] qsignals" ) ) ;
160
196
}
161
197
162
- self . signals . push ( parsed_signal_method) ;
198
+ signals. push ( parsed_signal_method) ;
163
199
164
200
// Test if the function is an inheritance method
165
201
//
@@ -175,15 +211,15 @@ impl ParsedCxxQtData {
175
211
let parsed_inherited_method =
176
212
ParsedInheritedMethod :: parse ( foreign_fn, auto_case) ?;
177
213
178
- self . inherited_methods . push ( parsed_inherited_method) ;
214
+ inherited . push ( parsed_inherited_method) ;
179
215
// Remaining methods are either C++ methods or invokables
180
216
} else {
181
217
let parsed_method = ParsedMethod :: parse (
182
218
foreign_fn,
183
219
auto_case,
184
220
foreign_mod. unsafety . is_some ( ) ,
185
221
) ?;
186
- self . methods . push ( parsed_method) ;
222
+ methods. push ( parsed_method) ;
187
223
}
188
224
}
189
225
ForeignItem :: Verbatim ( tokens) => {
@@ -199,12 +235,28 @@ impl ParsedCxxQtData {
199
235
200
236
// Note that we assume a compiler error will occur later
201
237
// if you had two structs with the same name
202
- self . qobjects . push ( qobject) ;
238
+ qobjects. push ( qobject) ;
203
239
}
204
- // Const Macro, Type are unsupported in extern "RustQt" for now
240
+ // Const, Macro, Type are unsupported in extern "RustQt" for now
205
241
_ => return Err ( err_unsupported_item ( & item) ) ,
206
242
}
207
243
}
244
+
245
+ // If there is exaclty one qobject in the block, it can be inlined as a self type.
246
+ let inline_self = qobjects. len ( ) == 1 ;
247
+ let inline_ident = qobjects
248
+ . last ( )
249
+ . map ( |obj| format_ident ! ( "{}" , obj. name. cxx_unqualified( ) ) ) ;
250
+
251
+ Self :: try_inline_self_types ( inline_self, & inline_ident, & mut methods) ?;
252
+ Self :: try_inline_self_types ( inline_self, & inline_ident, & mut signals) ?;
253
+ Self :: try_inline_self_types ( inline_self, & inline_ident, & mut inherited) ?;
254
+
255
+ self . qobjects . extend ( qobjects) ;
256
+ self . methods . extend ( methods) ;
257
+ self . signals . extend ( signals) ;
258
+ self . inherited_methods . extend ( inherited) ;
259
+
208
260
Ok ( ( ) )
209
261
}
210
262
@@ -735,4 +787,58 @@ mod tests {
735
787
Some ( "b" )
736
788
) ;
737
789
}
790
+
791
+ #[ test]
792
+ fn test_self_inlining_methods ( ) {
793
+ let mut parsed_cxxqtdata = ParsedCxxQtData :: new ( format_ident ! ( "ffi" ) , None ) ;
794
+ let extern_rust_qt: Item = parse_quote ! {
795
+ unsafe extern "RustQt" {
796
+ #[ qobject]
797
+ type MyObject = super :: T ;
798
+
799
+ fn my_method( & self ) ;
800
+
801
+ #[ inherit]
802
+ fn my_inherited_method( & self ) ;
803
+ }
804
+ } ;
805
+
806
+ parsed_cxxqtdata. parse_cxx_qt_item ( extern_rust_qt) . unwrap ( ) ;
807
+ }
808
+
809
+ #[ test]
810
+ fn test_self_inlining_methods_invalid ( ) {
811
+ assert_parse_errors ! {
812
+ |item| ParsedCxxQtData :: new( format_ident!( "ffi" ) , None ) . parse_cxx_qt_item( item) =>
813
+ {
814
+ extern "RustQt" {
815
+ fn my_method( & self ) ;
816
+ }
817
+ }
818
+
819
+ {
820
+ extern "RustQt" {
821
+ #[ qobject]
822
+ type MyObject = super :: T ;
823
+
824
+ #[ qobject]
825
+ type MyOtherObject = super :: S ;
826
+
827
+ fn my_method( & self ) ;
828
+ }
829
+ }
830
+ }
831
+ }
832
+
833
+ #[ test]
834
+ fn test_invalid_inline_call ( ) {
835
+ let method_sig = parse_quote ! {
836
+ fn test( & self ) ;
837
+ } ;
838
+ let mut methods = vec ! [ ParsedMethod :: mock_qinvokable( & method_sig) ] ;
839
+
840
+ // If inlining is set to take place, an Ident is required to inline, here it is `None`
841
+ let data = ParsedCxxQtData :: try_inline_self_types ( true , & None , & mut methods) ;
842
+ assert ! ( data. is_err( ) ) ;
843
+ }
738
844
}
0 commit comments