Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: wrong ErrorType parsing when a type is referenced before it's defined #65

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
24 changes: 24 additions & 0 deletions spec/parser_spec.cr
Expand Up @@ -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)
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