Rework variable references detection in macro

This commit is contained in:
Vitalii Elenhaupt 2018-05-12 20:06:52 +03:00
parent efe67212b0
commit bf907ee98b
No known key found for this signature in database
GPG key ID: 7558EF3A4056C706
4 changed files with 37 additions and 9 deletions

View file

@ -851,6 +851,22 @@ module Ameba::Rule
) )
subject.catch(s).should_not be_valid subject.catch(s).should_not be_valid
end end
it "doesn't report if assignment is referenced in a macro below" do
s = Source.new %(
class Foo
def foo
a = 1
macro_call
end
macro macro_call
puts a
end
end
)
subject.catch(s).should be_valid
end
end end
context "uninitialized" do context "uninitialized" do

View file

@ -19,9 +19,6 @@ module Ameba::AST
# The actual AST node that represents a current scope. # The actual AST node that represents a current scope.
getter node : Crystal::ASTNode getter node : Crystal::ASTNode
# Macro literals in current scope
getter macro_literals = [] of Crystal::MacroLiteral
delegate to_s, to: node delegate to_s, to: node
delegate location, to: node delegate location, to: node

View file

@ -117,7 +117,7 @@ module Ameba::AST
# false if not. # false if not.
def used_in_macro?(scope = @scope) def used_in_macro?(scope = @scope)
scope.inner_scopes.each do |inner_scope| scope.inner_scopes.each do |inner_scope|
return true if inner_scope.macro_literals.any? { |literal| literal.value.includes?(name) } return true if MacroLiteralFinder.new(inner_scope.node).references? node
end end
return true if (outer_scope = scope.outer_scope) && used_in_macro?(outer_scope) return true if (outer_scope = scope.outer_scope) && used_in_macro?(outer_scope)
false false
@ -142,5 +142,25 @@ module Ameba::AST
node.name == @node.name && node.name == @node.name &&
node.location == @node.location node.location == @node.location
end end
private class MacroLiteralFinder < Crystal::Visitor
@macro_literals = [] of Crystal::MacroLiteral
def initialize(node)
node.accept self
end
def references?(node : Crystal::Var)
@macro_literals.any? { |literal| literal.value.includes? node.name }
end
def visit(node : Crystal::ASTNode)
true
end
def visit(node : Crystal::MacroLiteral)
@macro_literals << node
end
end
end end
end end

View file

@ -127,11 +127,6 @@ module Ameba::AST
end end
end end
# :nodoc:
def visit(node : Crystal::MacroLiteral)
@current_scope.macro_literals << node
end
# :nodoc: # :nodoc:
def visit(node : Crystal::Call) def visit(node : Crystal::Call)
return true unless node.name == "super" && node.args.empty? return true unless node.name == "super" && node.args.empty?