From db0140212d8ad730116a6029b47dd1b0e27945d1 Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 4 Nov 2024 16:52:22 -0500 Subject: [PATCH 1/2] Define `GROUP_FOR_TYPE` for specifying the node types for each group predicate (`argument_type?`, etc.). --- lib/rubocop/ast/node.rb | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/rubocop/ast/node.rb b/lib/rubocop/ast/node.rb index 619b4bbf7..0081b4410 100644 --- a/lib/rubocop/ast/node.rb +++ b/lib/rubocop/ast/node.rb @@ -88,6 +88,34 @@ class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength EMPTY_PROPERTIES = {}.freeze private_constant :EMPTY_CHILDREN, :EMPTY_PROPERTIES + # @api private + GROUP_FOR_TYPE = { + arg: :argument, + optarg: :argument, + restarg: :argument, + kwarg: :argument, + kwoptarg: :argument, + kwrestarg: :argument, + blockarg: :argument, + forward_arg: :argument, + shardowarg: :argument, + + true: :boolean, + false: :boolean, + + int: :numeric, + float: :numeric, + rational: :numeric, + complex: :numeric, + + irange: :range, + erange: :range, + + send: :call, + csend: :call + }.freeze + private_constant :GROUP_FOR_TYPE + # Define a +recursive_?+ predicate method for the given node kind. private_class_method def self.def_recursive_literal_predicate(kind) # rubocop:disable Metrics/MethodLength recursive_kind = "recursive_#{kind}?" @@ -463,7 +491,7 @@ def parenthesized_call? end def call_type? - send_type? || csend_type? + GROUP_FOR_TYPE[type] == :call end def chained? @@ -475,19 +503,19 @@ def argument? end def argument_type? - ARGUMENT_TYPES.include?(type) + GROUP_FOR_TYPE[type] == :argument end def boolean_type? - true_type? || false_type? + GROUP_FOR_TYPE[type] == :boolean end def numeric_type? - int_type? || float_type? || rational_type? || complex_type? + GROUP_FOR_TYPE[type] == :numeric end def range_type? - irange_type? || erange_type? + GROUP_FOR_TYPE[type] == :range end def guard_clause? From d069ba16efc511362afba5c8ab1c51e4e3ce001d Mon Sep 17 00:00:00 2001 From: Daniel Vandersluis Date: Mon, 4 Nov 2024 13:16:54 -0500 Subject: [PATCH 2/2] Add `Node#type?` to reduce complexity of checking against multiple node types. --- ...ew_add_nodetype_to_reduce_complexity_of.md | 1 + lib/rubocop/ast/node.rb | 10 +++ spec/rubocop/ast/node_spec.rb | 61 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 changelog/new_add_nodetype_to_reduce_complexity_of.md diff --git a/changelog/new_add_nodetype_to_reduce_complexity_of.md b/changelog/new_add_nodetype_to_reduce_complexity_of.md new file mode 100644 index 000000000..9105963b2 --- /dev/null +++ b/changelog/new_add_nodetype_to_reduce_complexity_of.md @@ -0,0 +1 @@ +* [#329](https://github.com/rubocop/rubocop-ast/pull/329): Add `Node#type?` to reduce complexity of checking against multiple node types. ([@dvandersluis][]) diff --git a/lib/rubocop/ast/node.rb b/lib/rubocop/ast/node.rb index 0081b4410..98e175465 100644 --- a/lib/rubocop/ast/node.rb +++ b/lib/rubocop/ast/node.rb @@ -154,6 +154,16 @@ def initialize(type, children = EMPTY_CHILDREN, properties = EMPTY_PROPERTIES) end end + # 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`) + def type?(*types) + return true if types.include?(type) + + group_type = GROUP_FOR_TYPE[type] + !group_type.nil? && types.include?(group_type) + end + (Parser::Meta::NODE_TYPES - [:send]).each do |node_type| method_name = "#{node_type.to_s.gsub(/\W/, '')}_type?" class_eval <<~RUBY, __FILE__, __LINE__ + 1 diff --git a/spec/rubocop/ast/node_spec.rb b/spec/rubocop/ast/node_spec.rb index 9d6845138..96a795078 100644 --- a/spec/rubocop/ast/node_spec.rb +++ b/spec/rubocop/ast/node_spec.rb @@ -1048,4 +1048,65 @@ class << expr end end end + + describe '#type?' do + let(:src) do + <<~RUBY + foo.bar + RUBY + end + + context 'when it is one of the given types' do + it 'is true' do + expect(node).to be_type(:send, :const, :lvar) + end + end + + context 'when it is not one of the given types' do + it 'is false' do + expect(node).not_to be_type(:if, :while, :until) + end + end + + context 'when given :argument with an `arg` node' do + let(:src) { 'foo { |>> var <<| } ' } + + it 'is true' do + arg_node = ast.procarg0_type? ? ast.child_nodes.first : node + expect(arg_node).to be_type(:argument) + end + end + + context 'when given :boolean with an `true` node' do + let(:src) { 'true' } + + it 'is true' do + expect(node).to be_type(:boolean) + end + end + + context 'when given :numeric with an `int` node' do + let(:src) { '42' } + + it 'is true' do + expect(node).to be_type(:numeric) + end + end + + context 'when given :range with an `irange` node' do + let(:src) { '1..3' } + + it 'is true' do + expect(node).to be_type(:range) + end + end + + context 'when given :call with an `send` node' do + let(:src) { 'foo.bar' } + + it 'is true' do + expect(node).to be_type(:call) + end + end + end end