Skip to content

Commit

Permalink
Merge pull request crystal-lang#65 from olbat/fix-typedef-in-struct-bug
Browse files Browse the repository at this point in the history
Fix: wrong ErrorType parsing when a type is referenced before it's defined
  • Loading branch information
maxfierke committed Apr 4, 2020
2 parents ca7319b + 9642128 commit 33c0a69
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 3 deletions.
24 changes: 24 additions & 0 deletions spec/parser_spec.cr
Expand Up @@ -171,6 +171,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)
Expand Down
19 changes: 16 additions & 3 deletions src/crystal_lib/parser.cr
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 33c0a69

Please sign in to comment.