From 9642128bf0312950eaaa091254bf7d28a5b62749 Mon Sep 17 00:00:00 2001 From: Luc Sarzyniec Date: Sun, 19 Jan 2020 11:09:51 +0100 Subject: [PATCH] Fix: wrong ErrorType parsing when a type is referenced before it's defined Fixes #64 --- spec/parser_spec.cr | 24 ++++++++++++++++++++++++ src/crystal_lib/parser.cr | 19 ++++++++++++++++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/spec/parser_spec.cr b/spec/parser_spec.cr index 7610165..14b635f 100644 --- a/spec/parser_spec.cr +++ b/spec/parser_spec.cr @@ -162,6 +162,30 @@ describe Parser do fields.size.should eq(2) end + it "parses typedef and recursive struct" do + nodes = parse("typedef struct first { struct second *s; } FIRST; struct second { FIRST *f };") + + struct_first = nodes[-3].as(StructOrUnion) + struct_first.kind.should eq(:struct) + struct_first.name.should eq("struct first") + struct_first.fields.size.should eq(1) + struct_first.fields.first.name.should eq("s") + struct_first.fields.first.type.as(PointerType).type.as(NodeRef).node.as(StructOrUnion).name.should eq("struct second") + + type_first = nodes[-2].as(Typedef) + type_first.name.should eq("FIRST") + underlying_struct = type_first.type.as(NodeRef).node.as(StructOrUnion) + underlying_struct.kind.should eq(:struct) + underlying_struct.name.should eq("struct first") + + struct_second = nodes[-1].as(StructOrUnion) + struct_second.kind.should eq(:struct) + struct_second.name.should eq("struct second") + struct_second.fields.size.should eq(1) + struct_second.fields.first.name.should eq("f") + struct_second.fields.first.type.as(PointerType).type.as(TypedefType).name.should eq("FIRST") + end + it "parses typedef enum" do nodes = parse("typedef enum { x, y } point;") type = nodes.last.as(Typedef) diff --git a/src/crystal_lib/parser.cr b/src/crystal_lib/parser.cr index e3f2914..0dd9d4b 100644 --- a/src/crystal_lib/parser.cr +++ b/src/crystal_lib/parser.cr @@ -143,7 +143,15 @@ class CrystalLib::Parser def visit_typedef_declaration(cursor) name = cursor.spelling type = type(cursor.typedef_decl_underlying_type) - named_types[name] = TypedefType.new(name, type) + + if named_types.has_key?(name) + # this type definition might have been referenced -and created- before, + # in this case we just make sure it refers to the right underlying type + named_types[name].as(TypedefType).type = type + else + named_types[name] = TypedefType.new(name, type) + end + Typedef.new(name, type) end @@ -248,10 +256,15 @@ class CrystalLib::Parser when Clang::TypeKind::Typedef spelling = type.spelling spelling = spelling.gsub("const ", "").gsub("volatile ", "") - if !named_types.has_key?(spelling) && spelling == "__builtin_va_list" + + if named_types.has_key?(spelling) + named_types[spelling] + elsif spelling == "__builtin_va_list" VaListType.new else - named_types[spelling]? || error_type(spelling) + # create a new named type in case the type is referenced here but not yet declared + # (underlying type is set to ErrorType and will be overwritten by the declaration) + named_types[spelling] = TypedefType.new(spelling, error_type(spelling)) end when Clang::TypeKind::Unexposed, Clang::TypeKind::Elaborated