Skip to content

Instantly share code, notes, and snippets.

@davich
Created June 25, 2021 10:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davich/720f54ecbd524380830309b6080bcf6f to your computer and use it in GitHub Desktop.
Save davich/720f54ecbd524380830309b6080bcf6f to your computer and use it in GitHub Desktop.
Find methods that use instance variables set in other methods.
require 'parser/current'
code = <<-RUBY
class Model
def initialize
@a = 1
@b = 2
end
def a1
@b = 123
end
def a2
@b
end
def a3
puts @b
end
end
RUBY
class ParserRunner
def initialize(code)
@ast = Parser::CurrentRuby.parse(code)
end
def run
meths = find_methods(@ast)
method_to_assigns =
meths
.map { |m| [m.children[0], find_ivar_assigns(m)] }
.reject { |k,v| v.empty? }
return unless method_to_assigns.any?
uses = method_to_uses(meths)
result = []
method_to_assigns.each do |method, assigns|
assigns.each do |assign|
other_methods = uses[assign] - [method]
if other_methods.any?
result << "#{assign} defined in #{method}, used in #{other_methods}"
end
end
end
result
end
def method_to_uses(methods)
uses =
methods
.map {|m| [m.children[0], find_ivar_uses(m)] }
.reject {|k,v| v.empty? }
uses.each_with_object({}) do |(m, vars), hash|
vars.each { |v| hash[v] ||= []; hash[v] << m }
end
end
def find_methods(ast)
find_type(ast, :def).reject! { |ast| ast.children[0] == :initialize }
end
def find_ivar_assigns(ast)
find_type(ast, :ivasgn).map { |a| a.children[0] }
end
def find_ivar_uses(ast)
find_type(ast, :ivar).map { |a| a.children[0] }
end
def find_type(ast, type)
ast.children.map do |child|
next unless Parser::AST::Node === child
if child.type == type
child
else
find_type(child, type)
end
end.flatten.compact
end
end
p ParserRunner.new(code).run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment