Skip to content

Commit a877c24

Browse files
LeonMatthesKDABahayzen-kdab
authored andcommitted
TypeNames::cxx_qualified(): Error on onknown type
1 parent fa38323 commit a877c24

File tree

12 files changed

+119
-76
lines changed

12 files changed

+119
-76
lines changed

crates/cxx-qt-gen/src/generator/cpp/constructor.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,12 @@ mod tests {
162162

163163
use syn::parse_quote;
164164

165+
fn type_names_with_qobject() -> TypeNames {
166+
let mut type_names = TypeNames::mock();
167+
type_names.insert("QObject", None, None, None);
168+
type_names
169+
}
170+
165171
fn qobject_for_testing() -> GeneratedCppQObject {
166172
GeneratedCppQObject {
167173
ident: "MyObject".to_string(),
@@ -197,7 +203,7 @@ mod tests {
197203
&[],
198204
"BaseClass".to_owned(),
199205
&["member1(1)".to_string(), "member2{ 2 }".to_string()],
200-
&TypeNames::default(),
206+
&type_names_with_qobject(),
201207
)
202208
.unwrap();
203209

@@ -227,7 +233,7 @@ mod tests {
227233
&[],
228234
"BaseClass".to_owned(),
229235
&[],
230-
&TypeNames::default(),
236+
&type_names_with_qobject(),
231237
)
232238
.unwrap();
233239

@@ -258,7 +264,7 @@ mod tests {
258264
&[],
259265
"BaseClass".to_owned(),
260266
&[],
261-
&TypeNames::default(),
267+
&type_names_with_qobject(),
262268
)
263269
.unwrap();
264270

@@ -290,7 +296,7 @@ mod tests {
290296
}],
291297
"BaseClass".to_owned(),
292298
&[],
293-
&TypeNames::default(),
299+
&type_names_with_qobject(),
294300
)
295301
.unwrap();
296302

@@ -340,7 +346,7 @@ mod tests {
340346
}],
341347
"BaseClass".to_owned(),
342348
&["initializer".to_string()],
343-
&TypeNames::default(),
349+
&type_names_with_qobject(),
344350
)
345351
.unwrap();
346352

@@ -394,7 +400,7 @@ mod tests {
394400
],
395401
"BaseClass".to_owned(),
396402
&["initializer".to_string()],
397-
&TypeNames::default(),
403+
&type_names_with_qobject(),
398404
)
399405
.unwrap();
400406

crates/cxx-qt-gen/src/generator/cpp/method.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,10 @@ mod tests {
228228
];
229229
let qobject_idents = create_qobjectname();
230230

231-
let generated =
232-
generate_cpp_methods(&invokables, &qobject_idents, &TypeNames::default()).unwrap();
231+
let mut type_names = TypeNames::mock();
232+
type_names.insert("QColor", None, None, None);
233+
234+
let generated = generate_cpp_methods(&invokables, &qobject_idents, &type_names).unwrap();
233235

234236
// methods
235237
assert_eq!(generated.methods.len(), 5);

crates/cxx-qt-gen/src/generator/cpp/property/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ mod tests {
8181
];
8282
let qobject_idents = create_qobjectname();
8383

84-
let generated =
85-
generate_cpp_properties(&properties, &qobject_idents, &TypeNames::mock()).unwrap();
84+
let mut type_names = TypeNames::mock();
85+
type_names.insert("QColor", None, None, None);
86+
let generated = generate_cpp_properties(&properties, &qobject_idents, &type_names).unwrap();
8687

8788
// metaobjects
8889
assert_eq!(generated.metaobjects.len(), 2);
@@ -356,7 +357,7 @@ mod tests {
356357
fn test_generate_cpp_properties_mapped_cxx_name() {
357358
let properties = vec![ParsedQProperty {
358359
ident: format_ident!("mapped_property"),
359-
ty: parse_quote! { A1 },
360+
ty: parse_quote! { A },
360361
}];
361362
let qobject_idents = create_qobjectname();
362363

crates/cxx-qt-gen/src/generator/cpp/qenum.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn generate(
5353
let mut generated = GeneratedCppQObjectBlocks::default();
5454

5555
for qenum in qenums {
56-
let mut qualified_name = type_names.cxx_qualified(&qenum.ident);
56+
let mut qualified_name = type_names.cxx_qualified(&qenum.ident)?;
5757
let enum_name = type_names.cxx_unqualified(&qenum.ident)?;
5858
// TODO: this is a workaround for type_names.cxx_qualified not always returning a fully-qualified
5959
// identifier.

crates/cxx-qt-gen/src/generator/cpp/signal.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ fn parameter_types_and_values(
6666
let parameter_named_types = parameter_named_types_with_self.join(", ");
6767

6868
// Insert the extra argument into the closure
69-
let self_ty = type_names.cxx_qualified(self_ty);
69+
let self_ty = type_names.cxx_qualified(self_ty)?;
7070
parameter_named_types_with_self.insert(0, format!("{self_ty}& self"));
7171
parameter_types_with_self.insert(0, format!("{self_ty}&"));
7272
parameter_values_with_self.insert(0, "self".to_owned());
@@ -92,7 +92,7 @@ pub fn generate_cpp_signal(
9292
.insert("#include <cxx-qt/signalhandler.h>".to_owned());
9393

9494
// Build a namespace that includes any namespace for the T
95-
let qobject_ident_namespaced = type_names.cxx_qualified(qobject_ident);
95+
let qobject_ident_namespaced = type_names.cxx_qualified(qobject_ident)?;
9696

9797
// Prepare the idents
9898
let idents = QSignalName::from(signal);
@@ -250,8 +250,9 @@ mod tests {
250250
}];
251251
let qobject_idents = create_qobjectname();
252252

253-
let generated =
254-
generate_cpp_signals(&signals, &qobject_idents, &TypeNames::mock()).unwrap();
253+
let mut type_names = TypeNames::mock();
254+
type_names.insert("QColor", None, None, None);
255+
let generated = generate_cpp_signals(&signals, &qobject_idents, &type_names).unwrap();
255256

256257
assert_eq!(generated.methods.len(), 1);
257258
let header = if let CppFragment::Header(header) = &generated.methods[0] {
@@ -331,13 +332,13 @@ mod tests {
331332
fn test_generate_cpp_signals_mapped_cxx_name() {
332333
let signals = vec![ParsedSignal {
333334
method: parse_quote! {
334-
fn data_changed(self: Pin<&mut MyObject>, mapped: A1);
335+
fn data_changed(self: Pin<&mut MyObject>, mapped: A);
335336
},
336337
qobject_ident: format_ident!("MyObject"),
337338
mutable: true,
338339
parameters: vec![ParsedFunctionParameter {
339340
ident: format_ident!("mapped"),
340-
ty: parse_quote! { A1 },
341+
ty: parse_quote! { A },
341342
}],
342343
ident: CombinedIdent {
343344
cpp: format_ident!("dataChanged"),

crates/cxx-qt-gen/src/naming/cpp.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ fn path_segment_to_string(segment: &PathSegment, type_names: &TypeNames) -> Resu
209209
let ident_string = ident.to_string();
210210

211211
// If we are a Pin<T> then for C++ it becomes just T
212-
let args = match &*ident_string {
212+
let arg = match &*ident_string {
213213
"Pin" => {
214214
let mut args =
215215
path_argument_to_string(&segment.arguments, type_names)?.unwrap_or_else(Vec::new);
@@ -231,25 +231,27 @@ fn path_segment_to_string(segment: &PathSegment, type_names: &TypeNames) -> Resu
231231
};
232232

233233
// If there are template args check that its a supported template type.
234-
let ident = if args.is_some() {
234+
if let Some(arg) = arg {
235235
// A built in template base cannot have a cxx_name or a namespace
236-
if let Some(built_in) = possible_built_in_template_base(&ident_string) {
237-
built_in.to_owned()
236+
if let Some(ident) = possible_built_in_template_base(&ident_string) {
237+
Ok(format!("{ident}<{arg}>"))
238238
} else {
239-
return Err(Error::new_spanned(
239+
Err(Error::new_spanned(
240240
ident,
241241
format!("Unsupported template base: {ident}"),
242-
));
242+
))
243243
}
244244
} else {
245-
type_names.cxx_qualified(&segment.ident)
246-
};
247-
248-
Ok(format!(
249-
"{ident}{args}",
250-
ident = ident,
251-
args = args.map_or_else(|| "".to_owned(), |arg| format!("<{arg}>"))
252-
))
245+
// Currently, type_names only includes the names of types that are declared within the bridge.
246+
// Some types are built-in and available without declaration though.
247+
// Check whether its one of those, as otherwise the call to `cxx_qualified` will result in
248+
// an "unknown type" error.
249+
if let Some(built_in) = possible_built_in(&segment.ident.to_string()) {
250+
Ok(built_in.to_owned())
251+
} else {
252+
type_names.cxx_qualified(&segment.ident)
253+
}
254+
}
253255
}
254256

255257
/// Convert any built in types to known C++ equivalents
@@ -322,9 +324,14 @@ mod tests {
322324

323325
macro_rules! test_syn_types_to_cpp_types {
324326
[$($input_type:tt => $output_type:literal),*] => {
327+
let mut type_names = TypeNames::default();
328+
// Add some types to the list of available types so we can use them in tests.
329+
type_names.insert("T", None, None, None);
330+
type_names.insert("QColor", None, None, None);
331+
type_names.insert("QPoint", None, None, None);
325332
$(
326333
assert_eq!(
327-
syn_type_to_cpp_type(&parse_quote! $input_type, &TypeNames::default()).unwrap(),
334+
syn_type_to_cpp_type(&parse_quote! $input_type, &type_names).unwrap(),
328335
$output_type);
329336
)*
330337
}

crates/cxx-qt-gen/src/naming/name.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,12 @@ impl Name {
9393
module: module.clone(),
9494
})
9595
}
96+
97+
/// Get the unqualified name of the type in C++.
98+
/// This is either:
99+
/// - The cxx_name attribute value, if one is provided
100+
/// - The original ident, if no cxx_name was provided
101+
pub(super) fn cxx_unqualified(&self) -> String {
102+
self.cxx.clone().unwrap_or_else(|| self.rust.to_string())
103+
}
96104
}

crates/cxx-qt-gen/src/naming/type_names.rs

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ impl TypeNames {
177177
Ok(())
178178
}
179179

180+
fn unknown_type(&self, ident: &Ident) -> syn::Error {
181+
syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!"))
182+
}
183+
180184
/// For a given rust ident return the CXX name with its namespace
181185
///
182186
/// Ideally we'd want this type name to always be **fully** qualified, starting with `::`.
@@ -187,35 +191,28 @@ impl TypeNames {
187191
/// This needs to be considered in many places (properties, signals, invokables, etc.)
188192
/// Therefore, for now we'll use the qualified, but not fully qualified version of `namespace::type`.
189193
/// This should work in most cases, but it's not perfect.
190-
pub fn cxx_qualified(&self, ident: &Ident) -> String {
194+
pub fn cxx_qualified(&self, ident: &Ident) -> Result<String> {
191195
// Check if there is a cxx_name or namespace to handle
192-
let name = self.names.get(ident);
193-
194-
if name.is_none() {
195-
return ident.to_string();
196-
}
197-
let name = name.unwrap();
196+
let name = self
197+
.names
198+
.get(ident)
199+
.ok_or_else(|| self.unknown_type(ident))?;
198200

199-
let cxx_name = name.cxx.clone().unwrap_or_else(|| name.rust.to_string());
201+
let cxx_name = name.cxx_unqualified();
200202

201203
if let Some(namespace) = &name.namespace {
202-
format!("{namespace}::{cxx_name}")
204+
Ok(format!("{namespace}::{cxx_name}"))
203205
} else {
204-
cxx_name
206+
Ok(cxx_name)
205207
}
206208
}
207209

208-
fn unknown_type(&self, ident: &Ident) -> syn::Error {
209-
syn::Error::new_spanned(ident, format!("Undeclared type: `{ident}`!"))
210-
}
211-
212210
/// For a given rust ident return the CXX name **without** its namespace
213211
pub fn cxx_unqualified(&self, ident: &Ident) -> Result<String> {
214-
if let Some(name) = self.names.get(ident) {
215-
Ok(name.cxx.clone().unwrap_or_else(|| ident.to_string()))
216-
} else {
217-
Err(self.unknown_type(ident))
218-
}
212+
self.names
213+
.get(ident)
214+
.ok_or_else(|| self.unknown_type(ident))
215+
.map(Name::cxx_unqualified)
219216
}
220217

221218
/// For a given rust ident return the namespace if it's not empty
@@ -249,6 +246,7 @@ impl TypeNames {
249246
module_ident: &Ident,
250247
) -> Result<()> {
251248
let name = Name::from_ident_and_attrs(ident, attrs, parent_namespace, module_ident)?;
249+
// TODO: Check for duplicates
252250
self.names.insert(name.rust.clone(), name);
253251
Ok(())
254252
}
@@ -306,8 +304,8 @@ mod tests {
306304
assert_eq!(types.num_types(), 0);
307305

308306
assert!(types.cxx_unqualified(&format_ident!("A")).is_err());
307+
assert!(types.cxx_qualified(&format_ident!("A")).is_err());
309308
assert!(types.namespace(&format_ident!("A")).is_err());
310-
// assert!(types.cxx_qualified(&format_ident!("A")).is_err());
311309
}
312310

313311
#[test]
@@ -320,7 +318,7 @@ mod tests {
320318

321319
assert_eq!(types.num_types(), 1);
322320
assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A });
323-
assert_eq!(types.cxx_qualified(&ident), "A");
321+
assert_eq!(types.cxx_qualified(&ident).unwrap(), "A");
324322
assert!(types.namespace(&ident).unwrap().is_none());
325323
}
326324

@@ -338,7 +336,7 @@ mod tests {
338336
.is_ok());
339337

340338
assert_eq!(types.num_types(), 1);
341-
assert_eq!(types.cxx_qualified(&ident), "B");
339+
assert_eq!(types.cxx_qualified(&ident).unwrap(), "B");
342340
assert!(types.namespace(&ident).unwrap().is_none());
343341
assert_eq!(types.rust_qualified(&ident), parse_quote! { ffi::A });
344342
}
@@ -397,7 +395,7 @@ mod tests {
397395
.populate(&ident, &[], Some("bridge_namespace"), &format_ident!("ffi"))
398396
.is_ok());
399397

400-
assert_eq!(types.cxx_qualified(&ident), "bridge_namespace::A");
398+
assert_eq!(types.cxx_qualified(&ident).unwrap(), "bridge_namespace::A");
401399
assert_eq!(
402400
types.namespace(&ident).unwrap().unwrap(),
403401
"bridge_namespace"
@@ -443,7 +441,7 @@ mod tests {
443441
let type_names = parse_cxx_item(item);
444442
let ident = format_ident!("A");
445443
assert_eq!(type_names.num_types(), 1);
446-
assert_eq!(type_names.cxx_qualified(&ident), "B");
444+
assert_eq!(type_names.cxx_qualified(&ident).unwrap(), "B");
447445

448446
assert_eq!(type_names.rust_qualified(&ident), parse_quote! { ffi::A });
449447
}
@@ -476,15 +474,15 @@ mod tests {
476474
assert_eq!(types.num_types(), 3);
477475

478476
assert_eq!(
479-
&types.cxx_qualified(&format_ident!("A")),
477+
types.cxx_qualified(&format_ident!("A")).unwrap(),
480478
"type_namespace::B"
481479
);
482480
assert_eq!(
483-
&types.cxx_qualified(&format_ident!("C")),
481+
types.cxx_qualified(&format_ident!("C")).unwrap(),
484482
"extern_namespace::D"
485483
);
486484
assert_eq!(
487-
&types.cxx_qualified(&format_ident!("E")),
485+
types.cxx_qualified(&format_ident!("E")).unwrap(),
488486
"bridge_namespace::E"
489487
);
490488

@@ -534,7 +532,10 @@ mod tests {
534532
type_names.namespace(&ident).unwrap().unwrap(),
535533
"enum_namespace"
536534
);
537-
assert_eq!(type_names.cxx_qualified(&ident), "enum_namespace::EnumB");
535+
assert_eq!(
536+
type_names.cxx_qualified(&ident).unwrap(),
537+
"enum_namespace::EnumB"
538+
);
538539
assert_eq!(
539540
type_names.rust_qualified(&ident),
540541
parse_quote! { ffi::EnumA }
@@ -556,7 +557,10 @@ mod tests {
556557

557558
assert_eq!(types.num_types(), 1);
558559
assert_eq!(types.cxx_unqualified(&ident).unwrap(), "StructB");
559-
assert_eq!(types.cxx_qualified(&ident), "struct_namespace::StructB");
560+
assert_eq!(
561+
types.cxx_qualified(&ident).unwrap(),
562+
"struct_namespace::StructB"
563+
);
560564
assert_eq!(
561565
types.namespace(&ident).unwrap().unwrap(),
562566
"struct_namespace"

0 commit comments

Comments
 (0)