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 =
# File 'lib/rubocop/ast/node.rb', line 55(EQUALS_ASSIGNMENTS + SHORTHAND_ASSIGNMENTS).freeze
-
BASIC_CONDITIONALS =
# File 'lib/rubocop/ast/node.rb', line 58%i[if while until].to_set.freeze
-
BASIC_LITERALS =
# File 'lib/rubocop/ast/node.rb', line 42(LITERALS - COMPOSITE_LITERALS).freeze
-
COMPARISON_OPERATORS =
# File 'lib/rubocop/ast/node.rb', line 28
<⇒ isn’t included here, because it doesn’t return a boolean.
%i[== === != <= >= > <].to_set.freeze
-
COMPOSITE_LITERALS =
# File 'lib/rubocop/ast/node.rb', line 39%i[dstr xstr dsym array hash irange erange regexp].to_set.freeze
-
CONDITIONALS =
# 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 91[].freeze -
EMPTY_PROPERTIES =
private
# File 'lib/rubocop/ast/node.rb', line 92{}.freeze -
EQUALS_ASSIGNMENTS =
# File 'lib/rubocop/ast/node.rb', line 50%i[lvasgn ivasgn cvasgn gvasgn casgn masgn].to_set.freeze
-
FALSEY_LITERALS =
# File 'lib/rubocop/ast/node.rb', line 35%i[false nil].to_set.freeze
-
GROUP_FOR_TYPE =
private
# File 'lib/rubocop/ast/node.rb', line 96{ 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 =
# File 'lib/rubocop/ast/node.rb', line 47(LITERALS - MUTABLE_LITERALS).freeze
-
KEYWORDS =
# 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 =
# File 'lib/rubocop/ast/node.rb', line 37(TRUTHY_LITERALS + FALSEY_LITERALS).freeze
-
LITERAL_RECURSIVE_METHODS =
private
# File 'lib/rubocop/ast/node.rb', line 87(COMPARISON_OPERATORS + %i[* ! <=>]).freeze
-
LITERAL_RECURSIVE_TYPES =
private
# File 'lib/rubocop/ast/node.rb', line 88(OPERATOR_KEYWORDS + COMPOSITE_LITERALS + %i[begin pair]).freeze
-
LOOP_TYPES =
# File 'lib/rubocop/ast/node.rb', line 64(POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
-
MUTABLE_LITERALS =
# File 'lib/rubocop/ast/node.rb', line 44%i[str dstr xstr array hash regexp irange erange].to_set.freeze
-
OPERATOR_KEYWORDS =
# File 'lib/rubocop/ast/node.rb', line 76%i[and or].to_set.freeze
-
POST_CONDITION_LOOP_TYPES =
# File 'lib/rubocop/ast/node.rb', line 62%i[while_post until_post].to_set.freeze
-
REFERENCES =
# File 'lib/rubocop/ast/node.rb', line 68%i[nth_ref back_ref].to_set.freeze
-
SHORTHAND_ASSIGNMENTS =
# File 'lib/rubocop/ast/node.rb', line 53%i[op_asgn or_asgn and_asgn].to_set.freeze
-
SPECIAL_KEYWORDS =
# File 'lib/rubocop/ast/node.rb', line 78%w[__FILE__ __LINE__ __ENCODING__].to_set.freeze
-
SPECIAL_KEYWORD_TYPES =
private
# File 'lib/rubocop/ast/node.rb', line 84
The node types which could have a source matching SPECIAL_KEYWORDS:
FILEparses asstr,LINEasintandENCODINGasconst(or as their own node types when the builder emits them);strandsymalso cover word/symbol array elements, string parts and hash labels spelling out one of these keywords.%i[str sym int const __FILE__ __LINE__ __ENCODING__].to_set.freeze
-
TRUTHY_LITERALS =
# 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 =
# 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
nilif 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.
-
#type_in?(types) ⇒ Boolean
Internal use only
Non-splatting variant of #type?, used by the traversal hot paths to avoid allocating an array per visited node.
-
#updated(type = nil, children = nil, properties = {})
Override #updated so that
AST::Processordoes 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 162
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 141
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 565
def any_block_type? GROUP_FOR_TYPE[type] == :any_block end
#any_def_type? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 545
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 569
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 573
def any_str_type? GROUP_FOR_TYPE[type] == :any_str end
#any_sym_type? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 577
def any_sym_type? GROUP_FOR_TYPE[type] == :any_sym end
#argument? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 539
def argument? return false unless parent&.send_type? parent.arguments.any? { |argument| argument.equal?(self) } end
#argument_type? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 549
def argument_type? GROUP_FOR_TYPE[type] == :argument end
#assignment? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 491
def assignment? ASSIGNMENTS.include?(type) end
#basic_conditional? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 495
def basic_conditional? BASIC_CONDITIONALS.include?(type) end
#basic_literal? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 449
def basic_literal? BASIC_LITERALS.include?(type) end
#begin_value_used? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 727
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 553
def boolean_type? GROUP_FOR_TYPE[type] == :boolean end
#call_type? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 531
def call_type? GROUP_FOR_TYPE[type] == :call end
#case_if_value_used? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 738
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 535
def chained? parent&.call_type? && equal?(parent.receiver) end
#complete? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 236
def complete? @mutable_attributes.frozen? end
#conditional? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 499
def conditional? CONDITIONALS.include?(type) end
#empty_source? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 435
def empty_source? source_length.zero? end
#equals_asgn? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 483
def equals_asgn? EQUALS_ASSIGNMENTS.include?(type) end
#falsey_literal? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 457
def falsey_literal? FALSEY_LITERALS.include?(type) end
#for_value_used? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 732
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 581
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 465
def immutable_literal? IMMUTABLE_LITERALS.include?(type) end
#keyword? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 512
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 445
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 508
def loop_keyword? LOOP_TYPES.include?(type) end
#multiline? ⇒ Boolean (readonly)
Predicates
# File 'lib/rubocop/ast/node.rb', line 427
def multiline? line_count > 1 end
#mutable_literal? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 461
def mutable_literal? MUTABLE_LITERALS.include?(type) end
#numeric_type? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 557
def numeric_type? GROUP_FOR_TYPE[type] == :numeric end
#operator_keyword? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 523
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 213
def parent @mutable_attributes[:parent] end
#parent? ⇒ Boolean (rw)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 222
def parent? !!parent end
#parenthesized_call? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 527
def parenthesized_call? loc_is?(:begin, '(') end
#post_condition_loop? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 503
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 693
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 561
def range_type? GROUP_FOR_TYPE[type] == :range end
#reference? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 479
def reference? REFERENCES.include?(type) end
#root? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 227
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 206
def send_type? false end
#shorthand_asgn? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 487
def shorthand_asgn? SHORTHAND_ASSIGNMENTS.include?(type) end
#single_line? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 431
def single_line? line_count == 1 end
#special_keyword? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 519
def special_keyword? SPECIAL_KEYWORD_TYPES.include?(type) && SPECIAL_KEYWORDS.include?(source) end
#truthy_literal? ⇒ Boolean (readonly)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 453
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 663
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 475
def variable? VARIABLES.include?(type) end
#while_until_value_used? ⇒ Boolean (readonly, private)
[ GitHub ]
# File 'lib/rubocop/ast/node.rb', line 744
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 334
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 441
def_node_matcher :assignment_or_similar?, <<~PATTERN {assignment? (send _recv :<< ...)} PATTERN
#class_constructor?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 625
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 643
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 231
def complete! @mutable_attributes.freeze each_child_node(&:complete!) end
#const_name
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 380
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 400
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 391
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 405
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 322
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 348
def first_line loc.line end
#global_const?(node = self, name)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 622
def_node_matcher :global_const?, '(const {nil? cbase} %1)'
#lambda?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 616
def_node_matcher :lambda?, '(any_block (send nil? :lambda) ...)'
#lambda_or_proc?(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 619
def_node_matcher :lambda_or_proc?, '{lambda? proc?}'
#last_line
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 352
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 274
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 284
def left_siblings return [].freeze unless parent parent.children[0...sibling_index].freeze end
#line_count
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 356
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 589
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 597
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 604
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 650
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 788
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 305
alias node_parts to_a
#nonempty_line_count
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 362
def nonempty_line_count source.lines.grep(/\S/).size end
#parent_module_name
Searching the AST
# File 'lib/rubocop/ast/node.rb', line 411
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 774
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 762
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 749
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 609
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 373
def_node_matcher :receiver, <<~PATTERN {(send $_ ...) (any_block (call $_ ...) ...)} PATTERN
#recursive_basic_literal? ⇒ Boolean
# File 'lib/rubocop/ast/node.rb', line 473
def_recursive_literal_predicate :basic_literal
#recursive_literal? ⇒ Boolean
# File 'lib/rubocop/ast/node.rb', line 472
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 265
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 293
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 258
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 340
def source loc.expression&.source end
#source_length
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 366
def source_length source_range ? source_range.size : 0 end
#source_range
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 344
def source_range loc.expression end
#str_content(node = self)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 378
def_node_matcher :str_content, '(str $_)'
#struct_constructor?(node = self)
Use :class_constructor?
# File 'lib/rubocop/ast/node.rb', line 638
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 181
def type?(*types) type_in?(types) end
#type_in?(types) ⇒ Boolean
Non-splatting variant of #type?, used by the traversal hot paths to avoid allocating an array per visited node.
# File 'lib/rubocop/ast/node.rb', line 188
def type_in?(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.
#visit_ancestors(types) (private)
[ GitHub ]# File 'lib/rubocop/ast/node.rb', line 711
def visit_ancestors(types) last_node = self if types.empty? while (current_node = last_node.parent) yield current_node last_node = current_node end else while (current_node = last_node.parent) yield current_node if current_node.type_in?(types) last_node = current_node end end end