Class: RuboCop::AST::Node
Overview
Node
is a subclass of Parser::AST::Node
. It provides
access to parent nodes and an object-oriented way to traverse an AST with
the power of Enumerable
.
It has predicate methods for every node type, like this:
Constant Summary
-
ASSIGNMENTS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 55(EQUALS_ASSIGNMENTS + SHORTHAND_ASSIGNMENTS).freeze
-
BASIC_CONDITIONALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 58%i[if while until].to_set.freeze
-
BASIC_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 42(LITERALS - COMPOSITE_LITERALS).freeze
-
COMPARISON_OPERATORS =
Internal use only
<⇒ isn’t included here, because it doesn’t return a boolean.
%i[== === != <= >= > <].to_set.freeze
-
COMPOSITE_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 39%i[dstr xstr dsym array hash irange erange regexp].to_set.freeze
-
CONDITIONALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 60(BASIC_CONDITIONALS + %i[case case_match]).freeze
-
EMPTY_CHILDREN =
private
# File 'lib/rubocop/ast/node.rb', line 84[].freeze
-
EMPTY_PROPERTIES =
private
# File 'lib/rubocop/ast/node.rb', line 85{}.freeze
-
EQUALS_ASSIGNMENTS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 50%i[lvasgn ivasgn cvasgn gvasgn casgn masgn].to_set.freeze
-
FALSEY_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 35%i[false nil].to_set.freeze
-
GROUP_FOR_TYPE =
private
Internal use only
# File 'lib/rubocop/ast/node.rb', line 89{ def: :any_def, defs: :any_def, arg: :argument, optarg: :argument, restarg: :argument, kwarg: :argument, kwoptarg: :argument, kwrestarg: :argument, blockarg: :argument, forward_arg: :argument, shadowarg: :argument, true: :boolean, false: :boolean, int: :numeric, float: :numeric, rational: :numeric, complex: :numeric, str: :any_str, dstr: :any_str, xstr: :any_str, sym: :any_sym, dsym: :any_sym, irange: :range, erange: :range, send: :call, csend: :call, block: :any_block, numblock: :any_block, itblock: :any_block, match_pattern: :any_match_pattern, match_pattern_p: :any_match_pattern }.freeze
-
IMMUTABLE_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 47(LITERALS - MUTABLE_LITERALS).freeze
-
KEYWORDS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 70%i[alias and break case class def defs defined? kwbegin do else ensure for if module next not or postexe redo rescue retry return self super zsuper then undef until when while yield].to_set.freeze
-
LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 37(TRUTHY_LITERALS + FALSEY_LITERALS).freeze
-
LITERAL_RECURSIVE_METHODS =
private
# File 'lib/rubocop/ast/node.rb', line 80(COMPARISON_OPERATORS + %i[* ! <=>]).freeze
-
LITERAL_RECURSIVE_TYPES =
private
# File 'lib/rubocop/ast/node.rb', line 81(OPERATOR_KEYWORDS + COMPOSITE_LITERALS + %i[begin pair]).freeze
-
LOOP_TYPES =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 64(POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
-
MUTABLE_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 44%i[str dstr xstr array hash regexp irange erange].to_set.freeze
-
OPERATOR_KEYWORDS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 76%i[and or].to_set.freeze
-
POST_CONDITION_LOOP_TYPES =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 62%i[while_post until_post].to_set.freeze
-
REFERENCES =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 68%i[nth_ref back_ref].to_set.freeze
-
SHORTHAND_ASSIGNMENTS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 53%i[op_asgn or_asgn and_asgn].to_set.freeze
-
SPECIAL_KEYWORDS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 78%w[__FILE__ __LINE__ __ENCODING__].to_set.freeze
-
TRUTHY_LITERALS =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 31%i[str dstr xstr int float sym dsym array hash regexp true irange erange complex rational regopt].to_set.freeze
-
VARIABLES =
Internal use only
# File 'lib/rubocop/ast/node.rb', line 66%i[ivar gvar cvar lvar].to_set.freeze
Class Method Summary
- .new(type, children = EMPTY_CHILDREN, properties = EMPTY_PROPERTIES) ⇒ Node constructor
-
.def_recursive_literal_predicate(kind)
private
Define a
recursive_?
predicate method for the given node kind.
NodePattern::Macros
- Extended
def_node_matcher | Define a method which applies a pattern to an AST node. |
def_node_search | Define a method which recurses over the descendants of an AST node, checking whether any of them match the provided pattern. |
Instance Attribute Summary
- #any_block_type? ⇒ Boolean readonly
- #any_def_type? ⇒ Boolean readonly
- #any_match_pattern_type? ⇒ Boolean readonly
- #any_str_type? ⇒ Boolean readonly
- #any_sym_type? ⇒ Boolean readonly
- #argument? ⇒ Boolean readonly
- #argument_type? ⇒ Boolean readonly
- #assignment? ⇒ Boolean readonly
- #basic_conditional? ⇒ Boolean readonly
- #basic_literal? ⇒ Boolean readonly
- #boolean_type? ⇒ Boolean readonly
- #call_type? ⇒ Boolean readonly
- #chained? ⇒ Boolean readonly
- #complete? ⇒ Boolean readonly
- #conditional? ⇒ Boolean readonly
- #empty_source? ⇒ Boolean readonly
- #equals_asgn? ⇒ Boolean readonly
- #falsey_literal? ⇒ Boolean readonly
- #guard_clause? ⇒ Boolean readonly
- #immutable_literal? ⇒ Boolean readonly
- #keyword? ⇒ Boolean readonly
- #literal? ⇒ Boolean readonly
-
#loop_keyword? ⇒ Boolean
readonly
Note
loop { }
is a normal method call and thus not a loop keyword. -
#multiline? ⇒ Boolean
readonly
Predicates.
- #mutable_literal? ⇒ Boolean readonly
- #numeric_type? ⇒ Boolean readonly
- #operator_keyword? ⇒ Boolean readonly
-
#parent ⇒ Node?
readonly
Returns the parent node, or
nil
if the receiver is a root node. - #parent? ⇒ Boolean readonly
- #parenthesized_call? ⇒ Boolean readonly
- #post_condition_loop? ⇒ Boolean readonly
-
#pure? ⇒ Boolean
readonly
Some expressions are evaluated for their value, some for their side effects, and some for both.
- #range_type? ⇒ Boolean readonly
- #reference? ⇒ Boolean readonly
- #root? ⇒ Boolean readonly
-
#send_type? ⇒ Boolean
readonly
Most nodes are of 'send' type, so this method is defined separately to make this check as fast as possible.
- #shorthand_asgn? ⇒ Boolean readonly
- #single_line? ⇒ Boolean readonly
- #special_keyword? ⇒ Boolean readonly
- #truthy_literal? ⇒ Boolean readonly
-
#value_used? ⇒ Boolean
readonly
Some expressions are evaluated for their value, some for their side effects, and some for both If we know that an expression is useful only for its side effects, that means we can transform it in ways which preserve the side effects, but change the return value So, does the return value of this node matter? If we changed it to
(…; nil)
, might that affect anything? - #variable? ⇒ Boolean readonly
- #begin_value_used? ⇒ Boolean readonly private
- #case_if_value_used? ⇒ Boolean readonly private
- #for_value_used? ⇒ Boolean readonly private
- #while_until_value_used? ⇒ Boolean readonly private
Instance Method Summary
-
#ancestors ⇒ Array<Node>
Returns an array of ancestor nodes.
-
#assignment_or_similar?(node = self)
Some cops treat the shovel operator as a kind of assignment.
- #class_constructor?(node = self)
- #class_definition?(node = self)
- #complete!
- #const_name
- #defined_module
- #defined_module_name
-
#each_ancestor ⇒ self, Enumerator
Calls the given block for each ancestor node from parent to root.
- #first_line
- #global_const?(node = self, name)
- #lambda?(node = self)
- #lambda_or_proc?(node = self)
- #last_line
-
#left_sibling ⇒ Node?
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms.
-
#left_siblings ⇒ Array<Node>
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms.
- #line_count
-
#loc?(which_loc) ⇒ Boolean
Shortcut to safely check if a location is present.
-
#loc_is?(which_loc, str) ⇒ Boolean
Shortcut to safely test a particular location, even if this location does not exist or is
nil
. - #match_guard_clause?(node = self)
- #module_definition?(node = self)
-
#node_parts ⇒ Array<Node>
Common destructuring method.
- #nonempty_line_count
-
#parent_module_name
Searching the AST.
- #proc?(node = self)
- #receiver(node = self)
- #recursive_basic_literal? ⇒ Boolean
- #recursive_literal? ⇒ Boolean
-
#right_sibling ⇒ Node?
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms.
-
#right_siblings ⇒ Array<Node>
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms.
-
#sibling_index ⇒ Integer?
Returns the index of the receiver node in its siblings.
-
#source ⇒ String?
Note
Some rare nodes may have no source, like s(:args)
infoo {}
. - #source_length
- #source_range
- #str_content(node = self)
-
#struct_constructor?(node = self)
deprecated
Deprecated.
Use
:class_constructor?
-
#type?(*types) ⇒ Boolean
Determine if the node is one of several node types in a single query Allows specific single node types, as well as "grouped" types (e.g.
-
#updated(type = nil, children = nil, properties = {})
Override #updated so that
AST::Processor
does not try to mutate our ASTs. - #defined_module0(node = self) private
- #new_class_or_module_block?(node = self) private
- #parent_module_name_for_block(ancestor) private
- #parent_module_name_for_sclass(sclass_node) private
- #parent_module_name_part(node) private
- #visit_ancestors(types) private
Descendence
- Included
#child_nodes | Returns an array of child nodes. |
#descendants | Returns an array of descendant nodes. |
#each_child_node | Calls the given block for each child node. |
#each_descendant | Calls the given block for each descendant node with depth first order. |
#each_node | Calls the given block for the receiver and each descendant node in depth-first order. |
Sexp
- Included
#s | Creates a |
Constructor Details
.new(type, children = EMPTY_CHILDREN, properties = EMPTY_PROPERTIES) ⇒ Node
# File 'lib/rubocop/ast/node.rb', line 155
def initialize(type, children = EMPTY_CHILDREN, properties = EMPTY_PROPERTIES) @mutable_attributes = {} # ::AST::Node#initialize freezes itself. super # #parent= may be invoked multiple times for a node because there are # pending nodes while constructing AST and they are replaced later. # For example, `lvar` and `send` type nodes are initially created as an # `ident` type node and fixed to the appropriate type later. # So, the #parent attribute needs to be mutable. each_child_node do |child_node| child_node.parent = self unless child_node.complete? end end
Class Method Details
.def_recursive_literal_predicate(kind) (private)
Define a recursive_?
predicate method for the given node kind.
# File 'lib/rubocop/ast/node.rb', line 134
private_class_method def self.def_recursive_literal_predicate(kind) # rubocop:disable Metrics/MethodLength recursive_kind = "recursive_#{kind}?" kind_filter = "#{kind}?" class_eval <<~RUBY, __FILE__, __LINE__ + 1 def #{recursive_kind} # def recursive_literal? case type # case type when :send # when :send LITERAL_RECURSIVE_METHODS.include?(method_name) && # LITERAL_RECURSIVE_METHODS.include?(method_name) && receiver.send(:#{recursive_kind}) && # receiver.send(:recursive_literal?) && arguments.all?(&:#{recursive_kind}) # arguments.all?(&:recursive_literal?) when LITERAL_RECURSIVE_TYPES # when LITERAL_RECURSIVE_TYPES children.compact.all?(&:#{recursive_kind}) # children.compact.all?(&:recursive_literal?) else # else send(:#{kind_filter}) # send(:literal?) end # end end # end RUBY end
Instance Attribute Details
#any_block_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 549
def any_block_type? GROUP_FOR_TYPE[type] == :any_block end
#any_def_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 529
def any_def_type? GROUP_FOR_TYPE[type] == :any_def end
#any_match_pattern_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 553
def any_match_pattern_type? GROUP_FOR_TYPE[type] == :any_match_pattern end
#any_str_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 557
def any_str_type? GROUP_FOR_TYPE[type] == :any_str end
#any_sym_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 561
def any_sym_type? GROUP_FOR_TYPE[type] == :any_sym end
#argument? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 525
def argument? parent&.send_type? && parent.arguments.include?(self) end
#argument_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 533
def argument_type? GROUP_FOR_TYPE[type] == :argument end
#assignment? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 477
def assignment? ASSIGNMENTS.include?(type) end
#basic_conditional? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 481
def basic_conditional? BASIC_CONDITIONALS.include?(type) end
#basic_literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 435
def basic_literal? BASIC_LITERALS.include?(type) end
#begin_value_used? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 704
def begin_value_used? # the last child node determines the value of the parent sibling_index == parent.children.size - 1 ? parent.value_used? : false end
#boolean_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 537
def boolean_type? GROUP_FOR_TYPE[type] == :boolean end
#call_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 517
def call_type? GROUP_FOR_TYPE[type] == :call end
#case_if_value_used? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 715
def case_if_value_used? # (case <condition> <when...>) # (if <condition> <truebranch> <falsebranch>) sibling_index.zero? || parent.value_used? end
#chained? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 521
def chained? parent&.call_type? && eql?(parent.receiver) end
#complete? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 222
def complete? @mutable_attributes.frozen? end
#conditional? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 485
def conditional? CONDITIONALS.include?(type) end
#empty_source? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 421
def empty_source? source_length.zero? end
#equals_asgn? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 469
def equals_asgn? EQUALS_ASSIGNMENTS.include?(type) end
#falsey_literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 443
def falsey_literal? FALSEY_LITERALS.include?(type) end
#for_value_used? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 709
def for_value_used? # `for var in enum; body; end` # (for <var> <enum> <body>) sibling_index == 2 ? parent.value_used? : true end
#guard_clause? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 565
def guard_clause? node = operator_keyword? ? rhs : self node.match_guard_clause? end
#immutable_literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 451
def immutable_literal? IMMUTABLE_LITERALS.include?(type) end
#keyword? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 498
def keyword? return true if special_keyword? || (send_type? && prefix_not?) return false unless KEYWORDS.include?(type) !OPERATOR_KEYWORDS.include?(type) || loc.operator.is?(type.to_s) end
#literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 431
def literal? LITERALS.include?(type) end
#loop_keyword? ⇒ Boolean
(readonly)
Note
|
loop { } is a normal method call and thus not a loop keyword.
|
# File 'lib/rubocop/ast/node.rb', line 494
def loop_keyword? LOOP_TYPES.include?(type) end
#multiline? ⇒ Boolean
(readonly)
Predicates
# File 'lib/rubocop/ast/node.rb', line 413
def multiline? line_count > 1 end
#mutable_literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 447
def mutable_literal? MUTABLE_LITERALS.include?(type) end
#numeric_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 541
def numeric_type? GROUP_FOR_TYPE[type] == :numeric end
#operator_keyword? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 509
def operator_keyword? OPERATOR_KEYWORDS.include?(type) end
#parent ⇒ Node
? (rw)
Returns the parent node, or nil
if the receiver is a root node.
# File 'lib/rubocop/ast/node.rb', line 199
def parent @mutable_attributes[:parent] end
#parent? ⇒ Boolean
(rw)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 208
def parent? !!parent end
#parenthesized_call? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 513
def parenthesized_call? loc_is?(:begin, '(') end
#post_condition_loop? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 489
def post_condition_loop? POST_CONDITION_LOOP_TYPES.include?(type) end
#pure? ⇒ Boolean
(readonly)
Some expressions are evaluated for their value, some for their side effects, and some for both. If we know that expressions are useful only for their return values, and have no side effects, that means we can reorder them, change the number of times they are evaluated, or replace them with other expressions which are equivalent in value. So, is evaluation of this node free of side effects?
# File 'lib/rubocop/ast/node.rb', line 677
def pure? # Be conservative and return false if we're not sure case type when :__FILE__, :__LINE__, :const, :cvar, :defined?, :false, :float, :gvar, :int, :ivar, :lvar, :nil, :str, :sym, :true, :regopt true when :and, :array, :begin, :case, :dstr, :dsym, :eflipflop, :ensure, :erange, :for, :hash, :if, :iflipflop, :irange, :kwbegin, :not, :or, :pair, :regexp, :until, :until_post, :when, :while, :while_post child_nodes.all?(&:pure?) else false end end
#range_type? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 545
def range_type? GROUP_FOR_TYPE[type] == :range end
#reference? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 465
def reference? REFERENCES.include?(type) end
#root? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 213
def root? !parent end
#send_type? ⇒ Boolean
(readonly)
Most nodes are of 'send' type, so this method is defined separately to make this check as fast as possible.
# File 'lib/rubocop/ast/node.rb', line 192
def send_type? false end
#shorthand_asgn? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 473
def shorthand_asgn? SHORTHAND_ASSIGNMENTS.include?(type) end
#single_line? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 417
def single_line? line_count == 1 end
#special_keyword? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 505
def special_keyword? SPECIAL_KEYWORDS.include?(source) end
#truthy_literal? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 439
def truthy_literal? TRUTHY_LITERALS.include?(type) end
#value_used? ⇒ Boolean
(readonly)
Some expressions are evaluated for their value, some for their side
effects, and some for both
If we know that an expression is useful only for its side effects, that
means we can transform it in ways which preserve the side effects, but
change the return value
So, does the return value of this node matter? If we changed it to
(…; nil)
, might that affect anything?
# File 'lib/rubocop/ast/node.rb', line 647
def value_used? # rubocop:disable Metrics/MethodLength # Be conservative and return true if we're not sure. return false if parent.nil? case parent.type when :array, :defined?, :dstr, :dsym, :eflipflop, :erange, :float, :hash, :iflipflop, :irange, :not, :pair, :regexp, :str, :sym, :when, :xstr parent.value_used? when :begin, :kwbegin begin_value_used? when :for for_value_used? when :case, :if case_if_value_used? when :while, :until, :while_post, :until_post while_until_value_used? else true end end
#variable? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 461
def variable? VARIABLES.include?(type) end
#while_until_value_used? ⇒ Boolean
(readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 721
def while_until_value_used? # (while <condition> <body>) -> always evaluates to `nil` sibling_index.zero? end
Instance Method Details
#ancestors ⇒ Array
<Node
>
Returns an array of ancestor nodes.
This is a shorthand for node.each_ancestor.to_a
.
# File 'lib/rubocop/ast/node.rb', line 320
def ancestors each_ancestor.to_a end
#assignment_or_similar?(node = self)
Some cops treat the shovel operator as a kind of assignment.
# File 'lib/rubocop/ast/node.rb', line 427
def_node_matcher :assignment_or_similar?, <<~PATTERN {assignment? (send _recv :<< ...)} PATTERN
#class_constructor?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 609
def_node_matcher :class_constructor?, <<~PATTERN { (send #global_const?({:Class :Module :Struct}) :new ...) (send #global_const?(:Data) :define ...) (any_block { (send #global_const?({:Class :Module :Struct}) :new ...) (send #global_const?(:Data) :define ...) } ...) } PATTERN
#class_definition?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 627
def_node_matcher :class_definition?, <<~PATTERN {(class _ _ $_) (sclass _ $_) (any_block (send #global_const?({:Struct :Class}) :new ...) _ $_)} PATTERN
#complete!
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 217
def complete! @mutable_attributes.freeze each_child_node(&:complete!) end
#const_name
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 366
def const_name return unless const_type? || casgn_type? if namespace && !namespace.cbase_type? "#{namespace.const_name}::#{short_name}" else short_name.to_s end end
#defined_module
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 386
def defined_module namespace, name = *defined_module0 s(:const, namespace, name) if name end
#defined_module0(node = self) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 377
def_node_matcher :defined_module0, <<~PATTERN {(class (const $_ $_) ...) (module (const $_ $_) ...) (casgn $_ $_ (send #global_const?({:Class :Module}) :new ...)) (casgn $_ $_ (block (send #global_const?({:Class :Module}) :new ...) ...))} PATTERN
#defined_module_name
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 391
def defined_module_name (const = defined_module) && const.const_name end
#each_ancestor ⇒ self
, Enumerator
#each_ancestor(type) ⇒ self
, Enumerator
#each_ancestor(type_a, type_b, ...) ⇒ self
, Enumerator
self
, Enumerator
#each_ancestor(type) ⇒ self
, Enumerator
#each_ancestor(type_a, type_b, ...) ⇒ self
, Enumerator
Calls the given block for each ancestor node from parent to root.
If no block is given, an Enumerator
is returned.
# File 'lib/rubocop/ast/node.rb', line 308
def each_ancestor(*types, &block) return to_enum(__method__, *types) unless block visit_ancestors(types, &block) self end
#first_line
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 334
def first_line loc.line end
#global_const?(node = self, name)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 606
def_node_matcher :global_const?, '(const {nil? cbase} %1)'
#lambda?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 600
def_node_matcher :lambda?, '(any_block (send nil? :lambda) ...)'
#lambda_or_proc?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 603
def_node_matcher :lambda_or_proc?, '{lambda? proc?}'
#last_line
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 338
def last_line loc.last_line end
#left_sibling ⇒ Node
?
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms
# File 'lib/rubocop/ast/node.rb', line 260
def left_sibling i = sibling_index return if i.nil? || i.zero? parent.children[i - 1].freeze end
#left_siblings ⇒ Array
<Node
>
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms
# File 'lib/rubocop/ast/node.rb', line 270
def left_siblings return [].freeze unless parent parent.children[0...sibling_index].freeze end
#line_count
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 342
def line_count return 0 unless source_range source_range.last_line - source_range.first_line + 1 end
#loc?(which_loc) ⇒ Boolean
Shortcut to safely check if a location is present
# File 'lib/rubocop/ast/node.rb', line 573
def loc?(which_loc) return false unless loc.respond_to?(which_loc) !loc.public_send(which_loc).nil? end
#loc_is?(which_loc, str) ⇒ Boolean
Shortcut to safely test a particular location, even if
this location does not exist or is nil
# File 'lib/rubocop/ast/node.rb', line 581
def loc_is?(which_loc, str) return false unless loc?(which_loc) loc.public_send(which_loc).is?(str) end
#match_guard_clause?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 588
def_node_matcher :match_guard_clause?, <<~PATTERN [${(send nil? {:raise :fail} ...) return break next} single_line?] PATTERN
#module_definition?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 634
def_node_matcher :module_definition?, <<~PATTERN {(module _ $_) (any_block (send #global_const?(:Module) :new ...) _ $_)} PATTERN
#new_class_or_module_block?(node = self) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 765
def_node_matcher :new_class_or_module_block?, <<~PATTERN ^(casgn _ _ (block (send (const _ {:Class :Module}) :new) ...)) PATTERN
#node_parts ⇒ Array
<Node
>
Common destructuring method. This can be used to normalize destructuring for different variations of the node. Some node types override this with their own custom destructuring method.
# File 'lib/rubocop/ast/node.rb', line 291
alias node_parts to_a
#nonempty_line_count
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 348
def nonempty_line_count source.lines.grep(/\S/).size end
#parent_module_name
Searching the AST
# File 'lib/rubocop/ast/node.rb', line 397
def parent_module_name # what class or module is this method/constant/etc definition in? # returns nil if answer cannot be determined ancestors = each_ancestor(:class, :module, :sclass, :casgn, :block) result = ancestors.filter_map do |ancestor| parent_module_name_part(ancestor) do |full_name| return nil unless full_name full_name end end.reverse.join('::') result.empty? ? 'Object' : result end
#parent_module_name_for_block(ancestor) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 751
def parent_module_name_for_block(ancestor) if ancestor.method?(:class_eval) # `class_eval` with no receiver applies to whatever module or class # we are currently in return unless (receiver = ancestor.receiver) yield unless receiver.const_type? receiver.const_name elsif !new_class_or_module_block?(ancestor) yield end end
#parent_module_name_for_sclass(sclass_node) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 739
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? "#<Class:#{subject.const_name}>" elsif subject.self_type? "#<Class:#{sclass_node.parent_module_name}>" end end
#parent_module_name_part(node) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 726
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 node.defined_module_name when :sclass yield parent_module_name_for_sclass(node) else # block parent_module_name_for_block(node) { yield nil } end end
#proc?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 593
def_node_matcher :proc?, <<~PATTERN {(block (send nil? :proc) ...) (block (send #global_const?(:Proc) :new) ...) (send #global_const?(:Proc) :new)} PATTERN
#receiver(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 359
def_node_matcher :receiver, <<~PATTERN {(send $_ ...) (any_block (call $_ ...) ...)} PATTERN
#recursive_basic_literal? ⇒ Boolean
# File 'lib/rubocop/ast/node.rb', line 459
def_recursive_literal_predicate :basic_literal
#recursive_literal? ⇒ Boolean
# File 'lib/rubocop/ast/node.rb', line 458
def_recursive_literal_predicate :literal
#right_sibling ⇒ Node
?
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms
# File 'lib/rubocop/ast/node.rb', line 251
def right_sibling return unless parent parent.children[sibling_index + 1].freeze end
#right_siblings ⇒ Array
<Node
>
Use is discouraged, this is a potentially slow method and can lead to even slower algorithms
# File 'lib/rubocop/ast/node.rb', line 279
def right_siblings return [].freeze unless parent parent.children[(sibling_index + 1)..].freeze end
#sibling_index ⇒ Integer
?
Returns the index of the receiver node in its siblings. (Sibling index uses zero based numbering.) Use is discouraged, this is a potentially slow method.
# File 'lib/rubocop/ast/node.rb', line 244
def sibling_index parent&.children&.index { |sibling| sibling.equal?(self) } end
#source ⇒ String
?
Note
|
Some rare nodes may have no source, like s(:args) in foo {}
|
# File 'lib/rubocop/ast/node.rb', line 326
def source loc.expression&.source end
#source_length
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 352
def source_length source_range ? source_range.size : 0 end
#source_range
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 330
def source_range loc.expression end
#str_content(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 364
def_node_matcher :str_content, '(str $_)'
#struct_constructor?(node = self)
Use :class_constructor?
# File 'lib/rubocop/ast/node.rb', line 622
def_node_matcher :struct_constructor?, <<~PATTERN (any_block (send #global_const?(:Struct) :new ...) _ $_) PATTERN
#type?(*types) ⇒ Boolean
Determine if the node is one of several node types in a single query
Allows specific single node types, as well as "grouped" types
(e.g. :boolean
for :true
or :false
)
# File 'lib/rubocop/ast/node.rb', line 174
def type?(*types) return true if types.include?(type) group_type = GROUP_FOR_TYPE[type] !group_type.nil? && types.include?(group_type) end
#updated(type = nil, children = nil, properties = {})
Override #updated
so that AST::Processor
does not try to
mutate our ASTs. Since we keep references from children to parents and
not just the other way around, we cannot update an AST and share
identical subtrees. Rather, the entire AST must be copied any time any
part of it is changed.