authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-28 15:41:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-01-28 15:41:12-07:00
loged3117a77fffa424e7f902059a7bf3a52f758bfc
treef74d9b0bdd6b574daf54de1dfdcafb4e3c9bcdf3
parent47c3a30310b142dea390afaf4063a8b078a34f0d

parseh understands elaborated structs and enums


2 files changed, 132 insertions(+), 53 deletions(-)

src/parseh.cpp+108-53
...@@ -27,12 +27,18 @@ struct Context {...@@ -27,12 +27,18 @@ struct Context {
27 VisibMod visib_mod;27 VisibMod visib_mod;
28 bool have_c_void_decl_node;28 bool have_c_void_decl_node;
29 AstNode *root;29 AstNode *root;
30 HashMap<Buf *, bool, buf_hash, buf_eql_buf> type_table;30 HashMap<Buf *, bool, buf_hash, buf_eql_buf> root_type_table;
31 HashMap<Buf *, bool, buf_hash, buf_eql_buf> struct_type_table;
32 HashMap<Buf *, bool, buf_hash, buf_eql_buf> enum_type_table;
31 HashMap<Buf *, bool, buf_hash, buf_eql_buf> fn_table;33 HashMap<Buf *, bool, buf_hash, buf_eql_buf> fn_table;
32 SourceManager *source_manager;34 SourceManager *source_manager;
33 ZigList<AstNode *> aliases;35 ZigList<AstNode *> aliases;
34};36};
3537
38static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl);
39static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl,
40 HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table);
41
36__attribute__ ((format (printf, 3, 4)))42__attribute__ ((format (printf, 3, 4)))
37static void emit_warning(Context *c, const Decl *decl, const char *format, ...) {43static void emit_warning(Context *c, const Decl *decl, const char *format, ...) {
38 if (!c->warnings_on) {44 if (!c->warnings_on) {
...@@ -59,8 +65,6 @@ static void emit_warning(Context *c, const Decl *decl, const char *format, ...)...@@ -59,8 +65,6 @@ static void emit_warning(Context *c, const Decl *decl, const char *format, ...)
59 fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg));65 fprintf(stderr, "%s:%u:%u: warning: %s\n", buf_ptr(path), line, column, buf_ptr(msg));
60}66}
6167
62static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl);
63
64static AstNode *create_node(Context *c, NodeType type) {68static AstNode *create_node(Context *c, NodeType type) {
65 AstNode *node = allocate<AstNode>(1);69 AstNode *node = allocate<AstNode>(1);
66 node->type = type;70 node->type = type;
...@@ -129,7 +133,7 @@ static AstNode *add_typedef_node(Context *c, Buf *new_name, AstNode *target_node...@@ -129,7 +133,7 @@ static AstNode *add_typedef_node(Context *c, Buf *new_name, AstNode *target_node
129 }133 }
130 AstNode *node = create_var_decl_node(c, buf_ptr(new_name), target_node);134 AstNode *node = create_var_decl_node(c, buf_ptr(new_name), target_node);
131135
132 c->type_table.put(new_name, true);136 c->root_type_table.put(new_name, true);
133 c->root->data.root.top_level_decls.append(node);137 c->root->data.root.top_level_decls.append(node);
134 return node;138 return node;
135}139}
...@@ -157,7 +161,9 @@ static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) {...@@ -157,7 +161,9 @@ static AstNode *pointer_to_type(Context *c, AstNode *type_node, bool is_const) {
157 return create_prefix_node(c, PrefixOpMaybe, child_node);161 return create_prefix_node(c, PrefixOpMaybe, child_node);
158}162}
159163
160static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {164static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl,
165 HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table)
166{
161 switch (ty->getTypeClass()) {167 switch (ty->getTypeClass()) {
162 case Type::Builtin:168 case Type::Builtin:
163 {169 {
...@@ -258,7 +264,7 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {...@@ -258,7 +264,7 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {
258 } else if (buf_eql_str(type_name, "uintptr_t")) {264 } else if (buf_eql_str(type_name, "uintptr_t")) {
259 return create_symbol_node(c, "usize");265 return create_symbol_node(c, "usize");
260 } else {266 } else {
261 auto entry = c->type_table.maybe_get(type_name);267 auto entry = type_table->maybe_get(type_name);
262 if (entry) {268 if (entry) {
263 return create_symbol_node(c, buf_ptr(type_name));269 return create_symbol_node(c, buf_ptr(type_name));
264 } else {270 } else {
...@@ -267,13 +273,63 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {...@@ -267,13 +273,63 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {
267 }273 }
268 }274 }
269 case Type::Elaborated:275 case Type::Elaborated:
270 emit_warning(c, decl, "ignoring elaborated type");276 {
271 return nullptr;277 const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
278 switch (elaborated_ty->getKeyword()) {
279 case ETK_Struct:
280 return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(),
281 decl, &c->struct_type_table);
282 case ETK_Enum:
283 return make_qual_type_node_with_table(c, elaborated_ty->getNamedType(),
284 decl, &c->enum_type_table);
285 case ETK_Interface:
286 case ETK_Union:
287 case ETK_Class:
288 case ETK_Typename:
289 case ETK_None:
290 emit_warning(c, decl, "unsupported elaborated type");
291 return nullptr;
292 }
293 }
272 case Type::FunctionProto:294 case Type::FunctionProto:
273 emit_warning(c, decl, "ignoring function type");295 emit_warning(c, decl, "ignoring function type");
274 return nullptr;296 return nullptr;
275 case Type::Record:297 case Type::Record:
298 {
299 const RecordType *record_ty = static_cast<const RecordType*>(ty);
300 Buf *record_name = buf_create_from_str(decl_name(record_ty->getDecl()));
301 if (type_table->maybe_get(record_name)) {
302 const char *prefix_str;
303 if (type_table == &c->enum_type_table) {
304 prefix_str = "enum_";
305 } else if (type_table == &c->struct_type_table) {
306 prefix_str = "struct_";
307 } else {
308 prefix_str = "";
309 }
310 return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name))));
311 } else {
312 return nullptr;
313 }
314 }
276 case Type::Enum:315 case Type::Enum:
316 {
317 const EnumType *enum_ty = static_cast<const EnumType*>(ty);
318 Buf *record_name = buf_create_from_str(decl_name(enum_ty->getDecl()));
319 if (type_table->maybe_get(record_name)) {
320 const char *prefix_str;
321 if (type_table == &c->enum_type_table) {
322 prefix_str = "enum_";
323 } else if (type_table == &c->struct_type_table) {
324 prefix_str = "struct_";
325 } else {
326 prefix_str = "";
327 }
328 return create_symbol_node(c, buf_ptr(buf_sprintf("%s%s", prefix_str, buf_ptr(record_name))));
329 } else {
330 return nullptr;
331 }
332 }
277 case Type::BlockPointer:333 case Type::BlockPointer:
278 case Type::LValueReference:334 case Type::LValueReference:
279 case Type::RValueReference:335 case Type::RValueReference:
...@@ -314,8 +370,14 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {...@@ -314,8 +370,14 @@ static AstNode *make_type_node(Context *c, const Type *ty, const Decl *decl) {
314 }370 }
315}371}
316372
373static AstNode *make_qual_type_node_with_table(Context *c, QualType qt, const Decl *decl,
374 HashMap<Buf *, bool, buf_hash, buf_eql_buf> *type_table)
375{
376 return make_type_node(c, qt.getTypePtr(), decl, type_table);
377}
378
317static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl) {379static AstNode *make_qual_type_node(Context *c, QualType qt, const Decl *decl) {
318 return make_type_node(c, qt.getTypePtr(), decl);380 return make_qual_type_node_with_table(c, qt, decl, &c->root_type_table);
319}381}
320382
321static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {383static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
...@@ -334,7 +396,6 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {...@@ -334,7 +396,6 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
334 node->data.fn_proto.is_var_args = fn_decl->isVariadic();396 node->data.fn_proto.is_var_args = fn_decl->isVariadic();
335397
336 int arg_count = fn_decl->getNumParams();398 int arg_count = fn_decl->getNumParams();
337 bool all_ok = true;
338 for (int i = 0; i < arg_count; i += 1) {399 for (int i = 0; i < arg_count; i += 1) {
339 const ParmVarDecl *param = fn_decl->getParamDecl(i);400 const ParmVarDecl *param = fn_decl->getParamDecl(i);
340 AstNode *param_decl_node = create_node(c, NodeTypeParamDecl);401 AstNode *param_decl_node = create_node(c, NodeTypeParamDecl);
...@@ -347,8 +408,9 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {...@@ -347,8 +408,9 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
347 param_decl_node->data.param_decl.is_noalias = qt.isRestrictQualified();408 param_decl_node->data.param_decl.is_noalias = qt.isRestrictQualified();
348 param_decl_node->data.param_decl.type = make_qual_type_node(c, qt, fn_decl);409 param_decl_node->data.param_decl.type = make_qual_type_node(c, qt, fn_decl);
349 if (!param_decl_node->data.param_decl.type) {410 if (!param_decl_node->data.param_decl.type) {
350 all_ok = false;411 emit_warning(c, param, "skipping function %s, unresolved param type\n",
351 break;412 buf_ptr(&node->data.fn_proto.name));
413 return;
352 }414 }
353415
354 normalize_parent_ptrs(param_decl_node);416 normalize_parent_ptrs(param_decl_node);
...@@ -362,11 +424,8 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {...@@ -362,11 +424,8 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
362 }424 }
363425
364 if (!node->data.fn_proto.return_type) {426 if (!node->data.fn_proto.return_type) {
365 all_ok = false;427 emit_warning(c, fn_decl, "skipping function %s, unresolved return type\n",
366 }428 buf_ptr(&node->data.fn_proto.name));
367 if (!all_ok) {
368 // not all the types could be resolved, so we give up on the function decl
369 emit_warning(c, fn_decl, "skipping function %s\n", buf_ptr(&node->data.fn_proto.name));
370 return;429 return;
371 }430 }
372431
...@@ -404,13 +463,10 @@ static void add_alias(Context *c, const char *new_name, const char *target_name)...@@ -404,13 +463,10 @@ static void add_alias(Context *c, const char *new_name, const char *target_name)
404}463}
405464
406static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {465static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
407 Buf bare_name = BUF_INIT;466 Buf *bare_name = buf_create_from_str(decl_name(enum_decl));
408 buf_init_from_str(&bare_name, decl_name(enum_decl));467 Buf *full_type_name = buf_sprintf("enum_%s", buf_ptr(bare_name));
409
410 Buf *type_name = buf_alloc();
411 buf_appendf(type_name, "enum_%s", buf_ptr(&bare_name));
412468
413 if (c->type_table.maybe_get(type_name)) {469 if (c->enum_type_table.maybe_get(bare_name)) {
414 // we've already seen it470 // we've already seen it
415 return;471 return;
416 }472 }
...@@ -419,13 +475,13 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -419,13 +475,13 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
419475
420 if (!enum_def) {476 if (!enum_def) {
421 // this is a type that we can point to but that's it, same as `struct Foo;`.477 // this is a type that we can point to but that's it, same as `struct Foo;`.
422 add_typedef_node(c, type_name, create_symbol_node(c, "u8"));478 add_typedef_node(c, full_type_name, create_symbol_node(c, "u8"));
423 add_alias(c, buf_ptr(&bare_name), buf_ptr(type_name));479 add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
424 return;480 return;
425 }481 }
426482
427 AstNode *node = create_node(c, NodeTypeStructDecl);483 AstNode *node = create_node(c, NodeTypeStructDecl);
428 buf_init_from_buf(&node->data.struct_decl.name, type_name);484 buf_init_from_buf(&node->data.struct_decl.name, full_type_name);
429485
430 node->data.struct_decl.kind = ContainerKindEnum;486 node->data.struct_decl.kind = ContainerKindEnum;
431 node->data.struct_decl.visib_mod = VisibModExport;487 node->data.struct_decl.visib_mod = VisibModExport;
...@@ -439,7 +495,7 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -439,7 +495,7 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
439 {495 {
440 const EnumConstantDecl *enum_const = *it;496 const EnumConstantDecl *enum_const = *it;
441 if (enum_const->getInitExpr()) {497 if (enum_const->getInitExpr()) {
442 emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(type_name));498 emit_warning(c, enum_const, "skipping enum %s - has init expression\n", buf_ptr(bare_name));
443 return;499 return;
444 }500 }
445 Buf enum_val_name = BUF_INIT;501 Buf enum_val_name = BUF_INIT;
...@@ -447,8 +503,8 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -447,8 +503,8 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
447503
448 Buf field_name = BUF_INIT;504 Buf field_name = BUF_INIT;
449505
450 if (buf_starts_with_buf(&enum_val_name, &bare_name)) {506 if (buf_starts_with_buf(&enum_val_name, bare_name)) {
451 Buf *slice = buf_slice(&enum_val_name, buf_len(&bare_name), buf_len(&enum_val_name));507 Buf *slice = buf_slice(&enum_val_name, buf_len(bare_name), buf_len(&enum_val_name));
452 if (valid_symbol_starter(buf_ptr(slice)[0])) {508 if (valid_symbol_starter(buf_ptr(slice)[0])) {
453 buf_init_from_buf(&field_name, slice);509 buf_init_from_buf(&field_name, slice);
454 } else {510 } else {
...@@ -463,13 +519,12 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -463,13 +519,12 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
463 node->data.struct_decl.fields.append(field_node);519 node->data.struct_decl.fields.append(field_node);
464520
465 // in C each enum value is in the global namespace. so we put them there too.521 // in C each enum value is in the global namespace. so we put them there too.
466 AstNode *field_access_node = create_field_access_node(c, buf_ptr(type_name),522 AstNode *field_access_node = create_field_access_node(c, buf_ptr(full_type_name), buf_ptr(&field_name));
467 buf_ptr(&field_name));
468 AstNode *var_node = create_var_decl_node(c, buf_ptr(&enum_val_name), field_access_node);523 AstNode *var_node = create_var_decl_node(c, buf_ptr(&enum_val_name), field_access_node);
469 var_decls.append(var_node);524 var_decls.append(var_node);
470 }525 }
471526
472 c->type_table.put(type_name, true);527 c->enum_type_table.put(bare_name, true);
473528
474 normalize_parent_ptrs(node);529 normalize_parent_ptrs(node);
475 c->root->data.root.top_level_decls.append(node);530 c->root->data.root.top_level_decls.append(node);
...@@ -481,17 +536,20 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -481,17 +536,20 @@ static void visit_enum_decl(Context *c, const EnumDecl *enum_decl) {
481536
482 // make an alias without the "enum_" prefix. this will get emitted at the537 // make an alias without the "enum_" prefix. this will get emitted at the
483 // end if it doesn't conflict with anything else538 // end if it doesn't conflict with anything else
484 add_alias(c, buf_ptr(&bare_name), buf_ptr(type_name));539 add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
485}540}
486541
487static void visit_record_decl(Context *c, const RecordDecl *record_decl) {542static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
488 Buf bare_name = BUF_INIT;543 Buf *bare_name = buf_create_from_str(decl_name(record_decl));
489 buf_init_from_str(&bare_name, decl_name(record_decl));544
545 if (!record_decl->isStruct()) {
546 emit_warning(c, record_decl, "skipping record %s, not a struct", buf_ptr(bare_name));
547 return;
548 }
490549
491 Buf *type_name = buf_alloc();550 Buf *full_type_name = buf_sprintf("struct_%s", buf_ptr(bare_name));
492 buf_appendf(type_name, "struct_%s", buf_ptr(&bare_name));
493551
494 if (c->type_table.maybe_get(type_name)) {552 if (c->struct_type_table.maybe_get(bare_name)) {
495 // we've already seen it553 // we've already seen it
496 return;554 return;
497 }555 }
...@@ -499,18 +557,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {...@@ -499,18 +557,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
499 RecordDecl *record_def = record_decl->getDefinition();557 RecordDecl *record_def = record_decl->getDefinition();
500 if (!record_def) {558 if (!record_def) {
501 // this is a type that we can point to but that's it, such as `struct Foo;`.559 // this is a type that we can point to but that's it, such as `struct Foo;`.
502 add_typedef_node(c, type_name, create_symbol_node(c, "u8"));560 add_typedef_node(c, full_type_name, create_symbol_node(c, "u8"));
503 add_alias(c, buf_ptr(&bare_name), buf_ptr(type_name));561 add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
504 return;
505 }
506
507 if (!record_def->isStruct()) {
508 emit_warning(c, record_decl, "skipping record %s, not a struct", buf_ptr(&bare_name));
509 return;562 return;
510 }563 }
511564
512 AstNode *node = create_node(c, NodeTypeStructDecl);565 AstNode *node = create_node(c, NodeTypeStructDecl);
513 buf_init_from_buf(&node->data.struct_decl.name, type_name);566 buf_init_from_buf(&node->data.struct_decl.name, full_type_name);
514567
515 node->data.struct_decl.kind = ContainerKindStruct;568 node->data.struct_decl.kind = ContainerKindStruct;
516 node->data.struct_decl.visib_mod = VisibModExport;569 node->data.struct_decl.visib_mod = VisibModExport;
...@@ -523,13 +576,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {...@@ -523,13 +576,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
523 const FieldDecl *field_decl = *it;576 const FieldDecl *field_decl = *it;
524577
525 if (field_decl->isBitField()) {578 if (field_decl->isBitField()) {
526 emit_warning(c, field_decl, "skipping struct %s - has bitfield\n", buf_ptr(&bare_name));579 emit_warning(c, field_decl, "skipping struct %s - has bitfield\n", buf_ptr(bare_name));
527 return;580 return;
528 }581 }
529582
530 AstNode *type_node = make_qual_type_node(c, field_decl->getType(), field_decl);583 AstNode *type_node = make_qual_type_node(c, field_decl->getType(), field_decl);
531 if (!type_node) {584 if (!type_node) {
532 emit_warning(c, field_decl, "skipping struct %s - unhandled type\n", buf_ptr(&bare_name));585 emit_warning(c, field_decl, "skipping struct %s - unhandled type\n", buf_ptr(bare_name));
533 return;586 return;
534 }587 }
535588
...@@ -537,13 +590,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {...@@ -537,13 +590,13 @@ static void visit_record_decl(Context *c, const RecordDecl *record_decl) {
537 node->data.struct_decl.fields.append(field_node);590 node->data.struct_decl.fields.append(field_node);
538 }591 }
539592
540 c->type_table.put(type_name, true);593 c->struct_type_table.put(bare_name, true);
541 normalize_parent_ptrs(node);594 normalize_parent_ptrs(node);
542 c->root->data.root.top_level_decls.append(node);595 c->root->data.root.top_level_decls.append(node);
543596
544 // make an alias without the "struct_" prefix. this will get emitted at the597 // make an alias without the "struct_" prefix. this will get emitted at the
545 // end if it doesn't conflict with anything else598 // end if it doesn't conflict with anything else
546 add_alias(c, buf_ptr(&bare_name), buf_ptr(type_name));599 add_alias(c, buf_ptr(bare_name), buf_ptr(full_type_name));
547}600}
548601
549static bool decl_visitor(void *context, const Decl *decl) {602static bool decl_visitor(void *context, const Decl *decl) {
...@@ -574,7 +627,7 @@ static void render_aliases(Context *c) {...@@ -574,7 +627,7 @@ static void render_aliases(Context *c) {
574 AstNode *alias_node = c->aliases.at(i);627 AstNode *alias_node = c->aliases.at(i);
575 assert(alias_node->type == NodeTypeVariableDeclaration);628 assert(alias_node->type == NodeTypeVariableDeclaration);
576 Buf *name = &alias_node->data.variable_declaration.symbol;629 Buf *name = &alias_node->data.variable_declaration.symbol;
577 if (c->type_table.maybe_get(name)) {630 if (c->root_type_table.maybe_get(name)) {
578 continue;631 continue;
579 }632 }
580 if (c->fn_table.maybe_get(name)) {633 if (c->fn_table.maybe_get(name)) {
...@@ -618,8 +671,10 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors,...@@ -618,8 +671,10 @@ int parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors,
618 c->import = import;671 c->import = import;
619 c->errors = errors;672 c->errors = errors;
620 c->visib_mod = VisibModPub;673 c->visib_mod = VisibModPub;
621 c->type_table.init(32);674 c->root_type_table.init(16);
622 c->fn_table.init(32);675 c->enum_type_table.init(16);
676 c->struct_type_table.init(16);
677 c->fn_table.init(16);
623678
624 char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");679 char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");
625 if (ZIG_PARSEH_CFLAGS) {680 if (ZIG_PARSEH_CFLAGS) {
test/run_tests.cpp+24
...@@ -1839,6 +1839,30 @@ struct Foo {...@@ -1839,6 +1839,30 @@ struct Foo {
1839 y: ?&u8,1839 y: ?&u8,
1840}1840}
1841pub const Foo = struct_Foo;)OUTPUT");1841pub const Foo = struct_Foo;)OUTPUT");
1842
1843 add_parseh_case("qualified struct and enum", R"SOURCE(
1844struct Foo {
1845 int x;
1846 int y;
1847};
1848enum Bar {
1849 BarA,
1850 BarB,
1851};
1852void func(struct Foo *a, enum Bar **b);
1853 )SOURCE", R"OUTPUT(export struct struct_Foo {
1854 x: c_int,
1855 y: c_int,
1856}
1857export enum enum_Bar {
1858 A,
1859 B,
1860}
1861pub const BarA = enum_Bar.A;
1862pub const BarB = enum_Bar.B;
1863pub extern fn func(a: ?&struct_Foo, b: ?&?&enum_Bar);
1864pub const Foo = struct_Foo;
1865pub const Bar = enum_Bar;)OUTPUT");
1842}1866}
18431867
1844static void print_compiler_invocation(TestCase *test_case) {1868static void print_compiler_invocation(TestCase *test_case) {