2022-09-13 05:13:39 -04:00
|
|
|
#!/usr/bin/env ruby
|
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
# Emit AST from parsed Ruby code by RuboCop.
|
|
|
|
#
|
|
|
|
# This is an alternative to `ruby-parser` shipped with `parser` gem.
|
|
|
|
#
|
|
|
|
# Usage:
|
|
|
|
# rubocop-parse -e 'puts "hello"'
|
|
|
|
# (send nil :puts
|
|
|
|
# (str "hello"))
|
|
|
|
#
|
|
|
|
# rubocop-parse -e 'puts "hello"' -v 3.0
|
|
|
|
# (send nil :puts
|
|
|
|
# (str "hello"))
|
|
|
|
#
|
|
|
|
# rubocop-parse app/models/project.rb
|
|
|
|
# (begin
|
|
|
|
# (send nil :require
|
|
|
|
# (str "carrierwave/orm/activerecord"))
|
|
|
|
# (class
|
|
|
|
# (const nil :Project)
|
|
|
|
# (const nil :ApplicationRecord)
|
|
|
|
# (begin
|
|
|
|
# (send nil :include
|
|
|
|
# ...
|
|
|
|
|
|
|
|
require_relative '../config/bundler_setup'
|
|
|
|
|
|
|
|
require 'rubocop'
|
|
|
|
require 'optparse'
|
|
|
|
|
2022-10-18 14:09:22 -04:00
|
|
|
module Helper
|
|
|
|
extend self
|
|
|
|
|
|
|
|
class << self
|
|
|
|
attr_writer :ruby_version
|
|
|
|
end
|
|
|
|
|
|
|
|
def ast(source, file: '', version: nil)
|
|
|
|
version ||= ruby_version
|
|
|
|
puts RuboCop::AST::ProcessedSource.new(source, version).ast.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def ruby_version
|
|
|
|
@ruby_version ||= rubocop_target_ruby_version
|
|
|
|
end
|
|
|
|
|
|
|
|
def rubocop_target_ruby_version
|
|
|
|
@rubocop_target_ruby_version ||= RuboCop::ConfigStore.new.for_file('.').target_ruby_version
|
|
|
|
end
|
2022-09-13 05:13:39 -04:00
|
|
|
end
|
|
|
|
|
2022-10-18 14:09:22 -04:00
|
|
|
def start_irb
|
|
|
|
require 'irb'
|
|
|
|
|
|
|
|
include Helper # rubocop:disable Style/MixinUsage
|
|
|
|
|
|
|
|
puts "Ruby version: #{ruby_version}"
|
|
|
|
puts
|
|
|
|
puts "Use `ast(source_string, version: nil)` method to parse code and output AST. For example:"
|
|
|
|
puts " ast('puts :hello')"
|
|
|
|
puts
|
|
|
|
|
|
|
|
IRB.start
|
|
|
|
end
|
|
|
|
|
|
|
|
options = Struct.new(:eval, :interactive, :print_help, keyword_init: true).new
|
2022-09-13 05:13:39 -04:00
|
|
|
|
|
|
|
parser = OptionParser.new do |opts|
|
2022-10-14 17:09:20 -04:00
|
|
|
opts.banner = "Usage: #{$PROGRAM_NAME} [-e code] [FILE...]"
|
2022-09-13 05:13:39 -04:00
|
|
|
|
|
|
|
opts.on('-e FRAGMENT', '--eval FRAGMENT', 'Process a fragment of Ruby code') do |code|
|
|
|
|
options.eval = code
|
|
|
|
end
|
|
|
|
|
2022-10-18 14:09:22 -04:00
|
|
|
opts.on('-i', '--interactive', '') do
|
|
|
|
options.interactive = true
|
|
|
|
end
|
|
|
|
|
2022-09-13 05:13:39 -04:00
|
|
|
opts.on('-v RUBY_VERSION', '--ruby-version RUBY_VERSION',
|
|
|
|
'Parse as Ruby would. Defaults to RuboCop TargetRubyVersion setting.') do |ruby_version|
|
2022-10-18 14:09:22 -04:00
|
|
|
Helper.ruby_version = Float(ruby_version)
|
2022-09-13 05:13:39 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
opts.on('-h', '--help') do
|
|
|
|
options.print_help = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-10-18 14:09:22 -04:00
|
|
|
files = parser.parse!
|
2022-09-13 05:13:39 -04:00
|
|
|
|
|
|
|
if options.print_help
|
|
|
|
puts parser
|
2022-10-18 14:09:22 -04:00
|
|
|
elsif options.interactive
|
|
|
|
if options.eval || files.any?
|
|
|
|
puts "Cannot combine `--interactive` with `--eval` or passing files. Aborting..."
|
|
|
|
puts
|
2022-09-13 05:13:39 -04:00
|
|
|
|
2022-10-18 14:09:22 -04:00
|
|
|
puts parser
|
|
|
|
exit 1
|
2022-09-13 05:13:39 -04:00
|
|
|
else
|
2022-10-18 14:09:22 -04:00
|
|
|
start_irb
|
2022-09-13 05:13:39 -04:00
|
|
|
end
|
2022-10-18 14:09:22 -04:00
|
|
|
elsif options.eval
|
|
|
|
Helper.ast(options.eval)
|
|
|
|
elsif files.any?
|
|
|
|
files.each do |file|
|
|
|
|
if File.file?(file)
|
|
|
|
source = File.read(file)
|
|
|
|
Helper.ast(source, file: file)
|
|
|
|
else
|
|
|
|
warn "Skipping non-file #{file.inspect}"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
puts parser
|
2022-09-13 05:13:39 -04:00
|
|
|
end
|