#!/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' 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 end 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 parser = OptionParser.new do |opts| opts.banner = "Usage: #{$PROGRAM_NAME} [-e code] [FILE...]" opts.on('-e FRAGMENT', '--eval FRAGMENT', 'Process a fragment of Ruby code') do |code| options.eval = code end opts.on('-i', '--interactive', '') do options.interactive = true end opts.on('-v RUBY_VERSION', '--ruby-version RUBY_VERSION', 'Parse as Ruby would. Defaults to RuboCop TargetRubyVersion setting.') do |ruby_version| Helper.ruby_version = Float(ruby_version) end opts.on('-h', '--help') do options.print_help = true end end files = parser.parse! if options.print_help puts parser elsif options.interactive if options.eval || files.any? puts "Cannot combine `--interactive` with `--eval` or passing files. Aborting..." puts puts parser exit 1 else start_irb end 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 end