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
5 changes: 4 additions & 1 deletion lib/typeprof/core/ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,10 @@ def self.create_rbs_decl(raw_decl, lenv)
SigInterfaceNode.new(raw_decl, lenv)
when RBS::AST::Declarations::Constant
SigConstNode.new(raw_decl, lenv)
when RBS::AST::Declarations::AliasDecl
when RBS::AST::Declarations::ClassAlias
SigClassAliasNode.new(raw_decl, lenv)
when RBS::AST::Declarations::ModuleAlias
SigModuleAliasNode.new(raw_decl, lenv)
when RBS::AST::Declarations::TypeAlias
SigTypeAliasNode.new(raw_decl, lenv)
# TODO: check
Expand Down
46 changes: 46 additions & 0 deletions lib/typeprof/core/ast/sig_decl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,52 @@ def install0(genv)
end
end

class SigModuleAliasBaseNode < Node
def initialize(raw_decl, lenv)
super(raw_decl, lenv)
@cpath = AST.resolve_rbs_name(raw_decl.new_name, lenv)
@old_cpath = AST.resolve_rbs_name(raw_decl.old_name, lenv)
end

attr_reader :cpath, :old_cpath
def attrs = { cpath:, old_cpath: }

def define0(genv)
outer = genv.resolve_cpath(@cpath[0..-2])
cname = @cpath.last
alias_mod = outer.inner_modules[cname] ||= ModuleEntity.new(outer.cpath + [cname], outer)
target_mod = genv.resolve_cpath(@old_cpath)
alias_mod.add_alias_decl(genv, self, target_mod)
end

def define_copy(genv)
outer = genv.resolve_cpath(@cpath[0..-2])
alias_mod = outer.inner_modules[@cpath.last]
target_mod = genv.resolve_cpath(@old_cpath)
alias_mod.add_alias_decl(genv, self, target_mod)
alias_mod.remove_alias_decl(genv, @prev_node)
super(genv)
end

def undefine0(genv)
outer = genv.resolve_cpath(@cpath[0..-2])
alias_mod = outer.inner_modules[@cpath.last]
alias_mod.remove_alias_decl(genv, self)
end

def install0(genv)
mod_val = Source.new(Type::Singleton.new(genv, genv.resolve_cpath(@cpath)))
@changes.add_edge(genv, mod_val, @static_ret.vtx)
Source.new
end
end

class SigClassAliasNode < SigModuleAliasBaseNode
end

class SigModuleAliasNode < SigModuleAliasBaseNode
end

class SigConstNode < Node
def initialize(raw_decl, lenv)
super(raw_decl, lenv)
Expand Down
12 changes: 12 additions & 0 deletions lib/typeprof/core/env.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,18 @@ def resolve_cpath(cpath)
raise unless cpath # annotation
cpath.each do |cname|
mod = mod.inner_modules[cname] ||= ModuleEntity.new(mod.cpath + [cname], mod)
mod = follow_alias(mod)
end
mod
end

def follow_alias(mod)
visited = nil
while mod.alias_target
visited ||= Set.empty
break if visited.include?(mod) # cycle
visited << mod
mod = mod.alias_target
end
mod
end
Expand Down
25 changes: 24 additions & 1 deletion lib/typeprof/core/env/module_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ def initialize(cpath, outer_module = self)
@prepend_decls = []
@prepend_defs = []

# `class Foo = Bar` / `module Foo = Bar` declarations attached to this entity.
# Maps an alias decl to the target ModuleEntity at the time of registration.
@alias_decls = {}
@alias_target = nil

@inner_modules = {}
@outer_module = outer_module

Expand Down Expand Up @@ -42,6 +47,8 @@ def initialize(cpath, outer_module = self)
attr_reader :cpath
attr_reader :module_decls
attr_reader :module_defs
attr_reader :alias_decls
attr_reader :alias_target

attr_reader :inner_modules
attr_reader :outer_module
Expand Down Expand Up @@ -79,7 +86,7 @@ def get_cname
end

def exist?
!@module_decls.empty? || !@module_defs.empty?
!@module_decls.empty? || !@module_defs.empty? || !@alias_decls.empty?
end

def on_inner_modules_changed(genv, changed_cname)
Expand Down Expand Up @@ -176,6 +183,22 @@ def remove_module_def(genv, node)
on_module_removed(genv)
end

def add_alias_decl(genv, decl, target_mod)
on_module_added(genv)
@alias_decls[decl] = target_mod
@alias_target = @alias_decls.values.first
ce = @outer_module.get_const(get_cname)
ce.add_decl(decl)
ce
end

def remove_alias_decl(genv, decl)
@outer_module.get_const(get_cname).remove_decl(decl)
@alias_decls.delete(decl) || raise
@alias_target = @alias_decls.values.first
on_module_removed(genv)
end

def add_include_decl(genv, node)
@include_decls << node
genv.add_static_eval_queue(:parent_modules_changed, self)
Expand Down
10 changes: 10 additions & 0 deletions scenario/rbs/class-module-alias-cycle.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## update: test.rbs
module A = B
module B = A

## update: test.rb
def test
A
end

## diagnostics: test.rb
25 changes: 25 additions & 0 deletions scenario/rbs/class-module-alias-nested.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
## update: test.rbs
module Outer
module Inner
def self.greet: () -> String
CONST: Integer
end
module InnerAlias = Inner
end

## update: test.rb
def test1
Outer::InnerAlias.greet
end

def test2
Outer::InnerAlias::CONST
end

## assert: test.rb
class Object
def test1: -> String
def test2: -> Integer
end

## diagnostics: test.rb
44 changes: 44 additions & 0 deletions scenario/rbs/class-module-alias.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## update: test.rbs
class Foo
def foo: () -> Integer
end
class Bar = Foo
module M
def m: () -> String
CONST: Symbol
end
module N = M

## update: test.rb
def test1
Foo.new.foo
end

def test2
Bar.new.foo
end

def test3
N::CONST
end

class UseN
include N

def test4
m
end
end

## assert: test.rb
class Object
def test1: -> Integer
def test2: -> Integer
def test3: -> Symbol
end
class UseN
include M
def test4: -> String
end

## diagnostics: test.rb
Loading