Skip to content

Commit

Permalink
Prevent node reusing when the method definition result is used
Browse files Browse the repository at this point in the history
Typically, `v = def foo; end` made the invariant check fail due to node
reusing. This pattern should be very rare, so I handle this issue by
preventing node reusing if the result of the method definition is used.

Note that `private def ...` will prevent node reusing. I think this
pattern should be handled separatedly when we support method visibility.
  • Loading branch information
mame committed Jul 22, 2024
1 parent a552db8 commit c51e74f
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 19 deletions.
12 changes: 6 additions & 6 deletions lib/typeprof/core/ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def self.parse_rb(path, src)
ProgramNode.new(raw_scope, lenv)
end

#: (untyped, TypeProf::Core::LocalEnv) -> TypeProf::Core::AST::Node
def self.create_node(raw_node, lenv)
#: (untyped, TypeProf::Core::LocalEnv, ?bool) -> TypeProf::Core::AST::Node
def self.create_node(raw_node, lenv, use_result = true)
while true
case raw_node.type
when :parentheses_node
Expand All @@ -34,10 +34,10 @@ def self.create_node(raw_node, lenv)
case raw_node.type

# definition
when :statements_node then StatementsNode.new(raw_node, lenv)
when :module_node then ModuleNode.new(raw_node, lenv)
when :class_node then ClassNode.new(raw_node, lenv)
when :def_node then DefNode.new(raw_node, lenv)
when :statements_node then StatementsNode.new(raw_node, lenv, use_result)
when :module_node then ModuleNode.new(raw_node, lenv, use_result)
when :class_node then ClassNode.new(raw_node, lenv, use_result)
when :def_node then DefNode.new(raw_node, lenv, use_result)
when :alias_method_node then AliasNode.new(raw_node, lenv)

# control
Expand Down
3 changes: 2 additions & 1 deletion lib/typeprof/core/ast/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def initialize(raw_node, lenv)
def subnodes = {}
def attrs = {}

#: { (TypeProf::Core::AST::Node) -> void } -> nil
def each_subnode
queue = subnodes.values

Expand Down Expand Up @@ -196,7 +197,7 @@ def initialize(raw_node, lenv)
@tbl = raw_node.locals
raw_body = raw_node.statements

@body = AST.create_node(raw_body, lenv)
@body = AST.create_node(raw_body, lenv, false)
end

attr_reader :tbl, :body
Expand Down
12 changes: 9 additions & 3 deletions lib/typeprof/core/ast/method.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def self.parse_params(tbl, raw_args, lenv)
end

class DefNode < Node
def initialize(raw_node, lenv)
def initialize(raw_node, lenv, use_result)
super(raw_node, lenv)
singleton = !!raw_node.receiver
mid = raw_node.name
Expand Down Expand Up @@ -139,6 +139,10 @@ def initialize(raw_node, lenv)
@rest_keywords = h[:rest_keywords]
@block = h[:block]
@args_code_ranges = h[:args_code_ranges] || []

# If the result of `def` statement, stop reusing this node
# TODO: `private def ...` should be handled well
@reusable = !use_result
end

attr_reader :singleton, :mid, :mid_code_range
Expand All @@ -155,6 +159,7 @@ def initialize(raw_node, lenv)
attr_reader :block
attr_reader :body
attr_reader :rbs_method_type
attr_reader :reusable

def subnodes = {
body:,
Expand All @@ -175,17 +180,18 @@ def attrs = {
opt_keywords:,
rest_keywords:,
block:,
reusable:,
}

def mname_code_range(_name) = @mid_code_range

def define(genv) # NOT define0
return define_copy(genv) if @prev_node
return define_copy(genv) if @prev_node && @reusable
super(genv)
end

def install(genv) # NOT install0
return install_copy(genv) if @prev_node
return install_copy(genv) if @prev_node && @reusable
super(genv)
end

Expand Down
7 changes: 4 additions & 3 deletions lib/typeprof/core/ast/misc.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module TypeProf::Core
class AST
class StatementsNode < Node
def initialize(raw_node, lenv)
def initialize(raw_node, lenv, use_result)
super(raw_node, lenv)
@stmts = raw_node.body.map do |n|
stmts = raw_node.body
@stmts = stmts.map.with_index do |n, i|
if n
AST.create_node(n, lenv)
AST.create_node(n, lenv, i == stmts.length - 1 ? use_result : false)
else
last = code_range.last
DummyNilNode.new(TypeProf::CodeRange.new(last, last), lenv)
Expand Down
12 changes: 6 additions & 6 deletions lib/typeprof/core/ast/module.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module TypeProf::Core
class AST
class ModuleBaseNode < Node
def initialize(raw_node, lenv, raw_cpath, raw_scope)
def initialize(raw_node, lenv, raw_cpath, raw_scope, use_result)
super(raw_node, lenv)

@cpath = AST.create_node(raw_cpath, lenv)
Expand All @@ -13,7 +13,7 @@ def initialize(raw_node, lenv, raw_cpath, raw_scope)
if @static_cpath
ncref = CRef.new(@static_cpath, true, nil, lenv.cref)
nlenv = LocalEnv.new(@lenv.path, ncref, {}, [])
@body = raw_scope ? AST.create_node(raw_scope, nlenv) : DummyNilNode.new(code_range, lenv)
@body = raw_scope ? AST.create_node(raw_scope, nlenv, use_result) : DummyNilNode.new(code_range, lenv)
else
@body = nil
end
Expand Down Expand Up @@ -77,14 +77,14 @@ def modified_vars(tbl, vars)
end

class ModuleNode < ModuleBaseNode
def initialize(raw_node, lenv)
super(raw_node, lenv, raw_node.constant_path, raw_node.body)
def initialize(raw_node, lenv, use_result)
super(raw_node, lenv, raw_node.constant_path, raw_node.body, use_result)
end
end

class ClassNode < ModuleBaseNode
def initialize(raw_node, lenv)
super(raw_node, lenv, raw_node.constant_path, raw_node.body)
def initialize(raw_node, lenv, use_result)
super(raw_node, lenv, raw_node.constant_path, raw_node.body, use_result)
raw_superclass = raw_node.superclass
@superclass_cpath = raw_superclass ? AST.create_node(raw_superclass, lenv) : nil
end
Expand Down
13 changes: 13 additions & 0 deletions scenario/method/prevent-reuse.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## update: test.rb
class Foo
ary =
def foo
end
end

## update: test.rb
class Foo
ary =
def foo
end
end

0 comments on commit c51e74f

Please sign in to comment.