diff --git a/changelog/fix_fix_unexpected_nonlocal_exits_on_rubocop_ast_node_parent_module_name.md b/changelog/fix_fix_unexpected_nonlocal_exits_on_rubocop_ast_node_parent_module_name.md new file mode 100644 index 000000000..70646bc5e --- /dev/null +++ b/changelog/fix_fix_unexpected_nonlocal_exits_on_rubocop_ast_node_parent_module_name.md @@ -0,0 +1 @@ +* [#176](https://github.com/rubocop-hq/rubocop-ast/pull/176): Fix unexpected non-local exits on Rubocop::AST::Node#parent_module_name. ([@sanfrecce-osaka][]) diff --git a/lib/rubocop/ast/node.rb b/lib/rubocop/ast/node.rb index 98fddd52d..7cad4b5fa 100644 --- a/lib/rubocop/ast/node.rb +++ b/lib/rubocop/ast/node.rb @@ -612,19 +612,17 @@ def while_until_value_used? def parent_module_name_part(node) case node.type when :class, :module, :casgn - # TODO: if constant name has cbase (leading ::), then we don't need - # to keep traversing up through nested classes/modules + # TODO: if constant name has cbase (leading ::), then we must not + # keep traversing up through nested classes/modules node.defined_module_name when :sclass - yield parent_module_name_for_sclass(node) + parent_module_name_for_sclass(node) else # block parent_module_name_for_block(node) { yield nil } end end def parent_module_name_for_sclass(sclass_node) - # TODO: look for constant definition and see if it is nested - # inside a class or module subject = sclass_node.children[0] if subject.const_type? diff --git a/spec/rubocop/ast/node_pattern_spec.rb b/spec/rubocop/ast/node_pattern_spec.rb index a2a6638a3..b53dbd3a4 100644 --- a/spec/rubocop/ast/node_pattern_spec.rb +++ b/spec/rubocop/ast/node_pattern_spec.rb @@ -10,7 +10,6 @@ def parse(code) builder = RuboCop::AST::Builder.new Parser::CurrentRuby.new(builder).parse(buffer) end - let(:root_node) { parse(ruby) } let(:node) { root_node } let(:params) { [] } diff --git a/spec/rubocop/ast/node_spec.rb b/spec/rubocop/ast/node_spec.rb index d27828b80..ec534b76e 100644 --- a/spec/rubocop/ast/node_spec.rb +++ b/spec/rubocop/ast/node_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe RuboCop::AST::Node do - let(:ast) { RuboCop::AST::ProcessedSource.new(src, ruby_version).ast } + let(:ast) { parse_source(src).node } let(:node) { ast } describe '#value_used?' do @@ -659,4 +659,71 @@ def variables; end end end end + + describe '#parent_module_name' do + subject(:parent_module_name) { node.parent_module_name } + context 'when node on top level' do + let(:src) { 'def config; end' } + + it { is_expected.to eq 'Object' } + end + + context 'when node on module' do + let(:src) do + <<~RUBY + module Foo + >>attr_reader :config<< + end + RUBY + end + + it { is_expected.to eq 'Foo' } + end + + context 'when node on singleton class' do + let(:src) do + <<~RUBY + module Foo + class << self + >>attr_reader :config<< + end + end + RUBY + end + + it { is_expected.to eq 'Foo::#' } + end + + context 'when node on class in singleton class' do + let(:src) do + <<~RUBY + module Foo + class << self + class Bar + >>attr_reader :config<< + end + end + end + RUBY + end + + it { is_expected.to eq 'Foo::#::Bar' } + end + + context 'when node nested in an unknown block' do + let(:src) do + <<~RUBY + module Foo + foo do + class Bar + >>attr_reader :config<< + end + end + end + RUBY + end + + it { is_expected.to eq nil } + end + end end