diff --git a/Gemfile.lock b/Gemfile.lock index d3f340e1..985b13bd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -283,6 +283,7 @@ GEM zeitwerk (2.6.11) PLATFORMS + arm64-darwin-21 arm64-darwin-22 x86_64-darwin-22 x86_64-linux diff --git a/app/models/visitor.rb b/app/models/visitor.rb index 926baf4f..e8942541 100644 --- a/app/models/visitor.rb +++ b/app/models/visitor.rb @@ -131,7 +131,7 @@ def visit_def(node) elsif @modules.any? @modules.last else - @analyzer + inferred_context_for(target) end method_definition_args = { @@ -143,10 +143,12 @@ def visit_def(node) defined_files: [current_path], } - if target == "self" - context.class_methods << ClassMethod.new(**method_definition_args) - else + # the method can define itself onto "self", or can have a target of a constant + # to define the class there. see #inferred_context_for for more + if target.nil? context.instance_methods << InstanceMethod.new(**method_definition_args) + else + context.class_methods << ClassMethod.new(**method_definition_args) end @comments = [] @@ -188,4 +190,28 @@ def visit_command(node) super end + + private + + # handle method names like `Skiptrace.current_bindings` where the constant + # wasn't already visited + # rubocop:disable Lint/AssignmentInCondition + def inferred_context_for(target) + if existing = @analyzer.modules.detect { |name| name.qualified_name == target } + return prev if @modules.find { |mod| mod.qualified_name == existing.qualified_name } + + @modules << existing + @namespace << existing + return existing + + elsif existing = @analyzer.classes.detect { |name| name.qualified_name == target } + return prev if @classes.find { |klass| klass.qualified_name == existing.qualified_name } + + @classes << existing + return existing + + end + @analyzer + end + # rubocop:enable Lint/AssignmentInCondition end