From d23ad7f0abda60626661aae48d2a2bcac324bd04 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Wed, 10 Jan 2024 01:10:36 +0100 Subject: [PATCH] Make `Scope#*_def?` methods accept `check_outer_scopes` parameter --- spec/ameba/ast/scope_spec.cr | 22 ++++++++++++++++++++++ src/ameba/ast/scope.cr | 23 +++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/spec/ameba/ast/scope_spec.cr b/spec/ameba/ast/scope_spec.cr index 3d8b6ba3..1b957574 100644 --- a/spec/ameba/ast/scope_spec.cr +++ b/spec/ameba/ast/scope_spec.cr @@ -180,6 +180,28 @@ module Ameba::AST end end + describe "#def?" do + context "when check_outer_scopes: true" do + it "returns true if outer scope is Crystal::Def" do + nodes = as_nodes("def foo; 3.times {}; end") + outer_scope = Scope.new nodes.def_nodes.first + scope = Scope.new nodes.block_nodes.first, outer_scope + scope.def?(check_outer_scopes: true).should be_true + end + end + + it "returns true if Crystal::Def" do + nodes = as_nodes("def foo; end") + scope = Scope.new nodes.def_nodes.first + scope.def?.should be_true + end + + it "returns false otherwise" do + scope = Scope.new as_node("a = 1") + scope.def?.should be_false + end + end + describe "#in_macro?" do it "returns true if Crystal::Macro" do nodes = as_nodes <<-CRYSTAL diff --git a/src/ameba/ast/scope.cr b/src/ameba/ast/scope.cr index 3e0b4573..c6f64510 100644 --- a/src/ameba/ast/scope.cr +++ b/src/ameba/ast/scope.cr @@ -180,20 +180,15 @@ module Ameba::AST @visibility || outer_scope.try(&.visibility) end - # Returns `true` if current scope is a def, `false` otherwise. - def def? - node.is_a?(Crystal::Def) - end - - # Returns `true` if current scope is a class, `false` otherwise. - def class_def? - node.is_a?(Crystal::ClassDef) - end - - # Returns `true` if current scope is a module, `false` otherwise. - def module_def? - node.is_a?(Crystal::ModuleDef) - end + {% for type in %w[Def ClassDef ModuleDef EnumDef LibDef FunDef].map(&.id) %} + {% method_name = type.underscore %} + # Returns `true` if current scope is a {{ method_name[0..-5] }} def, `false` otherwise. + def {{ method_name }}?(*, check_outer_scopes = false) + node.is_a?(Crystal::{{ type }}) || + !!(check_outer_scopes && + outer_scope.try(&.{{ method_name }}?(check_outer_scopes: true))) + end + {% end %} # Returns `true` if this scope is a top level scope, `false` otherwise. def top_level?