diff --git a/spec/ameba/ast/scope_spec.cr b/spec/ameba/ast/scope_spec.cr index cfc4f072..72951b14 100644 --- a/spec/ameba/ast/scope_spec.cr +++ b/spec/ameba/ast/scope_spec.cr @@ -114,19 +114,30 @@ module Ameba::AST end end - describe "#macro?" do + describe "#in_macro?" do it "returns true if Crystal::Macro" do nodes = as_nodes %( macro included end ) scope = Scope.new nodes.macro_nodes.first - scope.macro?.should be_true + scope.in_macro?.should be_true + end + + it "returns true if node is nested to Crystal::Macro" do + nodes = as_nodes %( + macro included + {{@type.each do |type| a = type end}} + end + ) + outer_scope = Scope.new nodes.macro_nodes.first + scope = Scope.new nodes.block_nodes.first, outer_scope + scope.in_macro?.should be_true end it "returns false otherwise" do scope = Scope.new as_node "a = 1" - scope.macro?.should be_false + scope.in_macro?.should be_false end end end diff --git a/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr b/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr index d52870f6..2ed9112d 100644 --- a/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr +++ b/spec/ameba/rule/lint/shadowing_local_outer_var_spec.cr @@ -213,6 +213,29 @@ module Ameba::Rule::Lint ) subject.catch(source).should be_valid end + + it "does not report shadowed vars withing nested macro" do + source = Source.new %( + module Foo + macro included + def foo + {% for ann in instance_vars %} + {% pos_args = ann.args.empty? ? "Tuple.new".id : ann.args %} + {% end %} + end + + def bar + {{@type.instance_vars.map do |ivar| + ivar.annotations(Name).each do |ann| + puts ann.args + end + end}} + end + end + end + ) + subject.catch(source).should be_valid + end end end end diff --git a/src/ameba/ast/scope.cr b/src/ameba/ast/scope.cr index 62a129ac..3627ef73 100644 --- a/src/ameba/ast/scope.cr +++ b/src/ameba/ast/scope.cr @@ -110,9 +110,9 @@ module Ameba::AST call.try(&.name) == "spawn" end - # Returns true if current scope represents a macro. - def macro? - node.is_a?(Crystal::Macro) + # Returns true if current scope sits inside a macro. + def in_macro? + node.is_a?(Crystal::Macro) || !!outer_scope.try(&.in_macro?) end # Returns true instance variable assinged in this scope. diff --git a/src/ameba/rule/lint/shadowing_local_outer_var.cr b/src/ameba/rule/lint/shadowing_local_outer_var.cr index ba93d835..846cc484 100644 --- a/src/ameba/rule/lint/shadowing_local_outer_var.cr +++ b/src/ameba/rule/lint/shadowing_local_outer_var.cr @@ -54,7 +54,7 @@ module Ameba::Rule::Lint private def find_shadowing(source, scope) outer_scope = scope.outer_scope - return if outer_scope.nil? || outer_scope.macro? + return if outer_scope.nil? || outer_scope.in_macro? scope.arguments.reject(&.ignored?).each do |arg| variable = outer_scope.find_variable(arg.name)