Skip to content

Commit

Permalink
Showing 6 changed files with 91 additions and 5 deletions.
2 changes: 2 additions & 0 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
@@ -422,6 +422,8 @@ describe "Parser" do
it_parses "abstract class Foo; end", ClassDef.new("Foo".path, abstract: true)
it_parses "abstract struct Foo; end", ClassDef.new("Foo".path, abstract: true, struct: true)

it_parses "class Foo < self; end", ClassDef.new("Foo".path, superclass: Self.new)

it_parses "module Foo(*T); end", ModuleDef.new("Foo".path, type_vars: ["T"], splat_index: 0)
it_parses "class Foo(*T); end", ClassDef.new("Foo".path, type_vars: ["T"], splat_index: 0)
it_parses "class Foo(T, *U); end", ClassDef.new("Foo".path, type_vars: ["T", "U"], splat_index: 1)
44 changes: 44 additions & 0 deletions spec/compiler/type_inference/class_spec.cr
Original file line number Diff line number Diff line change
@@ -983,4 +983,48 @@ describe "Type inference: class" do
initialize
)) { int32 }
end

it "inherits self (#2890)" do
assert_type(%(
class Foo
class Bar < self
end
end
{{Foo::Bar.superclass}}
)) { types["Foo"].metaclass }
end

it "inherits Gen(self) (#2890)" do
assert_type(%(
class Gen(T)
def self.t
T
end
end
class Foo
class Bar < Gen(self)
end
end
Foo::Bar.t
)) { types["Foo"].metaclass }
end

it "errors if inheriting Gen(self) and there's no self (#2890)" do
assert_error %(
class Gen(T)
def self.t
T
end
end
class Bar < Gen(self)
end
Bar.t
),
"there's no self in this scope"
end
end
17 changes: 17 additions & 0 deletions spec/compiler/type_inference/super_spec.cr
Original file line number Diff line number Diff line change
@@ -340,4 +340,21 @@ describe "Type inference: super" do
Baz.new
), "wrong number of argument"
end

it "gives correct error when calling super and target is abstract method (#2675)" do
assert_error %(
abstract class Base
abstract def method
end
class Sub < Base
def method
super
end
end
Sub.new.method
),
"undefined method 'Base#method()'"
end
end
14 changes: 11 additions & 3 deletions src/compiler/crystal/semantic/call_error.cr
Original file line number Diff line number Diff line change
@@ -369,16 +369,24 @@ class Crystal::Call
owner.all_subclasses.each do |subclass|
submatches = subclass.lookup_matches(signature)
if submatches.empty?
raise "abstract `def #{def_full_name(a_def.owner, a_def)}` must be implemented by #{subclass}"
raise_abstract_method_must_be_implemented a_def, subclass
end
end
raise "abstract `def #{def_full_name(a_def.owner, a_def)}` must be implemented by #{owner}"
raise_abstract_method_must_be_implemented a_def, owner
else
raise "abstract `def #{def_full_name(a_def.owner, a_def)}` must be implemented by #{owner}"
raise_abstract_method_must_be_implemented a_def, owner
end
end
end

def raise_abstract_method_must_be_implemented(a_def, owner)
if owner.abstract?
raise "undefined method '#{def_full_name(a_def.owner, a_def)}'"
else
raise "abstract `def #{def_full_name(a_def.owner, a_def)}` must be implemented by #{owner}"
end
end

def append_matches(defs, arg_types, str, *, matched_def = nil, argument_name = nil)
defs.each do |a_def|
str << "\n - "
12 changes: 11 additions & 1 deletion src/compiler/crystal/semantic/type_lookup.cr
Original file line number Diff line number Diff line change
@@ -213,6 +213,10 @@ module Crystal
end

def visit(node : Self)
if @self_type.is_a?(Program)
node.raise "there's no self in this scope"
end

@type = @self_type.virtual_type
false
end
@@ -374,11 +378,17 @@ module Crystal
class InheritedGenericClass
def lookup_type(names : Array, already_looked_up = ObjectIdSet.new, lookup_in_container = true)
if (names.size == 1) && (m = @mapping[names[0]]?)
extending_class = self.extending_class
case extending_class
when GenericClassType
# skip
else
return TypeLookup.lookup(extending_class, m)
if extending_class.is_a?(NamedType)
self_type = extending_class.container
else
self_type = extending_class.program
end
return TypeLookup.lookup(extending_class, m, self_type: self_type)
end
end

7 changes: 6 additions & 1 deletion src/compiler/crystal/syntax/parser.cr
Original file line number Diff line number Diff line change
@@ -1520,7 +1520,12 @@ module Crystal

if @token.type == :"<"
next_token_skip_space_or_newline
superclass = parse_ident
if @token.keyword?(:self)
superclass = Self.new.at(@token.location)
next_token
else
superclass = parse_ident
end
end
skip_statement_end

0 comments on commit 2970e95

Please sign in to comment.