Code Monkey home page Code Monkey logo

rubocop-ast's Introduction

RuboCop Logo


Ruby Style Guide Gem Version CircleCI Status Actions Status Test Coverage Maintainability Discord

Role models are important.
-- Officer Alex J. Murphy / RoboCop

RuboCop is a Ruby static code analyzer (a.k.a. linter) and code formatter. Out of the box it will enforce many of the guidelines outlined in the community Ruby Style Guide. Apart from reporting the problems discovered in your code, RuboCop can also automatically fix many of them for you.

RuboCop is extremely flexible and most aspects of its behavior can be tweaked via various configuration options.


Patreon OpenCollective OpenCollective Tidelift

Working on RuboCop is often fun, but it also requires a great deal of time and energy.

Please consider financially supporting its ongoing development.

Installation

RuboCop's installation is pretty standard:

$ gem install rubocop

If you'd rather install RuboCop using bundler, add a line for it in your Gemfile (but set the require option to false, as it is a standalone tool):

gem 'rubocop', require: false

RuboCop is stable between minor versions, both in terms of API and cop configuration. We aim to ease the maintenance of RuboCop extensions and the upgrades between RuboCop releases. All big changes are reserved for major releases. To prevent an unwanted RuboCop update you might want to use a conservative version lock in your Gemfile:

gem 'rubocop', '~> 1.63', require: false

See our versioning policy for further details.

Quickstart

Just type rubocop in a Ruby project's folder and watch the magic happen.

$ cd my/cool/ruby/project
$ rubocop

You can also use this magic in your favorite editor with RuboCop's built-in LSP server.

Documentation

You can read a lot more about RuboCop in its official docs.

Compatibility

RuboCop officially supports the following runtime Ruby implementations:

  • MRI 2.7+
  • JRuby 9.4+

Targets Ruby 2.0+ code analysis.

See the compatibility documentation for further details.

Readme Badge

If you use RuboCop in your project, you can include one of these badges in your readme to let people know that your code is written following the community Ruby Style Guide.

Ruby Style Guide

Ruby Style Guide

Here are the Markdown snippets for the two badges:

[![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop/rubocop)

[![Ruby Style Guide](https://img.shields.io/badge/code_style-community-brightgreen.svg)](https://rubystyle.guide)

Team

Here's a list of RuboCop's core developers:

See the team page for more details.

Logo

RuboCop's logo was created by Dimiter Petrov. You can find the logo in various formats here.

The logo is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

Contributors

Here's a list of all the people who have contributed to the development of RuboCop.

I'm extremely grateful to each and every one of them!

If you'd like to contribute to RuboCop, please take the time to go through our short contribution guidelines.

Converting more of the Ruby Style Guide into RuboCop cops is our top priority right now. Writing a new cop is a great way to dive into RuboCop!

Of course, bug reports and suggestions for improvements are always welcome. GitHub pull requests are even better! :-)

Funding

While RuboCop is free software and will always be, the project would benefit immensely from some funding. Raising a monthly budget of a couple of thousand dollars would make it possible to pay people to work on certain complex features, fund other development related stuff (e.g. hardware, conference trips) and so on. Raising a monthly budget of over $5000 would open the possibility of someone working full-time on the project which would speed up the pace of development significantly.

We welcome both individual and corporate sponsors! We also offer a wide array of funding channels to account for your preferences (although currently Open Collective is our preferred funding platform).

If you're working in a company that's making significant use of RuboCop we'd appreciate it if you suggest to your company to become a RuboCop sponsor.

You can support the development of RuboCop via GitHub Sponsors, Patreon, PayPal, Open Collective and Tidelift .

Note: If doing a sponsorship in the form of donation is problematic for your company from an accounting standpoint, we'd recommend the use of Tidelift, where you can get a support-like subscription instead.

Open Collective Backers

Support us with a monthly donation and help us continue our activities. [Become a backer]

Open Collective Sponsors

Become a sponsor and get your logo on our README on GitHub with a link to your site. [Become a sponsor]

Changelog

RuboCop's changelog is available here.

Copyright

Copyright (c) 2012-2024 Bozhidar Batsov. See LICENSE.txt for further details.

rubocop-ast's People

Contributors

alexdowad avatar amatsuda avatar backus avatar bbatsov avatar bquorning avatar buehmann avatar deivid-rodriguez avatar dependabot[bot] avatar drenmi avatar dvandersluis avatar earlopain avatar fatkodima avatar garettarrowood avatar gsamokovarov avatar jonas054 avatar koic avatar lumeet avatar marcandre avatar owst avatar pirj avatar pocke avatar rrosenblum avatar sambostock avatar tatsuyafw avatar tdeo avatar tejasbubane avatar walf443 avatar wata727 avatar ydah avatar yujinakayama avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rubocop-ast's Issues

Experimental Prism feature listed as runtime dependency

Support for Prism was added in rubocop-ast 1.31.0 and marked as "experimental" in the changelog. However, Prism was also added as a runtime dependency in the Gemspec.

It seems contradictory to require a library for an experimental feature, particularly when it's only available in the latest Ruby release.

We're starting to see some failures in our CI pipelines because of this new requirement.

Would you be able to remove Prism as a runtime dependency? Thank you!

Parser and emit_forward_arg

I'd like to use the latest version of parser with emit_forward_arg turned on.

We could avoid this "hack" of redefining to_a as different from children

$ ruby-parse --legacy-forward-arg -e "def foo(...);end; def bar(*rest); end"
(begin
  (def :foo
    (forward-args) nil)
  (def :bar
    (args
      (restarg :rest)) nil))

$ ruby-parse -e "def foo(...);end; def bar(*rest); end"
(begin
  (def :foo
    (args
      (forward-arg)) nil)
  (def :bar
    (args
      (restarg :rest)) nil))

The later form is much improved as an argument signature is always inside an args node.

I doubt it would be much incompatibility, but v1 would be perfect for this.

Comments / objections?

What about parser's compatiblity switches

Parser has a few ways to emit things that have changed with time: https://github.com/whitequark/parser/#usage

I believe we don't turn any of these on.

I ask the question before v1: should we turn them on (and risk breaking some cops) or keep the compatibility mode like this (until at least v2).

Note that currently there's no way that I know of to use ruby-parse to output the same ast.

$ ruby-parse -e "{}[1] = 2"
(indexasgn
  (hash)
  (int 1)
  (int 2))

$ ruby-parse -e "->{}"
(block
  (lambda)
  (args) nil)

Within RuboCop, it is:

(send
  (hash)
  :[]=
  (int 1)
  (int 2)
)

(block
  (send nil :lambda)
  (args) nil)

I haven't spend much time thinking about it, but at first sight the current mode is typically simpler and doesn't make a distinction between very similar things (e.g. do |x, | vs do |x|, or ->{} vs lambda {}), which is typically what cop writers want. OTOH, if they need to distinguish these (e.g. writing a cop against |x, |), then NodePattern is not sufficient and they will have to check the loc or source themselves.

If we decide not to change, I'll make a PR so that there's a switch for ruby-parse to output the same as RuboCop's.

If we do decide to change, well, we should definitely do it before 1.0 as this might impact cops, as well as existing metrics (e.g. ->{} is not an actual :send so ABC metric is not quite correct)

@rubocop-hq/rubocop-core what say you?

Related to https://github.com/rubocop-hq/rubocop/issues/5999

Publish docs to our docs portal (docs.rubocop.org)

I'm wondering if we should publish some docs to RTD for the gem or keep things light. I'm guessing some documentation of the key concepts and the existing node extensions would be useful. Right now all we have is a section on NodePattern.

Automate the release process with a rake task

I'm suggesting this mostly because I've noticed in the previous release we forgot to set the docs version in antora.yml. In RuboCop itself the cut_release task does all such updates automatically.

Request: allow method calls on constants

Currently methods & predicates can be called on self:

(_ #predicate?(:arg, %1) ...)
(_ #method(:arg, %1) ...)

Request is to allow calling them on constants:

(_ #Foo.predicate?(:arg, %1) ...)
(_ #Foo::Bar.method(:arg, %1) ...)
(_ #::Global::X.method(:arg, %1) ...)

NodePattern for numblock "count" element

Consider:

ruby-parse --27 -e "something { _1 }"
(numblock
  (send nil :something) 1
  (lvar :_1))

The count of numbered parameters is returned as a bare integer, which makes it extra resistant to being captured in a node pattern. It's not an int node so int or int_type? don't work, and if I try #is_a?(Integer) I get ArgumentError: wrong number of arguments (given 2, expected 1) for some reason.

Any suggestions?

Idea: Node Pattern sample-based generator

Again, not a feature request, more like to start the discussion. Can't think of a better place for this discussion again.

As a prolific cop author, I personally find Node Pattern hard to write.

Please speak up if you use some tools, that visually indicate that a node pattern you're trying to craft matches a given set of source excerpts. I can think of this as:

(send {nil? (const :Greeter)} :hello str)

§ hello 'Dear'
§ Greeter.hello ''
§ hello 'world'
§ hello 1
§ hello

Think visual regexp matcher we got used to already.

I recall there are Regexp generators that build a minimal Regexp to match a given set of strings (and always returns .* haha).
Wondering if building a similar tool to automatically build node pattern given some code samples it should match, and some that it should not.

Unfortunately, I'm not aware of the principles of building such a tool. Do you have an idea of where to start?

on_index, on_indexasgn, on_lambda callbacks seem to be missing?


Expected behavior

Have callbacks available for whitequark/parser@7d72eba and _type?

Actual behavior

Above callbacks don't seem to be called for appropiate nodes

Steps to reproduce the problem

module RuboCop
  module Cop
    module Deprecation
       class MutatedStrings < Cop
           MSG = "Don't mutate strings!"
           
           def on_indexasgn(node)
              binding.pry
           end
        end
       end
    end
  end
end

And a quick test with:

  it 'register an offense when indexed assignment is being used on a string' do
    expect_offense(<<~RUBY)
      foo[1] = 'c'
    RUBY
 end

It appears that the callback is not being called, nor a node as indexasgn_type? available

RuboCop version

$ rubocop -V
0.57.2 (using Parser 2.5.1.0, running on ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]

I am fairly new to ruby and definitely to rubocop, so excuse the issue / vs potential PR, I would love to contribute with some tips!.

Fix `traversal`

  1. Handle all node types (even unknown ones)
  2. Optimize
  • avoid locating the symbols all the time
  • on_send can be optimized; others "custom" ones should be removed.

Missing files when attempting to do a gem build

When cloning the current head, running a 'gem build rubocop-ast.gemspec', causes the following error:
ERROR: While executing gem ... (Gem::InvalidSpecificationException)
["lib/rubocop/ast/node_pattern/lexer.rex.rb", "lib/rubocop/ast/node_pattern/parser.racc.rb"] are not files

(These are currently missing from the ast/node_pattern folder).

As a work around, I renamed the existing files that I think correspond, but either a) need to point to the updated files, or b) need to rename/create the correct files.

This is using Ruby 2.7 on a windows machine.

Handle numblock in each cops

Numbered Parameters feature has been introduced since 2.7.
The new node type, numblock, will be supported by rubocop/rubocop#7665, but we still need to modify cops that use on_block or block_type? methods.

For example, Style/BlockDelimiters cop does not work for numblock even if the pull request is merged.

# with TargetRubyVersion: 2.7, and the PR.

# The cop adds an offense to the following code.
items.each do |item| item / 5 end 

# The cop does not add an offense but it should.
items.each do _1 / 5 end

In this case, I guess we can add alias on_numblock on_block to the cop. And we can probably add the same code to the similar cops.

And I guess we also need to modify Node#block_type? method call.
I guess we can extend numblock node with BlockNode extension.


Expected behavior

Cops that work for block should work for numblock also.

Actual behavior

It does not work well even with rubocop/rubocop#7665

Steps to reproduce the problem

For Stlye/BlockDelimiters example, we can reproduce the problem with the following commands.

  • Install RuboCop with rubocop/rubocop#7665
  • Put .rubocop.yml with TargetRubyVersion: 2.7
  • Put the example as a Ruby file
  • Execute RuboCop

And we can find the potential target cops with git grep.

$ git grep on_block
lib/rubocop/ast/traversal.rb:      def on_block(node)
lib/rubocop/cop/layout/access_modifier_indentation.rb:        alias on_block  on_class
lib/rubocop/cop/layout/block_alignment.rb:        def on_block(node)
lib/rubocop/cop/layout/block_end_newline.rb:        def on_block(node)
lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb:        def on_block(node)
lib/rubocop/cop/layout/empty_lines_around_block_body.rb:        def on_block(node)
lib/rubocop/cop/layout/indentation_width.rb:        def on_block(node)
lib/rubocop/cop/layout/multiline_block_layout.rb:        def on_block(node)
lib/rubocop/cop/layout/space_around_block_parameters.rb:        def on_block(node)
lib/rubocop/cop/layout/space_around_keyword.rb:        def on_block(node)
lib/rubocop/cop/layout/space_before_block_braces.rb:        def on_block(node)
lib/rubocop/cop/layout/space_inside_block_braces.rb:        def on_block(node)
lib/rubocop/cop/lint/next_without_accumulator.rb:        def on_block(node)
lib/rubocop/cop/lint/non_deterministic_require_order.rb:        def on_block(node)
lib/rubocop/cop/lint/redundant_with_index.rb:        def on_block(node)
lib/rubocop/cop/lint/redundant_with_object.rb:        def on_block(node)
lib/rubocop/cop/lint/useless_access_modifier.rb:        def on_block(node)
lib/rubocop/cop/lint/void.rb:        def on_block(node)
lib/rubocop/cop/metrics/block_length.rb:        def on_block(node)
lib/rubocop/cop/metrics/method_length.rb:        def on_block(node)
lib/rubocop/cop/mixin/method_complexity.rb:      def on_block(node)
lib/rubocop/cop/naming/block_parameter_name.rb:        def on_block(node)
lib/rubocop/cop/naming/variable_name.rb:        alias on_blockarg  on_lvasgn
lib/rubocop/cop/style/block_delimiters.rb:        def on_block(node)
lib/rubocop/cop/style/collection_methods.rb:        def on_block(node)
lib/rubocop/cop/style/each_for_simple_loop.rb:        def on_block(node)
lib/rubocop/cop/style/each_with_object.rb:        def on_block(node)
lib/rubocop/cop/style/empty_block_parameter.rb:        def on_block(node)
lib/rubocop/cop/style/empty_lambda_parameter.rb:        def on_block(node)
lib/rubocop/cop/style/for.rb:        def on_block(node)
lib/rubocop/cop/style/inverse_methods.rb:        def on_block(node)
lib/rubocop/cop/style/lambda.rb:        def on_block(node)
lib/rubocop/cop/style/method_called_on_do_end_block.rb:        def on_block(node)
lib/rubocop/cop/style/multiline_block_chain.rb:        def on_block(node)
lib/rubocop/cop/style/multiline_block_chain.rb:            # found by subsequent calls to on_block.
lib/rubocop/cop/style/next.rb:        def on_block(node)
lib/rubocop/cop/style/proc.rb:        def on_block(node)
lib/rubocop/cop/style/redundant_begin.rb:        def on_block(node)
lib/rubocop/cop/style/redundant_self.rb:        def on_blockarg(node)
lib/rubocop/cop/style/redundant_self.rb:        def on_block(node)
lib/rubocop/cop/style/redundant_sort_by.rb:        def on_block(node)
lib/rubocop/cop/style/single_line_block_params.rb:        def on_block(node)
lib/rubocop/cop/style/symbol_proc.rb:        def on_block(node)

$ git grep -F block_type?
lib/rubocop/ast/node/mixin/method_dispatch_node.rb:        parent&.block_type? && eql?(parent.send_node)
lib/rubocop/cop/lint/ambiguous_block_association.rb:          send_node.last_argument.block_type? &&
lib/rubocop/cop/lint/assignment_in_condition.rb:          return if node.condition.block_type?
lib/rubocop/cop/lint/shadowed_argument.rb:          node.conditional? || node.block_type? ||
lib/rubocop/cop/metrics/block_nesting.rb:          count_blocks? && node.block_type?
lib/rubocop/cop/mixin/multiline_expression_indentation.rb:          break false if a.block_type?
lib/rubocop/cop/mixin/multiline_expression_indentation.rb:          ancestor.block_type? && part_of_block_body?(candidate, ancestor)
lib/rubocop/cop/style/auto_resource_cleanup.rb:            (parent && (parent.block_type? || !parent.lvasgn_type?))
lib/rubocop/cop/style/inverse_methods.rb:          if node.block_type?
lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb:            parent = node.parent&.block_type? ? node.parent.parent : node.parent
lib/rubocop/cop/style/method_called_on_do_end_block.rb:          return unless receiver&.block_type? &&
lib/rubocop/cop/style/multiline_block_chain.rb:            next unless receiver&.block_type? && receiver&.multiline?
lib/rubocop/cop/style/parallel_assignment.rb:          node.block_type? || node.send_type?
lib/rubocop/cop/style/redundant_parentheses.rb:          !ancestor.begin_type? && !ancestor.def_type? && !ancestor.block_type?
lib/rubocop/cop/style/safe_navigation.rb:          receiver = if method_chain.block_type?
lib/rubocop/cop/variable_force/variable.rb:          argument? && @scope.node.block_type?
lib/rubocop/cop/variable_force/variable_table.rb:            return nil unless scope.node.block_type?
lib/rubocop/cop/variable_force/variable_table.rb:            break variables unless scope.node.block_type?
lib/rubocop/cop/variable_force/variable_table.rb:          return unless current_scope.node.block_type?
spec/rubocop/ast/send_node_spec.rb:      it { expect(send_node.block_node.block_type?).to be(true) }
spec/rubocop/ast/super_node_spec.rb:      it { expect(super_node.block_node.block_type?).to be(true) }

RuboCop version

master (with rubocop/rubocop#7665 )

Missing patterns for Struct

We need a struct_constructor? matcher (see #8122)
A more general "class_definition?matcher that would matchclass Foo < Bar, Struct.new(...) doandClass.new(...) do` would be nice too

attribute_accessor? is too eager

It will capture calls without parameters:

RuboCop::ProcessedSource.new('do_something_with attr, asdf', RUBY_VERSION.to_f).ast.children[2].attribute_accessor? # => [:attr, []]

I suspect the pattern should be changed to something like:

(send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $({sym str} _)+)

Do we need an MasgnNode ?

Do we need a node to help with “multi-assignment”? I came across this code in the rubocop-thread_safety gem: https://github.com/rubocop/rubocop-thread_safety/blob/2662a8142f41a53bc3bc65eb2c670db18b3c581a/lib/rubocop/cop/thread_safety/mutable_class_instance_variable.rb#L99C13-L110

def on_masgn(node)
  return unless in_class?(node)

  mlhs, values = *node
  return unless values.array_type?

  mlhs.to_a.zip(values.to_a).each do |lhs, value|
    next unless lhs.ivasgn_type?

    on_assignment(value)
  end
end

RuboCop’s InternalAffairs/NodeDestructuring cop tells me to “Use the methods provided with the node extensions instead of manually destructuring nodes” on the mlhs, values = *node line. But the RuboCop::AST::Node class doesn’t really offer any specialised helper method for this type of node.

So I thought it may be useful to add a RuboCop::AST::MasgnNode class with access to the left-hand-side list (also a RuboCop::AST::Node instance) and the right-hand-side RuboCop::AST::ArrayNode of values? And perhaps the class could also have a method to iterate over each assignment pair, much like the mlhs.to_a.zip(values.to_a).each, though perhaps it should be lazy instead of eager.

NodePattern's sequential arguments

def_node_pattern :method_call?, 'some pattern'
# equivalent to:
def method_call?(node = self, arg1)
  # ...
end

### Should we change to
# general case:
def method_call?(node0, arg1) 
  node0.is_a?(Node) && # ...
end
# on `Node` class, or if `on_self: true`:
def method_call?(arg1)
  self.is_a?(Node) &&  # ...
end

This makes it easier to call with a positional argument Nevermind, I wasn't thinking straight. I realized this only after doing the work 🤷‍♂️

This makes it clear when a method is using self or when an argument is required. Currently it isn't and I believe that cop.some_matcher?() will return false instead of raising an error.

Potential incompatibilities would be on extensions of our Node via a module (this would require on_self: true), or if these matchers are used sometimes with given argument. I found two such uses in our code base.

Incomplete tuple matching

This is more of a question than a feature request.
In rubocop-rspec's cops there are two patterns have redundancy.

  1. RSpec/SubjectStub
{
  (block (send nil? :subject (sym $_)) ...)
  (block (send nil? $:subject) ...)
}

It is supposed to capture :subject or the subject's name from:

subject { ... }
# or
subject(:name) { ... }

What makes me think it's redundant?

  • an equal number of captures
  • same wrapping, e.g. base of the tree is the same

What makes it impossible to turn this into:

(block
  (send nil?
    {
      :subject (sym $_)
      $:subject
    }
  )
  ...
)

is that {} would consider all three expressions as matching options, and there is no way (that I'm aware of) to match two or more subsequent nodes that do not compose a complete sequence (terminology may be distorted, hope the idea is clear though).

  1. The same applies to another cop, RSpec/DescribeClass:
          (block
            [  (send #{RSPEC} :describe $[!const !#string_constant?] ...)
              !(send #{RSPEC} :describe ... (hash <#rails_metadata? ...>)) ]
            ...
          )

It can't be:

(block
  (send #{RSPEC} :describe
    [
      $[!const !#string_constant?] ...
      ... !(hash <#rails_metadata? ...>)
    ]
  )
  ...
)

because there's no way to combine those (newline-separated) elements into tuples.

I would really prefer to keep Node Pattern syntax simple. And, unlike the previously introduced unordered sequence (<>) there's much of a compelling benefit since I could only find two cops that would just slightly (and arguably) benefit from that.

On the other hand, <> is present already, but it disregards the order. Maybe it's possible somehow to respect the order given some specifier?
Not a real proposal, just thinking aloud:

(block (send nil? { <- :subject (sym $_)> $:subject } ) ... )

(Couldn't compile due to invalid token "<".)

(block
  (send #{RSPEC} :describe
     <- $[!const !#string_constant?] ...>
    !<- ... (hash <#rails_metadata? ...>)>
  ) ...)

where <- > is an "ordered sequence".

I didn't look much into RuboCop core's cops, can't estimate if any of them could benefit from such syntax.

Move NodePattern into AST namespace

Now that we have a separate gem for rubocop-ast, maybe it's time we move NodePattern out of the RuboCop namespace and into the gem namespace. WDYT? 🙂

Basic documentation of Node Pattern

👋 Hey all, thanks for this amazing tool.
I'm opening this issue after speaking privately with @bbatsov.

Could you please cover in documentation the basics of how to compose a pattern?

Existing examples like the following give hints on how patterns should be composed, but often the intuitions don't work, leading frustrating trial and error sessions.

(_type int _ str)

I assume that the first token in a pattern is the type, that should correspond to the node type (e.g. send), but what's next? Why often do I see nil? right after? What's $...? Probably a variadic capture.

Could you please help covering the basics of patterns? Thank you 🙏

1.0 release

Just a reminder for us to do it. Ideally it should happen alongside RuboCop 1.0, but that's not a hard requirement.

I don't think there's anything blocking us from doing 1.0 at any moment.

Long-shot parser -> lib-ruby-parser transition for performance boost?

Last Ruby Weekly mentioned https://github.com/lib-ruby-parser/lib-ruby-parser, a replacement for Ripper/parser.
But it has no Ruby bindings yet. Ruby bindings are WIP though.

lib-ruby-parser mentions that

AST is strongly typed, and so if something is nullable it's explicitly defined and documented

I am not certain if it's good or would get us any benefits.

It slipped my mind why rubocop itself still depends on parser? Is it for rewriting?

Idea: node pattern similarity detector tool

I imagine such a tool to be external since there's no central storage for node patterns. Most probably even external to rubocop-ast, but I couldn't think of a better place to discuss it.

That, given the two node patterns, e.g.:

(block (send (const {nil? cbase} :MyClass) :hello))
(block (send (const {nil? cbase} :MyClass) :goodbye))

would report:

Node patterns at file1.rb:32 and file2.rb:56 have {(const {nil? cbase} :MyClass) nil?} in common. Consider extracting to a helper method/node pattern.

That would make it possible to simplify those two:

(block (send #myclass :hello))
(block (send #myclass :goodbye))

with an extracted

def_node_matcher :myclass, '(const {nil? cbase} :MyClass)'

Picked this idea up from factor-lang's similarity detector.
Since the node pattern itself can be parsed into an AST, detecting trivial and complete matches should be trivial enough.

I can think of more complex similarity detections.

`ProcessedSource#ast_with_comments` doesn't differentiate between identical nodes

This spec passes but is strange behaviour:

RSpec.describe RuboCop::AST::ProcessedSource do
  describe '#ast_with_comments' do
    include RuboCop::AST::Sexp

    let(:source) do
      <<~RUBY
        # comment 1
        do_a
        # comment 2
        do_b
        # comment 3
        do_a
      RUBY
    end

    it do
      expect(processed_source.ast_with_comments).to include(
        s(:send, nil, :do_a) => [instance_of(Parser::Source::Comment), instance_of(Parser::Source::Comment)],
        s(:send, nil, :do_b) => [instance_of(Parser::Source::Comment)]
      )
    end
  end
end

Here is the actual hash created:

{
  s(:send, nil, :do_a) => [
    #<Parser::Source::Comment ast/and_node_spec.rb:1:1 "# comment 1">,
    #<Parser::Source::Comment ast/and_node_spec.rb:5:1 "# comment 3">
  ],
  s(:send, nil, :do_b) => [
    #<Parser::Source::Comment ast/and_node_spec.rb:3:1 "# comment 2">
  ]
}

Because there are two s(:send, nil, :do_a) nodes, both comments are associated with the same node in the hash, and then when this is used in rubocop (for instance CommentsHelp.begin_pos_with_comment), it returns potentially the wrong comment node.

I don't know that there is a good way to fix this necessarily without changing how the keys are constructed; this might also not be a RuboCop::AST bug directly either.

Set constants in NodePattern don't work in ruby 2.4

I was trying to DRY up a pattern for a PR in rubocop-performance and the NodePattern docs suggest that you can use %CONST with a Set. However, given that rubocop still supports ruby 2.4 I don't believe this is a good recommendation, because Set#=== does not exist in 2.4, and the pattern therefore fails to match there.

For example: https://app.circleci.com/pipelines/github/rubocop-hq/rubocop-performance/341/workflows/528ddd46-d4f6-4dc5-883c-fc02cc002540/jobs/2155

The pattern:

CANDIDATE_METHODS = Set[:select, :find_all, :filter]

def_node_matcher :detect_candidate?, <<~PATTERN
  {
    (send $(block (send _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
    (send $(block (send _ %CANDIDATE_METHODS) ...) $:[] (int ${0 -1}))
    (send $(send _ %CANDIDATE_METHODS ...) ${:first :last} $...)
    (send $(send _ %CANDIDATE_METHODS ...) $:[] (int ${0 -1}))
  }
PATTERN

I wonder if we can refine sets for 2.4 inside NodePattern to add #=== to it? Alternately, should Sets just not be used in rubocop patterns until 2.4 support is dropped? I was considering changing my constant to Set[...].method(:include?).to_proc but that just feels ugly to me.

Complex predicates with arguments

Background

According to the documentation, the following is possible:

def_node_matcher :global_const?, '(const {nil? cbase} %1)'
def_node_matcher :class_creator, '(send #global_const?({:Class :Module}) :new ...)'

where the argument to the predicate is {:Class :Module}.

Problem

I have a case with a number of similar node matchers, where I'd love to extract the common part:

def_node_matcher :expectation_with_return_block, <<~PATTERN
  (send
    (send nil? :expect ...) :to
    $(block #message_expectation? args _)
  )
PATTERN

def_node_matcher :expectation_with_configured_response, <<~PATTERN
  (send
    (send nil? :expect ...) :to
    $(send #message_expectation? #configured_response? _)
  )
PATTERN

The cop in question rubocop/rubocop-rspec#226

What I did

def self.expectation_with(matcher)
  "(send (send nil? :expect ...) :to #{matcher})"
end

def_node_matcher :expectation_with_return_block, expectation_with(<<~PATTERN)
    $(block #message_expectation? args _)
PATTERN

What I would love to do

def_node_matcher :expectation, <<~PATTERN
  (send (send nil? #{Expectations::ALL.node_pattern_union} ...) :to %1)
PATTERN

def_node_matcher :expectation_with_return_block, <<~PATTERN
  (#expectation(
    $(block #message_expectation? args _)
  ))
PATTERN

But it doesn't work this way. Doesn't seem that the capture is the culprit.

I might be doing it wrong, however. Or this might be too expensive to implement. In either case please feel free to close this - my current approach with expectation_with works quite fine already.

Type lists should be sets

E.g. TRUTHY_LITERALS

Although there's not much valid reason to use them directly, this could break some code relying on them, e.g. [...] - Set[...] TypeError (no implicit conversion of Set into Array), so v1 might be a good time...

Undefined method emit_forward_arg= in AST::Builder:Class

I'm getting the error message:

undefined method `emit_forward_arg=' for RuboCop::AST::Builder:Class (NoMethodError)

with the parser v2.7.0.2 gem. If I update to parser v3 the error goes away.

  • ruby 2.6.6
  • rubocop 1.7.0
  • rubocop-ast 1.3.0

Full trace:

Traceback (most recent call last):
	15: from /home/my_home/.rvm/gems/ruby-2.6.6/bin/ruby_executable_hooks:24:in `<main>'
	14: from /home/my_home/.rvm/gems/ruby-2.6.6/bin/ruby_executable_hooks:24:in `eval'
	13: from /home/my_home/.rvm/gems/ruby-2.6.6/bin/rubocop:23:in `<main>'
	12: from /home/my_home/.rvm/gems/ruby-2.6.6/bin/rubocop:23:in `load'
	11: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-1.7.0/exe/rubocop:6:in `<top (required)>'
	10: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-1.7.0/exe/rubocop:6:in `require'
	 9: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-1.7.0/lib/rubocop.rb:14:in `<top (required)>'
	 8: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-1.7.0/lib/rubocop.rb:14:in `require'
	 7: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop-ast.rb:3:in `<top (required)>'
	 6: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop-ast.rb:3:in `require_relative'
	 5: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast.rb:81:in `<top (required)>'
	 4: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast.rb:81:in `require_relative'
	 3: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast/builder.rb:3:in `<top (required)>'
	 2: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast/builder.rb:4:in `<module:RuboCop>'
	 1: from /home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast/builder.rb:16:in `<module:AST>'
/home/my_home/.rvm/gems/ruby-2.6.6/gems/rubocop-ast-1.3.0/lib/rubocop/ast/builder.rb:17:in `<class:Builder>': undefined method `emit_forward_arg=' for RuboCop::AST::Builder:Class (NoMethodError)

rubocop-ast-0.4.0 has broken Layout/LineLength

With the latest version of the gem I receive the following error when executing rubocop:
An error occurred while Layout/LineLength cop was inspecting /home/jonny/Programming/myapp/app/workers/bot.rb:330:8.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.