-
-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add
ConstNode
and some helper methods.
- Loading branch information
Showing
5 changed files
with
111 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module AST | ||
# A node extension for `const` nodes. | ||
class ConstNode < Node | ||
# The `send` node associated with this block. | ||
# | ||
# @return [Node, nil] the node associated with the scope (e.g. cbase, const, ...) | ||
def namespace | ||
children[0] | ||
end | ||
|
||
# @return [Symbol] the demodulized name of the constant: "::Foo::Bar" => :Bar | ||
def short_name | ||
children[1] | ||
end | ||
|
||
# The body of this block. | ||
# | ||
# @return [Boolean] if the constant is a Module / Class, according to the standard convention. | ||
# Note: some classes might have uppercase in which case this method | ||
# returns false | ||
def module_name? | ||
short_name.match?(/[[:lower:]]/) | ||
end | ||
alias class_name? module_name? | ||
|
||
# @return [Boolean] if the constant starts with `::` (aka s(:cbase)) | ||
def absolute? | ||
each_path.first.cbase_type? | ||
end | ||
|
||
# @return [Boolean] if the constant does not start with `::` (aka s(:cbase)) | ||
def relative? | ||
!absolute? | ||
end | ||
|
||
# Yield nodes for the namespace | ||
# | ||
# For `::Foo::Bar::BAZ` => yields: | ||
# s(:cbase), then | ||
# s(:const, :Foo), then | ||
# s(:const, s(:const, :Foo), :Bar) | ||
def each_path(&block) | ||
return to_enum(__method__) unless block_given? | ||
|
||
descendants = [] | ||
last = self | ||
loop do | ||
last = last.children.first | ||
break if last.nil? | ||
|
||
descendants << last | ||
break unless last.const_type? | ||
end | ||
descendants.reverse_each(&block) | ||
|
||
self | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::AST::ConstNode do | ||
let(:ast) { parse_source(source).ast } | ||
let(:const_node) { ast } | ||
let(:source) { '::Foo::Bar::BAZ' } | ||
|
||
describe '#namespace' do | ||
it { expect(const_node.namespace.source).to eq '::Foo::Bar' } | ||
end | ||
|
||
describe '#short_name' do | ||
it { expect(const_node.short_name).to eq :BAZ } | ||
end | ||
|
||
describe '#module_name?' do | ||
it { expect(const_node.module_name?).to eq false } | ||
|
||
context 'with a constant with a lowercase letter' do | ||
let(:source) { '::Foo::Bar' } | ||
|
||
it { expect(const_node.module_name?).to eq true } | ||
end | ||
end | ||
|
||
describe '#absolute?' do | ||
it { expect(const_node.absolute?).to eq true } | ||
|
||
context 'with a constant not starting with ::' do | ||
let(:source) { 'Foo::Bar::BAZ' } | ||
|
||
it { expect(const_node.absolute?).to eq false } | ||
end | ||
end | ||
|
||
describe '#each_path' do | ||
let(:source) { 'var = ::Foo::Bar::BAZ' } | ||
let(:const_node) { ast.children.last } | ||
|
||
it 'yields all parts of the namespace' do | ||
expect(const_node.each_path.map(&:type)).to eq %i[cbase const const] | ||
expect(const_node.each_path.to_a.last(2).map(&:short_name)).to eq %i[Foo Bar] | ||
end | ||
end | ||
end |