Hello community,
here is the log from the commit of package rubygem-rubocop-ast for
openSUSE:Factory checked in at 2020-08-18 14:00:50
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rubygem-rubocop-ast (Old)
and /work/SRC/openSUSE:Factory/.rubygem-rubocop-ast.new.3399 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-rubocop-ast"
Tue Aug 18 14:00:50 2020 rev:2 rq:827558 version:0.1.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/rubygem-rubocop-ast/rubygem-rubocop-ast.changes
2020-06-22 17:46:56.202041019 +0200
+++
/work/SRC/openSUSE:Factory/.rubygem-rubocop-ast.new.3399/rubygem-rubocop-ast.changes
2020-08-18 14:03:31.018326238 +0200
@@ -1,0 +2,19 @@
+Mon Aug 17 14:35:55 UTC 2020 - Dan Čermák <[email protected]>
+
+- New upstream release 0.1.0
+
+ New features
+
+ * #36: Add post_condition_loop? and loop_keyword? for Node. (@fatkodima)
+ * #38: Add helpers allowing to check whether the method is a nonmutating
operator method or a nonmutating method of several core classes. (@fatkodima)
+ * #37: Add enumerable_method? for MethodIdentifierPredicates. (@fatkodima)
+ * #4: Add interpolation? for RegexpNode. (@tejasbubane)
+ * #20: Add option predicates for RegexpNode. (@owst)
+ * #11: Add argument_type? method to make it easy to recognize argument
nodes. (@tejasbubane)
+ * #31: NodePattern now uses param === node to match params, which allows
Regexp, Proc, Set in addition to Nodes and literals. (@marcandre)
+ * #41: Add delimiters and related predicates for RegexpNode. (@owst)
+ * #46: Basic support for non-legacy AST output from parser. Note that there
is no support (yet) in main RuboCop gem. Expect emit_forward_arg to be set to
true in v1.0 (@marcandre)
+ * #48: Support Parser::Ruby28 for Ruby 2.8 (3.0) parser (experimental).
(@koic)
+ * #35: NodePattern now accepts %named_param and %CONST. The macros
def_node_pattern and def_node_search accept default named parameters.
(@marcandre)
+
+-------------------------------------------------------------------
Old:
----
rubocop-ast-0.0.3.gem
New:
----
rubocop-ast-0.1.0.gem
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ rubygem-rubocop-ast.spec ++++++
--- /var/tmp/diff_new_pack.INAe3W/_old 2020-08-18 14:03:31.866326618 +0200
+++ /var/tmp/diff_new_pack.INAe3W/_new 2020-08-18 14:03:31.870326620 +0200
@@ -25,7 +25,7 @@
# of those fields
#
Name: rubygem-rubocop-ast
-Version: 0.0.3
+Version: 0.1.0
Release: 0
Summary: RuboCop tools to deal with Ruby code AST
License: MIT
++++++ rubocop-ast-0.0.3.gem -> rubocop-ast-0.1.0.gem ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/README.md new/README.md
--- old/README.md 2020-05-15 09:39:32.000000000 +0200
+++ new/README.md 2020-06-26 06:40:59.000000000 +0200
@@ -2,12 +2,15 @@
[](https://badge.fury.io/rb/rubocop-ast)
[](https://github.com/rubocop-hq/rubocop-ast/actions?query=workflow%3ACI)
+[](https://codeclimate.com/github/rubocop-hq/rubocop-ast/test_coverage)
+[](https://codeclimate.com/github/rubocop-hq/rubocop-ast/maintainability)
Contains the classes needed by
[RuboCop](https://github.com/rubocop-hq/rubocop) to deal with Ruby's AST, in
particular:
* `RuboCop::AST::Node`
-* `RuboCop::AST::NodePattern` ([doc](manual/node_pattern.md))
+* `RuboCop::AST::NodePattern`
([doc](docs/modules/ROOT/pages/node_pattern.adoc))
-This gem may be used independently from the main RuboCop gem.
+This gem may be used independently from the main RuboCop gem. It was extracted
from RuboCop in version 0.84 and its only
+dependency is the `parser` gem, which `rubocop-ast` extends.
## Installation
@@ -25,7 +28,17 @@
## Usage
-Refer to the documentation of `RuboCop::AST::Node` and
[`RuboCop::AST::NodePattern`](manual/node_pattern.md)
+Refer to the documentation of `RuboCop::AST::Node` and
[`RuboCop::AST::NodePattern`](docs/modules/ROOT/pages/node_pattern.adoc)
+
+### Parser compatibility switches
+
+The main `RuboCop` gem uses [legacy AST output from
parser](https://github.com/whitequark/parser/#usage).
+This gem is meant to be compatible with all settings. For example, to have `->
{ ... }` emitted
+as `LambdaNode` instead of `SendNode`:
+
+```ruby
+RuboCop::AST::Builder.emit_lambda = true
+```
## Contributing
Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/builder.rb
new/lib/rubocop/ast/builder.rb
--- old/lib/rubocop/ast/builder.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/builder.rb 2020-06-26 06:40:59.000000000 +0200
@@ -35,9 +35,12 @@
hash: HashNode,
if: IfNode,
int: IntNode,
+ index: IndexNode,
+ indexasgn: IndexasgnNode,
irange: RangeNode,
erange: RangeNode,
kwsplat: KeywordSplatNode,
+ lambda: LambdaNode,
module: ModuleNode,
or: OrNode,
pair: PairNode,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/def_node.rb
new/lib/rubocop/ast/node/def_node.rb
--- old/lib/rubocop/ast/node/def_node.rb 2020-05-15 09:39:32.000000000
+0200
+++ new/lib/rubocop/ast/node/def_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -24,7 +24,7 @@
#
# @return [Boolean] whether the `def` node uses argument forwarding
def argument_forwarding?
- arguments.any?(&:forward_args_type?)
+ arguments.any?(&:forward_args_type?) ||
arguments.any?(&:forward_arg_type?)
end
# The name of the defined method as a symbol.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/forward_args_node.rb
new/lib/rubocop/ast/node/forward_args_node.rb
--- old/lib/rubocop/ast/node/forward_args_node.rb 2020-05-15
09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node/forward_args_node.rb 2020-06-26
06:40:59.000000000 +0200
@@ -5,6 +5,21 @@
# A node extension for `forward-args` nodes. This will be used in place
# of a plain node when the builder constructs the AST, making its methods
# available to all `forward-args` nodes within RuboCop.
+ #
+ # Not used with modern emitters:
+ #
+ # $ ruby-parse -e "def foo(...); end"
+ # (def :foo
+ # (args
+ # (forward-arg)) nil)
+ # $ ruby-parse --legacy -e "->(foo) { bar }"
+ # (def :foo
+ # (forward-args) nil)
+ #
+ # Note the extra 's' with legacy form.
+ #
+ # The main RuboCop runs in legacy mode; this node is only used
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_lambda=true`
class ForwardArgsNode < Node
include CollectionNode
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/index_node.rb
new/lib/rubocop/ast/node/index_node.rb
--- old/lib/rubocop/ast/node/index_node.rb 1970-01-01 01:00:00.000000000
+0100
+++ new/lib/rubocop/ast/node/index_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module AST
+ # Used for modern support only!
+ # Not as thoroughly tested as legacy equivalent
+ #
+ # $ ruby-parse -e "foo[:bar]"
+ # (index
+ # (send nil :foo)
+ # (sym :bar))
+ # $ ruby-parse --legacy -e "foo[:bar]"
+ # (send
+ # (send nil :foo) :[]
+ # (sym :bar))
+ #
+ # The main RuboCop runs in legacy mode; this node is only used
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
+ class IndexNode < Node
+ include ParameterizedNode
+ include MethodDispatchNode
+
+ # For similarity with legacy mode
+ def attribute_accessor?
+ false
+ end
+
+ # For similarity with legacy mode
+ def assignment_method?
+ false
+ end
+
+ # For similarity with legacy mode
+ def method_name
+ :[]
+ end
+
+ # An array containing the arguments of the dispatched method.
+ #
+ # @return [Array<Node>] the arguments of the dispatched method
+ def arguments
+ node_parts[1..-1]
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/indexasgn_node.rb
new/lib/rubocop/ast/node/indexasgn_node.rb
--- old/lib/rubocop/ast/node/indexasgn_node.rb 1970-01-01 01:00:00.000000000
+0100
+++ new/lib/rubocop/ast/node/indexasgn_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module AST
+ # Used for modern support only!
+ # Not as thoroughly tested as legacy equivalent
+ #
+ # $ ruby-parse -e "foo[:bar] = :baz"
+ # (indexasgn
+ # (send nil :foo)
+ # (sym :bar)
+ # (sym :baz))
+ # $ ruby-parse --legacy -e "foo[:bar] = :baz"
+ # (send
+ # (send nil :foo) :[]=
+ # (sym :bar)
+ # (sym :baz))
+ #
+ # The main RuboCop runs in legacy mode; this node is only used
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_index=true`
+ class IndexasgnNode < Node
+ include ParameterizedNode
+ include MethodDispatchNode
+
+ # For similarity with legacy mode
+ def attribute_accessor?
+ false
+ end
+
+ # For similarity with legacy mode
+ def assignment_method?
+ true
+ end
+
+ # For similarity with legacy mode
+ def method_name
+ :[]=
+ end
+
+ # An array containing the arguments of the dispatched method.
+ #
+ # @return [Array<Node>] the arguments of the dispatched method
+ def arguments
+ node_parts[1..-1]
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/lambda_node.rb
new/lib/rubocop/ast/node/lambda_node.rb
--- old/lib/rubocop/ast/node/lambda_node.rb 1970-01-01 01:00:00.000000000
+0100
+++ new/lib/rubocop/ast/node/lambda_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+module RuboCop
+ module AST
+ # Used for modern support only:
+ # Not as thoroughly tested as legacy equivalent
+ #
+ # $ ruby-parse -e "->(foo) { bar }"
+ # (block
+ # (lambda)
+ # (args
+ # (arg :foo))
+ # (send nil :bar))
+ # $ ruby-parse --legacy -e "->(foo) { bar }"
+ # (block
+ # (send nil :lambda)
+ # (args
+ # (arg :foo))
+ # (send nil :bar))
+ #
+ # The main RuboCop runs in legacy mode; this node is only used
+ # if user `AST::Builder.modernize` or `AST::Builder.emit_lambda=true`
+ class LambdaNode < Node
+ include ParameterizedNode
+ include MethodDispatchNode
+
+ # For similarity with legacy mode
+ def lambda?
+ true
+ end
+
+ # For similarity with legacy mode
+ def lambda_literal?
+ true
+ end
+
+ # For similarity with legacy mode
+ def attribute_accessor?
+ false
+ end
+
+ # For similarity with legacy mode
+ def assignment_method?
+ false
+ end
+
+ # For similarity with legacy mode
+ def method_name
+ :lambda
+ end
+
+ # For similarity with legacy mode
+ def arguments
+ []
+ end
+ end
+ end
+end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/mixin/method_dispatch_node.rb
new/lib/rubocop/ast/node/mixin/method_dispatch_node.rb
--- old/lib/rubocop/ast/node/mixin/method_dispatch_node.rb 2020-05-15
09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node/mixin/method_dispatch_node.rb 2020-06-26
06:40:59.000000000 +0200
@@ -3,7 +3,8 @@
module RuboCop
module AST
# Common functionality for nodes that are a kind of method dispatch:
- # `send`, `csend`, `super`, `zsuper`, `yield`, `defined?`
+ # `send`, `csend`, `super`, `zsuper`, `yield`, `defined?`,
+ # and (modern only): `index`, `indexasgn`, `lambda`
module MethodDispatchNode
extend NodePattern::Macros
include MethodIdentifierPredicates
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb
new/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb
--- old/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb
2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node/mixin/method_identifier_predicates.rb
2020-06-26 06:40:59.000000000 +0200
@@ -6,15 +6,62 @@
# `send`, `csend`, `def`, `defs`, `super`, `zsuper`
#
# @note this mixin expects `#method_name` and `#receiver` to be implemented
- module MethodIdentifierPredicates
+ module MethodIdentifierPredicates # rubocop:disable Metrics/ModuleLength
ENUMERATOR_METHODS = %i[collect collect_concat detect downto each
find find_all find_index inject loop map!
map reduce reject reject! reverse_each select
- select! times upto].freeze
+ select! times upto].to_set.freeze
+
+ ENUMERABLE_METHODS = (Enumerable.instance_methods +
[:each]).to_set.freeze
# http://phrogz.net/programmingruby/language.html#table_18.4
OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
- % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].freeze
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
+
+ NONMUTATING_BINARY_OPERATOR_METHODS = %i[* / % + - == === != < > <= >=
<=>].to_set.freeze
+ NONMUTATING_UNARY_OPERATOR_METHODS = %i[+@ -@ ~ !].to_set.freeze
+ NONMUTATING_OPERATOR_METHODS = (NONMUTATING_BINARY_OPERATOR_METHODS +
+ NONMUTATING_UNARY_OPERATOR_METHODS).freeze
+
+ NONMUTATING_ARRAY_METHODS = %i[
+ all? any? assoc at bsearch bsearch_index collect
+ combination compact count cycle deconstruct difference
+ dig drop drop_while each each_index empty? eql?
+ fetch filter find_index first flatten hash
+ include? index inspect intersection join
+ last length map max min minmax none? one? pack
+ permutation product rassoc reject
+ repeated_combination repeated_permutation reverse
+ reverse_each rindex rotate sample select shuffle
+ size slice sort sum take take_while
+ to_a to_ary to_h to_s transpose union uniq
+ values_at zip |
+ ].to_set.freeze
+
+ NONMUTATING_HASH_METHODS = %i[
+ any? assoc compact dig each each_key each_pair
+ each_value empty? eql? fetch fetch_values filter
+ flatten has_key? has_value? hash include? inspect
+ invert key key? keys? length member? merge rassoc
+ rehash reject select size slice to_a to_h to_hash
+ to_proc to_s transform_keys transform_values value?
+ values values_at
+ ].to_set.freeze
+
+ NONMUTATING_STRING_METHODS = %i[
+ ascii_only? b bytes bytesize byteslice capitalize
+ casecmp casecmp? center chars chomp chop chr codepoints
+ count crypt delete delete_prefix delete_suffix
+ downcase dump each_byte each_char each_codepoint
+ each_grapheme_cluster each_line empty? encode encoding
+ end_with? eql? getbyte grapheme_clusters gsub hash
+ hex include index inspect intern length lines ljust lstrip
+ match match? next oct ord partition reverse rindex rjust
+ rpartition rstrip scan scrub size slice squeeze start_with?
+ strip sub succ sum swapcase to_a to_c to_f to_i to_r to_s
+ to_str to_sym tr tr_s unicode_normalize unicode_normalized?
+ unpack unpack1 upcase upto valid_encoding?
+ ].to_set.freeze
# Checks whether the method name matches the argument.
#
@@ -31,6 +78,48 @@
OPERATOR_METHODS.include?(method_name)
end
+ # Checks whether the method is a nonmutating binary operator method.
+ #
+ # @return [Boolean] whether the method is a nonmutating binary operator
method
+ def nonmutating_binary_operator_method?
+ NONMUTATING_BINARY_OPERATOR_METHODS.include?(method_name)
+ end
+
+ # Checks whether the method is a nonmutating unary operator method.
+ #
+ # @return [Boolean] whether the method is a nonmutating unary operator
method
+ def nonmutating_unary_operator_method?
+ NONMUTATING_UNARY_OPERATOR_METHODS.include?(method_name)
+ end
+
+ # Checks whether the method is a nonmutating operator method.
+ #
+ # @return [Boolean] whether the method is a nonmutating operator method
+ def nonmutating_operator_method?
+ NONMUTATING_OPERATOR_METHODS.include?(method_name)
+ end
+
+ # Checks whether the method is a nonmutating Array method.
+ #
+ # @return [Boolean] whether the method is a nonmutating Array method
+ def nonmutating_array_method?
+ NONMUTATING_ARRAY_METHODS.include?(method_name)
+ end
+
+ # Checks whether the method is a nonmutating Hash method.
+ #
+ # @return [Boolean] whether the method is a nonmutating Hash method
+ def nonmutating_hash_method?
+ NONMUTATING_HASH_METHODS.include?(method_name)
+ end
+
+ # Checks whether the method is a nonmutating String method.
+ #
+ # @return [Boolean] whether the method is a nonmutating String method
+ def nonmutating_string_method?
+ NONMUTATING_STRING_METHODS.include?(method_name)
+ end
+
# Checks whether the method is a comparison method.
#
# @return [Boolean] whether the method is a comparison
@@ -53,6 +142,13 @@
method_name.to_s.start_with?('each_')
end
+ # Checks whether the method is an Enumerable method.
+ #
+ # @return [Boolean] whether the method is an Enumerable method
+ def enumerable_method?
+ ENUMERABLE_METHODS.include?(method_name)
+ end
+
# Checks whether the method is a predicate method.
#
# @return [Boolean] whether the method is a predicate method
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/mixin/parameterized_node.rb
new/lib/rubocop/ast/node/mixin/parameterized_node.rb
--- old/lib/rubocop/ast/node/mixin/parameterized_node.rb 2020-05-15
09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node/mixin/parameterized_node.rb 2020-06-26
06:40:59.000000000 +0200
@@ -4,6 +4,7 @@
module AST
# Common functionality for nodes that are parameterized:
# `send`, `super`, `zsuper`, `def`, `defs`
+ # and (modern only): `index`, `indexasgn`, `lambda`
module ParameterizedNode
# Checks whether this node's arguments are wrapped in parentheses.
#
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/regexp_node.rb
new/lib/rubocop/ast/node/regexp_node.rb
--- old/lib/rubocop/ast/node/regexp_node.rb 2020-05-15 09:39:32.000000000
+0200
+++ new/lib/rubocop/ast/node/regexp_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -31,6 +31,62 @@
def content
children.select(&:str_type?).map(&:str_content).join
end
+
+ # @return [Bool] if the regexp is a /.../ literal
+ def slash_literal?
+ loc.begin.source == '/'
+ end
+
+ # @return [Bool] if the regexp is a %r{...} literal (using any
delimiters)
+ def percent_r_literal?
+ !slash_literal?
+ end
+
+ # @return [String] the regexp delimiters (without %r)
+ def delimiters
+ [loc.begin.source[-1], loc.end.source[0]]
+ end
+
+ # @return [Bool] if char is one of the delimiters
+ def delimiter?(char)
+ delimiters.include?(char)
+ end
+
+ # @return [Bool] if regexp contains interpolation
+ def interpolation?
+ children.any?(&:begin_type?)
+ end
+
+ # @return [Bool] if regexp uses the multiline regopt
+ def multiline_mode?
+ regopt_include?(:m)
+ end
+
+ # @return [Bool] if regexp uses the extended regopt
+ def extended?
+ regopt_include?(:x)
+ end
+
+ # @return [Bool] if regexp uses the ignore-case regopt
+ def ignore_case?
+ regopt_include?(:i)
+ end
+
+ # @return [Bool] if regexp uses the single-interpolation regopt
+ def single_interpolation?
+ regopt_include?(:o)
+ end
+
+ # @return [Bool] if regexp uses the no-encoding regopt
+ def no_encoding?
+ regopt_include?(:n)
+ end
+
+ private
+
+ def regopt_include?(option)
+ regopt.children.include?(option)
+ end
end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node/send_node.rb
new/lib/rubocop/ast/node/send_node.rb
--- old/lib/rubocop/ast/node/send_node.rb 2020-05-15 09:39:32.000000000
+0200
+++ new/lib/rubocop/ast/node/send_node.rb 2020-06-26 06:40:59.000000000
+0200
@@ -10,7 +10,8 @@
include MethodDispatchNode
def_node_matcher :attribute_accessor?, <<~PATTERN
- (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
+ [(send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
+ (_ _ _ _ ...)]
PATTERN
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node.rb new/lib/rubocop/ast/node.rb
--- old/lib/rubocop/ast/node.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node.rb 2020-06-26 06:40:59.000000000 +0200
@@ -44,6 +44,8 @@
BASIC_CONDITIONALS = %i[if while until].freeze
CONDITIONALS = [*BASIC_CONDITIONALS, :case].freeze
+ POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze
+ LOOP_TYPES = (POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
VARIABLES = %i[ivar gvar cvar lvar].freeze
REFERENCES = %i[nth_ref back_ref].freeze
KEYWORDS = %i[alias and break case class def defs defined?
@@ -53,6 +55,7 @@
yield].freeze
OPERATOR_KEYWORDS = %i[and or].freeze
SPECIAL_KEYWORDS = %w[__FILE__ __LINE__ __ENCODING__].freeze
+ ARGUMENT_TYPES = %i[arg optarg restarg kwarg kwoptarg kwrestarg
blockarg].freeze
# @see https://www.rubydoc.info/gems/ast/AST/Node:initialize
def initialize(type, children = [], properties = {})
@@ -425,6 +428,15 @@
CONDITIONALS.include?(type)
end
+ def post_condition_loop?
+ POST_CONDITION_LOOP_TYPES.include?(type)
+ end
+
+ # Note: `loop { }` is a normal method call and thus not a loop keyword.
+ def loop_keyword?
+ LOOP_TYPES.include?(type)
+ end
+
def keyword?
return true if special_keyword? || send_type? && prefix_not?
return false unless KEYWORDS.include?(type)
@@ -456,6 +468,10 @@
parent&.send_type? && parent.arguments.include?(self)
end
+ def argument_type?
+ ARGUMENT_TYPES.include?(type)
+ end
+
def boolean_type?
true_type? || false_type?
end
@@ -500,7 +516,7 @@
# So, does the return value of this node matter? If we changed it to
# `(...; nil)`, might that affect anything?
#
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
+ # rubocop:disable Metrics/MethodLength
def value_used?
# Be conservative and return true if we're not sure.
return false if parent.nil?
@@ -522,7 +538,7 @@
true
end
end
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
+ # rubocop:enable Metrics/MethodLength
# Some expressions are evaluated for their value, some for their side
# effects, and some for both.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/node_pattern.rb
new/lib/rubocop/ast/node_pattern.rb
--- old/lib/rubocop/ast/node_pattern.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/node_pattern.rb 2020-06-26 06:40:59.000000000 +0200
@@ -70,13 +70,23 @@
# '(send %1 _)' # % stands for a parameter which must be
supplied to
# # #match at matching time
# # it will be compared to the corresponding value
in
- # # the AST using #==
+ # # the AST using #=== so you can pass Procs,
Regexp,
+ # # etc. in addition to Nodes or literals.
+ # # `Array#===` will never match a node element,
but
+ # # `Set#===` is an alias to `Set#include?` (Ruby
2.5+
+ # # only), and so can be very useful to match
within
+ # # many possible literals / Nodes.
# # a bare '%' is the same as '%1'
# # the number of extra parameters passed to #match
# # must equal the highest % value in the pattern
# # for consistency, %0 is the 'root node' which is
# # passed as the 1st argument to #match, where the
# # matching process starts
+ # '(send _ %named)' # arguments can also be passed as named
+ # # parameters (see `%1`)
+ # # Note that the macros `def_node_pattern` and
+ # # `def_node_search` accept default values for
these.
+ # '(send _ %CONST)' # the named constant will act like `%1` and
`%named`.
# '^^send' # each ^ ascends one level in the AST
# # so this matches against the grandparent node
# '`send' # descends any number of level in the AST
@@ -119,11 +129,13 @@
).freeze
NUMBER = /-?\d+(?:\.\d+)?/.freeze
STRING = /".+?"/.freeze
- METHOD_NAME = /\#?#{IDENTIFIER}[\!\?]?\(?/.freeze
+ METHOD_NAME = /\#?#{IDENTIFIER}[!?]?\(?/.freeze
+ PARAM_CONST = /%[A-Z:][a-zA-Z_:]+/.freeze
+ KEYWORD_NAME = /%[a-z_]+/.freeze
PARAM_NUMBER = /%\d*/.freeze
- SEPARATORS = /[\s]+/.freeze
- TOKENS = Regexp.union(META, PARAM_NUMBER, NUMBER,
+ SEPARATORS = /\s+/.freeze
+ TOKENS = Regexp.union(META, PARAM_CONST, KEYWORD_NAME,
PARAM_NUMBER, NUMBER,
METHOD_NAME, SYMBOL, STRING)
TOKEN = /\G(?:#{SEPARATORS}|#{TOKENS}|.)/.freeze
@@ -135,6 +147,8 @@
FUNCALL = /\A\##{METHOD_NAME}/.freeze
LITERAL = /\A(?:#{SYMBOL}|#{NUMBER}|#{STRING})\Z/.freeze
PARAM = /\A#{PARAM_NUMBER}\Z/.freeze
+ CONST = /\A#{PARAM_CONST}\Z/.freeze
+ KEYWORD = /\A#{KEYWORD_NAME}\Z/.freeze
CLOSING = /\A(?:\)|\}|\])\Z/.freeze
REST = '...'
@@ -193,6 +207,7 @@
@captures = 0 # number of captures seen
@unify = {} # named wildcard -> temp variable
@params = 0 # highest % (param) number seen
+ @keywords = Set[] # keyword parameters seen
run(node_var)
end
@@ -232,6 +247,8 @@
when LITERAL then compile_literal(token)
when PREDICATE then compile_predicate(token)
when NODE then compile_nodetype(token)
+ when KEYWORD then compile_keyword(token[1..-1])
+ when CONST then compile_const(token[1..-1])
when PARAM then compile_param(token[1..-1])
when CLOSING then fail_due_to("#{token} in invalid position")
when nil then fail_due_to('pattern ended prematurely')
@@ -612,7 +629,15 @@
end
def compile_param(number)
- "#{CUR_ELEMENT} == #{get_param(number)}"
+ "#{get_param(number)} === #{CUR_ELEMENT}"
+ end
+
+ def compile_const(const)
+ "#{get_const(const)} === #{CUR_ELEMENT}"
+ end
+
+ def compile_keyword(keyword)
+ "#{get_keyword(keyword)} === #{CUR_ELEMENT}"
end
def compile_args(tokens)
@@ -626,12 +651,14 @@
end
def compile_arg(token)
+ name = token[1..-1]
case token
- when WILDCARD then
- name = token[1..-1]
+ when WILDCARD
access_unify(name) || fail_due_to('invalid in arglist: ' + token)
when LITERAL then token
- when PARAM then get_param(token[1..-1])
+ when KEYWORD then get_keyword(name)
+ when CONST then get_const(name)
+ when PARAM then get_param(name)
when CLOSING then fail_due_to("#{token} in invalid position")
when nil then fail_due_to('pattern ended prematurely')
else fail_due_to("invalid token in arglist: #{token.inspect}")
@@ -650,6 +677,15 @@
number.zero? ? @root : "param#{number}"
end
+ def get_keyword(name)
+ @keywords << name
+ name
+ end
+
+ def get_const(const)
+ const # Output the constant exactly as given
+ end
+
def emit_yield_capture(when_no_capture = '')
yield_val = if @captures.zero?
when_no_capture
@@ -675,9 +711,15 @@
(1..@params).map { |n| "param#{n}" }.join(',')
end
- def emit_trailing_params
+ def emit_keyword_list(forwarding: false)
+ pattern = "%<keyword>s: #{'%<keyword>s' if forwarding}"
+ @keywords.map { |k| format(pattern, keyword: k) }.join(',')
+ end
+
+ def emit_trailing_params(forwarding: false)
params = emit_param_list
- params.empty? ? '' : ",#{params}"
+ keywords = emit_keyword_list(forwarding: forwarding)
+ [params, keywords].reject(&:empty?).map { |p| ", #{p}" }.join
end
def emit_method_code
@@ -753,68 +795,51 @@
def self.tokens(pattern)
pattern.scan(TOKEN).reject { |token| token =~ /\A#{SEPARATORS}\Z/ }
end
- end
- private_constant :Compiler
-
- # Helpers for defining methods based on a pattern string
- module Macros
- # Define a method which applies a pattern to an AST node
- #
- # The new method will return nil if the node does not match
- # If the node matches, and a block is provided, the new method will
- # yield to the block (passing any captures as block arguments).
- # If the node matches, and no block is provided, the new method will
- # return the captures, or `true` if there were none.
- def def_node_matcher(method_name, pattern_str)
- compiler = Compiler.new(pattern_str, 'node')
- src = "def #{method_name}(node = self" \
- "#{compiler.emit_trailing_params});" \
- "#{compiler.emit_method_code};end"
-
- location = caller_locations(1, 1).first
- class_eval(src, location.path, location.lineno)
- end
- # Define a method which recurses over the descendants of an AST node,
- # checking whether any of them match the provided pattern
- #
- # If the method name ends with '?', the new method will return `true`
- # as soon as it finds a descendant which matches. Otherwise, it will
- # yield all descendants which match.
- def def_node_search(method_name, pattern_str)
- compiler = Compiler.new(pattern_str, 'node')
- called_from = caller(1..1).first.split(':')
-
- if method_name.to_s.end_with?('?')
- node_search_first(method_name, compiler, called_from)
- else
- node_search_all(method_name, compiler, called_from)
+ def def_helper(base, method_name, **defaults)
+ location = caller_locations(3, 1).first
+ unless defaults.empty?
+ base.send :define_method, method_name do |*args, **values|
+ send method_name, *args, **defaults, **values
+ end
+ method_name = :"without_defaults_#{method_name}"
end
+ src = yield method_name
+ base.class_eval(src, location.path, location.lineno)
end
- def node_search_first(method_name, compiler, called_from)
- node_search(method_name, compiler, 'return true', '', called_from)
+ def def_node_matcher(base, method_name, **defaults)
+ def_helper(base, method_name, **defaults) do |name|
+ <<~RUBY
+ def #{name}(node = self#{emit_trailing_params})
+ #{emit_method_code}
+ end
+ RUBY
+ end
end
- def node_search_all(method_name, compiler, called_from)
- yield_code = compiler.emit_yield_capture('node')
- prelude = "return enum_for(:#{method_name}, node0" \
- "#{compiler.emit_trailing_params}) unless block_given?"
-
- node_search(method_name, compiler, yield_code, prelude, called_from)
+ def def_node_search(base, method_name, **defaults)
+ def_helper(base, method_name, **defaults) do |name|
+ emit_node_search(name)
+ end
end
- def node_search(method_name, compiler, on_match, prelude, called_from)
- src = node_search_body(method_name, compiler.emit_trailing_params,
- prelude, compiler.match_code, on_match)
- filename, lineno = *called_from
- class_eval(src, filename, lineno.to_i)
+ def emit_node_search(method_name)
+ if method_name.to_s.end_with?('?')
+ on_match = 'return true'
+ else
+ prelude = <<~RUBY
+ return enum_for(:#{method_name},
+ node0#{emit_trailing_params(forwarding: true)}) unless
block_given?
+ RUBY
+ on_match = emit_yield_capture('node')
+ end
+ emit_node_search_body(method_name, prelude: prelude, on_match:
on_match)
end
- def node_search_body(method_name, trailing_params, prelude, match_code,
- on_match)
+ def emit_node_search_body(method_name, prelude:, on_match:)
<<~RUBY
- def #{method_name}(node0#{trailing_params})
+ def #{method_name}(node0#{emit_trailing_params})
#{prelude}
node0.each_node do |node|
if #{match_code}
@@ -826,6 +851,33 @@
RUBY
end
end
+ private_constant :Compiler
+
+ # Helpers for defining methods based on a pattern string
+ module Macros
+ # Define a method which applies a pattern to an AST node
+ #
+ # The new method will return nil if the node does not match
+ # If the node matches, and a block is provided, the new method will
+ # yield to the block (passing any captures as block arguments).
+ # If the node matches, and no block is provided, the new method will
+ # return the captures, or `true` if there were none.
+ def def_node_matcher(method_name, pattern_str, **keyword_defaults)
+ Compiler.new(pattern_str, 'node')
+ .def_node_matcher(self, method_name, **keyword_defaults)
+ end
+
+ # Define a method which recurses over the descendants of an AST node,
+ # checking whether any of them match the provided pattern
+ #
+ # If the method name ends with '?', the new method will return `true`
+ # as soon as it finds a descendant which matches. Otherwise, it will
+ # yield all descendants which match.
+ def def_node_search(method_name, pattern_str, **keyword_defaults)
+ Compiler.new(pattern_str, 'node')
+ .def_node_search(self, method_name, **keyword_defaults)
+ end
+ end
attr_reader :pattern
@@ -837,11 +889,15 @@
instance_eval(src, __FILE__, __LINE__ + 1)
end
- def match(*args)
+ def match(*args, **rest)
# If we're here, it's because the singleton method has not been
defined,
# either because we've been dup'ed or serialized through YAML
initialize(pattern)
- match(*args)
+ if rest.empty?
+ match(*args)
+ else
+ match(*args, **rest)
+ end
end
def marshal_load(pattern)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/processed_source.rb
new/lib/rubocop/ast/processed_source.rb
--- old/lib/rubocop/ast/processed_source.rb 2020-05-15 09:39:32.000000000
+0200
+++ new/lib/rubocop/ast/processed_source.rb 2020-06-26 06:40:59.000000000
+0200
@@ -2,6 +2,7 @@
require 'digest/sha1'
+# rubocop:disable Metrics/ClassLength
module RuboCop
module AST
# ProcessedSource contains objects which are generated by Parser
@@ -176,6 +177,9 @@
when 2.7
require 'parser/ruby27'
Parser::Ruby27
+ when 2.8
+ require 'parser/ruby28'
+ Parser::Ruby28
else
raise ArgumentError,
"RuboCop found unknown Ruby version: #{ruby_version.inspect}"
@@ -201,3 +205,4 @@
end
end
end
+# rubocop:enable Metrics/ClassLength
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/traversal.rb
new/lib/rubocop/ast/traversal.rb
--- old/lib/rubocop/ast/traversal.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/traversal.rb 2020-06-26 06:40:59.000000000 +0200
@@ -19,9 +19,10 @@
rational str sym regopt self lvar
ivar cvar gvar nth_ref back_ref cbase
arg restarg blockarg shadowarg
- kwrestarg zsuper lambda redo retry
+ kwrestarg zsuper redo retry
forward_args forwarded_args
- match_var match_nil_pattern empty_else].freeze
+ match_var match_nil_pattern empty_else
+ forward_arg lambda procarg0 __ENCODING__].freeze
ONE_CHILD_NODE = %i[splat kwsplat block_pass not break next
preexe postexe match_current_line defined?
arg_expr pin match_rest if_guard unless_guard
@@ -33,7 +34,8 @@
match_with_lvasgn begin kwbegin return
in_match match_alt
match_as array_pattern array_pattern_with_tail
- hash_pattern const_pattern].freeze
+ hash_pattern const_pattern
+ index indexasgn].freeze
SECOND_CHILD_ONLY = %i[lvasgn ivasgn cvasgn gvasgn optarg kwarg
kwoptarg].freeze
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast/version.rb
new/lib/rubocop/ast/version.rb
--- old/lib/rubocop/ast/version.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast/version.rb 2020-06-26 06:40:59.000000000 +0200
@@ -3,7 +3,7 @@
module RuboCop
module AST
module Version
- STRING = '0.0.3'
+ STRING = '0.1.0'
end
end
end
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/lib/rubocop/ast.rb new/lib/rubocop/ast.rb
--- old/lib/rubocop/ast.rb 2020-05-15 09:39:32.000000000 +0200
+++ new/lib/rubocop/ast.rb 2020-06-26 06:40:59.000000000 +0200
@@ -2,6 +2,7 @@
require 'parser'
require 'forwardable'
+require 'set'
require_relative 'ast/node_pattern'
require_relative 'ast/sexp'
@@ -34,8 +35,11 @@
require_relative 'ast/node/float_node'
require_relative 'ast/node/hash_node'
require_relative 'ast/node/if_node'
+require_relative 'ast/node/index_node'
+require_relative 'ast/node/indexasgn_node'
require_relative 'ast/node/int_node'
require_relative 'ast/node/keyword_splat_node'
+require_relative 'ast/node/lambda_node'
require_relative 'ast/node/module_node'
require_relative 'ast/node/or_node'
require_relative 'ast/node/pair_node'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/metadata new/metadata
--- old/metadata 2020-05-15 09:39:32.000000000 +0200
+++ new/metadata 2020-06-26 06:40:59.000000000 +0200
@@ -1,7 +1,7 @@
--- !ruby/object:Gem::Specification
name: rubocop-ast
version: !ruby/object:Gem::Version
- version: 0.0.3
+ version: 0.1.0
platform: ruby
authors:
- Bozhidar Batsov
@@ -10,7 +10,7 @@
autorequire:
bindir: bin
cert_chain: []
-date: 2020-05-15 00:00:00.000000000 Z
+date: 2020-06-26 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: parser
@@ -77,8 +77,11 @@
- lib/rubocop/ast/node/forward_args_node.rb
- lib/rubocop/ast/node/hash_node.rb
- lib/rubocop/ast/node/if_node.rb
+- lib/rubocop/ast/node/index_node.rb
+- lib/rubocop/ast/node/indexasgn_node.rb
- lib/rubocop/ast/node/int_node.rb
- lib/rubocop/ast/node/keyword_splat_node.rb
+- lib/rubocop/ast/node/lambda_node.rb
- lib/rubocop/ast/node/mixin/basic_literal_node.rb
- lib/rubocop/ast/node/mixin/binary_operator_node.rb
- lib/rubocop/ast/node/mixin/collection_node.rb
@@ -120,7 +123,7 @@
homepage_uri: https://www.rubocop.org/
changelog_uri:
https://github.com/rubocop-hq/rubocop-ast/blob/master/CHANGELOG.md
source_code_uri: https://github.com/rubocop-hq/rubocop-ast/
- documentation_uri: https://docs.rubocop.org/
+ documentation_uri: https://docs.rubocop.org/rubocop-ast/
bug_tracker_uri: https://github.com/rubocop-hq/rubocop-ast/issues
post_install_message:
rdoc_options: []