Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion lib/typeprof/core/ast/module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,22 @@ class ClassNode < ModuleBaseNode
def initialize(raw_node, lenv, use_result)
super(raw_node, lenv, raw_node.constant_path, false, raw_node.body, use_result)
raw_superclass = raw_node.superclass
@superclass_cpath = raw_superclass ? AST.create_node(raw_superclass, lenv) : nil
if raw_superclass
# In Ruby, the superclass expression is evaluated before the class constant
# is created. When the superclass is a bare constant with the same name as
# the class being defined (e.g., `class Foo < Foo` inside a module), use the
# outer scope to avoid resolving to the class itself.
if @static_cpath && lenv.cref.outer &&
raw_superclass.type == :constant_read_node &&
raw_superclass.name == @static_cpath.last
slenv = LocalEnv.new(lenv.file_context, lenv.cref.outer, {}, [])
@superclass_cpath = AST.create_node(raw_superclass, slenv)
else
@superclass_cpath = AST.create_node(raw_superclass, lenv)
end
else
@superclass_cpath = nil
end
end

attr_reader :superclass_cpath
Expand All @@ -113,6 +128,21 @@ def undefine0(genv)

def install0(genv)
@superclass_cpath.install(genv) if @superclass_cpath
if @static_cpath && @superclass_cpath
const_read = @superclass_cpath.static_ret
if const_read && const_read.cpath
super_mod = genv.resolve_cpath(const_read.cpath)
self_mod = genv.resolve_cpath(@static_cpath)
mod = super_mod
while mod
if mod == self_mod
@changes.add_diagnostic(:code_range, "circular inheritance", @superclass_cpath)
break
end
mod = mod.superclass
end
end
end
super(genv)
end
end
Expand Down
3 changes: 3 additions & 0 deletions scenario/class/circular.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ class B # failed to identify its superclass
end
module M
end

## diagnostics
(4,10)-(4,11): circular inheritance
23 changes: 23 additions & 0 deletions scenario/class/circular_mutual.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## update
class Foo
end

class Bar
class Baz < Foo
end
class Foo < Baz
end
end

## assert
class Foo
end
class Bar
class Bar::Baz < Bar::Foo
end
class Bar::Foo # failed to identify its superclass
end
end

## diagnostics
(7,14)-(7,17): circular inheritance
19 changes: 19 additions & 0 deletions scenario/regressions/superclass-self-reference.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## update: model.rb
class Foo
def value = 1
end

module Bar
class Foo < Foo
end
end

## update: test.rb
def call
Bar::Foo.new.value
end

## assert: test.rb
class Object
def call: -> Integer
end