From 27405bc1b27cca24490ea80312563a6e252e75ca Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Nov 2018 13:46:57 +0000 Subject: [PATCH 001/256] Fix build --- .circleci/config.yml | 17 ++ .rubocop.yml | 1 + Gemfile | 2 + Gemfile.lock | 134 +++++------ bin/unparser | 2 + circle.yml | 6 - config/reek.yml | 272 +++++++++++------------ config/rubocop.yml | 1 - lib/unparser/ast/local_variable_scope.rb | 1 + lib/unparser/buffer.rb | 2 + lib/unparser/cli.rb | 4 + lib/unparser/cli/source.rb | 4 + lib/unparser/comments.rb | 2 + lib/unparser/emitter.rb | 3 + lib/unparser/emitter/if.rb | 2 + lib/unparser/emitter/send.rb | 1 + lib/unparser/node_helpers.rb | 4 + spec/integrations.yml | 4 +- spec/unit/unparser_spec.rb | 10 +- unparser.gemspec | 6 +- 20 files changed, 260 insertions(+), 218 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..c74cc1c9 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,17 @@ +defaults: &defaults + working_directory: ~/unparser + steps: + - checkout + - run: bundle install + - run: bundle exec rake ci +version: 2 +jobs: + ruby_2_5: + <<: *defaults + docker: + - image: circleci/ruby:2.5.3 +workflows: + version: 2 + test: + jobs: + - ruby_2_5 diff --git a/.rubocop.yml b/.rubocop.yml index dc630307..edae6504 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,3 +6,4 @@ AllCops: - 'vendor/**/*' - 'benchmarks/**/*' - 'tmp/**/*' + TargetRubyVersion: 2.5 diff --git a/Gemfile b/Gemfile index fa75df15..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + source 'https://rubygems.org' gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 95fa84ee..cddac1b4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.2.6) + unparser (0.2.9) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) @@ -21,12 +21,12 @@ GEM abstract_type (~> 0.0.7) adamantium (~> 0.2) equalizer (~> 0.0.11) - ast (2.3.0) + ast (2.4.0) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) - codeclimate-engine-rb (0.4.0) + codeclimate-engine-rb (0.4.1) virtus (~> 1.0) coercible (1.0.0) descendants_tracker (~> 0.0.1) @@ -35,39 +35,42 @@ GEM equalizer (~> 0.0.9) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.16) + devtools (0.1.21) + abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) concord (~> 0.1.5) - flay (~> 2.8.1) - flog (~> 4.4.0) - mutant (~> 0.8.11) - mutant-rspec (~> 0.8.11) + flay (~> 2.10.0) + flog (~> 4.6.1) + mutant (~> 0.8.16) + mutant-rspec (~> 0.8.16) procto (~> 0.0.3) - rake (~> 11.3.0) - reek (~> 4.5.0) - rspec (~> 3.5.0) - rspec-core (~> 3.5.4) + rake (~> 12.3.0) + reek (~> 5.0.2) + rspec (~> 3.8.0) + rspec-core (~> 3.8.0) rspec-its (~> 1.2.0) - rubocop (~> 0.47.0) - simplecov (~> 0.12.0) - yard (~> 0.9.1) + rubocop (~> 0.59.0) + simplecov (~> 0.16.1) + yard (~> 0.9.16) yardstick (~> 0.9.9) diff-lcs (1.3) - docile (1.1.5) + docile (1.3.1) equalizer (0.0.11) erubis (2.7.0) - flay (2.8.1) + flay (2.10.0) erubis (~> 2.7.0) path_expander (~> 1.0) ruby_parser (~> 3.0) sexp_processor (~> 4.0) - flog (4.4.1) + flog (4.6.2) path_expander (~> 1.0) ruby_parser (~> 3.1, > 3.1.0) - sexp_processor (~> 4.4) + sexp_processor (~> 4.8) ice_nine (0.11.2) - json (2.0.3) + jaro_winkler (1.5.1) + json (2.1.0) + kwalify (0.7.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) morpher (0.2.6) @@ -79,77 +82,80 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) - mutant (0.8.12) + mutant (0.8.19) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) ast (~> 2.2) concord (~> 0.1.5) - diff-lcs (~> 1.2) + diff-lcs (~> 1.3) equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) morpher (~> 0.2.6) parallel (~> 1.3) - parser (~> 2.3.1, >= 2.3.1.4) + parser (~> 2.5.1) procto (~> 0.0.2) - regexp_parser (~> 0.3.6) + regexp_parser (~> 1.2) unparser (~> 0.2.5) - mutant-rspec (0.8.11) - mutant (~> 0.8.11) - rspec-core (>= 3.4.0, < 3.6.0) - parallel (1.10.0) - parser (2.3.3.1) - ast (~> 2.2) - path_expander (1.0.1) - powerpack (0.1.1) + mutant-rspec (0.8.19) + mutant (~> 0.8.19) + rspec-core (>= 3.4.0, < 4.0.0) + parallel (1.12.1) + parser (2.5.3.0) + ast (~> 2.4.0) + path_expander (1.0.3) + powerpack (0.1.2) procto (0.0.3) - rainbow (2.2.1) - rake (11.3.0) - reek (4.5.4) + rainbow (3.0.0) + rake (12.3.1) + reek (5.0.2) codeclimate-engine-rb (~> 0.4.0) - parser (~> 2.3.1, >= 2.3.1.2) - rainbow (~> 2.0) - regexp_parser (0.3.6) - rspec (3.5.0) - rspec-core (~> 3.5.0) - rspec-expectations (~> 3.5.0) - rspec-mocks (~> 3.5.0) - rspec-core (3.5.4) - rspec-support (~> 3.5.0) - rspec-expectations (3.5.0) + kwalify (~> 0.7.0) + parser (>= 2.5.0.0, < 2.6, != 2.5.1.1) + rainbow (>= 2.0, < 4.0) + regexp_parser (1.3.0) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) + rspec-support (~> 3.8.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.5.0) + rspec-mocks (3.8.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.5.0) - rspec-support (3.5.0) - rubocop (0.47.1) - parser (>= 2.3.3.1, < 3.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + rubocop (0.59.2) + jaro_winkler (~> 1.5.1) + parallel (~> 1.10) + parser (>= 2.5, != 2.5.1.1) powerpack (~> 0.1) - rainbow (>= 1.99.1, < 3.0) + rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-progressbar (1.8.1) - ruby_parser (3.8.4) - sexp_processor (~> 4.1) - sexp_processor (4.7.0) - simplecov (0.12.0) - docile (~> 1.1.0) + ruby-progressbar (1.10.0) + ruby_parser (3.11.0) + sexp_processor (~> 4.9) + sexp_processor (4.11.0) + simplecov (0.16.1) + docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.0) - thread_safe (0.3.5) - unicode-display_width (1.1.3) + simplecov-html (0.10.2) + thread_safe (0.3.6) + unicode-display_width (1.4.0) virtus (1.0.5) axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - yard (0.9.8) + yard (0.9.16) yardstick (0.9.9) yard (~> 0.8, >= 0.8.7.2) @@ -158,9 +164,9 @@ PLATFORMS DEPENDENCIES anima (~> 0.3.0) - devtools (~> 0.1.3) + devtools (~> 0.1.21) morpher (~> 0.2.6) unparser! BUNDLED WITH - 1.13.7 + 1.17.1 diff --git a/bin/unparser b/bin/unparser index 501b9126..1258efaf 100755 --- a/bin/unparser +++ b/bin/unparser @@ -1,4 +1,6 @@ #!/usr/bin/env ruby +# frozen_string_literal: true + trap('INT') do |status| exit! 128 + status end diff --git a/circle.yml b/circle.yml deleted file mode 100644 index d24b5628..00000000 --- a/circle.yml +++ /dev/null @@ -1,6 +0,0 @@ -machine: - ruby: - version: '2.2' -test: - override: - - bundle exec rake ci diff --git a/config/reek.yml b/config/reek.yml index 08b7ff85..56ac7664 100644 --- a/config/reek.yml +++ b/config/reek.yml @@ -1,137 +1,137 @@ --- -UnusedParameters: - exclude: [] -UncommunicativeParameterName: - accept: [] - exclude: [] - enabled: true - reject: - - !ruby/regexp /^.$/ - - !ruby/regexp /[0-9]$/ - - !ruby/regexp /[A-Z]/ -TooManyInstanceVariables: - exclude: - - Unparser::CLI - enabled: true - max_instance_variables: 3 -TooManyMethods: - exclude: - - Unparser::Emitter # TODO: 13 methods, mostly helpers for deduplicate sublcasses - - Unparser::Buffer # 11 methods - - Unparser::CLI::Source # 11 methods - enabled: true - max_methods: 10 -UncommunicativeMethodName: - accept: - - s - - n - exclude: [] - enabled: true - reject: - - !ruby/regexp /^[a-z]$/ - - !ruby/regexp /[0-9]$/ - - !ruby/regexp /[A-Z]/ -LongParameterList: - max_params: 3 - exclude: - - Unparser#self.transquote - enabled: true - overrides: {} -FeatureEnvy: - exclude: - # False positives - - Unparser::CLI::Differ#collapsed_hunks - - Unparser::Emitter::Send::Arguments#effective_arguments - # Helper methods, false positive - - Unparser::CLI::Source#strip - - Unparser::CLI::Source#report_unparser - - Unparser::CLI#sources - enabled: true -ClassVariable: - exclude: [] - enabled: true -BooleanParameter: - exclude: [] - enabled: true -IrresponsibleModule: - exclude: [] - enabled: false # buggy / broken -UncommunicativeModuleName: - accept: [] - exclude: [] - enabled: true - reject: - - !ruby/regexp /^.$/ - - !ruby/regexp /[0-9]$/ -NestedIterators: - ignore_iterators: [] - exclude: - # Acceptable cases: - - Unparser::Emitter::Literal::Regexp#dispatch - - Unparser::CLI::Preprocessor::Dstr#collapsed_children - - Unparser::Preprocessor::CollapseStrChildren#collapsed_children - enabled: true - max_allowed_nesting: 1 -TooManyStatements: - max_statements: 6 - enabled: true - exclude: - - Unparser::CLI#add_options - - Unparser::CLI#initialize - - Unparser::CLI::Source#report_unparser - - Unparser::Emitter#delimited -DuplicateMethodCall: - allow_calls: [] - exclude: [] - enabled: false - max_calls: 1 -UtilityFunction: - max_helper_calls: 1 - exclude: - # Intent to be helper methods - - Unparser::CLI::Source#strip - - Unparser::CLI::Source#parse - - Unparser::NodeHelpers#n - - Unparser::NodeHelpers#s - - Unparser::CLI#sources - enabled: true -Attribute: - exclude: [] - enabled: false -UncommunicativeVariableName: - accept: [] - exclude: [] - enabled: true - reject: - - !ruby/regexp /^.$/ - - !ruby/regexp /[0-9]$/ - - !ruby/regexp /[A-Z]/ -RepeatedConditional: - exclude: - # TODO: - - Unparser::Comments - # False positives - - Unparser::Emitter::If - - Unparser::CLI - enabled: true - max_ifs: 1 -DataClump: - exclude: [] - enabled: true - max_copies: 1 - min_clump_size: 3 -ControlParameter: - exclude: - # False positive: - - Unparser::Emitter#emit_body - - Unparser::Emitter#conditional_parentheses - - Unparser::AST::LocalVariableScope#match - enabled: true -NilCheck: - enabled: false -LongYieldList: - max_params: 1 - exclude: - - Unparser::AST::LocalVariableScopeEnumerator#visit - - Unparser::AST::LocalVariableScope#match - enabled: true +detectors: + Attribute: + enabled: false + exclude: [] + BooleanParameter: + enabled: true + exclude: [] + ClassVariable: + enabled: true + exclude: [] + ControlParameter: + enabled: true + exclude: + - Mutant::Expression#match_length + DataClump: + enabled: true + exclude: [] + max_copies: 2 + min_clump_size: 2 + DuplicateMethodCall: + enabled: false + exclude: [] + max_calls: 1 + allow_calls: [] + FeatureEnvy: + enabled: false + # Buggy smell detector + IrresponsibleModule: + enabled: false + exclude: [] + LongParameterList: + enabled: true + exclude: + - Mutant::Matcher::Method::Instance#self.build + - Mutant::Meta::Example::DSL # 3 vars + max_params: 2 + LongYieldList: + enabled: true + exclude: [] + max_params: 2 + NestedIterators: + enabled: true + exclude: + - Mutant#self.singleton_subclass_instance + - Mutant::CLI#parse + - Mutant::Mutator::Node::Arguments#emit_argument_mutations + - Mutant::Mutator::Node::Resbody#mutate_captures + - Mutant::Mutator::Util::Array::Element#dispatch + - Mutant::Parallel::Master#run + - Mutant::RequireHighjack#self.call + - Mutant::Selector::Expression#call + - Parser::Lexer#self.new + max_allowed_nesting: 1 + ignore_iterators: [] + NilCheck: + enabled: false + RepeatedConditional: + enabled: true + exclude: + - Mutant::Mutator + - Mutant::Meta::Example::DSL + max_ifs: 1 + TooManyInstanceVariables: + enabled: true + exclude: + - Mutant::Mutator # 4 vars + - Mutant::Parallel::Master # 4 vars + - Mutant::Meta::Example::DSL # 4 vars + max_instance_variables: 3 + TooManyMethods: + enabled: true + exclude: + - Mutant::CLI + - Mutant::Mutator::Node + - Mutant::Parallel::Master + max_methods: 10 + TooManyStatements: + enabled: true + exclude: + - Mutant::CLI#add_debug_options + - Mutant::CLI#add_environment_options + - Mutant::Reporter::CLI::Printer::Config#run + - Mutant::Reporter::CLI::Printer::EnvProgress#run + - Mutant::Runner#run_driver + - Mutant::Zombifier::File#self.find + max_statements: 7 + UncommunicativeMethodName: + enabled: true + exclude: + - Mutant::AST::Sexp#s + - Mutant::Mutation#sha1 + reject: + - '/^[a-z]$/' + - '/[0-9]$/' + - '/[A-Z]/' + accept: [] + UncommunicativeModuleName: + enabled: true + exclude: [] + reject: + - '/^.$/' + - '/[0-9]$/' + accept: [] + UncommunicativeParameterName: + enabled: true + exclude: [] + reject: + - '/^.$/' + - '/[0-9]$/' + - '/[A-Z]/' + accept: [] + UncommunicativeVariableName: + enabled: true + exclude: [] + reject: + - '/^.$/' + - '/[0-9]$/' + - '/[A-Z]/' + accept: ['force_utf32'] + UnusedParameters: + enabled: true + exclude: [] + UtilityFunction: + enabled: true + exclude: + - Mutant::AST::Sexp#s + - Mutant::Actor::Env#new_mailbox + - Mutant::CLI#reporter + - Mutant::Integration::Null#call + - Mutant::Integration::Rspec#parse_example + - Mutant::Integration::Rspec#parse_expression # intentional, private + - Mutant::Meta::Example::Verification#format_mutations # intentional, private + - Mutant::Reporter::CLI::Format::Progressive#new_buffer + - Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super + - Mutant::Repository::Diff#tracks? # intentional, private + - Mutant::Repository::Diff#within_working_directory? # intentional, private diff --git a/config/rubocop.yml b/config/rubocop.yml index 74fb2f86..638738eb 100644 --- a/config/rubocop.yml +++ b/config/rubocop.yml @@ -5,7 +5,6 @@ AllCops: - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' - TargetRubyVersion: 2.1 # Avoid parameter lists longer than five parameters. ParameterLists: diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index 57b55da5..7e93b347 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -154,6 +154,7 @@ def current # # @api private # + # ignore :reek:LongYieldList def visit(node, &block) before = current.dup enter(node) diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index 20f2ecfa..0a0db366 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -1,6 +1,8 @@ module Unparser # Buffer used to emit into + # + # ignore :reek:TooManyMethods class Buffer NL = "\n".freeze diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 43c18612..6a6055d8 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -11,6 +11,7 @@ module Unparser # Unparser CLI implementation # # :reek:InstanceVariableAssumption + # :reek:TooManyInstanceVariables class CLI EXIT_SUCCESS = 0 @@ -37,6 +38,7 @@ def self.run(*arguments) # # @api private # + # ignore :reek:TooManyStatements def initialize(arguments) @sources = [] @ignore = Set.new @@ -62,6 +64,7 @@ def initialize(arguments) # # @api private # + # ignore :reek:TooManyStatements def add_options(builder) builder.banner = 'usage: unparse [options] FILE [FILE]' builder.separator('') @@ -148,6 +151,7 @@ def effective_sources # # @api private # + # ignore :reek:UtilityFunction def sources(file_name) files = if File.directory?(file_name) diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb index c10ea635..83cfd3a1 100644 --- a/lib/unparser/cli/source.rb +++ b/lib/unparser/cli/source.rb @@ -1,6 +1,8 @@ module Unparser class CLI # Source representation for CLI sources + # + # ignore :reek:TooManyMethods class Source include AbstractType, Adamantium::Flat, NodeHelpers @@ -84,6 +86,7 @@ def generated # # @api private # + # ignore :reek:UtilityFunction def strip(source) source = source.rstrip indent = source.scan(/^\s*/).first @@ -204,6 +207,7 @@ def original_ast # # @api private # + # ignore :reek:UtilityFunction def parse(source) Parser::CurrentRuby.parse(source) end diff --git a/lib/unparser/comments.rb b/lib/unparser/comments.rb index af358f86..b253e8d4 100644 --- a/lib/unparser/comments.rb +++ b/lib/unparser/comments.rb @@ -1,6 +1,8 @@ module Unparser # Holds the comments that remain to be emitted + # + # ignore :reek:RepeatedConditional class Comments # Proxy to singleton diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 69dda540..06a82a3a 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -4,6 +4,8 @@ module Unparser # # buggy, argument values are sends to self # rubocop:disable CircularArgumentReference + # + # ignore :reek:TooManyMethods class Emitter include Adamantium::Flat, AbstractType, Constants, NodeHelpers include Concord.new(:node, :parent) @@ -236,6 +238,7 @@ def visit_parentheses(node, *arguments) # # @api private # + # ignore :reek:ControlParameter def conditional_parentheses(flag) if flag parentheses { yield } diff --git a/lib/unparser/emitter/if.rb b/lib/unparser/emitter/if.rb index a1cd4221..11c431f5 100644 --- a/lib/unparser/emitter/if.rb +++ b/lib/unparser/emitter/if.rb @@ -1,6 +1,8 @@ module Unparser class Emitter # Emitter if nodes + # + # ignore :reek:RepeatedConditional class If < self handle :if diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index e6d48d43..0656498c 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -1,6 +1,7 @@ module Unparser class Emitter # Emitter for send + # ignore :reek:TooManyMethods class Send < self handle :send diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index a896b6bb..6ed2499b 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -9,6 +9,8 @@ module NodeHelpers # # @api private # + # ignore :reek:UncommunicativeMethodName + # ignore :reek:UtilityFunction def s(type, *children) Parser::AST::Node.new(type, children) end @@ -21,6 +23,8 @@ def s(type, *children) # # @api private # + # ignore :reek:UncommunicativeMethodName + # ignore :reek:UtilityFunction def n(type, children = []) Parser::AST::Node.new(type, children) end diff --git a/spec/integrations.yml b/spec/integrations.yml index d1683640..025f288c 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -57,9 +57,9 @@ - core/string/shared/succ.rb - core/string/shared/to_sym.rb - core/string/squeeze_spec.rb - - core/string/unpack/{b,c,h,m}_spec.rb - core/string/unpack/shared/float.rb - core/string/unpack/shared/integer.rb + - core/string/unpack/{b,c,h,m}_spec.rb - core/string/unpack/{u,w}_spec.rb - core/symbol/casecmp_spec.rb - core/time/_dump_spec.rb @@ -69,6 +69,7 @@ - language/for_spec.rb - language/regexp/encoding_spec.rb - language/regexp/escapes_spec.rb + - language/source_encoding_spec.rb - language/string_spec.rb - library/digest/md5/shared/constants.rb - library/digest/md5/shared/sample.rb @@ -83,3 +84,4 @@ - library/stringscanner/shared/get_byte.rb - library/zlib/inflate/set_dictionary_spec.rb - optional/capi/integer_spec.rb + - security/cve_2010_1330_spec.rb diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 2b7b581e..7633e921 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -6,9 +6,7 @@ describe '.unparse' do RUBY_VERSION_PARSERS = IceNine.deep_freeze( - '2.1' => Parser::Ruby21, - '2.2' => Parser::Ruby22, - '2.3' => Parser::Ruby23 + '2.5' => Parser::Ruby25 ) RUBY_VERSIONS = RUBY_VERSION_PARSERS.keys.freeze @@ -468,10 +466,8 @@ def foo end context 'conditional send (csend)' do - with_ruby_versions(beginning_at: '2.3') do - assert_terminated 'a&.b' - assert_terminated 'a&.b(c)' - end + assert_terminated 'a&.b' + assert_terminated 'a&.b(c)' end context 'send' do diff --git a/unparser.gemspec b/unparser.gemspec index 8f621b9b..cea4e6ee 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.2.8' + gem.version = '0.2.9' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -16,7 +16,7 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 2.1' + gem.required_ruby_version = '>= 2.5' gem.add_dependency('abstract_type', '~> 0.0.7') gem.add_dependency('adamantium', '~> 0.2.0') @@ -27,6 +27,6 @@ Gem::Specification.new do |gem| gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.0') - gem.add_development_dependency('devtools', '~> 0.1.3') + gem.add_development_dependency('devtools', '~> 0.1.21') gem.add_development_dependency('morpher', '~> 0.2.6') end From 5835dbd8c36473683d5d074648f5e1bcfad566de Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Nov 2018 14:07:12 +0000 Subject: [PATCH 002/256] Fix rubocop violations and mutation coverage --- Rakefile | 1 + config/devtools.yml | 2 +- config/flay.yml | 2 +- config/rubocop.yml | 14 ++++++++++ lib/unparser.rb | 2 ++ lib/unparser/ast.rb | 14 ++++++---- lib/unparser/ast/local_variable_scope.rb | 3 +- lib/unparser/buffer.rb | 4 ++- lib/unparser/cli.rb | 3 ++ lib/unparser/cli/color.rb | 2 ++ lib/unparser/cli/differ.rb | 2 ++ lib/unparser/cli/source.rb | 4 ++- lib/unparser/comments.rb | 3 ++ lib/unparser/constants.rb | 14 ++++++---- lib/unparser/dsl.rb | 2 ++ lib/unparser/emitter.rb | 24 ++++++++++------ lib/unparser/emitter/alias.rb | 2 ++ lib/unparser/emitter/argument.rb | 3 ++ lib/unparser/emitter/assignment.rb | 7 +++-- lib/unparser/emitter/begin.rb | 4 ++- lib/unparser/emitter/binary.rb | 2 ++ lib/unparser/emitter/block.rb | 3 ++ lib/unparser/emitter/case.rb | 4 +++ lib/unparser/emitter/cbase.rb | 2 ++ lib/unparser/emitter/class.rb | 3 ++ lib/unparser/emitter/def.rb | 3 ++ lib/unparser/emitter/defined.rb | 2 ++ lib/unparser/emitter/empty.rb | 2 ++ lib/unparser/emitter/ensure.rb | 2 ++ lib/unparser/emitter/flipflop.rb | 2 ++ lib/unparser/emitter/flow_modifier.rb | 4 ++- lib/unparser/emitter/for.rb | 2 ++ lib/unparser/emitter/hookexe.rb | 2 ++ lib/unparser/emitter/if.rb | 3 ++ lib/unparser/emitter/literal.rb | 2 ++ lib/unparser/emitter/literal/array.rb | 9 +++--- lib/unparser/emitter/literal/dynamic.rb | 2 ++ lib/unparser/emitter/literal/dynamic_body.rb | 2 ++ .../emitter/literal/execute_string.rb | 2 ++ lib/unparser/emitter/literal/hash.rb | 5 ++-- lib/unparser/emitter/literal/primitive.rb | 4 ++- lib/unparser/emitter/literal/range.rb | 2 ++ lib/unparser/emitter/literal/regexp.rb | 2 ++ lib/unparser/emitter/literal/singleton.rb | 2 ++ lib/unparser/emitter/match.rb | 2 ++ lib/unparser/emitter/meta.rb | 2 ++ lib/unparser/emitter/module.rb | 2 ++ lib/unparser/emitter/op_assign.rb | 2 ++ lib/unparser/emitter/redo.rb | 2 ++ lib/unparser/emitter/repetition.rb | 5 +++- lib/unparser/emitter/resbody.rb | 28 +++---------------- lib/unparser/emitter/rescue.rb | 7 +++-- lib/unparser/emitter/retry.rb | 2 ++ lib/unparser/emitter/root.rb | 2 ++ lib/unparser/emitter/send.rb | 2 ++ lib/unparser/emitter/send/arguments.rb | 2 ++ .../emitter/send/attribute_assignment.rb | 2 ++ lib/unparser/emitter/send/binary.rb | 2 ++ lib/unparser/emitter/send/conditional.rb | 2 ++ lib/unparser/emitter/send/index.rb | 2 ++ lib/unparser/emitter/send/regular.rb | 3 ++ lib/unparser/emitter/send/unary.rb | 8 ++++-- lib/unparser/emitter/splat.rb | 2 ++ lib/unparser/emitter/super.rb | 2 ++ lib/unparser/emitter/undef.rb | 2 ++ lib/unparser/emitter/variable.rb | 3 ++ lib/unparser/emitter/yield.rb | 3 ++ lib/unparser/finalize.rb | 2 ++ lib/unparser/node_helpers.rb | 2 ++ lib/unparser/preprocessor.rb | 3 ++ 70 files changed, 209 insertions(+), 67 deletions(-) diff --git a/Rakefile b/Rakefile index 1c1978e8..4e689165 100644 --- a/Rakefile +++ b/Rakefile @@ -8,6 +8,7 @@ namespace :metrics do task mutant: :coverage do args = %w[ bundle exec mutant + --ignore-subject Unparser::Buffer#initialize --include lib --require unparser --use rspec diff --git a/config/devtools.yml b/config/devtools.yml index dbdc79af..f7e1f615 100644 --- a/config/devtools.yml +++ b/config/devtools.yml @@ -1,2 +1,2 @@ --- -unit_test_timeout: 0.1 +unit_test_timeout: 1.0 diff --git a/config/flay.yml b/config/flay.yml index 2a8c1ee9..3e2df4e4 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 659 +total_score: 632 diff --git a/config/rubocop.yml b/config/rubocop.yml index 638738eb..c73cd291 100644 --- a/config/rubocop.yml +++ b/config/rubocop.yml @@ -2,6 +2,8 @@ inherit_from: ../.rubocop.yml AllCops: Include: + - 'lib/unparser.rb' + - 'lib/unparser/**/*.rb' - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' @@ -102,3 +104,15 @@ EmptyLinesAroundModuleBody: # I like my style more AccessModifierIndentation: Enabled: false + +Style/CommentedKeyword: + Enabled: false + +Style/MixinGrouping: + Enabled: false + +Lint/BooleanSymbol: + Enabled: false + +Style/AccessModifierDeclarations: + Enabled: false diff --git a/lib/unparser.rb b/lib/unparser.rb index 45f35245..46ad68c9 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'set' require 'abstract_type' require 'procto' diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index ce3d8c11..1aa15652 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # Namespace for AST processing tools # :reek:TooManyConstants @@ -6,7 +8,7 @@ module AST FIRST_CHILD = ->(node) { node.children.first }.freeze TAUTOLOGY = ->(_node) { true }.freeze - RESET_NODES = [:module, :class, :sclass, :def, :defs].freeze + RESET_NODES = %i[module class sclass def defs].freeze INHERIT_NODES = [:block].freeze CLOSE_NODES = (RESET_NODES + INHERIT_NODES).freeze @@ -14,7 +16,7 @@ module AST # # FIXME: Kwargs are missing. # - ASSIGN_NODES = [:lvasgn, :arg, :optarg, :restarg].freeze + ASSIGN_NODES = %i[lvasgn arg optarg restarg].freeze # Test for local variable inherited scope reset # @@ -98,7 +100,6 @@ def self.new(node, controller = TAUTOLOGY) # @api private # def each(&block) - return to_enum unless block_given? Walker.call(node, controller, &block) end @@ -176,18 +177,19 @@ def self.call(node, controller = TAUTOLOGY, &block) # # @param [Parser::AST::Node] node # - # @return [self] + # @return [undefined] # # @api private # def call(node) return unless controller.call(node) + block.call(node) node.children.each do |child| - next unless child.is_a?(Parser::AST::Node) + break unless child.instance_of?(Parser::AST::Node) + call(child) end - self end end # Walker diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index 7e93b347..0e27acee 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser module AST @@ -130,7 +132,6 @@ def self.each(node, &block) # @api private # def each(node, &block) - return to_enum(__method__, node) unless block_given? visit(node, &block) end diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index 0a0db366..d60d80fd 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # Buffer used to emit into @@ -14,7 +16,7 @@ class Buffer # @api private # def initialize - @content = '' + @content = +'' @indent = 0 end diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index d0f20ed3..515811eb 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'unparser' require 'optparse' require 'diff/lcs' @@ -94,6 +96,7 @@ def add_options(builder) def exit_status effective_sources.each do |source| next if @ignore.include?(source) + process_source(source) break if @fail_fast && !@success end diff --git a/lib/unparser/cli/color.rb b/lib/unparser/cli/color.rb index d26e40a9..9a1ca63e 100644 --- a/lib/unparser/cli/color.rb +++ b/lib/unparser/cli/color.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # Class to colorize strings class Color diff --git a/lib/unparser/cli/differ.rb b/lib/unparser/cli/differ.rb index 61db9cdb..b553bf42 100644 --- a/lib/unparser/cli/differ.rb +++ b/lib/unparser/cli/differ.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class CLI # Class to create diffs from source code diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb index 83cfd3a1..36111797 100644 --- a/lib/unparser/cli/source.rb +++ b/lib/unparser/cli/source.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class CLI # Source representation for CLI sources @@ -29,7 +31,7 @@ def success? def self.build(ast) source = Unparser.unparse(ast) new(source, ast, nil) - rescue => exception + rescue StandardError => exception new(nil, ast, exception) end end diff --git a/lib/unparser/comments.rb b/lib/unparser/comments.rb index b253e8d4..1c976689 100644 --- a/lib/unparser/comments.rb +++ b/lib/unparser/comments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # Holds the comments that remain to be emitted @@ -54,6 +56,7 @@ def consume(node, source_part = :expression) # def take_eol_comments return EMPTY_ARRAY unless @last_range_consumed + comments = take_up_to_line(@last_range_consumed.end.line) unshift_documents(comments) end diff --git a/lib/unparser/constants.rb b/lib/unparser/constants.rb index 226e1fbf..96acca1f 100644 --- a/lib/unparser/constants.rb +++ b/lib/unparser/constants.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true + module Unparser # All unparser constants maybe included in other libraries. # - # rubocop:disable MutableConstant # False positive since constants are frozen dynamically # to avoid duplication of `.freeze` calls # @@ -22,16 +23,16 @@ def self.symbol_set(enumerable) private_class_method :symbol_set # All unary operators of the ruby language - UNARY_OPERATORS = symbol_set %w( + UNARY_OPERATORS = symbol_set %w[ ! ~ -@ +@ - ) + ] # All binary operators of the ruby language - BINARY_OPERATORS = symbol_set %w( + BINARY_OPERATORS = symbol_set %w[ + - * / & | && || << >> == === != <= < <=> > >= =~ !~ ^ ** % - ) + ] COMMENT = '#' @@ -104,11 +105,12 @@ def self.symbol_set(enumerable) DEFAULT_DELIMITER = ', '.freeze - CURLY_BRACKETS = IceNine.deep_freeze(%w({ })) + CURLY_BRACKETS = IceNine.deep_freeze(%w[{ }]) KEYWORDS = constants.each_with_object([]) do |name, keywords| value = const_get(name).freeze next unless name.to_s.start_with?('K_') + keywords << value.to_sym end.to_set.freeze diff --git a/lib/unparser/dsl.rb b/lib/unparser/dsl.rb index ca2e48f9..99e8f499 100644 --- a/lib/unparser/dsl.rb +++ b/lib/unparser/dsl.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # DSL to help defining emitters module DSL diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 06a82a3a..df8473ec 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -1,9 +1,10 @@ +# frozen_string_literal: true + module Unparser # Emitter base class # # buggy, argument values are sends to self - # rubocop:disable CircularArgumentReference # # ignore :reek:TooManyMethods class Emitter @@ -14,11 +15,11 @@ class Emitter # Registry for node emitters REGISTRY = {} # rubocop:disable MutableConstant - NOINDENT = [:rescue, :ensure].to_set.freeze + NOINDENT = %i[rescue ensure].to_set.freeze DEFAULT_DELIMITER = ', '.freeze - CURLY_BRACKETS = IceNine.deep_freeze(%w({ })) + CURLY_BRACKETS = IceNine.deep_freeze(%w[{ }]) module Unterminated def terminated? @@ -268,8 +269,8 @@ def emitter(node) # # @api private # - def delimited_plain(nodes, delimiter = DEFAULT_DELIMITER) - delimited(nodes, delimiter, &method(:visit_plain)) + def delimited_plain(nodes) + delimited(nodes, &method(:visit_plain)) end # Emit delimited body @@ -281,13 +282,14 @@ def delimited_plain(nodes, delimiter = DEFAULT_DELIMITER) # # @api private # - def delimited(nodes, delimiter = DEFAULT_DELIMITER, &block) + def delimited(nodes, &block) return if nodes.empty? + block ||= method(:visit) head, *tail = nodes block.call(head) tail.each do |node| - write(delimiter) + write(DEFAULT_DELIMITER) block.call(node) end end @@ -324,6 +326,7 @@ def nl def emit_comments_before(source_part = :expression) comments_before = comments.take_before(node, source_part) return if comments_before.empty? + emit_comments(comments_before) buffer.nl end @@ -350,6 +353,7 @@ def emit_eof_comments emit_eol_comments comments_left = comments.take_all return if comments_left.empty? + buffer.nl emit_comments(comments_left) end @@ -431,8 +435,6 @@ def ws # # False positive: # - # rubocop:disable MethodCallWithoutArgsParentheses - # def indented buffer = buffer() buffer.indent @@ -450,6 +452,7 @@ def indented # # @api private # + # rubocop:disable MethodCallWithoutArgsParentheses def emit_body(body = body()) unless body buffer.indent @@ -459,6 +462,7 @@ def emit_body(body = body()) end visit_indented(body) end + # rubocop:enable MethodCallWithoutArgsParentheses # Visit indented node # @@ -498,9 +502,11 @@ def parent_type # # @api private # + # rubocop:disable MethodCallWithoutArgsParentheses def run(emitter, node = node()) emitter.new(node, self).write_to_buffer end + # rubocop:enable MethodCallWithoutArgsParentheses end # Emitter end # Unparser diff --git a/lib/unparser/emitter/alias.rb b/lib/unparser/emitter/alias.rb index 2f102b40..f6229207 100644 --- a/lib/unparser/emitter/alias.rb +++ b/lib/unparser/emitter/alias.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for alias nodes diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index 473e346e..ab13dc44 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -40,6 +42,7 @@ class Arguments < self def dispatch delimited(normal_arguments) return if shadowargs.empty? + write('; ') delimited(shadowargs) end diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index edaf4a06..dbb23434 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -41,6 +43,7 @@ def terminated? def emit_right right = right_node return unless right + write(WS, T_ASN, WS) visit(right) end @@ -144,8 +147,8 @@ class MLHS < Emitter private - NO_COMMA = [:splat, :restarg].to_set.freeze - PARENT_MLHS = [:mlhs, :masgn].freeze + NO_COMMA = %i[splat restarg].to_set.freeze + PARENT_MLHS = %i[mlhs masgn].freeze # Perform dispatch # diff --git a/lib/unparser/emitter/begin.rb b/lib/unparser/emitter/begin.rb index 701935ff..07496681 100644 --- a/lib/unparser/emitter/begin.rb +++ b/lib/unparser/emitter/begin.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -36,7 +38,7 @@ def terminated? children.empty? end - TERMINATING_PARENT = %i(root interpolated dyn_str_body).to_set.freeze + TERMINATING_PARENT = %i[root interpolated dyn_str_body].to_set.freeze private diff --git a/lib/unparser/emitter/binary.rb b/lib/unparser/emitter/binary.rb index 218c59ce..26025dcd 100644 --- a/lib/unparser/emitter/binary.rb +++ b/lib/unparser/emitter/binary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Base class for binary emitters diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index 75f940e0..e53e7ac5 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -34,6 +36,7 @@ def dispatch # def emit_block_arguments return if arguments.children.empty? + ws visit_parentheses(arguments, T_PIPE, T_PIPE) end diff --git a/lib/unparser/emitter/case.rb b/lib/unparser/emitter/case.rb index 39264ba6..0bc09493 100644 --- a/lib/unparser/emitter/case.rb +++ b/lib/unparser/emitter/case.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for case nodes @@ -34,6 +36,7 @@ def dispatch def emit_else else_branch = children.last return unless else_branch + write(K_ELSE) visit_indented(else_branch) end @@ -57,6 +60,7 @@ def emit_whens # def emit_condition return unless condition + write(WS) visit(condition) end diff --git a/lib/unparser/emitter/cbase.rb b/lib/unparser/emitter/cbase.rb index d52c99e9..0ca9f22d 100644 --- a/lib/unparser/emitter/cbase.rb +++ b/lib/unparser/emitter/cbase.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for toplevel constant reference nodes diff --git a/lib/unparser/emitter/class.rb b/lib/unparser/emitter/class.rb index deaecbcf..d92f465a 100644 --- a/lib/unparser/emitter/class.rb +++ b/lib/unparser/emitter/class.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for class nodes @@ -32,6 +34,7 @@ def dispatch # def emit_superclass return unless superclass + write(WS, T_LT, WS) visit(superclass) end diff --git a/lib/unparser/emitter/def.rb b/lib/unparser/emitter/def.rb index f99b4b1b..a10b37c0 100644 --- a/lib/unparser/emitter/def.rb +++ b/lib/unparser/emitter/def.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for def node @@ -47,6 +49,7 @@ def dispatch # def emit_arguments return if arguments.children.empty? + visit_parentheses(arguments) end diff --git a/lib/unparser/emitter/defined.rb b/lib/unparser/emitter/defined.rb index b593c2ff..ffa61159 100644 --- a/lib/unparser/emitter/defined.rb +++ b/lib/unparser/emitter/defined.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for defined? nodes diff --git a/lib/unparser/emitter/empty.rb b/lib/unparser/emitter/empty.rb index bc1fd6cc..2837e973 100644 --- a/lib/unparser/emitter/empty.rb +++ b/lib/unparser/emitter/empty.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter diff --git a/lib/unparser/emitter/ensure.rb b/lib/unparser/emitter/ensure.rb index 276ff5f1..320937be 100644 --- a/lib/unparser/emitter/ensure.rb +++ b/lib/unparser/emitter/ensure.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter diff --git a/lib/unparser/emitter/flipflop.rb b/lib/unparser/emitter/flipflop.rb index 7736d902..07da6f26 100644 --- a/lib/unparser/emitter/flipflop.rb +++ b/lib/unparser/emitter/flipflop.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for flip flops diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index 104eba47..6fec1510 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter control flow modifiers @@ -50,7 +52,7 @@ def emit_arguments end end - PARENS = [:if, :case, :begin].to_set.freeze + PARENS = %i[if case begin].to_set.freeze # Emit argument # diff --git a/lib/unparser/emitter/for.rb b/lib/unparser/emitter/for.rb index f5d68e4e..1e61db2b 100644 --- a/lib/unparser/emitter/for.rb +++ b/lib/unparser/emitter/for.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for for nodes diff --git a/lib/unparser/emitter/hookexe.rb b/lib/unparser/emitter/hookexe.rb index d18074fe..bc3f72ef 100644 --- a/lib/unparser/emitter/hookexe.rb +++ b/lib/unparser/emitter/hookexe.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Base class for pre and postexe emitters diff --git a/lib/unparser/emitter/if.rb b/lib/unparser/emitter/if.rb index 11c431f5..11827c3a 100644 --- a/lib/unparser/emitter/if.rb +++ b/lib/unparser/emitter/if.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter if nodes @@ -124,6 +126,7 @@ def emit_if_branch # def emit_else_branch return unless else_branch + write(K_ELSE) unless unless? visit_indented(else_branch) end diff --git a/lib/unparser/emitter/literal.rb b/lib/unparser/emitter/literal.rb index 77c49e52..504a348d 100644 --- a/lib/unparser/emitter/literal.rb +++ b/lib/unparser/emitter/literal.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Namespace class for literal emiters diff --git a/lib/unparser/emitter/literal/array.rb b/lib/unparser/emitter/literal/array.rb index 0bc95066..5fdaf530 100644 --- a/lib/unparser/emitter/literal/array.rb +++ b/lib/unparser/emitter/literal/array.rb @@ -1,12 +1,13 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal # Array literal emitter class Array < self - OPEN = '['.freeze - CLOSE = ']'.freeze - DELIMITER = ', '.freeze + OPEN = '['.freeze + CLOSE = ']'.freeze handle :array @@ -20,7 +21,7 @@ class Array < self # def dispatch parentheses(OPEN, CLOSE) do - delimited(children, DELIMITER) + delimited(children) end end diff --git a/lib/unparser/emitter/literal/dynamic.rb b/lib/unparser/emitter/literal/dynamic.rb index 0bea236d..24d89c03 100644 --- a/lib/unparser/emitter/literal/dynamic.rb +++ b/lib/unparser/emitter/literal/dynamic.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/literal/dynamic_body.rb b/lib/unparser/emitter/literal/dynamic_body.rb index 01779155..8bdf559f 100644 --- a/lib/unparser/emitter/literal/dynamic_body.rb +++ b/lib/unparser/emitter/literal/dynamic_body.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/literal/execute_string.rb b/lib/unparser/emitter/literal/execute_string.rb index 47377472..64b41764 100644 --- a/lib/unparser/emitter/literal/execute_string.rb +++ b/lib/unparser/emitter/literal/execute_string.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/literal/hash.rb b/lib/unparser/emitter/literal/hash.rb index 8cdc0ebc..fb958ce4 100644 --- a/lib/unparser/emitter/literal/hash.rb +++ b/lib/unparser/emitter/literal/hash.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal @@ -70,7 +72,6 @@ def dispatch # Emitter for hash bodies class HashBody < self - DELIMITER = ', '.freeze BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze handle :hash_body @@ -84,7 +85,7 @@ class HashBody < self # @api private # def dispatch - delimited(effective_body, DELIMITER) + delimited(effective_body) end # Return effective body diff --git a/lib/unparser/emitter/literal/primitive.rb b/lib/unparser/emitter/literal/primitive.rb index 132acfe3..07aeae8f 100644 --- a/lib/unparser/emitter/literal/primitive.rb +++ b/lib/unparser/emitter/literal/primitive.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal @@ -131,7 +133,7 @@ class Numeric < self # @api private # def dispatch - conditional_parentheses(parent.is_a?(Emitter::Send) && value < 0) do + conditional_parentheses(parent.is_a?(Send) && value.negative?) do write(value.inspect) end end diff --git a/lib/unparser/emitter/literal/range.rb b/lib/unparser/emitter/literal/range.rb index 87764b9a..04d9e931 100644 --- a/lib/unparser/emitter/literal/range.rb +++ b/lib/unparser/emitter/literal/range.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/literal/regexp.rb b/lib/unparser/emitter/literal/regexp.rb index 58e538fc..13db5ea2 100644 --- a/lib/unparser/emitter/literal/regexp.rb +++ b/lib/unparser/emitter/literal/regexp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/literal/singleton.rb b/lib/unparser/emitter/literal/singleton.rb index 1b53858e..cf664d41 100644 --- a/lib/unparser/emitter/literal/singleton.rb +++ b/lib/unparser/emitter/literal/singleton.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Literal diff --git a/lib/unparser/emitter/match.rb b/lib/unparser/emitter/match.rb index ccad2348..1af64185 100644 --- a/lib/unparser/emitter/match.rb +++ b/lib/unparser/emitter/match.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter diff --git a/lib/unparser/emitter/meta.rb b/lib/unparser/emitter/meta.rb index 48a9fc54..7d0b0846 100644 --- a/lib/unparser/emitter/meta.rb +++ b/lib/unparser/emitter/meta.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Namespace class for meta emitters diff --git a/lib/unparser/emitter/module.rb b/lib/unparser/emitter/module.rb index e41ccc27..364e2480 100644 --- a/lib/unparser/emitter/module.rb +++ b/lib/unparser/emitter/module.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for module nodes diff --git a/lib/unparser/emitter/op_assign.rb b/lib/unparser/emitter/op_assign.rb index 8b02dc76..0a2d91ec 100644 --- a/lib/unparser/emitter/op_assign.rb +++ b/lib/unparser/emitter/op_assign.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter diff --git a/lib/unparser/emitter/redo.rb b/lib/unparser/emitter/redo.rb index ad8a8edc..93faa1ae 100644 --- a/lib/unparser/emitter/redo.rb +++ b/lib/unparser/emitter/redo.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for redo nodes diff --git a/lib/unparser/emitter/repetition.rb b/lib/unparser/emitter/repetition.rb index ac8c23c4..870028f9 100644 --- a/lib/unparser/emitter/repetition.rb +++ b/lib/unparser/emitter/repetition.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -65,7 +67,8 @@ def dispatch # @api private # def postcontrol? - return false unless body + return nil unless body # greez from falsyness + local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition) end diff --git a/lib/unparser/emitter/resbody.rb b/lib/unparser/emitter/resbody.rb index 6e5f7c0f..33494ebc 100644 --- a/lib/unparser/emitter/resbody.rb +++ b/lib/unparser/emitter/resbody.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for rescue body nodes @@ -20,30 +22,6 @@ def dispatch write(K_RESCUE, WS) visit_plain(body) end - - # Emit exception - # - # @return [undefined] - # - # @api private - # - def emit_exception - return unless exception - ws - delimited(exception.children) - end - - # Emit assignment - # - # @return [undefined] - # - # @api private - # - def emit_assignment - return unless assignment - write(WS, T_ASR, WS) - visit(assignment) - end end # Emitter for resbody in keyworkd-embedded form @@ -74,6 +52,7 @@ def dispatch # def emit_exception return unless exception + ws delimited(exception.children) end @@ -86,6 +65,7 @@ def emit_exception # def emit_assignment return unless assignment + write(WS, T_ASR, WS) visit(assignment) end diff --git a/lib/unparser/emitter/rescue.rb b/lib/unparser/emitter/rescue.rb index 9c1b48b0..13c0bd09 100644 --- a/lib/unparser/emitter/rescue.rb +++ b/lib/unparser/emitter/rescue.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for rescue nodes @@ -10,9 +12,9 @@ class Rescue < self define_group :rescue_bodies, 1..-2 - EMBEDDED_TYPES = [:def, :defs, :kwbegin, :ensure].to_set.freeze + EMBEDDED_TYPES = %i[def defs kwbegin ensure].to_set.freeze - NOINDENT_STANDALONE_RESCUE = [:root, :begin, :pair_rocket, :pair_colon, :lvasgn, :ivasgn].to_set.freeze + NOINDENT_STANDALONE_RESCUE = %i[root begin pair_rocket pair_colon lvasgn ivasgn].to_set.freeze private @@ -86,6 +88,7 @@ def emit_embedded # def emit_else return unless else_branch + write(K_ELSE) visit_indented(else_branch) end diff --git a/lib/unparser/emitter/retry.rb b/lib/unparser/emitter/retry.rb index 3f05112f..c8ab4bfd 100644 --- a/lib/unparser/emitter/retry.rb +++ b/lib/unparser/emitter/retry.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for retry nodes diff --git a/lib/unparser/emitter/root.rb b/lib/unparser/emitter/root.rb index 44fa7ed2..5e0bb019 100644 --- a/lib/unparser/emitter/root.rb +++ b/lib/unparser/emitter/root.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Root emitter a special case diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 0656498c..98a7e52f 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for send diff --git a/lib/unparser/emitter/send/arguments.rb b/lib/unparser/emitter/send/arguments.rb index 207d75de..d4fd5ae7 100644 --- a/lib/unparser/emitter/send/arguments.rb +++ b/lib/unparser/emitter/send/arguments.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send diff --git a/lib/unparser/emitter/send/attribute_assignment.rb b/lib/unparser/emitter/send/attribute_assignment.rb index 8687b4aa..54b7c2d9 100644 --- a/lib/unparser/emitter/send/attribute_assignment.rb +++ b/lib/unparser/emitter/send/attribute_assignment.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send diff --git a/lib/unparser/emitter/send/binary.rb b/lib/unparser/emitter/send/binary.rb index 656cbc1c..8de416fa 100644 --- a/lib/unparser/emitter/send/binary.rb +++ b/lib/unparser/emitter/send/binary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send diff --git a/lib/unparser/emitter/send/conditional.rb b/lib/unparser/emitter/send/conditional.rb index e75b05e5..ac29782f 100644 --- a/lib/unparser/emitter/send/conditional.rb +++ b/lib/unparser/emitter/send/conditional.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send diff --git a/lib/unparser/emitter/send/index.rb b/lib/unparser/emitter/send/index.rb index 1cae6a85..a317a5ee 100644 --- a/lib/unparser/emitter/send/index.rb +++ b/lib/unparser/emitter/send/index.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send diff --git a/lib/unparser/emitter/send/regular.rb b/lib/unparser/emitter/send/regular.rb index d3e1ffc2..4ed6145d 100644 --- a/lib/unparser/emitter/send/regular.rb +++ b/lib/unparser/emitter/send/regular.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send @@ -27,6 +29,7 @@ def dispatch # def emit_receiver return unless first_child + visit(receiver) write(T_DOT) end diff --git a/lib/unparser/emitter/send/unary.rb b/lib/unparser/emitter/send/unary.rb index d8819be5..906b9e3b 100644 --- a/lib/unparser/emitter/send/unary.rb +++ b/lib/unparser/emitter/send/unary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter class Send @@ -8,8 +10,8 @@ class Unary < self private MAP = IceNine.deep_freeze( - :'-@' => '-', - :'+@' => '+' + '-@': '-', + '+@': '+' ) # Perform dispatch @@ -21,7 +23,7 @@ class Unary < self def dispatch name = selector write(MAP.fetch(name, name).to_s) - if receiver.type.equal?(:int) && selector.equal?(:'+@') && receiver.children.first > 0 + if receiver.type.equal?(:int) && selector.equal?(:'+@') write('+') end diff --git a/lib/unparser/emitter/splat.rb b/lib/unparser/emitter/splat.rb index 675097bb..c0944bca 100644 --- a/lib/unparser/emitter/splat.rb +++ b/lib/unparser/emitter/splat.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for splats diff --git a/lib/unparser/emitter/super.rb b/lib/unparser/emitter/super.rb index 83abe488..669d3761 100644 --- a/lib/unparser/emitter/super.rb +++ b/lib/unparser/emitter/super.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter diff --git a/lib/unparser/emitter/undef.rb b/lib/unparser/emitter/undef.rb index 33597855..fef3c383 100644 --- a/lib/unparser/emitter/undef.rb +++ b/lib/unparser/emitter/undef.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter # Emitter for undef nodes diff --git a/lib/unparser/emitter/variable.rb b/lib/unparser/emitter/variable.rb index f02ee9d3..905e7673 100644 --- a/lib/unparser/emitter/variable.rb +++ b/lib/unparser/emitter/variable.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -52,6 +54,7 @@ def dispatch # def emit_scope return unless scope + visit(scope) write(T_DCL) unless scope.type.equal?(:cbase) end diff --git a/lib/unparser/emitter/yield.rb b/lib/unparser/emitter/yield.rb index f9f41c9c..a457b122 100644 --- a/lib/unparser/emitter/yield.rb +++ b/lib/unparser/emitter/yield.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser class Emitter @@ -18,6 +20,7 @@ class Yield < self def dispatch write(K_YIELD) return if children.empty? + parentheses do delimited(children) end diff --git a/lib/unparser/finalize.rb b/lib/unparser/finalize.rb index d279bc51..11f8c990 100644 --- a/lib/unparser/finalize.rb +++ b/lib/unparser/finalize.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + Unparser::Emitter::REGISTRY.freeze diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 6ed2499b..26aeb5e0 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser module NodeHelpers diff --git a/lib/unparser/preprocessor.rb b/lib/unparser/preprocessor.rb index 4b39e115..c3b57f19 100644 --- a/lib/unparser/preprocessor.rb +++ b/lib/unparser/preprocessor.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Unparser # Preprocessor to normalize AST generated by parser class Preprocessor @@ -23,6 +25,7 @@ class Preprocessor # def self.run(node, parent_type = nil) return EMPTY if node.nil? + REGISTRY.fetch(node.type, [Noop]).reduce(node) do |current, processor| processor.call(current, parent_type) end From f3a7aeb23725fd588312901871e4a574cbda7f62 Mon Sep 17 00:00:00 2001 From: Guillaume Malette Date: Thu, 15 Nov 2018 10:30:54 -0500 Subject: [PATCH 003/256] Correct documentation; add_option takes a OptionParser --- lib/unparser/cli.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 6a6055d8..d0f20ed3 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -58,7 +58,7 @@ def initialize(arguments) # Add options # - # @param [Optparse::Builder] builder + # @param [OptionParser] builder # # @return [undefined] # From a4e948b48a538dd1395fe2cce28ecde8704a0faf Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Nov 2018 16:05:30 +0000 Subject: [PATCH 004/256] Bump version to 0.3.0 --- Changelog.md | 4 ++++ unparser.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 73f7e72a..d899ecd5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.3.0 2018-11-16 + +* Drop support for Ruby < 2.5 + # v0.2.7 2018-07-18 * Add emitters for `__FILE__` and `__LINE__` diff --git a/unparser.gemspec b/unparser.gemspec index cea4e6ee..be058b17 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.2.9' + gem.version = '0.3.0' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 726982abf8f83e078c2a40640644018378926ece Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 22 Nov 2018 17:10:10 +0000 Subject: [PATCH 005/256] Fix unparsing constant a like selectors --- config/flay.yml | 2 +- lib/unparser/emitter/send.rb | 21 ++++++++++++++++++++- spec/unit/unparser_spec.rb | 4 ++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/config/flay.yml b/config/flay.yml index 0e19ad1d..3a0feb10 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 650 +total_score: 646 diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 50298970..76518ecb 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -160,7 +160,7 @@ def arguments # def emit_arguments if arguments.empty? - write('()') if local_variable_clash? + write('()') if receiver.nil? && avoid_clash? else normal_arguments end @@ -195,6 +195,16 @@ def effective_arguments end end + # Test if clash with local variable or constant needs to be avoided + # + # @return [Boolean] + # + # @api private + # + def avoid_clash? + local_variable_clash? || parses_as_constant? + end + # Test for local variable clash # # @return [Boolean] @@ -214,6 +224,15 @@ def non_assignment_selector string_selector[NON_ASSIGN_RANGE] end + # Test if selector parses as constant + # + # @return [Boolean] + # + # @api private + # + def parses_as_constant? + Unparser.parse(selector.to_s).type.equal?(:const) + end end # Send end # Emitter end # Unparser diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 0ec607ed..5fb27993 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -597,6 +597,10 @@ def foo end.baz RUBY + assert_source <<~'RUBY' + FOO() + RUBY + assert_terminated '(1..2).max' assert_terminated '1..2.max' assert_unterminated 'a || return' From 72c502d91e57a405681b0201401d8b48e8f03be9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 22 Nov 2018 17:12:46 +0000 Subject: [PATCH 006/256] Update CircleCI for more fine grained reporting --- .circleci/config.yml | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c74cc1c9..9f21ac00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,17 +1,41 @@ defaults: &defaults working_directory: ~/unparser - steps: - - checkout - - run: bundle install - - run: bundle exec rake ci + docker: + - image: circleci/ruby:2.5.3 version: 2 jobs: - ruby_2_5: + unit_specs: <<: *defaults - docker: - - image: circleci/ruby:2.5.3 + steps: + - checkout + - run: bundle install + - run: bundle exec rspec spec/unit + integration_specs: + <<: *defaults + steps: + - checkout + - run: bundle install + - run: bundle exec rspec spec/integration + metrics: + <<: *defaults + steps: + - checkout + - run: bundle install + - run: bundle exec rake metrics:rubocop + - run: bundle exec rake metrics:reek + - run: bundle exec rake metrics:flay + - run: bundle exec rake metrics:flog + mutant: + <<: *defaults + steps: + - checkout + - run: bundle install + - run: bundle exec rake metrics:mutant workflows: version: 2 test: jobs: - - ruby_2_5 + - unit_specs + - integration_specs + - metrics + - mutant From ee793848ed598f9710fc3220868f6c6ab66014f4 Mon Sep 17 00:00:00 2001 From: Daniel Gollahon Date: Sat, 24 Nov 2018 19:37:38 -0800 Subject: [PATCH 007/256] Support ruby 2.5 block rescue syntax - Prior to this commit, unparser would emit incorrect source representations for users using the new ruby 2.5 block syntax for rescue/ensure. --- config/flay.yml | 2 +- lib/unparser/emitter/rescue.rb | 2 +- spec/unit/unparser_spec.rb | 75 ++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/config/flay.yml b/config/flay.yml index 3e2df4e4..c0f6a832 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 632 +total_score: 633 diff --git a/lib/unparser/emitter/rescue.rb b/lib/unparser/emitter/rescue.rb index 13c0bd09..0846aa36 100644 --- a/lib/unparser/emitter/rescue.rb +++ b/lib/unparser/emitter/rescue.rb @@ -12,7 +12,7 @@ class Rescue < self define_group :rescue_bodies, 1..-2 - EMBEDDED_TYPES = %i[def defs kwbegin ensure].to_set.freeze + EMBEDDED_TYPES = %i[block def defs kwbegin ensure].to_set.freeze NOINDENT_STANDALONE_RESCUE = %i[root begin pair_rocket pair_colon lvasgn ivasgn].to_set.freeze diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 7633e921..63f58e4b 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -859,6 +859,81 @@ class Foo end RUBY + assert_source <<-'RUBY' + m do + rescue Exception => e + end + RUBY + + assert_source <<-'RUBY' + m do + ensure + end + RUBY + + assert_source <<-'RUBY' + m do + rescue + ensure + end + RUBY + + assert_source <<-'RUBY' + m do + foo + rescue Exception => bar + bar + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue SomeError, *bar + baz + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue SomeError, *bar => exception + baz + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue *bar + baz + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue LoadError + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue + else + baz + end + RUBY + + assert_source <<-'RUBY' + m do + bar + rescue *bar => exception + baz + end + RUBY + assert_source 'foo rescue bar' assert_source 'foo rescue return bar' assert_source 'x = (foo rescue return bar)' From e249645b3f84ca71883eeea70254c0b956ab7604 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 25 Nov 2018 20:24:57 +0000 Subject: [PATCH 008/256] Remove support for dynamic ruby versions in spec --- spec/unit/unparser_spec.rb | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 7633e921..a636c821 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -19,36 +19,8 @@ def self.builder_options=(options) @builder_options = options end - def self.ruby_versions - @ruby_versions ||= RUBY_VERSIONS - end - - def self.ruby_versions=(versions) - @ruby_versions = versions - end - - def self.with_ruby_versions(beginning_at: nil, ending_at: nil, only: nil) - original_ruby_versions = ruby_versions - if only - self.ruby_versions = only & ruby_versions # intersection - else - if ending_at - idx = ruby_versions.index(ending_at) || fail('Invalid Ruby specified') - self.ruby_versions = ruby_versions[0..idx] - end - if beginning_at - idx = ruby_versions.index(beginning_at) || fail('Invalid Ruby specified') - self.ruby_versions = ruby_versions[idx..-1] - end - end - - yield - - self.ruby_versions = original_ruby_versions - end - def self.current_parsers - ruby_versions.map do |ruby_version| + RUBY_VERSIONS.map do |ruby_version| if builder_options != {} ParserClassGenerator.generate_with_options(parser_for_ruby_version(ruby_version), builder_options) else From 66a06a3f8ec322b06f5bcab3c66f95022cc32ac5 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 25 Nov 2018 20:30:40 +0000 Subject: [PATCH 009/256] Remove too coarse grained parser requires --- spec/unit/unparser_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index a636c821..5b4e913a 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -1,5 +1,4 @@ require 'spec_helper' -require 'parser/all' require 'support/parser_class_generator' describe Unparser, mutant_expression: 'Unparser::Emitter*' do From 3475fd1706ed3c5effae239e1ff6edf0c9d8c878 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 25 Nov 2018 20:31:16 +0000 Subject: [PATCH 010/256] Remove sematically dead spec setup branch --- spec/unit/unparser_spec.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 5b4e913a..b4dde8ed 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -20,11 +20,10 @@ def self.builder_options=(options) def self.current_parsers RUBY_VERSIONS.map do |ruby_version| - if builder_options != {} - ParserClassGenerator.generate_with_options(parser_for_ruby_version(ruby_version), builder_options) - else - parser_for_ruby_version(ruby_version) - end + ParserClassGenerator.generate_with_options( + parser_for_ruby_version(ruby_version), + builder_options + ) end end From 7dc6ca5ee2e2e3d0f96f60c1d51800686d99c8e6 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 25 Nov 2018 20:33:14 +0000 Subject: [PATCH 011/256] Remove ruby version number indirection --- spec/unit/unparser_spec.rb | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index b4dde8ed..bec4544e 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -4,11 +4,7 @@ describe Unparser, mutant_expression: 'Unparser::Emitter*' do describe '.unparse' do - RUBY_VERSION_PARSERS = IceNine.deep_freeze( - '2.5' => Parser::Ruby25 - ) - - RUBY_VERSIONS = RUBY_VERSION_PARSERS.keys.freeze + RUBY_PARSERS = IceNine.deep_freeze([Parser::Ruby25]) def self.builder_options @builder_options ||= {} @@ -19,9 +15,9 @@ def self.builder_options=(options) end def self.current_parsers - RUBY_VERSIONS.map do |ruby_version| + RUBY_PARSERS.map do |parser_class| ParserClassGenerator.generate_with_options( - parser_for_ruby_version(ruby_version), + parser_class, builder_options ) end @@ -36,12 +32,6 @@ def self.with_builder_options(options) self.builder_options = original_options end - def self.parser_for_ruby_version(version) - RUBY_VERSION_PARSERS.fetch(version) do - raise "Unrecognized Ruby version #{version}" - end - end - def assert_round_trip(input, parser) ast, comments = parser.parse_with_comments(input) generated = Unparser.unparse(ast, comments) From f04ce49764fc9722d21aa95acb33697d5b280675 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 28 Nov 2018 23:11:01 +0000 Subject: [PATCH 012/256] Upgrade Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index cddac1b4..32315c6a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.2.9) + unparser (0.3.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) From 609d95d1eee31ef2f61c43ba6aac17b8dfab8509 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 00:06:19 +0000 Subject: [PATCH 013/256] Refactor tests to reduce duplication --- spec/support/parser_class_generator.rb | 21 -------- spec/unit/unparser_spec.rb | 72 ++++++++++++-------------- 2 files changed, 33 insertions(+), 60 deletions(-) delete mode 100644 spec/support/parser_class_generator.rb diff --git a/spec/support/parser_class_generator.rb b/spec/support/parser_class_generator.rb deleted file mode 100644 index 7a7c5148..00000000 --- a/spec/support/parser_class_generator.rb +++ /dev/null @@ -1,21 +0,0 @@ -module ParserClassGenerator - def self.generate_with_options(base_parser_class, builder_options) - # This builds a dynamic subclass of the base_parser_class (e.g. Parser::Ruby23) - # and overrides the default_parser method to return a parser whose builder - # has various options set. - # - # Currently the only builder option is :emit_file_line_as_literals - - Class.new(base_parser_class) do - define_singleton_method(:default_parser) do |*args| - super(*args).tap do |parser| - parser.builder.emit_file_line_as_literals = builder_options[:emit_file_line_as_literals] - end - end - - define_singleton_method(:inspect) do - "#{base_parser_class.inspect} with builder options: #{builder_options.inspect}" - end - end - end -end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 09679966..668421ee 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -1,55 +1,53 @@ require 'spec_helper' -require 'support/parser_class_generator' describe Unparser, mutant_expression: 'Unparser::Emitter*' do describe '.unparse' do + let(:builder_options) { {} } - RUBY_PARSERS = IceNine.deep_freeze([Parser::Ruby25]) - - def self.builder_options - @builder_options ||= {} - end - - def self.builder_options=(options) - @builder_options = options + def parser + Parser::CurrentRuby.new.tap do |parser| + builder_options.each do |name, value| + parser.builder.public_send(:"#{name}=", value) + end + end end - def self.current_parsers - RUBY_PARSERS.map do |parser_class| - ParserClassGenerator.generate_with_options( - parser_class, - builder_options - ) + def buffer(input) + Parser::Source::Buffer.new('(string)').tap do |buffer| + buffer.source = input end end - def self.with_builder_options(options) - original_options = builder_options - self.builder_options = builder_options.merge(options) + def parse_with_comments(string) + parser.parse_with_comments(buffer(string)) + end - yield + def self.with_builder_options(options, &block) + context "with #{options}" do + let(:builder_options) { options } - self.builder_options = original_options + class_eval(&block) + end end - def assert_round_trip(input, parser) - ast, comments = parser.parse_with_comments(input) + def assert_round_trip(string, parser) + ast, comments = parse_with_comments(string) generated = Unparser.unparse(ast, comments) - expect(generated).to eql(input) - generated_ast, _comments = parser.parse_with_comments(generated) + expect(generated).to eql(string) + generated_ast, _comments = parse_with_comments(generated) expect(ast == generated_ast).to be(true) end def assert_generates_from_string(parser, string, expected) string = strip(string) - ast_with_comments = parser.parse_with_comments(string) + ast_with_comments = parse_with_comments(string) assert_generates_from_ast(parser, ast_with_comments, expected) end def assert_generates_from_ast(parser, ast_with_comments, expected) generated = Unparser.unparse(*ast_with_comments) expect(generated).to eql(expected) - ast, comments = parser.parse_with_comments(generated) + ast, comments = parse_with_comments(generated) expect(Unparser.unparse(ast, comments)).to eql(expected) end @@ -64,24 +62,20 @@ def self.assert_terminated(expression) assert_source("#{expression}.foo") end - def self.assert_generates(ast_or_string, expected) - current_parsers.each do |parser| - it "should generate #{ast_or_string} as #{expected} under #{parser.inspect}" do - if ast_or_string.is_a?(String) - expected = strip(expected) - assert_generates_from_string(parser, ast_or_string, expected) - else - assert_generates_from_ast(parser, [ast_or_string, []], expected) - end + def self.assert_generates(input, expected) + it "should generate #{input} as #{expected}" do + if input.is_a?(String) + expected = strip(expected) + assert_generates_from_string(parser, input, expected) + else + assert_generates_from_ast(parser, [input, []], expected) end end end def self.assert_round_trip(input) - current_parsers.each do |parser| - it "should round trip #{input} under #{parser.inspect}" do - assert_round_trip(input, parser) - end + it "should round trip #{input}" do + assert_round_trip(input, parser) end end From aad0ff9e48624b85c4eba5ccdd938062455506c6 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 00:09:55 +0000 Subject: [PATCH 014/256] Change README to referece 2.5 target --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index b9f2c458..8196ab85 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,7 @@ unparser Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). Excluding the macruby extensions the parser gem implemnents on top of ruby syntax. -Excluding the MacRuby / RubyMotion extensions the parser gem implemenents on top of MRI Ruby -syntax starting with parser release 2.3. If you feel the need to get them supported, contact me. - -This library is able to reproduce 100% of Ruby 2.1 - 2.3 syntax. Including its own source code. +This library targets reproduce 100% of MRI/Ruby 2.5 syntax. It serves well for [mutant](https://github.com/mbj/mutant) mutators and the in-memory vendoring for self hosting, and other tooling. From 2e72f5dfda0a029f0e273a1bf13932c03a49862a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 00:17:33 +0000 Subject: [PATCH 015/256] Add modern AST support --- Changelog.md | 5 + Gemfile | 2 + Gemfile.lock | 46 +++--- README.md | 41 +++--- config/flay.yml | 2 +- lib/unparser.rb | 64 ++++++++- lib/unparser/ast.rb | 2 +- lib/unparser/cli/source.rb | 17 +-- lib/unparser/emitter/argument.rb | 62 ++++++++ lib/unparser/emitter/assignment.rb | 7 +- lib/unparser/emitter/block.rb | 33 ++++- lib/unparser/emitter/index.rb | 136 ++++++++++++++++++ lib/unparser/emitter/lambda.rb | 25 ++++ lib/unparser/emitter/meta.rb | 4 +- lib/unparser/emitter/send.rb | 39 ++--- .../emitter/send/attribute_assignment.rb | 34 ++--- lib/unparser/emitter/send/index.rb | 127 ---------------- spec/unit/unparser/comments/consume_spec.rb | 2 +- spec/unit/unparser/comments/take_all_spec.rb | 2 +- .../unparser/comments/take_before_spec.rb | 4 +- .../comments/take_eol_comments_spec.rb | 12 +- spec/unit/unparser_spec.rb | 110 ++++++++++++-- unparser.gemspec | 2 +- 23 files changed, 513 insertions(+), 265 deletions(-) create mode 100644 lib/unparser/emitter/index.rb create mode 100644 lib/unparser/emitter/lambda.rb delete mode 100644 lib/unparser/emitter/send/index.rb diff --git a/Changelog.md b/Changelog.md index d899ecd5..888fc070 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# v0.4.0 2018-12-01 + +* Change to modern AST format. +* Add experimental `Unparser.{parser,parse,parse_with_comments}` + # v0.3.0 2018-11-16 * Drop support for Ruby < 2.5 diff --git a/Gemfile b/Gemfile index 7f4f5e95..a8b83f2b 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,5 @@ source 'https://rubygems.org' gemspec + +gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'change/to-modern-ast' diff --git a/Gemfile.lock b/Gemfile.lock index 32315c6a..697184f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,31 @@ +GIT + remote: https://github.com/mbj/mutant + revision: 6b36372385ec1bedc0d170470e65d7d33740769e + branch: change/to-modern-ast + specs: + mutant (0.8.20) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.0) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + morpher (~> 0.2.6) + parser (~> 2.5.1) + procto (~> 0.0.2) + regexp_parser (~> 1.2) + unparser (~> 0.3.0) + mutant-rspec (0.8.20) + mutant (~> 0.8.20) + rspec-core (>= 3.4.0, < 4.0.0) + PATH remote: . specs: - unparser (0.3.0) + unparser (0.4.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) @@ -82,25 +106,6 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) - mutant (0.8.19) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.0) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - morpher (~> 0.2.6) - parallel (~> 1.3) - parser (~> 2.5.1) - procto (~> 0.0.2) - regexp_parser (~> 1.2) - unparser (~> 0.2.5) - mutant-rspec (0.8.19) - mutant (~> 0.8.19) - rspec-core (>= 3.4.0, < 4.0.0) parallel (1.12.1) parser (2.5.3.0) ast (~> 2.4.0) @@ -166,6 +171,7 @@ DEPENDENCIES anima (~> 0.3.0) devtools (~> 0.1.21) morpher (~> 0.2.6) + mutant! unparser! BUNDLED WITH diff --git a/README.md b/README.md index 8196ab85..a099ff80 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,12 @@ unparser [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). -Excluding the macruby extensions the parser gem implemnents on top of ruby syntax. -This library targets reproduce 100% of MRI/Ruby 2.5 syntax. +The following constraints apply: + +* No support for macruby extensions +* Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format +* Only support for Ruby >= 2.5 It serves well for [mutant](https://github.com/mbj/mutant) mutators and the in-memory vendoring for self hosting, and other tooling. @@ -17,15 +20,19 @@ and other tooling. Public API: ----------- -While unparser is in the `0.x` versions its public API can change any moment. I recommend to use `~> 0.x.y` style -version constraints that should give the best mileage. +While unparser is in the `0.x` versions its public API can change any moment. +I recommend to use `~> 0.x.y` style version constraints that should give the best mileage. Usage ----- ```ruby +require 'parser/current' require 'unparser' -Unparser.unparse(your_ast) # => "the code" + +ast = Unpaser.parse('your(ruby(code))') + +Unparser.unparse(ast) # => 'your(ruby(code))' ``` To preserve the comments from the source: @@ -33,8 +40,10 @@ To preserve the comments from the source: ```ruby require 'parser/current' require 'unparser' -ast, comments = Parser::CurrentRuby.parse_with_comments(your_source) -Unparser.unparse(ast, comments) # => "the code # with comments" + +ast, comments = Unpaser.parse_with_comments('your(ruby(code)) # with comments') + +Unparser.unparse(ast, comments) # => 'your(ruby(code)) # with comments' ``` Passing in manually constructed AST: @@ -68,22 +77,19 @@ Unparser.unparse(node) # => "def foo(x)\n x + 3\nend" Note: DO NOT attempt to pass in nodes generated via `AST::Sexp#s`, these ones return API incompatible `AST::Node` instances, unparser needs `Parser::AST::Node` instances. - Equivalent vs identical: ```ruby require 'unparser' -code = <<-RUBY -%w(foo bar) +node = Unparser.parse(<<~'RUBY') + %w[foo bar] RUBY -node = Parser::CurrentRuby.parse(code) - -generated = Unparser.unparse(node) # ["foo", "bar"], NOT %w(foo bar) ! +generated = Unparser.unparse(node) # ["foo", "bar"], NOT %w[foo bar] ! code == generated # false, not identical code -Parser::CurrentRuby.parse(generated) == node # true, but identical AST +Unparser.parse(generated) == node # true, but identical AST ``` Summary: unparser does not reproduce your source! It produces equivalent source. @@ -91,7 +97,7 @@ Summary: unparser does not reproduce your source! It produces equivalent source. Testing: -------- -Unparser currently successfully round trips almost all ruby code around. Using MRI-2.0.0. +Unparser currently successfully round trips almost all ruby code around. Using MRI-2.5.x. If there is a non round trippable example that is NOT subjected to known [Limitations](#limitations). please report a bug. @@ -103,10 +109,7 @@ Limitations: Source parsed with magic encoding headers other than UTF-8 and that have literal strings. where parts can be represented in UTF-8 will fail to get reproduced. -Please note: If you are on 1.9.3 or any 1.9 mode ruby and use UTF-8 encoded source via the magic encoding header: -Unparser does not reproduce these. - -A fix might be possible and requires some guessing or parser metadata the raw AST does not carry. +A fix is possible as with latest updates the parser gem carries the information. Example: diff --git a/config/flay.yml b/config/flay.yml index 92c133b0..3ccd025c 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 629 +total_score: 640 diff --git a/lib/unparser.rb b/lib/unparser.rb index 46ad68c9..baa11f6b 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -8,10 +8,15 @@ # Library namespace module Unparser + # Unparser specific AST builder defaulting to modern AST format + class Builder < Parser::Builders::Default + modernize + end EMPTY_STRING = ''.freeze + EMPTY_ARRAY = [].freeze - EMPTY_ARRAY = [].freeze + private_constant(*constants(false)) # Unparse an AST (and, optionally, comments) into a string # @@ -31,6 +36,60 @@ def self.unparse(node, comment_array = []) buffer.content end + # Parse string into AST + # + # @param [String] source + # + # @return [Parser::AST::Node] + def self.parse(source) + parser.parse(buffer(source)) + end + + # Parse string into AST, with comments + # + # @param [String] source + # + # @return [Parser::AST::Node] + def self.parse_with_comments(source) + parser.parse_with_comments(buffer(source)) + end + + # Parser instance that produces AST unparser understands + # + # @return [Parser::Base] + # + # @api private + # + # ignore :reek:NestedIterators + def self.parser + Parser::CurrentRuby.new(Builder.new).tap do |parser| + parser.diagnostics.tap do |diagnostics| + diagnostics.all_errors_are_fatal = true + diagnostics.consumer = method(:consume_diagnostic) + end + end + end + + # Consume diagnostic + # + # @param [Parser::Diagnostic] diagnostic + # + # @return [undefined] + def self.consume_diagnostic(diagnostic) + Kernel.warn(diagnostic.render) + end + private_class_method :consume_diagnostic + + # Construct a parser buffer from string + # + # @param [String] source + # + # @return [Parser::Source::Buffer] + def self.buffer(source) + Parser::Source::Buffer.new('(string)').tap do |buffer| + buffer.source = source + end + end end # Unparser require 'unparser/buffer' @@ -56,7 +115,6 @@ def self.unparse(node, comment_array = []) require 'unparser/emitter/send' require 'unparser/emitter/send/unary' require 'unparser/emitter/send/binary' -require 'unparser/emitter/send/index' require 'unparser/emitter/send/regular' require 'unparser/emitter/send/conditional' require 'unparser/emitter/send/arguments' @@ -93,5 +151,7 @@ def self.unparse(node, comment_array = []) require 'unparser/emitter/rescue' require 'unparser/emitter/resbody' require 'unparser/emitter/ensure' +require 'unparser/emitter/index' +require 'unparser/emitter/lambda' # make it easy for zombie require 'unparser/finalize' diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 1aa15652..1ce3a919 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -16,7 +16,7 @@ module AST # # FIXME: Kwargs are missing. # - ASSIGN_NODES = %i[lvasgn arg optarg restarg].freeze + ASSIGN_NODES = %i[arg lvasgn optarg procarg0 restarg].freeze # Test for local variable inherited scope reset # diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb index 36111797..16a7f2c9 100644 --- a/lib/unparser/cli/source.rb +++ b/lib/unparser/cli/source.rb @@ -182,7 +182,7 @@ def ast_diff # @api private # def generated_ast - generated.success? && Preprocessor.run(parse(generated.source)) + generated.success? && Preprocessor.run(Unparser.parse(generated.source)) rescue Parser::SyntaxError nil end @@ -195,25 +195,12 @@ def generated_ast # @api private # def original_ast - Preprocessor.run(parse(original_source)) + Preprocessor.run(Unparser.parse(original_source)) rescue Parser::SyntaxError nil end memoize :original_ast - # Parse source with current ruby - # - # @param [String] source - # - # @return [Parser::AST::Node] - # - # @api private - # - # ignore :reek:UtilityFunction - def parse(source) - Parser::CurrentRuby.parse(source) - end - # CLI source from string class String < self include Concord.new(:original_source) diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index ab13dc44..c8c85585 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -30,6 +30,7 @@ class Arguments < self handle :args SHADOWARGS = ->(node) { node.type.equal?(:shadowarg) }.freeze + ARG = ->(node) { node.type.equal?(:arg) }.freeze private @@ -41,12 +42,35 @@ class Arguments < self # def dispatch delimited(normal_arguments) + + write(', ') if procarg_disambiguator? + return if shadowargs.empty? write('; ') delimited(shadowargs) end + # Test for procarg_disambiguator + # + # @return [Boolean] + # + # @api private + # + def procarg_disambiguator? + regular_block? && normal_arguments.all?(&ARG) && normal_arguments.one? + end + + # Test for regular block + # + # @return [Boolean] + # + # @api private + # + def regular_block? + parent_type.equal?(:block) && !parent.node.children.first.type.equal?(:lambda) + end + # Return normal arguments # # @return [Enumerable] @@ -56,6 +80,7 @@ def dispatch def normal_arguments children.reject(&SHADOWARGS) end + memoize :normal_arguments # Return shadow args # @@ -209,6 +234,43 @@ def dispatch end # Argument + # Progarg emitter + class Procarg < self + include Terminated + + handle :procarg0 + + children :first_argument + + private + + # Perform dispatch + # + # @return [undefined] + # + # @api private + # + def dispatch + if first_argument.instance_of?(Symbol) + write(first_argument.to_s) + else + emit_multiple_children + end + end + + # Emit multiple children + # + # @return [undefined] + # + # @api private + # + def emit_multiple_children + parentheses do + delimited(children) + end + end + end + # Block pass node emitter class BlockPass < self include Terminated diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index 83b2535d..508648d7 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -29,7 +29,7 @@ class Single < self # @api private # def terminated? - right_node.nil? + right.nil? end private @@ -41,7 +41,6 @@ def terminated? # @api private # def emit_right - right = right_node return unless right write(WS, T_ASN, WS) @@ -55,7 +54,7 @@ class Variable < self handle :lvasgn, :ivasgn, :cvasgn, :gvasgn - children :name, :right_node + children :name, :right private @@ -76,7 +75,7 @@ class Constant < self handle :casgn - children :base, :name, :right_node + children :base, :name, :right private diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index e53e7ac5..f360ada2 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -4,12 +4,14 @@ module Unparser class Emitter # Block emitter + # + # ignore :reek:RepeatedConditional class Block < self include Terminated handle :block - children :send, :arguments, :body + children :target, :arguments, :body private @@ -20,14 +22,37 @@ class Block < self # @api private # def dispatch - visit(send) + emit_target write(WS, K_DO) - comments.consume(node, :begin) - emit_block_arguments + emit_block_arguments unless stabby_lambda? emit_body k_end end + # Emit target + # + # @return [undefined] + # + # @api private + # + def emit_target + visit(target) + + if stabby_lambda? + parentheses { visit(arguments) } + end + end + + # Test if we are emitting a stabby lambda + # + # @return [Boolean] + # + # @api private + # + def stabby_lambda? + target.type.equal?(:lambda) + end + # Emit arguments # # @return [undefined] diff --git a/lib/unparser/emitter/index.rb b/lib/unparser/emitter/index.rb new file mode 100644 index 00000000..42efeebe --- /dev/null +++ b/lib/unparser/emitter/index.rb @@ -0,0 +1,136 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for send to index references + # + # ignore :reek:RepeatedConditional + class Index < self + + # Perform dispatch + # + # @return [undefined] + # + # @api private + # + def dispatch + emit_receiver + emit_operation + end + + private + + # Emit receiver + # + # @return [undefined] + # + # @api private + # + def emit_receiver + visit(first_child) + end + + # Test for mlhs + # + # @return [Boolean] + # + # @api private + # + def mlhs? + parent_type.equal?(:mlhs) + end + + class Reference < self + include Terminated + + define_group(:indices, 1..-1) + + handle :index + + private + + # Emit arguments + # + # @return [undefined] + # + # @api private + # + def emit_operation + parentheses(*BRACKETS_SQUARE) do + delimited_plain(indices) + end + end + end # Reference + + # Emitter for assign to index nodes + class Assign < self + + handle :indexasgn + + VALUE_RANGE = (1..-2).freeze + NO_VALUE_PARENT = IceNine.deep_freeze(%i[and_asgn op_asgn or_asgn].to_set) + + # Test if assign will be emitted terminated + # + # @return [Boolean] + # + # @api private + # + def terminated? + !emit_value? + end + + private + + # Emit arguments + # + # @return [undefined] + # + # @api private + # + def emit_operation + parentheses(*BRACKETS_SQUARE) do + delimited_plain(indices) + end + + if emit_value? + write(WS, T_ASN, WS) + visit(children.last) + end + end + + # The indices + # + # @return [Array] + # + def indices + if emit_value? + children[VALUE_RANGE] + else + children.drop(1) + end + end + + # Test if value should be emitted + # + # @return [Boolean] + # + # @api private + # + def emit_value? + !mlhs? && !no_value_parent? + end + + # Test for no value parent + # + # @return [Boolean] + # + # @api private + # + def no_value_parent? + NO_VALUE_PARENT.include?(parent_type) + end + end # Assign + end # Index + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/lambda.rb b/lib/unparser/emitter/lambda.rb new file mode 100644 index 00000000..2bcb4323 --- /dev/null +++ b/lib/unparser/emitter/lambda.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for lambda nodes + class Lambda < self + include Terminated + + handle :lambda + + private + + # Perform dispatch + # + # @return [undefined] + # + # @api private + # + def dispatch + write('->') + end + + end # Lambda + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/meta.rb b/lib/unparser/emitter/meta.rb index 7d0b0846..d84078d3 100644 --- a/lib/unparser/emitter/meta.rb +++ b/lib/unparser/emitter/meta.rb @@ -6,10 +6,10 @@ class Emitter class Meta < self include Terminated - handle(:__FILE__, :__LINE__) + handle(:__ENCODING__, :__FILE__, :__LINE__) def dispatch - write(node.type.to_s) # (e.g. literally write '__FILE__' or '__LINE__') + write(node.type.to_s) end end # Meta end # Emitter diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 3f087056..3dc12e72 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -8,9 +8,10 @@ class Send < self handle :send - INDEX_REFERENCE = :'[]' - INDEX_ASSIGN = :'[]=' - ASSIGN_SUFFIX = '='.freeze + ASSIGN_SUFFIX = '='.freeze + INDEX_ASSIGN = :'[]=' + INDEX_REFERENCE = :'[]' + NON_ASSIGN_RANGE = (0..-2).freeze children :receiver, :selector @@ -47,23 +48,6 @@ def effective_emitter # @api private # def effective_emitter_class - case selector - when INDEX_REFERENCE - Index::Reference - when INDEX_ASSIGN - Index::Assign - else - non_index_emitter - end - end - - # Return non index emitter - # - # @return [Class:Emitter] - # - # @api private - # - def non_index_emitter if binary_operator? Binary elsif unary_operator? @@ -112,11 +96,7 @@ def binary_operator? # @api private # def emit_selector - name = string_selector - if mlhs? - name = name[0..-2] - end - write(name) + write(mlhs? ? non_assignment_selector : string_selector) end # Test for mlhs @@ -196,6 +176,15 @@ def local_variable_clash? local_variable_scope.local_variable_defined_for_node?(node, selector) end + # The non assignment selector + # + # @return [String] + # + # @api private + def non_assignment_selector + string_selector[NON_ASSIGN_RANGE] + end + end # Send end # Emitter end # Unparser diff --git a/lib/unparser/emitter/send/attribute_assignment.rb b/lib/unparser/emitter/send/attribute_assignment.rb index 54b7c2d9..569d577c 100644 --- a/lib/unparser/emitter/send/attribute_assignment.rb +++ b/lib/unparser/emitter/send/attribute_assignment.rb @@ -7,6 +7,8 @@ class Send class AttributeAssignment < self include Unterminated + children :receiver, :selector, :first_argument + # Perform regular dispatch # # @return [undefined] @@ -16,8 +18,13 @@ class AttributeAssignment < self def dispatch emit_receiver emit_attribute - emit_operator - visit(arguments.first) + write(T_ASN) + + if arguments.one? + visit(first_argument) + else + parentheses { delimited(arguments) } + end end private @@ -40,29 +47,8 @@ def emit_receiver # @api private # def emit_attribute - write(attribute_name) - end - - # Emit assignment operator - # - # @return [undefined] - # - # @api private - # - def emit_operator - write(WS, T_ASN, WS) - end - - # Return attribute name - # - # @return [String] - # - # @api private - # - def attribute_name - string_selector[0..-2] + write(non_assignment_selector) end - end # AttributeAssignment end # Send end # Emitter diff --git a/lib/unparser/emitter/send/index.rb b/lib/unparser/emitter/send/index.rb deleted file mode 100644 index c17d639d..00000000 --- a/lib/unparser/emitter/send/index.rb +++ /dev/null @@ -1,127 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Send - # Emitter for send to index references - class Index < self - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - emit_receiver - emit_operation - end - - # Emit receiver - # - # @return [undefined] - # - # @api private - # - def emit_receiver - visit(first_child) - end - - # Emitter for index reference nodes - class Reference < self - include Terminated - - private - - # Emit arguments - # - # @return [undefined] - # - # @api private - # - def emit_operation - parentheses(*BRACKETS_SQUARE) do - delimited_plain(arguments) - end - end - end # Reference - - # Emitter for assign to index nodes - class Assign < self - include Unterminated - - # Test if assign will be emitted terminated - # - # @return [Boolean] - # - # @api private - # - def terminated? - mlhs? - end - - private - - define_group(:indices, 2..-2) - define_child(:value, -1) - - # Emit arguments - # - # @return [undefined] - # - # @api private - # - def emit_operation - if arguments.empty? - emit_regular_with_empty_args - elsif mlhs? - emit_mlhs_operation - else - emit_normal_operation - end - end - - # Emit mlhs arguments - # - # @return [undefined] - # - # @api private - # - def emit_mlhs_operation - parentheses(*BRACKETS_SQUARE) do - delimited(arguments) - end - end - - # Emit normal arguments - # - # @return [undefined] - # - # @api private - # - def emit_normal_operation - parentheses(*BRACKETS_SQUARE) do - delimited_plain(indices) - end - write(WS, T_ASN, WS) - visit(value) - end - - # Emit regular with empty ars - # - # @return [undefined] - # - # @api private - # - def emit_regular_with_empty_args - write(T_DOT, '[]=()') - end - - end # Assign - - end # Index - end # Send - end # Emitter -end # Unparser diff --git a/spec/unit/unparser/comments/consume_spec.rb b/spec/unit/unparser/comments/consume_spec.rb index 1feb715f..57b1702e 100644 --- a/spec/unit/unparser/comments/consume_spec.rb +++ b/spec/unit/unparser/comments/consume_spec.rb @@ -3,7 +3,7 @@ describe Unparser::Comments, '#consume' do let(:ast_and_comments) do - Parser::CurrentRuby.parse_with_comments(<<-RUBY) + Unparser.parse_with_comments(<<~'RUBY') def hi # EOL 1 end # EOL 2 RUBY diff --git a/spec/unit/unparser/comments/take_all_spec.rb b/spec/unit/unparser/comments/take_all_spec.rb index 23f023c9..594f6729 100644 --- a/spec/unit/unparser/comments/take_all_spec.rb +++ b/spec/unit/unparser/comments/take_all_spec.rb @@ -3,7 +3,7 @@ describe Unparser::Comments, '#take_all' do let(:ast_and_comments) do - Parser::CurrentRuby.parse_with_comments(<<-RUBY) + Unparser.parse_with_comments(<<~'RUBY') def hi # EOL 1 end # EOL 2 RUBY diff --git a/spec/unit/unparser/comments/take_before_spec.rb b/spec/unit/unparser/comments/take_before_spec.rb index afa14ec4..288cccd2 100644 --- a/spec/unit/unparser/comments/take_before_spec.rb +++ b/spec/unit/unparser/comments/take_before_spec.rb @@ -9,7 +9,7 @@ context 'usual case' do let(:ast_and_comments) do - Parser::CurrentRuby.parse_with_comments(<<-RUBY) + Unparser.parse_with_comments(<<~'RUBY') def hi # EOL 1 # comment end # EOL 2 @@ -29,7 +29,7 @@ def hi # EOL 1 context 'when node does not respond to source part' do let(:ast_and_comments) do - Parser::CurrentRuby.parse_with_comments(<<-RUBY) + Unparser.parse_with_comments(<<~'RUBY') expression ? :foo : :bar # EOL 1 # EOL 2 RUBY diff --git a/spec/unit/unparser/comments/take_eol_comments_spec.rb b/spec/unit/unparser/comments/take_eol_comments_spec.rb index 727ab1bf..d2ff3920 100644 --- a/spec/unit/unparser/comments/take_eol_comments_spec.rb +++ b/spec/unit/unparser/comments/take_eol_comments_spec.rb @@ -3,12 +3,12 @@ describe Unparser::Comments, '#take_eol_comments' do let(:ast_and_comments) do - Parser::CurrentRuby.parse_with_comments(<<-RUBY) -def hi # EOL 1 -=begin -doc comment -=end -end # EOL 2 + Unparser.parse_with_comments(<<~'RUBY') + def hi # EOL 1 + =begin + doc comment + =end + end # EOL 2 RUBY end let(:ast) { ast_and_comments[0] } diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index b7c69701..0ec607ed 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -1,11 +1,75 @@ require 'spec_helper' describe Unparser, mutant_expression: 'Unparser::Emitter*' do + describe '.buffer' do + let(:source) { 'a + b' } + + def apply + described_class.buffer(source) + end + + it 'returns parser buffer with expected name' do + expect(apply.name).to eql('(string)') + end + + it 'returns parser buffer with pre-filled source' do + expect(apply.source).to eql(source) + end + end + + describe '.parser' do + let(:invalid_source_buffer) { Unparser.buffer('a +') } + + def apply + described_class.parser + end + + context 'failure' do + def apply + super.tap do |parser| + parser.diagnostics.consumer = ->(_) {} + end + end + + it 'returns a parser that fails with syntax error' do + expect { apply.parse(invalid_source_buffer) } + .to raise_error(Parser::SyntaxError) + end + end + + context 'warnings' do + before do + allow(Kernel).to receive(:warn) + end + + it 'returns a parser that warns on diagnostics' do + expect { apply.parse(invalid_source_buffer) } + .to raise_error(Parser::SyntaxError) + + expect(Kernel).to have_received(:warn) + .with([ + "(string):1:4: error: unexpected token $end", + "(string):1: a +", "(string):1: " + ]) + end + end + end + + describe '.parse' do + def apply + described_class.parse('self[1]=2') + end + + it 'returns expected AST' do + expect(apply).to eql(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2))) + end + end + describe '.unparse' do let(:builder_options) { {} } def parser - Parser::CurrentRuby.new.tap do |parser| + Unparser.parser.tap do |parser| builder_options.each do |name, value| parser.builder.public_send(:"#{name}=", value) end @@ -13,9 +77,7 @@ def parser end def buffer(input) - Parser::Source::Buffer.new('(string)').tap do |buffer| - buffer.source = input.chomp - end + Unparser.buffer(input) end def parse_with_comments(string) @@ -272,7 +334,7 @@ def foo(bar:, baz: "value") end context 'magic keywords' do - assert_generates '__ENCODING__', 'Encoding::UTF_8' + assert_source '__ENCODING__' # These two assertions don't actually need to be wrapped in this block since `true` is the default, # but it is helpful to contrast with the assertions farther down. @@ -436,19 +498,29 @@ def foo assert_terminated 'foo(&(foo || bar))' assert_terminated 'foo(*arguments)' assert_terminated 'foo(*arguments)' + assert_source <<~'RUBY' foo do end RUBY assert_source <<~'RUBY' - foo(1) do - nil + foo do |a| + end + RUBY + + assert_source <<~'RUBY' + foo do |a, | end RUBY assert_source <<~'RUBY' foo do |a, b| + end + RUBY + + assert_source <<~'RUBY' + foo(1) do nil end RUBY @@ -616,8 +688,9 @@ module Foo assert_terminated 'foo(*args, &block)' assert_terminated 'foo.bar(&baz)' assert_terminated 'foo.bar(:baz, &baz)' - assert_terminated 'foo.bar = :baz' - assert_unterminated 'self.foo = :bar' + assert_terminated 'foo.bar=:baz' + + assert_unterminated 'self.foo=:bar' assert_terminated 'foo.bar(baz: boz)' assert_terminated 'foo.bar(foo, "baz" => boz)' @@ -1378,7 +1451,10 @@ def foo assert_source 'foo[a, b] = value' assert_source 'foo[1..2] = value' assert_source 'foo.[]=()' + assert_source 'foo.[]=true' + assert_source 'foo.[]=(1, 2)' assert_source 'foo[] = 1' + assert_unterminated 'foo[] = 1' %w(+ - * / % & | || &&).each do |operator| context "with #{operator}" do @@ -1414,6 +1490,21 @@ def foo a end RUBY + + assert_source <<~'RUBY' + ->() do + end + RUBY + + assert_source <<~'RUBY' + ->(a) do + end + RUBY + + assert_source <<~'RUBY' + ->(a, b) do + end + RUBY end context 'match operators' do @@ -1732,6 +1823,5 @@ def noop block comment =end RUBY - end end diff --git a/unparser.gemspec b/unparser.gemspec index be058b17..32b098fc 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.3.0' + gem.version = '0.4.0' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From e6248c4873b02459f74ca8d685b16363b98f7377 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 00:35:50 +0000 Subject: [PATCH 016/256] Refactor to use squiggly heredocs --- spec/spec_helper.rb | 7 - spec/unit/unparser_spec.rb | 378 ++++++++++++++++++------------------- 2 files changed, 188 insertions(+), 197 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 590c8234..d1b1da49 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -15,13 +15,6 @@ module SpecHelper def s(type, *children) Parser::AST::Node.new(type, children) end - - def strip(source) - source = source.rstrip - indent = source.scan(/^\s*/).min_by(&:length) - source.gsub(/^#{indent}/, '') - end - end RSpec.configure do |config| diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 668421ee..b7c69701 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -14,7 +14,7 @@ def parser def buffer(input) Parser::Source::Buffer.new('(string)').tap do |buffer| - buffer.source = input + buffer.source = input.chomp end end @@ -33,15 +33,14 @@ def self.with_builder_options(options, &block) def assert_round_trip(string, parser) ast, comments = parse_with_comments(string) generated = Unparser.unparse(ast, comments) - expect(generated).to eql(string) + expect(generated).to eql(string.chomp) generated_ast, _comments = parse_with_comments(generated) expect(ast == generated_ast).to be(true) end def assert_generates_from_string(parser, string, expected) - string = strip(string) ast_with_comments = parse_with_comments(string) - assert_generates_from_ast(parser, ast_with_comments, expected) + assert_generates_from_ast(parser, ast_with_comments, expected.chomp) end def assert_generates_from_ast(parser, ast_with_comments, expected) @@ -65,7 +64,6 @@ def self.assert_terminated(expression) def self.assert_generates(input, expected) it "should generate #{input} as #{expected}" do if input.is_a?(String) - expected = strip(expected) assert_generates_from_string(parser, input, expected) else assert_generates_from_ast(parser, [input, []], expected) @@ -80,20 +78,20 @@ def self.assert_round_trip(input) end def self.assert_source(input) - assert_round_trip(strip(input)) + assert_round_trip(input) end context 'kwargs' do - assert_source <<-RUBY + assert_source <<~RUBY def foo(bar:, baz:) end RUBY - assert_source <<-RUBY + assert_source <<~RUBY foo(**bar) RUBY - assert_source <<-RUBY + assert_source <<~RUBY def foo(bar:, baz: "value") end RUBY @@ -147,7 +145,7 @@ def foo(bar:, baz: "value") assert_terminated '"foo bar"' assert_terminated '"foo\nbar"' # Within indentation - assert_generates <<-'RUBY', <<-'RUBY' + assert_generates <<~'RUBY', <<~'RUBY' if foo " #{foo} @@ -187,7 +185,7 @@ def foo(bar:, baz: "value") assert_terminated '/\n/' assert_terminated "/\n/x" # Within indentation - assert_source <<-RUBY + assert_source <<~RUBY if foo / / @@ -307,7 +305,7 @@ def foo(bar:, baz: "value") context 'lvar introduction from condition' do assert_source 'foo = bar while foo' assert_source 'foo = bar until foo' - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo = exp while foo foo = bar @@ -320,7 +318,7 @@ def foo(bar:, baz: "value") # pair = :foo # foo # end - assert_source <<-'RUBY' + assert_source <<~'RUBY' if foo do |pair| pair end @@ -329,13 +327,13 @@ def foo(bar:, baz: "value") end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' while foo foo = bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' each do |bar| while foo foo = bar @@ -343,13 +341,13 @@ def foo(bar:, baz: "value") end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo foo = bar while foo != baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' each do |baz| while foo foo = bar @@ -357,7 +355,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' each do |foo| while foo foo = bar @@ -395,7 +393,7 @@ def foo assert_unterminated "#{keyword} *nil" assert_unterminated "#{keyword} *foo, bar" - assert_generates <<-RUBY, <<-RUBY + assert_generates <<~RUBY, <<~RUBY foo do |bar| bar =~ // || #{keyword} baz @@ -407,7 +405,7 @@ def foo end RUBY - assert_generates <<-RUBY, <<-RUBY + assert_generates <<~RUBY, <<~RUBY #{keyword}(a ? b : c) RUBY #{keyword} (if a @@ -438,91 +436,91 @@ def foo assert_terminated 'foo(&(foo || bar))' assert_terminated 'foo(*arguments)' assert_terminated 'foo(*arguments)' - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo do end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo(1) do nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo do |a, b| nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo do |a, *b| nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo do |a, *| nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo do bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar(*args) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |(a)| d end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |(a, b), c| d end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |*a; b| end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |a; b| end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |; a, b| end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |((*))| d end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |(a, (*))| d end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do |(a, b)| d end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo.bar do end.baz RUBY @@ -532,17 +530,17 @@ def foo assert_unterminated 'a || return' assert_unterminated 'foo << (bar * baz)' - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo ||= (a, _ = b) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin rescue end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case (def foo end :bar) @@ -550,58 +548,58 @@ def foo end.baz RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when bar end.baz RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class << self end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def self.foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' until foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' while foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' loop do end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class Foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' module Foo end.bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' if foo end.baz RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' local = 1 local.bar RUBY @@ -611,7 +609,7 @@ module Foo assert_terminated 'foo.bar(*args, foo)' assert_terminated 'foo.bar(foo, *args)' assert_terminated 'foo.bar(foo, *args, &block)' - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo(bar, *args) RUBY @@ -630,17 +628,17 @@ module Foo context 'begin; end' do assert_generates s(:begin), '' - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo bar @@ -649,7 +647,7 @@ module Foo end context 'begin / rescue / ensure' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo ensure @@ -658,7 +656,7 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo rescue @@ -666,7 +664,7 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin begin foo @@ -679,14 +677,14 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin raise(Exception) rescue foo = bar rescue Exception end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo bar @@ -696,7 +694,7 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo rescue Exception @@ -704,7 +702,7 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo rescue => bar @@ -712,7 +710,7 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo rescue Exception, Other => bar @@ -720,44 +718,44 @@ module Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class << self undef :bar rescue nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' module Foo undef :bar rescue nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class Foo undef :bar rescue nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin rescue Exception => e end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin ensure end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin rescue ensure end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo rescue Exception => bar @@ -765,7 +763,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue SomeError, *bar @@ -773,7 +771,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue SomeError, *bar => exception @@ -781,7 +779,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue *bar @@ -789,14 +787,14 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue LoadError end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue @@ -805,7 +803,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin bar rescue *bar => exception @@ -813,26 +811,26 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do rescue Exception => e end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do ensure end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do rescue ensure end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do foo rescue Exception => bar @@ -840,7 +838,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue SomeError, *bar @@ -848,7 +846,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue SomeError, *bar => exception @@ -856,7 +854,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue *bar @@ -864,14 +862,14 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue LoadError end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue @@ -880,7 +878,7 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' m do bar rescue *bar => exception @@ -893,14 +891,14 @@ class Foo assert_source 'x = (foo rescue return bar)' %w(while until if).each do |keyword| - assert_source <<-RUBY + assert_source <<~RUBY #{keyword} ( foo rescue false ) end RUBY - assert_generates <<-RUBY, <<-GENERATED + assert_generates <<~RUBY, <<~GENERATED foo rescue false #{keyword} true RUBY #{keyword} true @@ -909,7 +907,7 @@ class Foo GENERATED end - assert_generates <<-'RUBY', <<-GENERATED + assert_generates <<~'RUBY', <<~GENERATED case (foo rescue false) when true end @@ -931,31 +929,31 @@ class Foo assert_source 'super(&block)' assert_source 'super(a, &block)' - assert_source <<-'RUBY' + assert_source <<~'RUBY' super(a do foo end) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' super do foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' super(a) do foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' super() do foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' super(a, b) do foo end @@ -969,7 +967,7 @@ class Foo end context 'BEGIN' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' BEGIN { foo } @@ -977,7 +975,7 @@ class Foo end context 'END' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' END { foo } @@ -985,11 +983,11 @@ class Foo end context 'alias' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' alias $foo $bar RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' alias :foo :bar RUBY end @@ -1009,19 +1007,19 @@ class Foo end context 'if statement' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' if /foo/ bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' if 3 9 end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' if 4 5 else @@ -1029,32 +1027,32 @@ class Foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' unless 3 nil end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' unless 3 9 end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' if foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo = bar if foo RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' foo = bar unless foo RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(*foo) unless foo foo = bar @@ -1062,7 +1060,7 @@ def foo(*foo) end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' each do |foo| unless foo foo = bar @@ -1074,18 +1072,18 @@ def foo(*foo) context 'def' do context 'on instance' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo foo rescue @@ -1095,7 +1093,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo ensure @@ -1103,7 +1101,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo bar ensure @@ -1111,7 +1109,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def self.foo bar rescue @@ -1119,7 +1117,7 @@ def self.foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo bar rescue @@ -1127,134 +1125,134 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar, baz) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar = ()) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar = (baz nil)) end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar = true) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar, baz = true) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar: 1) end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(*) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(*bar) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar, *baz) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(baz = true, *bor) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(baz = true, *bor, &block) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar, baz = true, *bor) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(&block) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo(bar, &block) bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo bar baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def (foo do |bar| end).bar bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def (foo(1)).bar bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def (Foo::Bar.baz).bar baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def (Foo::Bar).bar baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def Foo.bar baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def foo.bar baz end @@ -1262,25 +1260,25 @@ def foo.bar end context 'on singleton' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' def self.foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def self.foo bar end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def self.foo bar baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def Foo.bar bar end @@ -1289,43 +1287,43 @@ def Foo.bar end context 'class' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' class TestClass end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class << some_object end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class << some_object the_body end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class SomeNameSpace::TestClass end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class Some::Name::Space::TestClass end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class TestClass < Object end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class TestClass < SomeNameSpace::Object end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class TestClass def foo :bar @@ -1333,7 +1331,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' class ::TestClass end RUBY @@ -1341,22 +1339,22 @@ class ::TestClass context 'module' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' module TestModule end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' module SomeNameSpace::TestModule end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' module Some::Name::Space::TestModule end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' module TestModule def foo :bar @@ -1391,27 +1389,27 @@ def foo end context 'defined?' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' defined?(@foo) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' defined?(Foo) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' defined?((a, b = [1, 2])) RUBY end end context 'lambda' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' lambda do end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' lambda do |a, b| a end @@ -1479,7 +1477,7 @@ def foo context 'flip flops' do context 'inclusive' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' if ((i == 4)..(i == 4)) foo end @@ -1487,7 +1485,7 @@ def foo end context 'exclusive' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' if ((i == 4)...(i == 4)) foo end @@ -1496,7 +1494,7 @@ def foo end context 'case statement' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' case when bar baz @@ -1505,7 +1503,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when bar when baz @@ -1513,7 +1511,7 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when bar baz @@ -1522,21 +1520,21 @@ def foo end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when bar, baz :other end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when *bar :value end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' case foo when bar baz @@ -1547,24 +1545,24 @@ def foo end context 'for' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' bar(for a in bar do baz end) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' for a in bar do baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' for a, *b in bar do baz end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' for a, b in bar do baz end @@ -1583,7 +1581,7 @@ def foo end context 'loop' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' loop do foo end @@ -1591,26 +1589,26 @@ def foo end context 'post conditions' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' x = (begin foo end while baz) RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo end while baz RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo bar end until baz RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' begin foo bar @@ -1619,18 +1617,18 @@ def foo end context 'while' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' while false end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' while false 3 end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' while (foo do end) :body @@ -1639,18 +1637,18 @@ def foo end context 'until' do - assert_source <<-'RUBY' + assert_source <<~'RUBY' until false end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' until false 3 end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' until (foo do end) :body @@ -1658,16 +1656,16 @@ def foo RUBY end - assert_source <<-'RUBY' + assert_source <<~'RUBY' # comment before a_line_of_code RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' a_line_of_code # comment after RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' nested do # first # second something # comment @@ -1676,7 +1674,7 @@ def foo # last RUBY - assert_generates <<-'RUBY', <<-'RUBY' + assert_generates <<~'RUBY', <<~'RUBY' foo if bar # comment RUBY @@ -1686,13 +1684,13 @@ def foo # comment RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' def noop # do nothing end RUBY - assert_source <<-'RUBY' + assert_source <<~'RUBY' =begin block comment =end @@ -1707,14 +1705,14 @@ def noop end RUBY - assert_generates(<<-'RUBY', <<-'RUBY') + assert_generates(<<~'RUBY', <<~'RUBY') 1 + # first 2 # second RUBY 1 + 2 # first # second RUBY - assert_generates(<<-'RUBY', <<-'RUBY') + assert_generates(<<~'RUBY', <<~'RUBY') 1 + # first 2 # second @@ -1722,7 +1720,7 @@ def noop 1 + 2 # first # second RUBY - assert_generates(<<-'RUBY', <<-'RUBY') + assert_generates(<<~'RUBY', <<~'RUBY') 1 + =begin block comment From b142060233b0c5d366f9b4a68970a99f20f6973f Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 00:39:35 +0000 Subject: [PATCH 017/256] Remove redundant parser requires --- spec/spec_helper.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d1b1da49..83cb9b49 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -6,10 +6,6 @@ require 'devtools/spec_helper' require 'parser/current' -require 'parser/ruby19' -require 'parser/ruby20' -require 'parser/ruby21' -require 'parser/ruby22' module SpecHelper def s(type, *children) From ea9cd6f90c7d21b01c5e0cb1cdde3b870be83371 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Nov 2018 22:26:50 +0000 Subject: [PATCH 018/256] Fix CLI diff --- lib/unparser/cli/differ.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/unparser/cli/differ.rb b/lib/unparser/cli/differ.rb index b553bf42..cf6beb46 100644 --- a/lib/unparser/cli/differ.rb +++ b/lib/unparser/cli/differ.rb @@ -97,7 +97,7 @@ def collapsed_hunks # @api private # def diff - output = '' + output = +'' collapsed_hunks.each do |hunk| output << hunk.diff(:unified) << "\n" From 6c5f58bcd5b4400fab3b3616166631c9cb9a766b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 2 Dec 2018 22:34:07 +0000 Subject: [PATCH 019/256] Remove dead code --- config/flay.yml | 2 +- lib/unparser/emitter.rb | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/config/flay.yml b/config/flay.yml index c0f6a832..92c133b0 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 633 +total_score: 629 diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index df8473ec..d5d455b2 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -111,16 +111,6 @@ def write_to_buffer end memoize :write_to_buffer - # Emit node - # - # @return [self] - # - # @api private - # - def self.emit(*arguments) - new(*arguments).write_to_buffer - end - # Return emitter # # @return [Emitter] From 75ab5635374b5590e060407b9b4f21e19fbcbefc Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 2 Dec 2018 23:18:06 +0000 Subject: [PATCH 020/256] Remove redundant constant --- lib/unparser/emitter.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index d5d455b2..56063bca 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -17,8 +17,6 @@ class Emitter NOINDENT = %i[rescue ensure].to_set.freeze - DEFAULT_DELIMITER = ', '.freeze - CURLY_BRACKETS = IceNine.deep_freeze(%w[{ }]) module Unterminated From 0446cfb7caad42659ee729b1ff80691ddb8c8a4d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 2 Dec 2018 23:18:07 +0000 Subject: [PATCH 021/256] Refactor bracket constants --- lib/unparser/constants.rb | 6 ++++-- lib/unparser/emitter.rb | 2 -- lib/unparser/emitter/hookexe.rb | 2 +- lib/unparser/emitter/literal/array.rb | 5 +---- lib/unparser/emitter/send.rb | 3 --- lib/unparser/emitter/send/index.rb | 6 +++--- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/unparser/constants.rb b/lib/unparser/constants.rb index 96acca1f..faa4a5a0 100644 --- a/lib/unparser/constants.rb +++ b/lib/unparser/constants.rb @@ -22,6 +22,10 @@ def self.symbol_set(enumerable) end private_class_method :symbol_set + BRACKETS_CURLY = IceNine.deep_freeze(%w[{ }]) + BRACKETS_ROUND = IceNine.deep_freeze(%w[( )]) + BRACKETS_SQUARE = IceNine.deep_freeze(%w([ ])) + # All unary operators of the ruby language UNARY_OPERATORS = symbol_set %w[ ! ~ -@ +@ @@ -105,8 +109,6 @@ def self.symbol_set(enumerable) DEFAULT_DELIMITER = ', '.freeze - CURLY_BRACKETS = IceNine.deep_freeze(%w[{ }]) - KEYWORDS = constants.each_with_object([]) do |name, keywords| value = const_get(name).freeze next unless name.to_s.start_with?('K_') diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 56063bca..7949d954 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -17,8 +17,6 @@ class Emitter NOINDENT = %i[rescue ensure].to_set.freeze - CURLY_BRACKETS = IceNine.deep_freeze(%w[{ }]) - module Unterminated def terminated? false diff --git a/lib/unparser/emitter/hookexe.rb b/lib/unparser/emitter/hookexe.rb index bc3f72ef..bb71d1e9 100644 --- a/lib/unparser/emitter/hookexe.rb +++ b/lib/unparser/emitter/hookexe.rb @@ -24,7 +24,7 @@ class Hookexe < self # def dispatch write(MAP.fetch(node.type), WS) - parentheses(*CURLY_BRACKETS) do + parentheses(*BRACKETS_CURLY) do emit_body end end diff --git a/lib/unparser/emitter/literal/array.rb b/lib/unparser/emitter/literal/array.rb index 5fdaf530..a55345aa 100644 --- a/lib/unparser/emitter/literal/array.rb +++ b/lib/unparser/emitter/literal/array.rb @@ -6,9 +6,6 @@ class Literal # Array literal emitter class Array < self - OPEN = '['.freeze - CLOSE = ']'.freeze - handle :array private @@ -20,7 +17,7 @@ class Array < self # @api private # def dispatch - parentheses(OPEN, CLOSE) do + parentheses(*BRACKETS_SQUARE) do delimited(children) end end diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 98a7e52f..3f087056 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -8,9 +8,6 @@ class Send < self handle :send - INDEX_PARENS = IceNine.deep_freeze(%w([ ])) - NORMAL_PARENS = IceNine.deep_freeze(%w[( )]) - INDEX_REFERENCE = :'[]' INDEX_ASSIGN = :'[]=' ASSIGN_SUFFIX = '='.freeze diff --git a/lib/unparser/emitter/send/index.rb b/lib/unparser/emitter/send/index.rb index a317a5ee..c17d639d 100644 --- a/lib/unparser/emitter/send/index.rb +++ b/lib/unparser/emitter/send/index.rb @@ -42,7 +42,7 @@ class Reference < self # @api private # def emit_operation - parentheses(*INDEX_PARENS) do + parentheses(*BRACKETS_SQUARE) do delimited_plain(arguments) end end @@ -90,7 +90,7 @@ def emit_operation # @api private # def emit_mlhs_operation - parentheses(*INDEX_PARENS) do + parentheses(*BRACKETS_SQUARE) do delimited(arguments) end end @@ -102,7 +102,7 @@ def emit_mlhs_operation # @api private # def emit_normal_operation - parentheses(*INDEX_PARENS) do + parentheses(*BRACKETS_SQUARE) do delimited_plain(indices) end write(WS, T_ASN, WS) From 858ed50c256136dc056e4ad93ee9cac21c7e942d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 2 Dec 2018 23:25:47 +0000 Subject: [PATCH 022/256] Remove dead code --- lib/unparser/emitter/assignment.rb | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index dbb23434..83b2535d 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -103,8 +103,6 @@ class Multiple < self handle :masgn - PARENS = IceNine.deep_freeze(%w([ ])) - private # Emit left @@ -125,16 +123,7 @@ def emit_left # def emit_right write(WS, T_ASN, WS) - right = children.last - case right.type - when :array - children = right.children - parentheses(*PARENS) do - delimited(children) - end - else - visit(right) - end + visit(children.last) end end # Multiple From 96faef09a7ab4733de438a9c3af2928507a5e1ec Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 00:20:36 +0000 Subject: [PATCH 023/256] Remove redundant reek excludes --- config/reek.yml | 57 ++++++++----------------------------------------- 1 file changed, 9 insertions(+), 48 deletions(-) diff --git a/config/reek.yml b/config/reek.yml index 56ac7664..3826d7f6 100644 --- a/config/reek.yml +++ b/config/reek.yml @@ -11,8 +11,7 @@ detectors: exclude: [] ControlParameter: enabled: true - exclude: - - Mutant::Expression#match_length + exclude: [] DataClump: enabled: true exclude: [] @@ -31,9 +30,7 @@ detectors: exclude: [] LongParameterList: enabled: true - exclude: - - Mutant::Matcher::Method::Instance#self.build - - Mutant::Meta::Example::DSL # 3 vars + exclude: [] max_params: 2 LongYieldList: enabled: true @@ -41,55 +38,30 @@ detectors: max_params: 2 NestedIterators: enabled: true - exclude: - - Mutant#self.singleton_subclass_instance - - Mutant::CLI#parse - - Mutant::Mutator::Node::Arguments#emit_argument_mutations - - Mutant::Mutator::Node::Resbody#mutate_captures - - Mutant::Mutator::Util::Array::Element#dispatch - - Mutant::Parallel::Master#run - - Mutant::RequireHighjack#self.call - - Mutant::Selector::Expression#call - - Parser::Lexer#self.new + exclude: [] max_allowed_nesting: 1 ignore_iterators: [] NilCheck: enabled: false RepeatedConditional: enabled: true - exclude: - - Mutant::Mutator - - Mutant::Meta::Example::DSL + exclude: [] max_ifs: 1 TooManyInstanceVariables: enabled: true - exclude: - - Mutant::Mutator # 4 vars - - Mutant::Parallel::Master # 4 vars - - Mutant::Meta::Example::DSL # 4 vars + exclude: [] max_instance_variables: 3 TooManyMethods: enabled: true - exclude: - - Mutant::CLI - - Mutant::Mutator::Node - - Mutant::Parallel::Master + exclude: [] max_methods: 10 TooManyStatements: enabled: true - exclude: - - Mutant::CLI#add_debug_options - - Mutant::CLI#add_environment_options - - Mutant::Reporter::CLI::Printer::Config#run - - Mutant::Reporter::CLI::Printer::EnvProgress#run - - Mutant::Runner#run_driver - - Mutant::Zombifier::File#self.find + exclude: [] max_statements: 7 UncommunicativeMethodName: enabled: true - exclude: - - Mutant::AST::Sexp#s - - Mutant::Mutation#sha1 + exclude: [] reject: - '/^[a-z]$/' - '/[0-9]$/' @@ -123,15 +95,4 @@ detectors: exclude: [] UtilityFunction: enabled: true - exclude: - - Mutant::AST::Sexp#s - - Mutant::Actor::Env#new_mailbox - - Mutant::CLI#reporter - - Mutant::Integration::Null#call - - Mutant::Integration::Rspec#parse_example - - Mutant::Integration::Rspec#parse_expression # intentional, private - - Mutant::Meta::Example::Verification#format_mutations # intentional, private - - Mutant::Reporter::CLI::Format::Progressive#new_buffer - - Mutant::Reporter::CLI::Printer::StatusProgressive#object # False positive calls super - - Mutant::Repository::Diff#tracks? # intentional, private - - Mutant::Repository::Diff#within_working_directory? # intentional, private + exclude: [] From 1a3f0d32128288e8e6cc8892ca296fe30789c398 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 10:10:55 +0000 Subject: [PATCH 024/256] Refactor arguments emitter --- config/flay.yml | 2 +- lib/unparser.rb | 1 - lib/unparser/emitter/send.rb | 35 ++++++++++++++++++-- lib/unparser/emitter/send/arguments.rb | 45 -------------------------- 4 files changed, 33 insertions(+), 50 deletions(-) delete mode 100644 lib/unparser/emitter/send/arguments.rb diff --git a/config/flay.yml b/config/flay.yml index 3ccd025c..0e19ad1d 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 640 +total_score: 650 diff --git a/lib/unparser.rb b/lib/unparser.rb index baa11f6b..18fd26a9 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -117,7 +117,6 @@ def self.buffer(source) require 'unparser/emitter/send/binary' require 'unparser/emitter/send/regular' require 'unparser/emitter/send/conditional' -require 'unparser/emitter/send/arguments' require 'unparser/emitter/send/attribute_assignment' require 'unparser/emitter/block' require 'unparser/emitter/assignment' diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 3dc12e72..50298970 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -159,10 +159,39 @@ def arguments # @api private # def emit_arguments - if arguments.empty? && receiver.nil? && local_variable_clash? - write('()') + if arguments.empty? + write('()') if local_variable_clash? else - run(Arguments, n(:arguments, arguments)) + normal_arguments + end + end + + # Emit normal arguments + # + # @return [undefined] + # + # @api private + # + def normal_arguments + parentheses do + delimited_plain(effective_arguments) + end + end + + # The effective arguments + # + # @return [Parser::AST::Node] + # + # @api private + # + def effective_arguments + last = arguments.length - 1 + arguments.each_with_index.map do |argument, index| + if last.equal?(index) && argument.type.equal?(:hash) && argument.children.any? + argument.updated(:hash_body) + else + argument + end end end diff --git a/lib/unparser/emitter/send/arguments.rb b/lib/unparser/emitter/send/arguments.rb deleted file mode 100644 index d4fd5ae7..00000000 --- a/lib/unparser/emitter/send/arguments.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Send - # Emitter for arguments of send nodes - class Arguments < Emitter - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - return if children.empty? - - parentheses do - delimited_plain(effective_arguments) - end - end - - # Return effective arguments - # - # @return [Parser::AST::Node] - # - # @api private - # - def effective_arguments - last = children.length - 1 - children.each_with_index.map do |argument, index| - if last.equal?(index) && argument.type.equal?(:hash) && argument.children.any? - argument.updated(:hash_body) - else - argument - end - end - end - - end # Arguments - end # Send - end # Emitter -end # Unparser From 379f8833ae2aa4df9bee66dc3fd83eb03e59bc98 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 10:40:18 +0000 Subject: [PATCH 025/256] Refactor array into set --- config/flay.yml | 2 +- lib/unparser/ast.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/config/flay.yml b/config/flay.yml index 3a0feb10..6a1f83de 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 646 +total_score: 647 diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 1ce3a919..e6a03f94 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -16,7 +16,14 @@ module AST # # FIXME: Kwargs are missing. # - ASSIGN_NODES = %i[arg lvasgn optarg procarg0 restarg].freeze + ASSIGN_NODES = + %i[ + arg + lvasgn + optarg + procarg0 + restarg + ].to_set.freeze # Test for local variable inherited scope reset # From 811051d30d0e335b36ca2e17e13f4571c2360c63 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 10:41:51 +0000 Subject: [PATCH 026/256] Fix kwoptarg to introduce an lvar [fix #83] --- config/flay.yml | 2 +- lib/unparser/ast.rb | 5 ++--- spec/unit/unparser_spec.rb | 10 ++++++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config/flay.yml b/config/flay.yml index 6a1f83de..19f247bc 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 647 +total_score: 645 diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index e6a03f94..2f598047 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -13,12 +13,11 @@ module AST CLOSE_NODES = (RESET_NODES + INHERIT_NODES).freeze # Nodes that assign a local variable - # - # FIXME: Kwargs are missing. - # ASSIGN_NODES = %i[ arg + kwarg + kwoptarg lvasgn optarg procarg0 diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 5fb27993..785c7167 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -1243,6 +1243,16 @@ def foo(bar: 1) end RUBY + assert_source <<~'RUBY' + def foo(bar: bar) + end + RUBY + + assert_source <<~'RUBY' + def foo(bar: bar()) + end + RUBY + assert_source <<~'RUBY' def foo(*) bar From fc3a3cad243ef5620495edceed4fc20fcc32d3ad Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 10:45:37 +0000 Subject: [PATCH 027/256] Upgrade gem dependencies --- Gemfile | 2 +- Gemfile.lock | 27 +++++++++++++-------------- config/rubocop.yml | 4 ++++ lib/unparser/emitter/argument.rb | 2 +- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Gemfile b/Gemfile index a8b83f2b..b6143c44 100644 --- a/Gemfile +++ b/Gemfile @@ -4,4 +4,4 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'change/to-modern-ast' +gem 'mutant', git: 'https://github.com/mbj/mutant' diff --git a/Gemfile.lock b/Gemfile.lock index 697184f0..aaf3861f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,6 @@ GIT remote: https://github.com/mbj/mutant - revision: 6b36372385ec1bedc0d170470e65d7d33740769e - branch: change/to-modern-ast + revision: fd6bfe89c6be85433d116b62d1b2407af3ff7804 specs: mutant (0.8.20) abstract_type (~> 0.0.7) @@ -17,7 +16,7 @@ GIT parser (~> 2.5.1) procto (~> 0.0.2) regexp_parser (~> 1.2) - unparser (~> 0.3.0) + unparser (~> 0.4.0) mutant-rspec (0.8.20) mutant (~> 0.8.20) rspec-core (>= 3.4.0, < 4.0.0) @@ -59,22 +58,22 @@ GEM equalizer (~> 0.0.9) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.21) + devtools (0.1.22) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) concord (~> 0.1.5) - flay (~> 2.10.0) - flog (~> 4.6.1) - mutant (~> 0.8.16) - mutant-rspec (~> 0.8.16) + flay (~> 2.12.0) + flog (~> 4.6.2) + mutant (~> 0.8.19) + mutant-rspec (~> 0.8.19) procto (~> 0.0.3) rake (~> 12.3.0) - reek (~> 5.0.2) + reek (~> 5.2.0) rspec (~> 3.8.0) rspec-core (~> 3.8.0) rspec-its (~> 1.2.0) - rubocop (~> 0.59.0) + rubocop (~> 0.60.0) simplecov (~> 0.16.1) yard (~> 0.9.16) yardstick (~> 0.9.9) @@ -82,7 +81,7 @@ GEM docile (1.3.1) equalizer (0.0.11) erubis (2.7.0) - flay (2.10.0) + flay (2.12.0) erubis (~> 2.7.0) path_expander (~> 1.0) ruby_parser (~> 3.0) @@ -114,7 +113,7 @@ GEM procto (0.0.3) rainbow (3.0.0) rake (12.3.1) - reek (5.0.2) + reek (5.2.0) codeclimate-engine-rb (~> 0.4.0) kwalify (~> 0.7.0) parser (>= 2.5.0.0, < 2.6, != 2.5.1.1) @@ -136,14 +135,14 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) - rubocop (0.59.2) + rubocop (0.60.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.5, != 2.5.1.1) powerpack (~> 0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.0, >= 1.0.1) + unicode-display_width (~> 1.4.0) ruby-progressbar (1.10.0) ruby_parser (3.11.0) sexp_processor (~> 4.9) diff --git a/config/rubocop.yml b/config/rubocop.yml index c73cd291..eb0647aa 100644 --- a/config/rubocop.yml +++ b/config/rubocop.yml @@ -116,3 +116,7 @@ Lint/BooleanSymbol: Style/AccessModifierDeclarations: Enabled: false + +Layout/AlignHash: + EnforcedColonStyle: table + EnforcedHashRocketStyle: table diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index c8c85585..fefcdf61 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -100,7 +100,7 @@ class Morearg < self include Terminated MAP = { - blockarg: T_AMP, + blockarg: T_AMP, kwrestarg: T_DSPLAT }.freeze From 104c5b6efcd58dab3fd17b10315c3253e3848635 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 Dec 2018 11:09:46 +0000 Subject: [PATCH 028/256] Change version to 0.4.1 --- Changelog.md | 6 +++++- unparser.gemspec | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 888fc070..a01647c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,8 @@ -# v0.4.0 2018-12-01 +# v0.4.0 2018-12-03 + +* Fix unparsing of `def foo(bar: bar())` + +# v0.4.0 2018-12-03 * Change to modern AST format. * Add experimental `Unparser.{parser,parse,parse_with_comments}` diff --git a/unparser.gemspec b/unparser.gemspec index 32b098fc..0d594f0c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.0' + gem.version = '0.4.1' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 4886c008d22ec1b5a05a95b2eea57ade7ad7269c Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 4 Dec 2018 13:10:58 +0000 Subject: [PATCH 029/256] Remove ruby version requirement in gemspec --- Changelog.md | 6 +++++- Gemfile.lock | 2 +- unparser.gemspec | 4 +--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index a01647c6..e31a277d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,8 @@ -# v0.4.0 2018-12-03 +# v0.4.2 2018-12-04 + +* Drop hard ruby version requirement. Still officially I'll only support 2.5. + +# v0.4.1 2018-12-03 * Fix unparsing of `def foo(bar: bar())` diff --git a/Gemfile.lock b/Gemfile.lock index aaf3861f..f9708e34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,7 +24,7 @@ GIT PATH remote: . specs: - unparser (0.4.0) + unparser (0.4.2) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) diff --git a/unparser.gemspec b/unparser.gemspec index 0d594f0c..d93daeb2 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.1' + gem.version = '0.4.2' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -16,8 +16,6 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 2.5' - gem.add_dependency('abstract_type', '~> 0.0.7') gem.add_dependency('adamantium', '~> 0.2.0') gem.add_dependency('equalizer', '~> 0.0.9') From 280fbefc6584b556fe3e0aab62f591cae715c784 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 30 Dec 2018 23:43:39 +0000 Subject: [PATCH 030/256] Change mutant dependency to released version * The tie breaker is not required anymore --- Gemfile | 2 -- Gemfile.lock | 44 +++++++++++++++++++------------------------- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/Gemfile b/Gemfile index b6143c44..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -3,5 +3,3 @@ source 'https://rubygems.org' gemspec - -gem 'mutant', git: 'https://github.com/mbj/mutant' diff --git a/Gemfile.lock b/Gemfile.lock index f9708e34..28b485cf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,26 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant - revision: fd6bfe89c6be85433d116b62d1b2407af3ff7804 - specs: - mutant (0.8.20) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.0) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - morpher (~> 0.2.6) - parser (~> 2.5.1) - procto (~> 0.0.2) - regexp_parser (~> 1.2) - unparser (~> 0.4.0) - mutant-rspec (0.8.20) - mutant (~> 0.8.20) - rspec-core (>= 3.4.0, < 4.0.0) - PATH remote: . specs: @@ -105,6 +82,24 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) + mutant (0.8.24) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.0) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + morpher (~> 0.2.6) + parser (~> 2.5.1) + procto (~> 0.0.2) + regexp_parser (~> 1.2) + unparser (~> 0.4.2) + mutant-rspec (0.8.24) + mutant (~> 0.8.24) + rspec-core (>= 3.4.0, < 4.0.0) parallel (1.12.1) parser (2.5.3.0) ast (~> 2.4.0) @@ -170,8 +165,7 @@ DEPENDENCIES anima (~> 0.3.0) devtools (~> 0.1.21) morpher (~> 0.2.6) - mutant! unparser! BUNDLED WITH - 1.17.1 + 1.17.3 From 529a5d7f76129158cef64c121c0ff987538cd4e8 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Fri, 11 Jan 2019 13:00:34 -0500 Subject: [PATCH 031/256] Unpaser -> Unparser in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a099ff80..94542735 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Usage require 'parser/current' require 'unparser' -ast = Unpaser.parse('your(ruby(code))') +ast = Unparser.parse('your(ruby(code))') Unparser.unparse(ast) # => 'your(ruby(code))' ``` @@ -41,7 +41,7 @@ To preserve the comments from the source: require 'parser/current' require 'unparser' -ast, comments = Unpaser.parse_with_comments('your(ruby(code)) # with comments') +ast, comments = Unparser.parse_with_comments('your(ruby(code)) # with comments') Unparser.unparse(ast, comments) # => 'your(ruby(code)) # with comments' ``` From 31b095c4f8109e0ac5b1abe2df4b054419aac700 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 15 Feb 2019 13:47:29 +0000 Subject: [PATCH 032/256] Upgrade anima to 0.3.1 --- Gemfile.lock | 4 ++-- unparser.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 28b485cf..8f8f2c5f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,7 +17,7 @@ GEM adamantium (0.2.0) ice_nine (~> 0.11.0) memoizable (~> 0.4.0) - anima (0.3.0) + anima (0.3.1) abstract_type (~> 0.0.7) adamantium (~> 0.2) equalizer (~> 0.0.11) @@ -162,7 +162,7 @@ PLATFORMS ruby DEPENDENCIES - anima (~> 0.3.0) + anima (~> 0.3.1) devtools (~> 0.1.21) morpher (~> 0.2.6) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index d93daeb2..a8c8157a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.3.1.2', '< 2.6') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('anima', '~> 0.3.0') + gem.add_development_dependency('anima', '~> 0.3.1') gem.add_development_dependency('devtools', '~> 0.1.21') gem.add_development_dependency('morpher', '~> 0.2.6') end From 6a46b7fe75443908d39a2bd1d56cb834d5e943c1 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 23 Feb 2019 22:18:54 +0000 Subject: [PATCH 033/256] Upgrade devtools --- Gemfile.lock | 32 +++++++++++++++++--------------- unparser.gemspec | 2 +- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 8f8f2c5f..51989fd9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -35,22 +35,22 @@ GEM equalizer (~> 0.0.9) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.22) + devtools (0.1.23) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) concord (~> 0.1.5) flay (~> 2.12.0) flog (~> 4.6.2) - mutant (~> 0.8.19) - mutant-rspec (~> 0.8.19) + mutant (~> 0.8.24) + mutant-rspec (~> 0.8.24) procto (~> 0.0.3) rake (~> 12.3.0) - reek (~> 5.2.0) + reek (~> 5.3.0) rspec (~> 3.8.0) rspec-core (~> 3.8.0) rspec-its (~> 1.2.0) - rubocop (~> 0.60.0) + rubocop (~> 0.61.1) simplecov (~> 0.16.1) yard (~> 0.9.16) yardstick (~> 0.9.9) @@ -68,8 +68,8 @@ GEM ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.8) ice_nine (0.11.2) - jaro_winkler (1.5.1) - json (2.1.0) + jaro_winkler (1.5.2) + json (2.2.0) kwalify (0.7.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) @@ -100,18 +100,20 @@ GEM mutant-rspec (0.8.24) mutant (~> 0.8.24) rspec-core (>= 3.4.0, < 4.0.0) - parallel (1.12.1) + parallel (1.13.0) parser (2.5.3.0) ast (~> 2.4.0) path_expander (1.0.3) powerpack (0.1.2) procto (0.0.3) + psych (3.1.0) rainbow (3.0.0) - rake (12.3.1) - reek (5.2.0) + rake (12.3.2) + reek (5.3.1) codeclimate-engine-rb (~> 0.4.0) kwalify (~> 0.7.0) - parser (>= 2.5.0.0, < 2.6, != 2.5.1.1) + parser (>= 2.5.0.0, < 2.7, != 2.5.1.1) + psych (~> 3.1.0) rainbow (>= 2.0, < 4.0) regexp_parser (1.3.0) rspec (3.8.0) @@ -130,7 +132,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-support (3.8.0) - rubocop (0.60.0) + rubocop (0.61.1) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.5, != 2.5.1.1) @@ -139,7 +141,7 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.4.0) ruby-progressbar (1.10.0) - ruby_parser (3.11.0) + ruby_parser (3.12.0) sexp_processor (~> 4.9) sexp_processor (4.11.0) simplecov (0.16.1) @@ -148,13 +150,13 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.2) thread_safe (0.3.6) - unicode-display_width (1.4.0) + unicode-display_width (1.4.1) virtus (1.0.5) axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - yard (0.9.16) + yard (0.9.18) yardstick (0.9.9) yard (~> 0.8, >= 0.8.7.2) diff --git a/unparser.gemspec b/unparser.gemspec index a8c8157a..5a7d194f 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -25,6 +25,6 @@ Gem::Specification.new do |gem| gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') - gem.add_development_dependency('devtools', '~> 0.1.21') + gem.add_development_dependency('devtools', '~> 0.1.23') gem.add_development_dependency('morpher', '~> 0.2.6') end From 467ab86b2f8521e94d55bbce7096d3455bdb676a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 24 Feb 2019 20:30:02 +0000 Subject: [PATCH 034/256] Upgrade parser dependency --- .circleci/config.yml | 2 +- Gemfile | 6 ++++++ Gemfile.lock | 49 ++++++++++++++++++++++++-------------------- config/mutant.yml | 7 +++++-- unparser.gemspec | 2 +- 5 files changed, 40 insertions(+), 26 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f21ac00..23e7d207 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,7 @@ jobs: steps: - checkout - run: bundle install - - run: bundle exec rake metrics:mutant + - run: bundle exec mutant --since origin/master --zombie -- 'Unparser*' workflows: version: 2 test: diff --git a/Gemfile b/Gemfile index 7f4f5e95..6ca58bd4 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,9 @@ source 'https://rubygems.org' gemspec + +gem( + 'mutant', + branch: 'upgrade/dependencies', + git: 'https://github.com/mbj/mutant.git' +) diff --git a/Gemfile.lock b/Gemfile.lock index 51989fd9..6483c559 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,26 @@ +GIT + remote: https://github.com/mbj/mutant.git + revision: d1d1993427ec01cc9b57a3a87ce23a7c41456a39 + branch: upgrade/dependencies + specs: + mutant (0.8.25) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.1) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + morpher (~> 0.2.6) + parser (~> 2.6.0) + procto (~> 0.0.2) + unparser (~> 0.4.2) + mutant-rspec (0.8.25) + mutant (~> 0.8.25) + rspec-core (>= 3.8.0, < 4.0.0) + PATH remote: . specs: @@ -7,7 +30,7 @@ PATH concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (>= 2.3.1.2, < 2.6) + parser (~> 2.6.0) procto (~> 0.0.2) GEM @@ -82,26 +105,8 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) - mutant (0.8.24) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.0) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - morpher (~> 0.2.6) - parser (~> 2.5.1) - procto (~> 0.0.2) - regexp_parser (~> 1.2) - unparser (~> 0.4.2) - mutant-rspec (0.8.24) - mutant (~> 0.8.24) - rspec-core (>= 3.4.0, < 4.0.0) parallel (1.13.0) - parser (2.5.3.0) + parser (2.6.0.0) ast (~> 2.4.0) path_expander (1.0.3) powerpack (0.1.2) @@ -115,7 +120,6 @@ GEM parser (>= 2.5.0.0, < 2.7, != 2.5.1.1) psych (~> 3.1.0) rainbow (>= 2.0, < 4.0) - regexp_parser (1.3.0) rspec (3.8.0) rspec-core (~> 3.8.0) rspec-expectations (~> 3.8.0) @@ -165,8 +169,9 @@ PLATFORMS DEPENDENCIES anima (~> 0.3.1) - devtools (~> 0.1.21) + devtools (~> 0.1.23) morpher (~> 0.2.6) + mutant! unparser! BUNDLED WITH diff --git a/config/mutant.yml b/config/mutant.yml index 42a64793..8c4cfe43 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -1,3 +1,6 @@ --- -name: unparser -namespace: Unparser +includes: +- lib +integration: rspec +requires: +- unparser diff --git a/unparser.gemspec b/unparser.gemspec index 5a7d194f..9a49710f 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('parser', '>= 2.3.1.2', '< 2.6') + gem.add_dependency('parser', '~> 2.6.0') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') From 45f9fd5f5c839604da70cea62650691b82da5b91 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 24 Feb 2019 20:49:28 +0000 Subject: [PATCH 035/256] Bump version to 0.4.3 --- Changelog.md | 4 ++++ unparser.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e31a277d..a1ba0c22 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.4.3 2019-02-24 + +* Bump parser dependency to ~> 2.6.0 + # v0.4.2 2018-12-04 * Drop hard ruby version requirement. Still officially I'll only support 2.5. diff --git a/unparser.gemspec b/unparser.gemspec index 9a49710f..783801c3 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.2' + gem.version = '0.4.3' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 3698d0eb642996132211f2ba28fce2c6de1bd035 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 5 Mar 2019 13:12:10 +0000 Subject: [PATCH 036/256] Upgrade mutant --- Gemfile | 6 +----- Gemfile.lock | 8 +++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Gemfile b/Gemfile index 6ca58bd4..eab1a186 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,4 @@ source 'https://rubygems.org' gemspec -gem( - 'mutant', - branch: 'upgrade/dependencies', - git: 'https://github.com/mbj/mutant.git' -) +gem('mutant', git: 'https://github.com/mbj/mutant.git') diff --git a/Gemfile.lock b/Gemfile.lock index 6483c559..a0c525ff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,6 @@ GIT remote: https://github.com/mbj/mutant.git - revision: d1d1993427ec01cc9b57a3a87ce23a7c41456a39 - branch: upgrade/dependencies + revision: 2b7791503fb12827afdf3397cef0db490a176134 specs: mutant (0.8.25) abstract_type (~> 0.0.7) @@ -13,10 +12,9 @@ GIT equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) - morpher (~> 0.2.6) parser (~> 2.6.0) procto (~> 0.0.2) - unparser (~> 0.4.2) + unparser (~> 0.4.3) mutant-rspec (0.8.25) mutant (~> 0.8.25) rspec-core (>= 3.8.0, < 4.0.0) @@ -24,7 +22,7 @@ GIT PATH remote: . specs: - unparser (0.4.2) + unparser (0.4.3) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) From da833f1931e8d20ee621a4cf7451c74cf343924c Mon Sep 17 00:00:00 2001 From: Filipp Pirozhkov Date: Thu, 21 Mar 2019 19:06:46 +0700 Subject: [PATCH 037/256] Upgrade parser dependency Recently, `parser` 2.6.2 came out with support for recently released Ruby 2.5.5 --- .circleci/config.yml | 2 +- Gemfile.lock | 4 ++-- unparser.gemspec | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23e7d207..34141e99 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ defaults: &defaults working_directory: ~/unparser docker: - - image: circleci/ruby:2.5.3 + - image: circleci/ruby:2.5.5 version: 2 jobs: unit_specs: diff --git a/Gemfile.lock b/Gemfile.lock index a0c525ff..e3251d69 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -28,7 +28,7 @@ PATH concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (~> 2.6.0) + parser (~> 2.6.2) procto (~> 0.0.2) GEM @@ -104,7 +104,7 @@ GEM ice_nine (~> 0.11.0) procto (~> 0.0.2) parallel (1.13.0) - parser (2.6.0.0) + parser (2.6.2.0) ast (~> 2.4.0) path_expander (1.0.3) powerpack (0.1.2) diff --git a/unparser.gemspec b/unparser.gemspec index 783801c3..a1c6d8ca 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('parser', '~> 2.6.0') + gem.add_dependency('parser', '~> 2.6.2') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') From 33ef472832c29aa728472b1a06838312ca4d18de Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 27 Mar 2019 08:47:27 +0000 Subject: [PATCH 038/256] Upgrade version to 0.4.4 --- Changelog.md | 4 ++++ unparser.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index a1ba0c22..d36af15c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.4.4 2019-03-27 + +* Bump parser dependency to ~> 2.6.2 + # v0.4.3 2019-02-24 * Bump parser dependency to ~> 2.6.0 diff --git a/unparser.gemspec b/unparser.gemspec index a1c6d8ca..b5ae6666 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.3' + gem.version = '0.4.4' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 07906f1d3d08c249474d708207a0618870b94efd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 9 May 2019 22:10:49 +0000 Subject: [PATCH 039/256] Upgrade parser dependency --- Gemfile | 6 +++++- Gemfile.lock | 24 ++++++++++++++---------- unparser.gemspec | 2 +- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index eab1a186..1615cc5d 100644 --- a/Gemfile +++ b/Gemfile @@ -4,4 +4,8 @@ source 'https://rubygems.org' gemspec -gem('mutant', git: 'https://github.com/mbj/mutant.git') +gem 'mutant', git: 'https://github.com/mbj/mutant.git' + +source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do + gem 'mutant-license' +end diff --git a/Gemfile.lock b/Gemfile.lock index e3251d69..ee2f19f0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/mbj/mutant.git - revision: 2b7791503fb12827afdf3397cef0db490a176134 + revision: e07953eeb3a011d9c3496d6594fed263375fec59 specs: mutant (0.8.25) abstract_type (~> 0.0.7) @@ -12,6 +12,7 @@ GIT equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) + mutant-license (~> 0.0.1) parser (~> 2.6.0) procto (~> 0.0.2) unparser (~> 0.4.3) @@ -22,17 +23,18 @@ GIT PATH remote: . specs: - unparser (0.4.3) + unparser (0.4.4) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (~> 2.6.2) + parser (~> 2.6.3) procto (~> 0.0.2) GEM remote: https://rubygems.org/ + remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ specs: abstract_type (0.0.7) adamantium (0.2.0) @@ -103,8 +105,9 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) - parallel (1.13.0) - parser (2.6.2.0) + mutant-license (0.0.1) + parallel (1.17.0) + parser (2.6.3.0) ast (~> 2.4.0) path_expander (1.0.3) powerpack (0.1.2) @@ -112,7 +115,7 @@ GEM psych (3.1.0) rainbow (3.0.0) rake (12.3.2) - reek (5.3.1) + reek (5.3.2) codeclimate-engine-rb (~> 0.4.0) kwalify (~> 0.7.0) parser (>= 2.5.0.0, < 2.7, != 2.5.1.1) @@ -124,7 +127,7 @@ GEM rspec-mocks (~> 3.8.0) rspec-core (3.8.0) rspec-support (~> 3.8.0) - rspec-expectations (3.8.2) + rspec-expectations (3.8.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-its (1.2.0) @@ -143,9 +146,9 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (~> 1.4.0) ruby-progressbar (1.10.0) - ruby_parser (3.12.0) + ruby_parser (3.13.1) sexp_processor (~> 4.9) - sexp_processor (4.11.0) + sexp_processor (4.12.0) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -158,7 +161,7 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - yard (0.9.18) + yard (0.9.19) yardstick (0.9.9) yard (~> 0.8, >= 0.8.7.2) @@ -170,6 +173,7 @@ DEPENDENCIES devtools (~> 0.1.23) morpher (~> 0.2.6) mutant! + mutant-license! unparser! BUNDLED WITH diff --git a/unparser.gemspec b/unparser.gemspec index b5ae6666..980d3763 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('parser', '~> 2.6.2') + gem.add_dependency('parser', '~> 2.6.3') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') From ee601ecbf7880d06cc00b683b8cfb57433027db2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 10 May 2019 11:50:46 +0000 Subject: [PATCH 040/256] Change version to 0.4.5 --- Changelog.md | 4 ++++ unparser.gemspec | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d36af15c..ca0072c2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.4.5 2019-05-10 + +* Bump parser dependency to ~> 2.6.3 + # v0.4.4 2019-03-27 * Bump parser dependency to ~> 2.6.2 diff --git a/unparser.gemspec b/unparser.gemspec index 980d3763..3d3a41db 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.4' + gem.version = '0.4.5' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 9c4dbb6c45c8d8d9e99470419379b34044e43332 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 1 Jan 2020 00:18:12 +0000 Subject: [PATCH 041/256] Add rubyspec excludes --- spec/integrations.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/integrations.yml b/spec/integrations.yml index 025f288c..e87ff544 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -40,10 +40,12 @@ - core/env/element_reference_spec.rb - core/io/readpartial_spec.rb - core/io/shared/gets_ascii.rb + - core/kernel/shared/sprintf_encoding.rb - core/marshal/dump_spec.rb - core/marshal/fixtures/marshal_data.rb - core/marshal/shared/load.rb - core/random/bytes_spec.rb + - core/regexp/shared/new.rb - core/regexp/shared/new_ascii.rb - core/regexp/shared/new_ascii_8bit.rb - core/regexp/shared/quote.rb @@ -51,6 +53,8 @@ - core/string/casecmp_spec.rb - core/string/codepoints_spec.rb - core/string/count_spec.rb + - core/string/encode_spec.rb + - core/string/inspect_spec.rb - core/string/shared/codepoints.rb - core/string/shared/each_codepoint_without_block.rb - core/string/shared/eql.rb @@ -71,6 +75,7 @@ - language/regexp/escapes_spec.rb - language/source_encoding_spec.rb - language/string_spec.rb + - library/base64/decode64_spec.rb - library/digest/md5/shared/constants.rb - library/digest/md5/shared/sample.rb - library/digest/sha1/shared/constants.rb From ef7b595d7eff43dd0a3472f77a9c17b6969aa30b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 1 Jan 2020 00:18:13 +0000 Subject: [PATCH 042/256] Upgrade gem dependencies --- Gemfile.lock | 53 ++++++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ee2f19f0..a09404c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ GIT remote: https://github.com/mbj/mutant.git - revision: e07953eeb3a011d9c3496d6594fed263375fec59 + revision: 97d44b4d3f410fd6b8e0d547ed1fa4a2dfc05191 specs: - mutant (0.8.25) + mutant (0.9.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -12,18 +12,15 @@ GIT equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) - mutant-license (~> 0.0.1) - parser (~> 2.6.0) + mutant-license (~> 0.1.0) + parser (= 2.6.3) procto (~> 0.0.2) - unparser (~> 0.4.3) - mutant-rspec (0.8.25) - mutant (~> 0.8.25) - rspec-core (>= 3.8.0, < 4.0.0) + unparser (~> 0.4.5) PATH remote: . specs: - unparser (0.4.4) + unparser (0.4.5) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) @@ -58,15 +55,13 @@ GEM equalizer (~> 0.0.9) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.23) + devtools (0.1.24) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) concord (~> 0.1.5) flay (~> 2.12.0) flog (~> 4.6.2) - mutant (~> 0.8.24) - mutant-rspec (~> 0.8.24) procto (~> 0.0.3) rake (~> 12.3.0) reek (~> 5.3.0) @@ -78,21 +73,21 @@ GEM yard (~> 0.9.16) yardstick (~> 0.9.9) diff-lcs (1.3) - docile (1.3.1) + docile (1.3.2) equalizer (0.0.11) erubis (2.7.0) - flay (2.12.0) + flay (2.12.1) erubis (~> 2.7.0) path_expander (~> 1.0) ruby_parser (~> 3.0) sexp_processor (~> 4.0) - flog (4.6.2) + flog (4.6.4) path_expander (~> 1.0) ruby_parser (~> 3.1, > 3.1.0) sexp_processor (~> 4.8) ice_nine (0.11.2) - jaro_winkler (1.5.2) - json (2.2.0) + jaro_winkler (1.5.4) + json (2.3.0) kwalify (0.7.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) @@ -105,16 +100,16 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) - mutant-license (0.0.1) - parallel (1.17.0) + mutant-license (0.1.0) + parallel (1.19.1) parser (2.6.3.0) ast (~> 2.4.0) - path_expander (1.0.3) + path_expander (1.1.0) powerpack (0.1.2) procto (0.0.3) psych (3.1.0) rainbow (3.0.0) - rake (12.3.2) + rake (12.3.3) reek (5.3.2) codeclimate-engine-rb (~> 0.4.0) kwalify (~> 0.7.0) @@ -125,18 +120,18 @@ GEM rspec-core (~> 3.8.0) rspec-expectations (~> 3.8.0) rspec-mocks (~> 3.8.0) - rspec-core (3.8.0) + rspec-core (3.8.2) rspec-support (~> 3.8.0) - rspec-expectations (3.8.3) + rspec-expectations (3.8.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.8.0) + rspec-mocks (3.8.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) - rspec-support (3.8.0) + rspec-support (3.8.3) rubocop (0.61.1) jaro_winkler (~> 1.5.1) parallel (~> 1.10) @@ -145,10 +140,10 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.4.0) - ruby-progressbar (1.10.0) - ruby_parser (3.13.1) + ruby-progressbar (1.10.1) + ruby_parser (3.14.1) sexp_processor (~> 4.9) - sexp_processor (4.12.0) + sexp_processor (4.13.0) simplecov (0.16.1) docile (~> 1.1) json (>= 1.8, < 3) @@ -161,7 +156,7 @@ GEM coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - yard (0.9.19) + yard (0.9.22) yardstick (0.9.9) yard (~> 0.8, >= 0.8.7.2) From f93a8595985be497b752196c4859d20a55e4653a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 1 Jan 2020 00:42:00 +0000 Subject: [PATCH 043/256] Add Unparser::AST::LocalVariableScope mutant exclusion --- .circleci/config.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 34141e99..f221ff60 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -30,7 +30,15 @@ jobs: steps: - checkout - run: bundle install - - run: bundle exec mutant --since origin/master --zombie -- 'Unparser*' + - run: | + bundle \ + exec \ + mutant \ + --ignore-subject 'Unparser::AST::LocalVariableScope*' \ + --since origin/master \ + --zombie \ + -- \ + 'Unparser*' workflows: version: 2 test: From dccfd003f722b6f37379e200f6b10e260ee761aa Mon Sep 17 00:00:00 2001 From: Olle Jonsson Date: Wed, 1 Jan 2020 00:42:01 +0000 Subject: [PATCH 044/256] Fix YARD warnings --- lib/unparser/ast.rb | 2 +- lib/unparser/ast/local_variable_scope.rb | 12 ++++++------ lib/unparser/cli.rb | 2 +- lib/unparser/cli/source.rb | 4 ++-- lib/unparser/constants.rb | 2 +- lib/unparser/dsl.rb | 2 +- lib/unparser/emitter.rb | 6 ++---- lib/unparser/emitter/literal/primitive.rb | 2 +- lib/unparser/node_helpers.rb | 6 ++++-- lib/unparser/preprocessor.rb | 2 +- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 2f598047..61ec3f99 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -50,7 +50,7 @@ def self.not_reset_scope?(node) # Return local variables that get assigned in scope # - # @param [Parser::AST::Node] + # @param [Parser::AST::Node] node # # @return [Set] # diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index 0e27acee..c4e3c88e 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -56,8 +56,8 @@ def local_variable_defined_for_node?(node, name) # Test if local variables where first assigned in body and read by conditional # - # @param [Parser::AST::Node] conditional # @param [Parser::AST::Node] body + # @param [Parser::AST::Node] condition # # @api private # @@ -78,16 +78,16 @@ def first_assignment_in_body_and_used_in_condition?(body, condition) # Match node # - # @param [Parser::AST::Node] node + # @param [Parser::AST::Node] needle # if block given # # @return [Boolean] # # @api private # - def match(neddle) + def match(needle) @items.each do |node, current, before| - return yield(current, before) if node.equal?(neddle) + return yield(current, before) if node.equal?(needle) end false end @@ -149,7 +149,7 @@ def current # Visit node and record local variable state # - # @param [Parser::AST::Node] + # @param [Parser::AST::Node] node # # @return [undefined] # @@ -168,7 +168,7 @@ def visit(node, &block) # Record local variable state # - # @param [Parser::AST::Node] + # @param [Parser::AST::Node] node # # @return [undefined] # diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 515811eb..a5b1c155 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -108,7 +108,7 @@ def exit_status # Process source # - # @param [CLI::Source] + # @param [CLI::Source] source # # @return [undefined] # diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb index 16a7f2c9..7195960d 100644 --- a/lib/unparser/cli/source.rb +++ b/lib/unparser/cli/source.rb @@ -24,7 +24,7 @@ def success? # Build generated source # - # @param [Parser::AST::Node] + # @param [Parser::AST::Node] ast # # @api private # @@ -82,7 +82,7 @@ def generated # Return stripped source # - # @param [String] string + # @param [String] source # # @return [String] # diff --git a/lib/unparser/constants.rb b/lib/unparser/constants.rb index faa4a5a0..eb789e0b 100644 --- a/lib/unparser/constants.rb +++ b/lib/unparser/constants.rb @@ -11,7 +11,7 @@ module Constants # Return frozen symbol set from enumerable # - # @param [Enumerable] + # @param [Enumerable] enumerable # # @return [Set] # diff --git a/lib/unparser/dsl.rb b/lib/unparser/dsl.rb index 99e8f499..77eb2204 100644 --- a/lib/unparser/dsl.rb +++ b/lib/unparser/dsl.rb @@ -45,7 +45,7 @@ def define_child(name, index) # # @return [undefined] # - # @pai private + # @api private # def define_group(name, range) define_method(name) do diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 7949d954..8aa9c3ca 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -79,7 +79,7 @@ def node_type # Register emitter for type # - # @param [Symbol] type + # @param [Symbol] types # # @return [undefined] # @@ -249,7 +249,6 @@ def emitter(node) # Emit delimited body # # @param [Enumerable] nodes - # @param [String] delimiter # # @return [undefined] # @@ -262,7 +261,6 @@ def delimited_plain(nodes) # Emit delimited body # # @param [Enumerable] nodes - # @param [String] delimiter # # @return [undefined] # @@ -432,7 +430,7 @@ def indented # Emit non nil body # - # @param [Parser::AST::Node] node + # @param [Parser::AST::Node] body # # @return [undefined] # diff --git a/lib/unparser/emitter/literal/primitive.rb b/lib/unparser/emitter/literal/primitive.rb index 07aeae8f..a9b69ac6 100644 --- a/lib/unparser/emitter/literal/primitive.rb +++ b/lib/unparser/emitter/literal/primitive.rb @@ -107,7 +107,7 @@ def dispatch # Write rational format # - # @param [#to_s] + # @param [#to_s] value # # @return [undefined] # diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 26aeb5e0..926feae9 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -5,7 +5,8 @@ module NodeHelpers # Helper for building nodes # - # @param [Symbol] + # @param [Symbol] type + # @param [Parser::AST::Node] children # # @return [Parser::AST::Node] # @@ -19,9 +20,10 @@ def s(type, *children) # Helper for building nodes # - # @param [Symbol] + # @param [Symbol] type # # @return [Parser::AST::Node] + # @param [Array] children # # @api private # diff --git a/lib/unparser/preprocessor.rb b/lib/unparser/preprocessor.rb index c3b57f19..7400661a 100644 --- a/lib/unparser/preprocessor.rb +++ b/lib/unparser/preprocessor.rb @@ -114,7 +114,7 @@ class Infinity < self # Return preprocessor result # - # @param [Parser::AST::Node] + # @return [Parser::AST::Node] # # @api private # From d3d4a413558b8729fddd3e2ef9c5d2c06f8664e8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 1 Jan 2020 00:51:53 +0000 Subject: [PATCH 045/256] Fix readme badges * Drop defunct * Change to SVG --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 94542735..4d171ba6 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,8 @@ unparser ======== -[![Build Status](https://secure.travis-ci.org/mbj/unparser.png?branch=master)](http://travis-ci.org/mbj/unparser) -[![Dependency Status](https://gemnasium.com/mbj/unparser.png)](https://gemnasium.com/mbj/unparser) -[![Code Climate](https://codeclimate.com/github/mbj/unparser.png)](https://codeclimate.com/github/mbj/unparser) +[![Build Status](https://secure.travis-ci.org/mbj/unparser.svg?branch=master)](http://travis-ci.org/mbj/unparser) +[![Code Climate](https://codeclimate.com/github/mbj/unparser.svg)](https://codeclimate.com/github/mbj/unparser) [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). From 30e70f3f767b51b2da17e8760a0ca61936e57db7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 2 Jan 2020 16:38:11 +0000 Subject: [PATCH 046/256] Upgrade parser to ~> 2.6.5 --- Changelog.md | 4 ++++ Gemfile | 2 +- Gemfile.lock | 13 +++++++------ config/flay.yml | 2 +- lib/unparser/emitter/argument.rb | 22 +++++++++------------- spec/unit/unparser_spec.rb | 18 ++++++++++++------ unparser.gemspec | 4 ++-- 7 files changed, 36 insertions(+), 29 deletions(-) diff --git a/Changelog.md b/Changelog.md index ca0072c2..26b47f83 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.4.6 2020-01-02 + +* Upgrades to allow parser dependency to ~> 2.6.5 + # v0.4.5 2019-05-10 * Bump parser dependency to ~> 2.6.3 diff --git a/Gemfile b/Gemfile index 1615cc5d..af459559 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,7 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant.git' +gem 'mutant', git: 'https://github.com/mbj/mutant.git', branch: 'upgrade/parser' source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' diff --git a/Gemfile.lock b/Gemfile.lock index a09404c3..a7e40614 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,9 @@ GIT remote: https://github.com/mbj/mutant.git - revision: 97d44b4d3f410fd6b8e0d547ed1fa4a2dfc05191 + revision: f138cc7b16ed6dab728ef2e2f46731754086b9e8 + branch: upgrade/parser specs: - mutant (0.9.0) + mutant (0.9.1) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -13,20 +14,20 @@ GIT ice_nine (~> 0.11.1) memoizable (~> 0.4.2) mutant-license (~> 0.1.0) - parser (= 2.6.3) + parser (~> 2.6.5) procto (~> 0.0.2) unparser (~> 0.4.5) PATH remote: . specs: - unparser (0.4.5) + unparser (0.4.6) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (~> 2.6.3) + parser (~> 2.6.5) procto (~> 0.0.2) GEM @@ -102,7 +103,7 @@ GEM procto (~> 0.0.2) mutant-license (0.1.0) parallel (1.19.1) - parser (2.6.3.0) + parser (2.6.5.0) ast (~> 2.4.0) path_expander (1.1.0) powerpack (0.1.2) diff --git a/config/flay.yml b/config/flay.yml index 19f247bc..0b198697 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 645 +total_score: 644 diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index fefcdf61..260af9be 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -240,7 +240,7 @@ class Procarg < self handle :procarg0 - children :first_argument + PARENS = %i[restarg mlhs].freeze private @@ -251,22 +251,18 @@ class Procarg < self # @api private # def dispatch - if first_argument.instance_of?(Symbol) - write(first_argument.to_s) + if needs_parens? + parentheses do + delimited(children) + end else - emit_multiple_children + delimited(children) end end - # Emit multiple children - # - # @return [undefined] - # - # @api private - # - def emit_multiple_children - parentheses do - delimited(children) + def needs_parens? + children.length > 1 || children.any? do |node| + PARENS.include?(node.type) end end end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 785c7167..b09dff84 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -547,12 +547,6 @@ def foo foo.bar(*args) RUBY - assert_source <<~'RUBY' - foo.bar do |(a)| - d - end - RUBY - assert_source <<~'RUBY' foo.bar do |(a, b), c| d @@ -574,6 +568,18 @@ def foo end RUBY + assert_source <<~'RUBY' + foo.bar do |*| + d + end + RUBY + + assert_source <<~'RUBY' + foo.bar do |(*)| + d + end + RUBY + assert_source <<~'RUBY' foo.bar do |((*))| d diff --git a/unparser.gemspec b/unparser.gemspec index 3d3a41db..d415c79c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.5' + gem.version = '0.4.6' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('parser', '~> 2.6.3') + gem.add_dependency('parser', '~> 2.6.5') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') From 141894d57a7725df08e7b20a90065a479953ca45 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 4 Jan 2020 00:44:21 +0000 Subject: [PATCH 047/256] Change to mutant from rubygems --- Gemfile | 2 -- Gemfile.lock | 47 ++++++++++++++++++++++++++--------------------- unparser.gemspec | 8 +++++--- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/Gemfile b/Gemfile index af459559..1419592e 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,6 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant.git', branch: 'upgrade/parser' - source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index a7e40614..58b9e856 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,23 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant.git - revision: f138cc7b16ed6dab728ef2e2f46731754086b9e8 - branch: upgrade/parser - specs: - mutant (0.9.1) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.1) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - mutant-license (~> 0.1.0) - parser (~> 2.6.5) - procto (~> 0.0.2) - unparser (~> 0.4.5) - PATH remote: . specs: @@ -101,7 +81,31 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.0) procto (~> 0.0.2) + mprelude (0.1.0) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + concord (~> 0.1.5) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + procto (~> 0.0.2) + mutant (0.9.4) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.1) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + mprelude (~> 0.1.0) + parser (~> 2.6.5) + procto (~> 0.0.2) + unparser (~> 0.4.6) mutant-license (0.1.0) + mutant-rspec (0.9.4) + mutant (~> 0.9.4) + rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.1) parser (2.6.5.0) ast (~> 2.4.0) @@ -168,8 +172,9 @@ DEPENDENCIES anima (~> 0.3.1) devtools (~> 0.1.23) morpher (~> 0.2.6) - mutant! + mutant (~> 0.9.4) mutant-license! + mutant-rspec (~> 0.9.4) unparser! BUNDLED WITH diff --git a/unparser.gemspec b/unparser.gemspec index d415c79c..658fcd2c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -24,7 +24,9 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '~> 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('anima', '~> 0.3.1') - gem.add_development_dependency('devtools', '~> 0.1.23') - gem.add_development_dependency('morpher', '~> 0.2.6') + gem.add_development_dependency('anima', '~> 0.3.1') + gem.add_development_dependency('devtools', '~> 0.1.23') + gem.add_development_dependency('morpher', '~> 0.2.6') + gem.add_development_dependency('mutant', '~> 0.9.4') + gem.add_development_dependency('mutant-rspec', '~> 0.9.4') end From 45bd093c6a75744d17bd9bd2014a838ea99286ab Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 4 Jan 2020 00:44:22 +0000 Subject: [PATCH 048/256] Change parser bounds to allow 2.7 branch --- Changelog.md | 5 +++++ Gemfile.lock | 2 +- unparser.gemspec | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 26b47f83..171f8653 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# v0.4.7 2020-01-03 + +* Change to allow parser 2.7, even while syntax is not yet supported. + This reduces downstream complexity. + # v0.4.6 2020-01-02 * Upgrades to allow parser dependency to ~> 2.6.5 diff --git a/Gemfile.lock b/Gemfile.lock index 58b9e856..23e15f05 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,7 +7,7 @@ PATH concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) - parser (~> 2.6.5) + parser (>= 2.6.5) procto (~> 0.0.2) GEM diff --git a/unparser.gemspec b/unparser.gemspec index 658fcd2c..3df65e1a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.6' + gem.version = '0.4.7' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('parser', '~> 2.6.5') + gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('anima', '~> 0.3.1') From 7b251f7d93fc80362b58acab6997039d0079b3b4 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 4 Jan 2020 00:44:23 +0000 Subject: [PATCH 049/256] Add support for endless ranges --- .circleci/config.yml | 2 +- Changelog.md | 1 + Gemfile.lock | 2 +- config/flay.yml | 2 +- lib/unparser/emitter/literal/range.rb | 2 +- spec/unit/unparser_spec.rb | 2 ++ 6 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f221ff60..226ac393 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ defaults: &defaults working_directory: ~/unparser docker: - - image: circleci/ruby:2.5.5 + - image: circleci/ruby:2.6.5 version: 2 jobs: unit_specs: diff --git a/Changelog.md b/Changelog.md index 171f8653..eca2e644 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # v0.4.7 2020-01-03 +* Add support for endless ranges * Change to allow parser 2.7, even while syntax is not yet supported. This reduces downstream complexity. diff --git a/Gemfile.lock b/Gemfile.lock index 23e15f05..18e6ee03 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.4.6) + unparser (0.4.7) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) concord (~> 0.1.5) diff --git a/config/flay.yml b/config/flay.yml index 0b198697..7b5e3d18 100644 --- a/config/flay.yml +++ b/config/flay.yml @@ -1,3 +1,3 @@ --- threshold: 13 -total_score: 644 +total_score: 638 diff --git a/lib/unparser/emitter/literal/range.rb b/lib/unparser/emitter/literal/range.rb index 04d9e931..a1160ba6 100644 --- a/lib/unparser/emitter/literal/range.rb +++ b/lib/unparser/emitter/literal/range.rb @@ -27,7 +27,7 @@ class Range < self def dispatch visit(begin_node) write(TOKENS.fetch(node.type)) - visit(end_node) + visit(end_node) if end_node end end # Range diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index b09dff84..d92a1855 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -265,6 +265,7 @@ def foo(bar:, baz: "value") end context 'irange' do + assert_unterminated '1..' assert_unterminated '1..2' assert_unterminated '(0.0 / 0.0)..1' assert_unterminated '1..(0.0 / 0.0)' @@ -272,6 +273,7 @@ def foo(bar:, baz: "value") end context 'erange' do + assert_unterminated '1...' assert_unterminated '1...2' end From 2689f97be01dc40d2290cd0dd56d42116d4685a6 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 2 Feb 2020 22:45:04 +0000 Subject: [PATCH 050/256] Upgrade mutant --- Gemfile.lock | 36 +++++++++++++++++------------------- config/rubocop.yml | 5 ++++- spec/integrations.yml | 2 +- unparser.gemspec | 4 ++-- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 18e6ee03..5a61d775 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,7 +36,7 @@ GEM equalizer (~> 0.0.9) descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.24) + devtools (0.1.25) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.0) @@ -45,11 +45,11 @@ GEM flog (~> 4.6.2) procto (~> 0.0.3) rake (~> 12.3.0) - reek (~> 5.3.0) + reek (~> 5.6.0) rspec (~> 3.8.0) rspec-core (~> 3.8.0) rspec-its (~> 1.2.0) - rubocop (~> 0.61.1) + rubocop (~> 0.79.0) simplecov (~> 0.16.1) yard (~> 0.9.16) yardstick (~> 0.9.9) @@ -88,7 +88,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.4) + mutant (0.9.5) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -99,26 +99,25 @@ GEM ice_nine (~> 0.11.1) memoizable (~> 0.4.2) mprelude (~> 0.1.0) - parser (~> 2.6.5) + parser (~> 2.7.0.2) procto (~> 0.0.2) unparser (~> 0.4.6) mutant-license (0.1.0) - mutant-rspec (0.9.4) - mutant (~> 0.9.4) + mutant-rspec (0.9.5) + mutant (~> 0.9.5) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.1) - parser (2.6.5.0) + parser (2.7.0.2) ast (~> 2.4.0) path_expander (1.1.0) - powerpack (0.1.2) procto (0.0.3) psych (3.1.0) rainbow (3.0.0) rake (12.3.3) - reek (5.3.2) + reek (5.6.0) codeclimate-engine-rb (~> 0.4.0) kwalify (~> 0.7.0) - parser (>= 2.5.0.0, < 2.7, != 2.5.1.1) + parser (>= 2.5.0.0, < 2.8, != 2.5.1.1) psych (~> 3.1.0) rainbow (>= 2.0, < 4.0) rspec (3.8.0) @@ -137,14 +136,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.8.0) rspec-support (3.8.3) - rubocop (0.61.1) + rubocop (0.79.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) - parser (>= 2.5, != 2.5.1.1) - powerpack (~> 0.1) + parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) - unicode-display_width (~> 1.4.0) + unicode-display_width (>= 1.4.0, < 1.7) ruby-progressbar (1.10.1) ruby_parser (3.14.1) sexp_processor (~> 4.9) @@ -155,13 +153,13 @@ GEM simplecov-html (~> 0.10.0) simplecov-html (0.10.2) thread_safe (0.3.6) - unicode-display_width (1.4.1) + unicode-display_width (1.6.1) virtus (1.0.5) axiom-types (~> 0.1) coercible (~> 1.0) descendants_tracker (~> 0.0, >= 0.0.3) equalizer (~> 0.0, >= 0.0.9) - yard (0.9.22) + yard (0.9.24) yardstick (0.9.9) yard (~> 0.8, >= 0.8.7.2) @@ -172,9 +170,9 @@ DEPENDENCIES anima (~> 0.3.1) devtools (~> 0.1.23) morpher (~> 0.2.6) - mutant (~> 0.9.4) + mutant (~> 0.9.5) mutant-license! - mutant-rspec (~> 0.9.4) + mutant-rspec (~> 0.9.5) unparser! BUNDLED WITH diff --git a/config/rubocop.yml b/config/rubocop.yml index eb0647aa..2a41104c 100644 --- a/config/rubocop.yml +++ b/config/rubocop.yml @@ -117,6 +117,9 @@ Lint/BooleanSymbol: Style/AccessModifierDeclarations: Enabled: false -Layout/AlignHash: +Layout/HashAlignment: EnforcedColonStyle: table EnforcedHashRocketStyle: table + +Naming/RescuedExceptionsVariableName: + Enabled: false diff --git a/spec/integrations.yml b/spec/integrations.yml index e87ff544..9efa123c 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -18,7 +18,7 @@ - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' # Revision of rubyspec on the last CI build of unparser that passed - repo_ref: 'origin/master' + repo_ref: 'b40189b88' exclude: - command_line/fixtures/bad_syntax.rb - core/array/pack/shared/float.rb diff --git a/unparser.gemspec b/unparser.gemspec index 3df65e1a..bd9a2010 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('anima', '~> 0.3.1') gem.add_development_dependency('devtools', '~> 0.1.23') gem.add_development_dependency('morpher', '~> 0.2.6') - gem.add_development_dependency('mutant', '~> 0.9.4') - gem.add_development_dependency('mutant-rspec', '~> 0.9.4') + gem.add_development_dependency('mutant', '~> 0.9.5') + gem.add_development_dependency('mutant-rspec', '~> 0.9.5') end From be8f93ca1402f4b6a819ebbcc27d0c7dc7e2acc4 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 15 Mar 2020 02:50:40 +0000 Subject: [PATCH 051/256] Add 2.7 support * But also fix all bugs discovered from using parsers tests to discover the 2.7 support. [fix #131][fix #82] --- .github/workflows/ci.yml | 26 +- .rubocop.yml | 13 +- Changelog.md | 6 + bin/unparser | 2 +- lib/unparser.rb | 157 +- lib/unparser/ast.rb | 1 - lib/unparser/ast/local_variable_scope.rb | 82 +- lib/unparser/buffer.rb | 35 +- lib/unparser/cli.rb | 65 +- lib/unparser/color.rb | 3 - lib/unparser/comments.rb | 26 - lib/unparser/constants.rb | 57 +- lib/unparser/diff.rb | 17 - lib/unparser/dsl.rb | 32 - lib/unparser/emitter.rb | 446 +---- lib/unparser/emitter/alias.rb | 10 +- lib/unparser/emitter/args.rb | 47 + lib/unparser/emitter/argument.rb | 174 +- lib/unparser/emitter/array.rb | 27 + lib/unparser/emitter/array_pattern.rb | 29 + lib/unparser/emitter/assignment.rb | 163 +- lib/unparser/emitter/begin.rb | 93 +- lib/unparser/emitter/binary.rb | 27 +- lib/unparser/emitter/block.rb | 98 +- lib/unparser/emitter/case.rb | 54 +- lib/unparser/emitter/case_guard.rb | 27 + lib/unparser/emitter/case_match.rb | 40 + lib/unparser/emitter/cbase.rb | 4 +- lib/unparser/emitter/class.rb | 32 +- lib/unparser/emitter/const_pattern.rb | 24 + lib/unparser/emitter/def.rb | 58 +- lib/unparser/emitter/defined.rb | 14 +- lib/unparser/emitter/dstr.rb | 22 + lib/unparser/emitter/dsym.rb | 41 + lib/unparser/emitter/empty.rb | 23 - lib/unparser/emitter/ensure.rb | 37 - lib/unparser/emitter/flipflop.rb | 21 +- lib/unparser/emitter/float.rb | 28 + lib/unparser/emitter/flow_modifier.rb | 63 +- lib/unparser/emitter/for.rb | 24 +- lib/unparser/emitter/hash.rb | 74 + lib/unparser/emitter/hash_pattern.rb | 67 + lib/unparser/emitter/hookexe.rb | 16 +- lib/unparser/emitter/if.rb | 82 +- lib/unparser/emitter/in_match.rb | 21 + lib/unparser/emitter/in_pattern.rb | 34 + lib/unparser/emitter/index.rb | 109 +- lib/unparser/emitter/kwbegin.rb | 31 + lib/unparser/emitter/lambda.rb | 8 - lib/unparser/emitter/literal.rb | 10 - lib/unparser/emitter/literal/array.rb | 29 - lib/unparser/emitter/literal/dynamic.rb | 53 - lib/unparser/emitter/literal/dynamic_body.rb | 132 -- .../emitter/literal/execute_string.rb | 38 - lib/unparser/emitter/literal/hash.rb | 156 -- lib/unparser/emitter/literal/primitive.rb | 145 -- lib/unparser/emitter/literal/range.rb | 36 - lib/unparser/emitter/literal/regexp.rb | 114 -- lib/unparser/emitter/literal/singleton.rb | 26 - lib/unparser/emitter/masgn.rb | 20 + lib/unparser/emitter/match.rb | 20 +- lib/unparser/emitter/match_alt.rb | 23 + lib/unparser/emitter/match_as.rb | 21 + lib/unparser/emitter/match_rest.rb | 26 + lib/unparser/emitter/match_var.rb | 19 + lib/unparser/emitter/meta.rb | 16 - lib/unparser/emitter/mlhs.rb | 40 + lib/unparser/emitter/module.rb | 12 +- lib/unparser/emitter/op_assign.rb | 39 +- lib/unparser/emitter/pin.rb | 19 + lib/unparser/emitter/primitive.rb | 91 + lib/unparser/emitter/range.rb | 35 + lib/unparser/emitter/redo.rb | 25 - lib/unparser/emitter/regexp.rb | 35 + lib/unparser/emitter/repetition.rb | 74 +- lib/unparser/emitter/resbody.rb | 76 - lib/unparser/emitter/rescue.rb | 98 +- lib/unparser/emitter/retry.rb | 25 - lib/unparser/emitter/root.rb | 13 +- lib/unparser/emitter/send.rb | 229 +-- lib/unparser/emitter/send/binary.rb | 57 - lib/unparser/emitter/send/conditional.rb | 40 - lib/unparser/emitter/send/regular.rb | 40 - lib/unparser/emitter/simple.rb | 33 + lib/unparser/emitter/splat.rb | 20 +- lib/unparser/emitter/super.rb | 30 +- lib/unparser/emitter/undef.rb | 10 +- lib/unparser/emitter/variable.rb | 32 +- lib/unparser/emitter/xstr.rb | 72 + lib/unparser/emitter/yield.rb | 10 +- lib/unparser/generation.rb | 250 +++ lib/unparser/node_details.rb | 21 + lib/unparser/node_details/send.rb | 62 + lib/unparser/node_helpers.rb | 51 +- lib/unparser/preprocessor.rb | 159 -- lib/unparser/validation.rb | 72 +- lib/unparser/writer.rb | 15 + lib/unparser/writer/binary.rb | 99 + lib/unparser/writer/dynamic_string.rb | 229 +++ lib/unparser/writer/resbody.rb | 40 + lib/unparser/writer/rescue.rb | 39 + lib/unparser/writer/send.rb | 124 ++ .../send/attribute_assignment.rb | 37 +- lib/unparser/writer/send/binary.rb | 27 + lib/unparser/writer/send/conditional.rb | 25 + lib/unparser/writer/send/regular.rb | 33 + .../{emitter => writer}/send/unary.rb | 27 +- mutant.sh | 31 +- spec/integration/unparser/corpus_spec.rb | 12 - spec/spec_helper.rb | 2 +- spec/unit/unparser/buffer/append_spec.rb | 24 - .../buffer/append_without_prefix_spec.rb | 23 - .../unparser/buffer/capture_content_spec.rb | 17 - spec/unit/unparser/buffer/content_spec.rb | 38 - spec/unit/unparser/buffer/fresh_line_spec.rb | 20 - spec/unit/unparser/buffer/indent_spec.rb | 20 - spec/unit/unparser/buffer/nl_spec.rb | 16 - spec/unit/unparser/buffer/unindent_spec.rb | 20 - spec/unit/unparser/buffer_spec.rb | 171 ++ .../emitter/class_methods/handle_spec.rb | 2 +- spec/unit/unparser/validation_spec.rb | 24 +- spec/unit/unparser_spec.rb | 1711 +---------------- test/corpus/literal/alias.rb | 2 + test/corpus/literal/assignment.rb | 42 + test/corpus/literal/binary.rb | 20 + test/corpus/literal/block.rb | 96 + test/corpus/literal/case.rb | 31 + test/corpus/literal/class.rb | 35 + test/corpus/literal/control.rb | 5 + test/corpus/literal/def.rb | 129 ++ test/corpus/literal/defined.rb | 3 + test/corpus/literal/defs.rb | 40 + test/corpus/literal/dstr.rb | 19 + test/corpus/literal/empty_begin.rb | 1 + test/corpus/literal/flipflop.rb | 6 + test/corpus/literal/for.rb | 12 + test/corpus/literal/hookexe.rb | 7 + test/corpus/literal/if.rb | 36 + test/corpus/literal/kwbegin.rb | 80 + test/corpus/literal/lambda.rb | 13 + test/corpus/literal/literal.rb | 92 + test/corpus/literal/module.rb | 16 + test/corpus/literal/opasgn.rb | 24 + test/corpus/literal/pattern.rb | 38 + test/corpus/literal/pragma.rb | 4 + test/corpus/literal/rescue.rb | 3 + test/corpus/literal/send.rb | 79 + test/corpus/literal/since/27.rb | 4 + test/corpus/literal/singletons.rb | 4 + test/corpus/literal/super.rb | 21 + test/corpus/literal/unary.rb | 8 + test/corpus/literal/undef.rb | 2 + test/corpus/literal/variables.rb | 10 + test/corpus/literal/while.rb | 73 + test/corpus/literal/yield.rb | 3 + test/corpus/semantic/and.rb | 8 + test/corpus/semantic/block.rb | 26 + test/corpus/semantic/def.rb | 7 + test/corpus/semantic/dstr.rb | 127 ++ test/corpus/semantic/kwbegin.rb | 42 + test/corpus/semantic/literal.rb | 14 + test/corpus/semantic/send.rb | 6 + test/corpus/semantic/undef.rb | 2 + test/corpus/semantic/while.rb | 25 + test/helper.rb | 0 test/parse_helper.rb | 0 test/run-parser-tests.rb | 229 +++ unparser.gemspec | 2 +- 168 files changed, 4182 insertions(+), 5334 deletions(-) create mode 100644 lib/unparser/emitter/args.rb create mode 100644 lib/unparser/emitter/array.rb create mode 100644 lib/unparser/emitter/array_pattern.rb create mode 100644 lib/unparser/emitter/case_guard.rb create mode 100644 lib/unparser/emitter/case_match.rb create mode 100644 lib/unparser/emitter/const_pattern.rb create mode 100644 lib/unparser/emitter/dstr.rb create mode 100644 lib/unparser/emitter/dsym.rb delete mode 100644 lib/unparser/emitter/empty.rb delete mode 100644 lib/unparser/emitter/ensure.rb create mode 100644 lib/unparser/emitter/float.rb create mode 100644 lib/unparser/emitter/hash.rb create mode 100644 lib/unparser/emitter/hash_pattern.rb create mode 100644 lib/unparser/emitter/in_match.rb create mode 100644 lib/unparser/emitter/in_pattern.rb create mode 100644 lib/unparser/emitter/kwbegin.rb delete mode 100644 lib/unparser/emitter/literal.rb delete mode 100644 lib/unparser/emitter/literal/array.rb delete mode 100644 lib/unparser/emitter/literal/dynamic.rb delete mode 100644 lib/unparser/emitter/literal/dynamic_body.rb delete mode 100644 lib/unparser/emitter/literal/execute_string.rb delete mode 100644 lib/unparser/emitter/literal/hash.rb delete mode 100644 lib/unparser/emitter/literal/primitive.rb delete mode 100644 lib/unparser/emitter/literal/range.rb delete mode 100644 lib/unparser/emitter/literal/regexp.rb delete mode 100644 lib/unparser/emitter/literal/singleton.rb create mode 100644 lib/unparser/emitter/masgn.rb create mode 100644 lib/unparser/emitter/match_alt.rb create mode 100644 lib/unparser/emitter/match_as.rb create mode 100644 lib/unparser/emitter/match_rest.rb create mode 100644 lib/unparser/emitter/match_var.rb delete mode 100644 lib/unparser/emitter/meta.rb create mode 100644 lib/unparser/emitter/mlhs.rb create mode 100644 lib/unparser/emitter/pin.rb create mode 100644 lib/unparser/emitter/primitive.rb create mode 100644 lib/unparser/emitter/range.rb delete mode 100644 lib/unparser/emitter/redo.rb create mode 100644 lib/unparser/emitter/regexp.rb delete mode 100644 lib/unparser/emitter/resbody.rb delete mode 100644 lib/unparser/emitter/retry.rb delete mode 100644 lib/unparser/emitter/send/binary.rb delete mode 100644 lib/unparser/emitter/send/conditional.rb delete mode 100644 lib/unparser/emitter/send/regular.rb create mode 100644 lib/unparser/emitter/simple.rb create mode 100644 lib/unparser/emitter/xstr.rb create mode 100644 lib/unparser/generation.rb create mode 100644 lib/unparser/node_details.rb create mode 100644 lib/unparser/node_details/send.rb delete mode 100644 lib/unparser/preprocessor.rb create mode 100644 lib/unparser/writer.rb create mode 100644 lib/unparser/writer/binary.rb create mode 100644 lib/unparser/writer/dynamic_string.rb create mode 100644 lib/unparser/writer/resbody.rb create mode 100644 lib/unparser/writer/rescue.rb create mode 100644 lib/unparser/writer/send.rb rename lib/unparser/{emitter => writer}/send/attribute_assignment.rb (51%) create mode 100644 lib/unparser/writer/send/binary.rb create mode 100644 lib/unparser/writer/send/conditional.rb create mode 100644 lib/unparser/writer/send/regular.rb rename lib/unparser/{emitter => writer}/send/unary.rb (51%) delete mode 100644 spec/unit/unparser/buffer/append_spec.rb delete mode 100644 spec/unit/unparser/buffer/append_without_prefix_spec.rb delete mode 100644 spec/unit/unparser/buffer/capture_content_spec.rb delete mode 100644 spec/unit/unparser/buffer/content_spec.rb delete mode 100644 spec/unit/unparser/buffer/fresh_line_spec.rb delete mode 100644 spec/unit/unparser/buffer/indent_spec.rb delete mode 100644 spec/unit/unparser/buffer/nl_spec.rb delete mode 100644 spec/unit/unparser/buffer/unindent_spec.rb create mode 100644 spec/unit/unparser/buffer_spec.rb create mode 100644 test/corpus/literal/alias.rb create mode 100644 test/corpus/literal/assignment.rb create mode 100644 test/corpus/literal/binary.rb create mode 100644 test/corpus/literal/block.rb create mode 100644 test/corpus/literal/case.rb create mode 100644 test/corpus/literal/class.rb create mode 100644 test/corpus/literal/control.rb create mode 100644 test/corpus/literal/def.rb create mode 100644 test/corpus/literal/defined.rb create mode 100644 test/corpus/literal/defs.rb create mode 100644 test/corpus/literal/dstr.rb create mode 100644 test/corpus/literal/empty_begin.rb create mode 100644 test/corpus/literal/flipflop.rb create mode 100644 test/corpus/literal/for.rb create mode 100644 test/corpus/literal/hookexe.rb create mode 100644 test/corpus/literal/if.rb create mode 100644 test/corpus/literal/kwbegin.rb create mode 100644 test/corpus/literal/lambda.rb create mode 100644 test/corpus/literal/literal.rb create mode 100644 test/corpus/literal/module.rb create mode 100644 test/corpus/literal/opasgn.rb create mode 100644 test/corpus/literal/pattern.rb create mode 100644 test/corpus/literal/pragma.rb create mode 100644 test/corpus/literal/rescue.rb create mode 100644 test/corpus/literal/send.rb create mode 100644 test/corpus/literal/since/27.rb create mode 100644 test/corpus/literal/singletons.rb create mode 100644 test/corpus/literal/super.rb create mode 100644 test/corpus/literal/unary.rb create mode 100644 test/corpus/literal/undef.rb create mode 100644 test/corpus/literal/variables.rb create mode 100644 test/corpus/literal/while.rb create mode 100644 test/corpus/literal/yield.rb create mode 100644 test/corpus/semantic/and.rb create mode 100644 test/corpus/semantic/block.rb create mode 100644 test/corpus/semantic/def.rb create mode 100644 test/corpus/semantic/dstr.rb create mode 100644 test/corpus/semantic/kwbegin.rb create mode 100644 test/corpus/semantic/literal.rb create mode 100644 test/corpus/semantic/send.rb create mode 100644 test/corpus/semantic/undef.rb create mode 100644 test/corpus/semantic/while.rb create mode 100644 test/helper.rb create mode 100644 test/parse_helper.rb create mode 100644 test/run-parser-tests.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff03a029..f39d3a63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6'] + ruby: ['2.6', '2.7'] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6'] + ruby: ['2.6', '2.7'] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -46,26 +46,6 @@ jobs: gem install bundler bundle install - run: bundle exec rspec spec/integration - ruby-mutant: - name: Mutation coverage - runs-on: ${{ matrix.os }} - timeout-minutes: 5 - strategy: - fail-fast: false - matrix: - ruby: ['2.6'] - os: [ubuntu-latest] - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - uses: actions/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - - run: | - gem install bundler - bundle install - - run: ./mutant.sh --since HEAD~1 -- 'Unparser*' ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} @@ -73,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6'] + ruby: ['2.6', '2.7'] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/.rubocop.yml b/.rubocop.yml index c75fe8d6..7fac64f6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,9 @@ AllCops: - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' + Exclude: + - lib/unparser/precedence/data.rb + - tmp # Avoid parameter lists longer than five parameters. Metrics/ParameterLists: @@ -30,12 +33,16 @@ Style/CollectionMethods: find: 'detect' find_all: 'select' +# Use `fail` as `raise` implies re-raising +Style/SignalException: + EnforcedStyle: semantic + # Limit line length Layout/LineLength: Max: 113 # TODO: lower to 79 once the rubocop branch in shared/Gemfile is removed Metrics/ClassLength: - Max: 175 + Max: 205 # Prefer modifiers and explicit if statements over returning early for small methods Style/GuardClause: @@ -50,10 +57,6 @@ Metrics/BlockLength: Style/RedundantFreeze: Enabled: false -# Allow Fixnum and Bignum. This Gem supports versions before 2.4 -Lint/UnifiedInteger: - Enabled: false - # Disabled because of indenting with private keyword in class bodies. Layout/IndentationWidth: Enabled: false diff --git a/Changelog.md b/Changelog.md index ce5a0b6a..d455a1fc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.5.0 unreleased + +* Add 2.7 syntax support +* Fix lots of edge cases via leveraging parser specs +* Add `--literal` mode for CLI + # v0.4.9 2020-09-10 * Change packaging to avoid git in gemspec. diff --git a/bin/unparser b/bin/unparser index 1258efaf..23fb2fbe 100755 --- a/bin/unparser +++ b/bin/unparser @@ -5,6 +5,6 @@ trap('INT') do |status| exit! 128 + status end -require 'unparser/cli' +require 'unparser' exit Unparser::CLI.run(ARGV) diff --git a/lib/unparser.rb b/lib/unparser.rb index f82c4a01..95d1552a 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -6,6 +6,7 @@ require 'diff/lcs' require 'diff/lcs/hunk' require 'mprelude' +require 'optparse' require 'parser/current' require 'procto' require 'set' @@ -15,6 +16,10 @@ module Unparser # Unparser specific AST builder defaulting to modern AST format class Builder < Parser::Builders::Default modernize + + def initialize + self.emit_file_line_as_literals = false + end end EMPTY_STRING = ''.freeze @@ -32,12 +37,27 @@ class Builder < Parser::Builders::Default # @api private # def self.unparse(node, comment_array = []) - node = Preprocessor.run(node) - buffer = Buffer.new - comments = Comments.new(comment_array) - root = Emitter::Root.new(Parser::AST::Node.new(:root, [node]), buffer, comments) - Emitter.emitter(node, root).write_to_buffer - buffer.content + return '' if node.nil? + + Buffer.new.tap do |buffer| + Emitter::Root.new( + buffer, + node, + Comments.new(comment_array) + ).write_to_buffer + end.content + end + + # Unparse capturing errors + # + # This is mostly useful for writing testing tools against unparser. + # + # @param [Parser::AST::Node, nil] node + # + # @return [Either] + def self.unparse_either(node) + MPrelude::Either + .wrap_error(Exception) { unparse(node) } end # Parse string into AST @@ -74,8 +94,6 @@ def self.parse_with_comments(source) # @return [Parser::Base] # # @api private - # - # ignore :reek:NestedIterators def self.parser Parser::CurrentRuby.new(Builder.new).tap do |parser| parser.diagnostics.tap do |diagnostics| @@ -100,75 +118,92 @@ def self.consume_diagnostic(diagnostic) # @param [String] source # # @return [Parser::Source::Buffer] - def self.buffer(source) - Parser::Source::Buffer.new('(string)').tap do |buffer| - buffer.source = source - end + def self.buffer(source, identification = '(string)') + Parser::Source::Buffer.new(identification, source: source) end end # Unparser -require 'unparser/buffer' require 'unparser/node_helpers' -require 'unparser/preprocessor' -require 'unparser/comments' -require 'unparser/constants' -require 'unparser/dsl' require 'unparser/ast' require 'unparser/ast/local_variable_scope' -require 'unparser/diff' +require 'unparser/buffer' +require 'unparser/generation' require 'unparser/color' +require 'unparser/comments' +require 'unparser/constants' +require 'unparser/diff' +require 'unparser/dsl' require 'unparser/emitter' -require 'unparser/emitter/literal' -require 'unparser/emitter/literal/primitive' -require 'unparser/emitter/literal/singleton' -require 'unparser/emitter/literal/dynamic' -require 'unparser/emitter/literal/regexp' -require 'unparser/emitter/literal/array' -require 'unparser/emitter/literal/hash' -require 'unparser/emitter/literal/range' -require 'unparser/emitter/literal/dynamic_body' -require 'unparser/emitter/literal/execute_string' -require 'unparser/emitter/meta' -require 'unparser/emitter/send' -require 'unparser/emitter/send/unary' -require 'unparser/emitter/send/binary' -require 'unparser/emitter/send/regular' -require 'unparser/emitter/send/conditional' -require 'unparser/emitter/send/attribute_assignment' -require 'unparser/emitter/block' -require 'unparser/emitter/assignment' -require 'unparser/emitter/variable' -require 'unparser/emitter/splat' -require 'unparser/emitter/cbase' +require 'unparser/emitter/alias' +require 'unparser/emitter/args' require 'unparser/emitter/argument' +require 'unparser/emitter/array' +require 'unparser/emitter/array_pattern' +require 'unparser/emitter/assignment' require 'unparser/emitter/begin' -require 'unparser/emitter/flow_modifier' -require 'unparser/emitter/undef' -require 'unparser/emitter/def' +require 'unparser/emitter/binary' +require 'unparser/emitter/block' +require 'unparser/emitter/case' +require 'unparser/emitter/case_guard' +require 'unparser/emitter/case_match' +require 'unparser/emitter/cbase' require 'unparser/emitter/class' -require 'unparser/emitter/module' -require 'unparser/emitter/op_assign' +require 'unparser/emitter/const_pattern' +require 'unparser/emitter/def' require 'unparser/emitter/defined' +require 'unparser/emitter/dstr' +require 'unparser/emitter/dsym' +require 'unparser/emitter/flipflop' +require 'unparser/emitter/float' +require 'unparser/emitter/flow_modifier' +require 'unparser/emitter/for' +require 'unparser/emitter/hash' +require 'unparser/emitter/hash_pattern' require 'unparser/emitter/hookexe' -require 'unparser/emitter/super' -require 'unparser/emitter/retry' -require 'unparser/emitter/redo' require 'unparser/emitter/if' -require 'unparser/emitter/alias' -require 'unparser/emitter/yield' -require 'unparser/emitter/binary' -require 'unparser/emitter/case' -require 'unparser/emitter/for' -require 'unparser/emitter/repetition' -require 'unparser/emitter/root' -require 'unparser/emitter/match' -require 'unparser/emitter/empty' -require 'unparser/emitter/flipflop' -require 'unparser/emitter/rescue' -require 'unparser/emitter/resbody' -require 'unparser/emitter/ensure' +require 'unparser/emitter/in_match' +require 'unparser/emitter/in_pattern' require 'unparser/emitter/index' +require 'unparser/emitter/kwbegin' require 'unparser/emitter/lambda' +require 'unparser/emitter/masgn' +require 'unparser/emitter/match' +require 'unparser/emitter/match_alt' +require 'unparser/emitter/match_as' +require 'unparser/emitter/match_rest' +require 'unparser/emitter/match_var' +require 'unparser/emitter/mlhs' +require 'unparser/emitter/module' +require 'unparser/emitter/op_assign' +require 'unparser/emitter/pin' +require 'unparser/emitter/primitive' +require 'unparser/emitter/range' +require 'unparser/emitter/regexp' +require 'unparser/emitter/repetition' +require 'unparser/emitter/rescue' +require 'unparser/emitter/root' +require 'unparser/emitter/send' +require 'unparser/emitter/simple' +require 'unparser/emitter/splat' +require 'unparser/emitter/super' +require 'unparser/emitter/undef' +require 'unparser/emitter/variable' +require 'unparser/emitter/xstr' +require 'unparser/emitter/yield' +require 'unparser/writer' +require 'unparser/writer/binary' +require 'unparser/writer/dynamic_string' +require 'unparser/writer/resbody' +require 'unparser/writer/rescue' +require 'unparser/writer/send' +require 'unparser/writer/send/attribute_assignment' +require 'unparser/writer/send/binary' +require 'unparser/writer/send/regular' +require 'unparser/writer/send/unary' +require 'unparser/node_details' +require 'unparser/node_details/send' +require 'unparser/cli' + require 'unparser/validation' # make it easy for zombie require 'unparser/finalize' diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 61ec3f99..3ac7b17c 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -2,7 +2,6 @@ module Unparser # Namespace for AST processing tools - # :reek:TooManyConstants module AST FIRST_CHILD = ->(node) { node.children.first }.freeze diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index c4e3c88e..cfd029b7 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -61,35 +61,22 @@ def local_variable_defined_for_node?(node, name) # # @api private # - def first_assignment_in_body_and_used_in_condition?(body, condition) - condition_reads = AST.local_variable_reads(condition) + def first_assignment_in?(left, right) + condition_reads = AST.local_variable_reads(right) - candidates = AST.local_variable_assignments(body).select do |node| - name = node.children.first - condition_reads.include?(name) + candidates = AST.local_variable_assignments(left).select do |node| + condition_reads.include?(node.children.first) end - candidates.any? do |node| - first_assignment?(node) - end + candidates.any?(&public_method(:first_assignment?)) end private - # Match node - # - # @param [Parser::AST::Node] needle - # if block given - # - # @return [Boolean] - # - # @api private - # def match(needle) @items.each do |node, current, before| return yield(current, before) if node.equal?(needle) end - false end end # LocalVariableScope @@ -137,25 +124,10 @@ def each(node, &block) private - # Return current set of local variables - # - # @return [Set] - # - # @api private - # def current @stack.last end - # Visit node and record local variable state - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - # ignore :reek:LongYieldList def visit(node, &block) before = current.dup enter(node) @@ -166,75 +138,33 @@ def visit(node, &block) leave(node) end - # Record local variable state - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # def enter(node) case node.type when *RESET_NODES push_reset - when *ASSIGN_NODES + when ASSIGN_NODES define(node.children.first) when *INHERIT_NODES push_inherit end end - # Pop from local variable state - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # def leave(node) pop if CLOSE_NODES.include?(node.type) end - # Define a local variable on current stack - # - # @param [Symbol] name - # - # @return [undefined] - # - # @api private - # def define(name) current << name end - # Push reset scope on stack - # - # @return [undefined] - # - # @api private - # def push_reset @stack << Set.new end - # Push inherited lvar scope on stack - # - # @return [undefined] - # - # @api private - # def push_inherit @stack << current.dup end - # Pop lvar scope from stack - # - # @return [undefined] - # - # @api private - # def pop @stack.pop end diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index d60d80fd..19674f15 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -3,8 +3,6 @@ module Unparser # Buffer used to emit into - # - # ignore :reek:TooManyMethods class Buffer NL = "\n".freeze @@ -82,6 +80,13 @@ def nl self end + def root_indent + before = @indent + @indent = 0 + yield + @indent = before + end + # Test for a fresh line # # @return [Boolean] @@ -114,26 +119,24 @@ def capture_content @content[size_before..-1] end - private - - INDENT_SPACE = ' '.freeze - - # Write prefix - # - # @return [String] + # Write raw fragment to buffer # - # @api private - # - def prefix - write(INDENT_SPACE * @indent) - end - - # Write to content buffer + # Does not do indentation logic. # # @param [String] fragment # + # @return [self] def write(fragment) @content << fragment + self + end + + private + + INDENT_SPACE = ' '.freeze + + def prefix + write(INDENT_SPACE * @indent) end end # Buffer diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index a611a19a..3ca21a50 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -1,13 +1,7 @@ # frozen_string_literal: true -require 'unparser' -require 'optparse' - module Unparser # Unparser CLI implementation - # - # :reek:InstanceVariableAssumption - # :reek:TooManyInstanceVariables class CLI EXIT_SUCCESS = 0 @@ -26,6 +20,13 @@ class Path < self def validation Validation.from_path(path) end + + # Literal for this target + # + # @return [Validation] + def literal_validation + Validation::Literal.from_path(path) + end end # String target @@ -38,6 +39,13 @@ class String def validation Validation.from_string(string) end + + # Literal for this target + # + # @return [Validation] + def literal_validation + Validation::Literal.from_string(path) + end end # String end # Target @@ -47,7 +55,7 @@ def validation # # @param [Array] arguments # - # @return [Fixnum] + # @return [Integer] # the exit status # # @api private @@ -63,15 +71,14 @@ def self.run(*arguments) # @return [undefined] # # @api private - # - # ignore :reek:TooManyStatements def initialize(arguments) @ignore = Set.new @targets = [] - @success = true - @fail_fast = false - @verbose = false + @fail_fast = false + @success = true + @validation = :validation + @verbose = false opts = OptionParser.new do |builder| add_options(builder) @@ -90,7 +97,7 @@ def initialize(arguments) # # @api private # - # ignore :reek:TooManyStatements + # rubocop:disable Metrics/MethodLength def add_options(builder) builder.banner = 'usage: unparse [options] FILE [FILE]' builder.separator('') @@ -103,6 +110,9 @@ def add_options(builder) builder.on('-v', '--verbose') do @verbose = true end + builder.on('-l', '--literal') do + @validation = :literal_validation + end builder.on('--ignore FILE') do |file| @ignore.merge(targets(file)) end @@ -110,10 +120,11 @@ def add_options(builder) @fail_fast = true end end + # rubocop:enable Metrics/MethodLength # Return exit status # - # @return [Fixnum] + # @return [Integer] # # @api private # @@ -128,16 +139,8 @@ def exit_status private - # Process target - # - # @param [Target] target - # - # @return [undefined] - # - # @api private - # def process_target(target) - validation = target.validation + validation = target.public_send(@validation) if validation.success? puts validation.report if @verbose puts "Success: #{validation.identification}" @@ -148,12 +151,6 @@ def process_target(target) end end - # Return effective targets - # - # @return [Enumerable] - # - # @api private - # def effective_targets if @start_with reject = true @@ -169,15 +166,6 @@ def effective_targets end.reject(&@ignore.method(:include?)) end - # Return targets for file name - # - # @param [String] file_name - # - # @return [Enumerable] - # - # @api private - # - # ignore :reek:UtilityFunction def targets(file_name) if File.directory?(file_name) Dir.glob(File.join(file_name, '**/*.rb')).sort @@ -187,6 +175,5 @@ def targets(file_name) Dir.glob(file_name).sort end.map { |file| Target::Path.new(Pathname.new(file)) } end - end # CLI end # Unparser diff --git a/lib/unparser/color.rb b/lib/unparser/color.rb index 483d1501..e4106582 100644 --- a/lib/unparser/color.rb +++ b/lib/unparser/color.rb @@ -28,9 +28,6 @@ def format(text) private - # Initialize null color - # - # @return [undefined] def initialize; end end.new diff --git a/lib/unparser/comments.rb b/lib/unparser/comments.rb index 1c976689..5a7696bd 100644 --- a/lib/unparser/comments.rb +++ b/lib/unparser/comments.rb @@ -3,8 +3,6 @@ module Unparser # Holds the comments that remain to be emitted - # - # ignore :reek:RepeatedConditional class Comments # Proxy to singleton @@ -113,39 +111,15 @@ def self.source_range(node, part) private - # Take comments while the provided block returns true - # - # @yield [Parser::Source::Comment] - # - # @return [Array] - # - # @api private - # def take_while number_to_take = @comments.index { |comment| !yield(comment) } || @comments.size @comments.shift(number_to_take) end - # Take comments up to the line number - # - # @param [Fixnum] line - # - # @return [Array] - # - # @api private - # def take_up_to_line(line) take_while { |comment| comment.location.expression.line <= line } end - # Unshift document comments and return the rest - # - # @param [Array] comments - # - # @return [Array] - # - # @api private - # def unshift_documents(comments) doc_comments, other_comments = comments.partition(&:document?) doc_comments.reverse_each { |comment| @comments.unshift(comment) } diff --git a/lib/unparser/constants.rb b/lib/unparser/constants.rb index eb789e0b..41a7fb8d 100644 --- a/lib/unparser/constants.rb +++ b/lib/unparser/constants.rb @@ -2,66 +2,19 @@ module Unparser # All unparser constants maybe included in other libraries. - # - # False positive since constants are frozen dynamically - # to avoid duplication of `.freeze` calls - # - # :reek:TooManyConstants module Constants - # Return frozen symbol set from enumerable - # - # @param [Enumerable] enumerable - # - # @return [Set] - # - # @api private - # - def self.symbol_set(enumerable) - enumerable.map(&:to_sym).freeze - end - private_class_method :symbol_set - - BRACKETS_CURLY = IceNine.deep_freeze(%w[{ }]) - BRACKETS_ROUND = IceNine.deep_freeze(%w[( )]) - BRACKETS_SQUARE = IceNine.deep_freeze(%w([ ])) - # All unary operators of the ruby language - UNARY_OPERATORS = symbol_set %w[ + UNARY_OPERATORS = %i[ ! ~ -@ +@ - ] + ].to_set.freeze # All binary operators of the ruby language - BINARY_OPERATORS = symbol_set %w[ + BINARY_OPERATORS = %i[ + - * / & | && || << >> == === != <= < <=> > >= =~ !~ ^ ** % - ] - - COMMENT = '#' - - WS = ' ' - NL = "\n" - T_DOT = '.' - T_LT = '<' - T_DLT = '<<' - T_AMP = '&' - T_ASN = '=' - T_SPLAT = '*' - T_DSPLAT = '**' - T_ASR = '=>' - T_PIPE = '|' - T_DCL = '::' - T_NEG = '!' - T_OR = '||' - T_AND = '&&' - T_COLON = ':' - - M_PO = '(' - M_PC = ')' - - SNGL_QUOTE = "'" - DBL_QUOTE = '"' + ].to_set.freeze # Keywords K_DO = 'do' @@ -107,8 +60,6 @@ def self.symbol_set(enumerable) K_FILE = '__FILE__' K_THEN = 'then' - DEFAULT_DELIMITER = ', '.freeze - KEYWORDS = constants.each_with_object([]) do |name, keywords| value = const_get(name).freeze next unless name.to_s.start_with?('K_') diff --git a/lib/unparser/diff.rb b/lib/unparser/diff.rb index 2ef2d6c0..f27053ab 100644 --- a/lib/unparser/diff.rb +++ b/lib/unparser/diff.rb @@ -59,25 +59,16 @@ def self.lines(source) private - # Diffs between old and new - # - # @return [Array] def diffs ::Diff::LCS.diff(old, new) end - # Raw diff-lcs hunks - # - # @return [Array] def hunks diffs.map do |diff| ::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0) end end - # Minimized hunk - # - # @return Diff::LCS::Hunk def minimized_hunk head, *tail = hunks @@ -87,18 +78,10 @@ def minimized_hunk end end - # Max length of source line in new and old - # - # @return [Integer] def max_length [old, new].map(&:length).max end - # Colorized a unified diff line - # - # @param [String] line - # - # @return [String] def self.colorize_line(line) case line[0] when ADDITION diff --git a/lib/unparser/dsl.rb b/lib/unparser/dsl.rb index 77eb2204..48b2927e 100644 --- a/lib/unparser/dsl.rb +++ b/lib/unparser/dsl.rb @@ -6,14 +6,6 @@ module DSL private - # Define remaining children - # - # @param [Enumerable] names - # - # @return [undefined] - # - # @api private - # def define_remaining_children(names) range = names.length..-1 define_method(:remaining_children) do @@ -22,15 +14,6 @@ def define_remaining_children(names) private :remaining_children end - # Define named child - # - # @param [Symbol] name - # @param [Fixnum] index - # - # @return [undefined] - # - # @api private - # def define_child(name, index) define_method(name) do children.at(index) @@ -38,15 +21,6 @@ def define_child(name, index) private name end - # Define a group of children - # - # @param [Symbol] name - # @param [Range] range - # - # @return [undefined] - # - # @api private - # def define_group(name, range) define_method(name) do children[range] @@ -55,12 +29,6 @@ def define_group(name, range) memoize(name) end - # Create name helpers - # - # @return [undefined] - # - # @api private - # def children(*names) define_remaining_children(names) diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 7abc884c..87b84d18 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -4,34 +4,20 @@ module Unparser UnknownNodeError = Class.new(ArgumentError) # Emitter base class - # - # buggy, argument values are sends to self - # - # ignore :reek:TooManyMethods class Emitter - include Adamantium::Flat, AbstractType, Constants, NodeHelpers - include Concord.new(:node, :parent) + include Adamantium::Flat, AbstractType, Constants, Generation, NodeHelpers + include Anima.new(:buffer, :comments, :node, :local_variable_scope) + + public :node + extend DSL # Registry for node emitters REGISTRY = {} # rubocop:disable Style/MutableConstant - NOINDENT = %i[rescue ensure].to_set.freeze - - module Unterminated - def terminated? - false - end - end - - module Terminated - def terminated? - true - end - end + NO_INDENT = %i[ensure rescue].freeze module LocalVariableRoot - # Return local variable root # # @return [Parser::AST::Node] @@ -47,33 +33,8 @@ def self.included(descendant) memoize :local_variable_scope end end - end # LocalVariableRoot - # Return local variable root - # - # @return [Parser::AST::Node] - # - # @api private - # - def local_variable_scope - parent.local_variable_scope - end - - # Return assigned lvars - # - # @return [Array] - # - # @api private - # - abstract_method :local_variables - - # Return node type - # - # @return [Symbol] - # - # @api private - # def node_type node.type end @@ -88,25 +49,16 @@ def node_type # def self.handle(*types) types.each do |type| + fail "Handler for type: #{type} already registered" if REGISTRY.key?(type) + REGISTRY[type] = self end end private_class_method :handle - # Trigger write to buffer - # - # @return [self] - # - # @api private - # - def write_to_buffer - emit_comments_before if buffer.fresh_line? + def emit_mlhs dispatch - comments.consume(node) - emit_eof_comments if parent.is_a?(Root) - self end - memoize :write_to_buffer # Return emitter # @@ -114,384 +66,30 @@ def write_to_buffer # # @api private # - def self.emitter(node, parent) + # rubocop:disable Metrics/ParameterLists + def self.emitter(buffer:, comments:, node:, local_variable_scope:) type = node.type - klass = REGISTRY.fetch(type) do - raise UnknownNodeError, "Unknown node type: #{type.inspect}" - end - klass.new(node, parent) - end - - # Dispatch node - # - # @return [undefined] - # - # @api private - # - abstract_method :dispatch - - # Test if node is emitted as terminated expression - # - # @return [Boolean] - # - # @api private - # - abstract_method :terminated? - protected - - # Return buffer - # - # @return [Buffer] buffer - # - # @api private - # - def buffer - parent.buffer - end - memoize :buffer, freezer: :noop - - # Return comments - # - # @return [Comments] comments - # - # @api private - # - def comments - parent.comments - end - memoize :comments, freezer: :noop - - private - - # Emit contents of block within parentheses - # - # @return [undefined] - # - # @api private - # - def parentheses(open = M_PO, close = M_PC) - write(open) - yield - write(close) - end - - # Visit node - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def visit_plain(node) - emitter = emitter(node) - emitter.write_to_buffer - end - - # Visit ambiguous node - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def visit(node) - emitter = emitter(node) - conditional_parentheses(!emitter.terminated?) do - emitter.write_to_buffer - end - end - - # Visit within parentheses - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def visit_parentheses(node, *arguments) - parentheses(*arguments) do - visit_plain(node) - end - end - - # Call block in optional parentheses - # - # @param [true, false] flag - # - # @return [undefined] - # - # @api private - # - # ignore :reek:ControlParameter - def conditional_parentheses(flag) - if flag - parentheses { yield } - else - yield - end - end - - # Return emitter for node - # - # @param [Parser::AST::Node] node - # - # @return [Emitter] - # - # @api private - # - def emitter(node) - self.class.emitter(node, self) - end - - # Emit delimited body - # - # @param [Enumerable] nodes - # - # @return [undefined] - # - # @api private - # - def delimited_plain(nodes) - delimited(nodes, &method(:visit_plain)) - end - - # Emit delimited body - # - # @param [Enumerable] nodes - # - # @return [undefined] - # - # @api private - # - def delimited(nodes, &block) - return if nodes.empty? - - block ||= method(:visit) - head, *tail = nodes - block.call(head) - tail.each do |node| - write(DEFAULT_DELIMITER) - block.call(node) - end - end - - # Return children of node - # - # @return [Array] - # - # @api private - # - def children - node.children - end - - # Write newline - # - # @return [undefined] - # - # @api private - # - def nl - emit_eol_comments - buffer.nl - end - - # Write comments that appeared before source_part in the source - # - # @param [Symbol] source_part - # - # @return [undefined] - # - # @api private - # - def emit_comments_before(source_part = :expression) - comments_before = comments.take_before(node, source_part) - return if comments_before.empty? - - emit_comments(comments_before) - buffer.nl - end - - # Write end-of-line comments - # - # @return [undefined] - # - # @api private - # - def emit_eol_comments - comments.take_eol_comments.each do |comment| - write(WS, comment.text) - end - end - - # Write end-of-file comments - # - # @return [undefined] - # - # @api private - # - def emit_eof_comments - emit_eol_comments - comments_left = comments.take_all - return if comments_left.empty? - - buffer.nl - emit_comments(comments_left) - end - - # Write each comment to a separate line - # - # @param [Array] comments - # - # @return [undefined] - # - # @api private - # - def emit_comments(comments) - max = comments.size - 1 - comments.each_with_index do |comment, index| - if comment.type.equal?(:document) - buffer.append_without_prefix(comment.text.chomp) - else - write(comment.text) - end - buffer.nl if index < max - end - end - - # Write strings into buffer - # - # @return [undefined] - # - # @api private - # - def write(*strings) - strings.each do |string| - buffer.append(string) - end - end - - # Write end keyword - # - # @return [undefined] - # - # @api private - # - def k_end - buffer.indent - emit_comments_before(:end) - buffer.unindent - write(K_END) - end - - # Return first child - # - # @return [Parser::AST::Node] - # if present - # - # @return [nil] - # otherwise - # - # @api private - # - def first_child - children.first - end - - # Write whitespace - # - # @return [undefined] - # - # @api private - # - def ws - write(WS) - end - - # Call emit contents of block indented - # - # @return [undefined] - # - # @api private - # - # False positive: - # - def indented - buffer = buffer() - buffer.indent - nl - yield - nl - buffer.unindent - end - - # Emit non nil body - # - # @param [Parser::AST::Node] body - # - # @return [undefined] - # - # @api private - # - # rubocop:disable Style/MethodCallWithoutArgsParentheses - def emit_body(body = body()) - unless body - buffer.indent - nl - buffer.unindent - return - end - visit_indented(body) - end - # rubocop:enable Style/MethodCallWithoutArgsParentheses - - # Visit indented node - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def visit_indented(node) - if NOINDENT.include?(node.type) - visit_plain(node) - else - indented { visit_plain(node) } + klass = REGISTRY.fetch(type) do + fail UnknownNodeError, "Unknown node type: #{type.inspect}" end - end - # Return parent type - # - # @return [Symbol] - # if parent is present - # - # @return [nil] - # otherwise - # - # @api private - # - def parent_type - parent.node_type + klass.new( + buffer: buffer, + comments: comments, + local_variable_scope: local_variable_scope, + node: node + ) end + # rubocop:enable Metrics/ParameterLists - # Delegate to emitter - # - # @param [Class:Emitter] emitter + # Dispatch node write as statement # # @return [undefined] # # @api private # - # rubocop:disable Style/MethodCallWithoutArgsParentheses - def run(emitter, node = node()) - emitter.new(node, self).write_to_buffer - end - # rubocop:enable Style/MethodCallWithoutArgsParentheses + abstract_method :dispatch end # Emitter end # Unparser diff --git a/lib/unparser/emitter/alias.rb b/lib/unparser/emitter/alias.rb index f6229207..9b9189d5 100644 --- a/lib/unparser/emitter/alias.rb +++ b/lib/unparser/emitter/alias.rb @@ -11,16 +11,10 @@ class Alias < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_ALIAS, WS) + write('alias ') visit(target) - write(WS) + ws visit(source) end diff --git a/lib/unparser/emitter/args.rb b/lib/unparser/emitter/args.rb new file mode 100644 index 00000000..9a3cdd6f --- /dev/null +++ b/lib/unparser/emitter/args.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Arguments emitter + class Args < self + def emit_block_arguments + delimited(normal_arguments) + + if normal_arguments.one? + write(',') if n_arg?(normal_arguments.first) + end + + emit_shadowargs + end + + def emit_def_arguments + delimited(normal_arguments) + end + + def emit_lambda_arguments + delimited(normal_arguments) + emit_shadowargs + end + + private + + def emit_shadowargs + return if shadowargs.empty? + + write('; ') + delimited(shadowargs) + end + + def normal_arguments + children.reject(&method(:n_shadowarg?)) + end + memoize :normal_arguments + + def shadowargs + children.select(&method(:n_shadowarg?)) + end + memoize :shadowargs + + end # Arguments + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index 260af9be..93d44320 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -2,121 +2,19 @@ module Unparser class Emitter - - # Arg expr (pattern args) emitter - class ArgExpr < self - - handle :arg_expr - - children :body - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - visit_parentheses(body) - end - end # ArgExpr - - # Arguments emitter - class Arguments < self - include Terminated - - handle :args - - SHADOWARGS = ->(node) { node.type.equal?(:shadowarg) }.freeze - ARG = ->(node) { node.type.equal?(:arg) }.freeze - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - delimited(normal_arguments) - - write(', ') if procarg_disambiguator? - - return if shadowargs.empty? - - write('; ') - delimited(shadowargs) - end - - # Test for procarg_disambiguator - # - # @return [Boolean] - # - # @api private - # - def procarg_disambiguator? - regular_block? && normal_arguments.all?(&ARG) && normal_arguments.one? - end - - # Test for regular block - # - # @return [Boolean] - # - # @api private - # - def regular_block? - parent_type.equal?(:block) && !parent.node.children.first.type.equal?(:lambda) - end - - # Return normal arguments - # - # @return [Enumerable] - # - # @api private - # - def normal_arguments - children.reject(&SHADOWARGS) - end - memoize :normal_arguments - - # Return shadow args - # - # @return [Enumerable] - # - # @api private - # - def shadowargs - children.select(&SHADOWARGS) - end - memoize :shadowargs - - end # Arguments - # Emitter for block and kwrestarg arguments class Morearg < self - include Terminated - MAP = { - blockarg: T_AMP, - kwrestarg: T_DSPLAT + blockarg: '&', + kwrestarg: '**' }.freeze - handle :blockarg - handle :kwrestarg + handle(*MAP.keys) children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write(MAP.fetch(node_type), name.to_s) end @@ -125,44 +23,28 @@ def dispatch # Optional argument emitter class Optarg < self - include Terminated - handle :optarg children :name, :value private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(name.to_s, WS, T_ASN, WS) + write(name.to_s, ' = ') visit(value) end end # Optional keyword argument emitter class KeywordOptional < self - include Terminated - handle :kwoptarg children :name, :value private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(name.to_s, T_COLON, WS) + write(name.to_s, ': ') visit(value) end @@ -170,64 +52,40 @@ def dispatch # Keyword argument emitter class Kwarg < self - include Terminated - handle :kwarg children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(name.to_s, T_COLON) + write(name.to_s, ':') end end # Restarg # Rest argument emitter class Restarg < self - include Terminated - handle :restarg children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(T_SPLAT, name.to_s) + write('*', name.to_s) end end # Restarg # Argument emitter class Argument < self - include Terminated - handle :arg, :shadowarg children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write(name.to_s) end @@ -236,20 +94,12 @@ def dispatch # Progarg emitter class Procarg < self - include Terminated - handle :procarg0 PARENS = %i[restarg mlhs].freeze private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch if needs_parens? parentheses do @@ -269,22 +119,14 @@ def needs_parens? # Block pass node emitter class BlockPass < self - include Terminated - handle :block_pass children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(T_AMP) + write('&') visit(name) end diff --git a/lib/unparser/emitter/array.rb b/lib/unparser/emitter/array.rb new file mode 100644 index 00000000..6f710cb5 --- /dev/null +++ b/lib/unparser/emitter/array.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Array literal emitter + class Array < self + handle :array + + def emit_heredoc_reminders + emitters.each(&:emit_heredoc_reminders) + end + + private + + def dispatch + parentheses('[', ']') do + delimited(emitters, &:write_to_buffer) + end + end + + def emitters + children.map(&method(:emitter)) + end + memoize :emitters + end # Array + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/array_pattern.rb b/lib/unparser/emitter/array_pattern.rb new file mode 100644 index 00000000..ae1833de --- /dev/null +++ b/lib/unparser/emitter/array_pattern.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for array patterns + class ArrayPattern < self + + handle :array_pattern + handle :array_pattern_with_tail + + private + + def dispatch + write('[') + delimited(children, &method(:emit_member)) + write(', ') if node_type.equal?(:array_pattern_with_tail) + write(']') + end + + def emit_member(node) + if n_match_rest?(node) + writer_with(MatchRest, node).emit_array_pattern + else + visit(node) + end + end + end # Pin + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index 508648d7..d907c87b 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -5,163 +5,72 @@ class Emitter # Base class for assignment emitters class Assignment < self + BINARY_OPERATOR = %i[and or].freeze + + def symbol_name + true + end + + def emit_heredoc_reminders + return unless right + + emitter(right).emit_heredoc_reminders + end private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch emit_left emit_right end - # Single assignment emitter - class Single < self - - # Test for terminated emit - # - # @return [Boolean] - # - # @api private - # - def terminated? - right.nil? - end - - private + def emit_right + return unless right - # Emit right - # - # @return [undefined] - # - # @api private - # - def emit_right - return unless right + write(' = ') - write(WS, T_ASN, WS) + if BINARY_OPERATOR.include?(right.type) + writer_with(Writer::Binary, right).emit_operator + else visit(right) end + end - abstract_method :emit_left - - # Variable assignment emitter - class Variable < self - - handle :lvasgn, :ivasgn, :cvasgn, :gvasgn - - children :name, :right - - private - - # Emit left - # - # @return [undefined] - # - # @api private - # - def emit_left - write(name.to_s) - end - - end # Variable - - # Constant assignment emitter - class Constant < self - - handle :casgn - - children :base, :name, :right - - private - - # Emit left - # - # @return [undefined] - # - # @api private - # - def emit_left - if base - visit(base) - write(T_DCL) if base.type != :cbase - end - write(name.to_s) - end + abstract_method :emit_left - end # Constant - end # Single + # Variable assignment emitter + class Variable < self - # Multiple assignment - class Multiple < self - include Unterminated + handle :lvasgn, :ivasgn, :cvasgn, :gvasgn - handle :masgn + children :name, :right private - # Emit left - # - # @return [undefined] - # - # @api private - # def emit_left - visit_plain(first_child) + write(name.to_s) end - # Emit right - # - # @return [undefined] - # - # @api private - # - def emit_right - write(WS, T_ASN, WS) - visit(children.last) - end + end # Variable - end # Multiple + # Constant assignment emitter + class Constant < self - # Emitter for multiple assignment left hand side - class MLHS < Emitter - include Unterminated + handle :casgn - handle :mlhs + children :base, :name, :right private - NO_COMMA = %i[splat restarg].to_set.freeze - PARENT_MLHS = %i[mlhs masgn].freeze - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - delimited(children) - - write(',') if children.one? && mlhs? - end - - # Test for mlhs context - # - # @return [undefined] - # - # @api private - # - def mlhs? - !NO_COMMA.include?(first_child.type) && PARENT_MLHS.include?(parent_type) + def emit_left + if base + visit(base) + write('::') unless n_cbase?(base) + end + write(name.to_s) end - end # MLHS - + end # Constant end # Assignment end # Emitter end # Unparser diff --git a/lib/unparser/emitter/begin.rb b/lib/unparser/emitter/begin.rb index 07496681..8767dbe8 100644 --- a/lib/unparser/emitter/begin.rb +++ b/lib/unparser/emitter/begin.rb @@ -5,97 +5,22 @@ class Emitter # Emitter for begin nodes class Begin < self - + handle :begin children :body - private - - # Emit inner nodes - # - # @return [undefined] - # - # @api private - # - def emit_inner - children.each_with_index do |child, index| - visit_plain(child) - write(NL) if index < children.length - 1 + def emit_heredoc_reminders + children.each do |child| + emitter(child).emit_heredoc_reminders end end - # Emitter for implicit begins - class Implicit < self - - handle :begin - - # Test if begin is terminated - # - # @return [Boolean] - # - # @api private - # - def terminated? - children.empty? - end - - TERMINATING_PARENT = %i[root interpolated dyn_str_body].to_set.freeze - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - if terminated? && !TERMINATING_PARENT.include?(parent_type) - write('()') - else - emit_inner - end - end - - end # Implicit - - # Emitter for explicit begins - class Explicit < self - include Terminated - - handle :kwbegin - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_BEGIN) - emit_body - k_end - end + private - # Emit body - # - # @return [undefined] - # - # @api private - # - def emit_body - if body.nil? - nl - elsif NOINDENT.include?(body.type) - emit_inner - else - indented { emit_inner } - end + def dispatch + parentheses do + delimited(children, '; ') end - - end # Explicit - + end end # Begin end # Emitter end # Unparser diff --git a/lib/unparser/emitter/binary.rb b/lib/unparser/emitter/binary.rb index 26025dcd..8ad482e5 100644 --- a/lib/unparser/emitter/binary.rb +++ b/lib/unparser/emitter/binary.rb @@ -2,33 +2,20 @@ module Unparser class Emitter - # Base class for binary emitters + # Non send binary operator / keyword emitter class Binary < self - include Unterminated - - children :left, :right - - MAP = { - or: T_OR, - and: T_AND - }.freeze - - handle(*MAP.keys) + handle :and, :or private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - visit(left) - write(WS, MAP.fetch(node.type), WS) - visit(right) + writer.dispatch end + def writer + writer_with(Writer::Binary, node) + end + memoize :writer end # Binary end # Emitter end # Unparser diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index f360ada2..0bd00665 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -4,66 +4,82 @@ module Unparser class Emitter # Block emitter - # - # ignore :reek:RepeatedConditional class Block < self - include Terminated - - handle :block + handle :block, :numblock children :target, :arguments, :body private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch emit_target - write(WS, K_DO) - emit_block_arguments unless stabby_lambda? - emit_body - k_end + ws + write_open + target_writer.emit_heredoc_reminders if n_send?(target) + emit_block_arguments unless n_lambda?(target) + emit_optional_body_ensure_rescue(body) + write_close end - # Emit target - # - # @return [undefined] - # - # @api private - # - def emit_target - visit(target) + def need_do? + body && (n_rescue?(body) || n_ensure?(body)) + end + + def write_open + if need_do? + write('do') + else + write('{') + end + end - if stabby_lambda? - parentheses { visit(arguments) } + def write_close + if need_do? + k_end + else + write('}') end end - # Test if we are emitting a stabby lambda - # - # @return [Boolean] - # - # @api private - # - def stabby_lambda? - target.type.equal?(:lambda) + def target_writer + writer_with(Writer::Send::Regular, target) + end + memoize :target_writer + + def emit_target + case target.type + when :send + emit_send_target + when :lambda + visit(target) + emit_lambda_arguments unless node.type.equal?(:numblock) + else + visit(target) + end + end + + def emit_send_target + target_writer.emit_receiver + target_writer.emit_selector + target_writer.emit_arguments_without_heredoc_body + end + + def emit_lambda_arguments + parentheses { writer_with(Args, arguments).emit_lambda_arguments } + end + + def numblock? + node.type.equal?(:numblock) end - # Emit arguments - # - # @return [undefined] - # - # @api private - # def emit_block_arguments - return if arguments.children.empty? + return if numblock? || arguments.children.empty? ws - visit_parentheses(arguments, T_PIPE, T_PIPE) + + parentheses('|', '|') do + writer_with(Args, arguments).emit_block_arguments + end end end # Block diff --git a/lib/unparser/emitter/case.rb b/lib/unparser/emitter/case.rb index 0bc09493..6d0a511f 100644 --- a/lib/unparser/emitter/case.rb +++ b/lib/unparser/emitter/case.rb @@ -4,8 +4,6 @@ module Unparser class Emitter # Emitter for case nodes class Case < self - include Terminated - handle :case children :condition @@ -13,89 +11,49 @@ class Case < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_CASE) + write('case') emit_condition emit_whens emit_else k_end end - # Emit else - # - # @return [undefined] - # - # @api private - # def emit_else else_branch = children.last return unless else_branch - write(K_ELSE) - visit_indented(else_branch) + write('else') + emit_body(else_branch) end - # Emit whens - # - # @return [undefined] - # - # @api private - # def emit_whens nl whens.each(&method(:visit)) end - # Emit condition - # - # @return [undefined] - # - # @api private - # def emit_condition return unless condition - write(WS) + ws visit(condition) end - end # Case # Emitter for when nodes class When < self - include Terminated - handle :when define_group :captures, 0..-2 private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_WHEN, WS) + write('when ') emit_captures - body = children.last - emit_body(body) + emit_optional_body(children.last) end - # Emit captures - # - # @return [undefined] - # - # @api private - # def emit_captures delimited(captures) end diff --git a/lib/unparser/emitter/case_guard.rb b/lib/unparser/emitter/case_guard.rb new file mode 100644 index 00000000..b507a9e8 --- /dev/null +++ b/lib/unparser/emitter/case_guard.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for case guards + class CaseGuard < self + + handle :if_guard, :unless_guard + + MAP = { + if_guard: 'if', + unless_guard: 'unless' + }.freeze + + children :condition + + private + + def dispatch + write(MAP.fetch(node_type)) + ws + visit(condition) + end + + end # UnlessGuard + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/case_match.rb b/lib/unparser/emitter/case_match.rb new file mode 100644 index 00000000..5714f2e5 --- /dev/null +++ b/lib/unparser/emitter/case_match.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for case matches + class CaseMatch < self + + handle :case_match + + children :target + + define_group :patterns, 1..-2 + + private + + def else_branch + children.last + end + + def dispatch + write('case ') + visit(target) + nl + patterns.each(&method(:visit)) + nl unless buffer.fresh_line? + emit_else_branch + k_end + end + + def emit_else_branch + if else_branch + write('else') + emit_body(else_branch) unless n_empty_else?(else_branch) + nl unless buffer.fresh_line? + end + end + + end # CaseMatch + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/cbase.rb b/lib/unparser/emitter/cbase.rb index 0ca9f22d..219c87a9 100644 --- a/lib/unparser/emitter/cbase.rb +++ b/lib/unparser/emitter/cbase.rb @@ -4,8 +4,6 @@ module Unparser class Emitter # Emitter for toplevel constant reference nodes class CBase < self - include Terminated - handle :cbase private @@ -17,7 +15,7 @@ class CBase < self # @api private # def dispatch - write(T_DCL) + write('::') end end # CBase diff --git a/lib/unparser/emitter/class.rb b/lib/unparser/emitter/class.rb index d92f465a..ccdd26bf 100644 --- a/lib/unparser/emitter/class.rb +++ b/lib/unparser/emitter/class.rb @@ -4,7 +4,7 @@ module Unparser class Emitter # Emitter for class nodes class Class < self - include LocalVariableRoot, Terminated + include LocalVariableRoot handle :class @@ -12,30 +12,18 @@ class Class < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_CLASS, WS) + write('class ') visit(name) emit_superclass - emit_body + emit_optional_body(body) k_end end - # Emit superclass - # - # @return [undefined] - # - # @api private - # def emit_superclass return unless superclass - write(WS, T_LT, WS) + write(' < ') visit(superclass) end @@ -43,24 +31,16 @@ def emit_superclass # Emitter for sclass nodes class SClass < self - include Terminated - handle :sclass children :object, :body private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_CLASS, WS, T_DLT, WS) + write('class << ') visit(object) - emit_body + emit_optional_body(body) k_end end diff --git a/lib/unparser/emitter/const_pattern.rb b/lib/unparser/emitter/const_pattern.rb new file mode 100644 index 00000000..f5fcce46 --- /dev/null +++ b/lib/unparser/emitter/const_pattern.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for const pattern node + class ConstPattern < self + + handle :const_pattern + + children :const, :pattern + + private + + def dispatch + visit(const) + if n_hash_pattern?(pattern) + emitter(pattern).emit_const_pattern + else + visit(pattern) + end + end + end # ConstPattern + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/def.rb b/lib/unparser/emitter/def.rb index a10b37c0..7d75ee1c 100644 --- a/lib/unparser/emitter/def.rb +++ b/lib/unparser/emitter/def.rb @@ -4,70 +4,40 @@ module Unparser class Emitter # Emitter for def node class Def < self - include LocalVariableRoot, Terminated + include LocalVariableRoot private - # Emit name - # - # @return [undefined] - # - # @api private - # abstract_method :emit_name private :emit_name - # Return body node - # - # @return [Parser::AST::Node] - # - # @api private - # abstract_method :body private :body - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_DEF, WS) + write('def ') emit_name - comments.consume(node, :name) emit_arguments - emit_body + emit_optional_body_ensure_rescue(body) k_end end - # Emit arguments - # - # @return [undefined] - # - # @api private - # def emit_arguments return if arguments.children.empty? - visit_parentheses(arguments) + parentheses do + writer_with(Args, arguments).emit_def_arguments + end end # Instance def emitter class Instance < self - handle :def children :name, :arguments, :body private - # Emit name - # - # @return [undefined] - # - # @api private - # def emit_name write(name.to_s) end @@ -83,25 +53,13 @@ class Singleton < self private - # Return mame - # - # @return [String] - # - # @api private - # def emit_name conditional_parentheses(!subject_without_parens?) do visit(subject) end - write(T_DOT, name.to_s) + write('.', name.to_s) end - # Test if subject needs parentheses - # - # @return [Boolean] - # - # @api private - # def subject_without_parens? case subject.type when :self @@ -111,8 +69,6 @@ def subject_without_parens? when :send receiver, _selector, *arguments = *subject !receiver && arguments.empty? - else - false end end diff --git a/lib/unparser/emitter/defined.rb b/lib/unparser/emitter/defined.rb index ffa61159..c848741a 100644 --- a/lib/unparser/emitter/defined.rb +++ b/lib/unparser/emitter/defined.rb @@ -4,25 +4,15 @@ module Unparser class Emitter # Emitter for defined? nodes class Defined < self - include Terminated - handle :defined? children :subject private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_DEFINED) - parentheses do - visit(subject) - end + write('defined?') + parentheses { visit(subject) } end end # Defined diff --git a/lib/unparser/emitter/dstr.rb b/lib/unparser/emitter/dstr.rb new file mode 100644 index 00000000..00957873 --- /dev/null +++ b/lib/unparser/emitter/dstr.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Dynamic string emitter + class DStr < self + + handle :dstr + + def emit_heredoc_reminders + writer_with(Writer::DynamicString, node).emit_heredoc_reminder + end + + private + + def dispatch + writer_with(Writer::DynamicString, node).dispatch + end + + end # DStr + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/dsym.rb b/lib/unparser/emitter/dsym.rb new file mode 100644 index 00000000..f8ee4c87 --- /dev/null +++ b/lib/unparser/emitter/dsym.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Dynamic symbol literal emitter + class DSym < self + handle :dsym + + private + + def dispatch + write(':"') + children.each do |child| + case child.type + when :str + emit_str_child(child) + when :begin + emit_begin_child(child) + end + end + write('"') + end + + def emit_str_child(value) + string = value.children.first + if string.end_with?("\n") + write(string.inspect[1..-4]) + nl + else + write(string.inspect[1..-2]) + end + end + + def emit_begin_child(component) + write('#{') + visit(unwrap_single_begin(component)) + write('}') + end + end # DSym + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/empty.rb b/lib/unparser/emitter/empty.rb deleted file mode 100644 index 2837e973..00000000 --- a/lib/unparser/emitter/empty.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - - # Emitter for artifical empty node - class Empty < self - - handle :empty - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch; end - - end - end -end # Unparser diff --git a/lib/unparser/emitter/ensure.rb b/lib/unparser/emitter/ensure.rb deleted file mode 100644 index 320937be..00000000 --- a/lib/unparser/emitter/ensure.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - - # Emitter for ensure nodes - class Ensure < self - - handle :ensure - - children :body, :ensure_body - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - if body - visit_indented(body) - else - nl - end - write(K_ENSURE) - if ensure_body - visit_indented(ensure_body) - else - nl - end - end - - end # Ensure - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/flipflop.rb b/lib/unparser/emitter/flipflop.rb index 07da6f26..d7fd717c 100644 --- a/lib/unparser/emitter/flipflop.rb +++ b/lib/unparser/emitter/flipflop.rb @@ -4,12 +4,19 @@ module Unparser class Emitter # Emitter for flip flops class FlipFlop < self - include Unterminated - - MAP = IceNine.deep_freeze( + MAP = { iflipflop: '..', eflipflop: '...' - ).freeze + }.freeze + + SYMBOLS = { + eflipflop: :tDOT3, + iflipflop: :tDOT2 + }.freeze + + def symbol_name + true + end handle(*MAP.keys) @@ -17,12 +24,6 @@ class FlipFlop < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch visit(left) write(MAP.fetch(node.type)) diff --git a/lib/unparser/emitter/float.rb b/lib/unparser/emitter/float.rb new file mode 100644 index 00000000..4be6023c --- /dev/null +++ b/lib/unparser/emitter/float.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emiter for float literals + class Float < self + handle :float + + children :value + + INFINITY = ::Float::INFINITY + NEG_INFINITY = -::Float::INFINITY + + private + + def dispatch + if value.eql?(INFINITY) + write('10e1000000000000000000') + elsif value.eql?(NEG_INFINITY) + write('-10e1000000000000000000') + else + write(value.inspect) + end + end + + end # Float + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index 6fec1510..93c81f4d 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -4,77 +4,30 @@ module Unparser class Emitter # Emitter control flow modifiers class FlowModifier < self - MAP = { - return: K_RETURN, - next: K_NEXT, - break: K_BREAK + return: 'return', + next: 'next', + break: 'break' }.freeze - handle(*MAP.keys) + private_constant(*constants(false)) - def terminated? - children.empty? - end + handle(*MAP.keys) private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write(MAP.fetch(node.type)) - case children.length - when 0 # rubocop:disable Lint/EmptyWhen - when 1 - emit_single_argument - else + + unless children.empty? emit_arguments end end - # Emit break or return arguments - # - # @return [undefined] - # - # @api private - # def emit_arguments ws - head, *tail = children - emit_argument(head) - tail.each do |node| - write(DEFAULT_DELIMITER) - emit_argument(node) - end - end - - PARENS = %i[if case begin].to_set.freeze - - # Emit argument - # - # @param [Parser::AST::Node] node - # - # @api private - # - def emit_argument(node) - visit_plain(node) - end - - # Emit single argument - # - # @api private - # - def emit_single_argument - ws - conditional_parentheses(PARENS.include?(first_child.type)) do - visit_plain(first_child) - end + delimited(children) end - end # Return end # Emitter end # Unparser diff --git a/lib/unparser/emitter/for.rb b/lib/unparser/emitter/for.rb index 1e61db2b..5532eb57 100644 --- a/lib/unparser/emitter/for.rb +++ b/lib/unparser/emitter/for.rb @@ -4,38 +4,24 @@ module Unparser class Emitter # Emitter for for nodes class For < self - include Terminated - handle :for children :condition, :assignment, :body private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_FOR, WS) + write('for ') emit_condition - emit_body + emit_optional_body(body) k_end end - # Emit assignment - # - # @return [undefined] - # - # @api private - # def emit_condition - visit_plain(condition) - write(WS, K_IN, WS) + visit(condition) + write(' in ') visit(assignment) - write(WS, K_DO) + write(' do') end end # For diff --git a/lib/unparser/emitter/hash.rb b/lib/unparser/emitter/hash.rb new file mode 100644 index 00000000..8d05dca0 --- /dev/null +++ b/lib/unparser/emitter/hash.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for Hash literals + class Hash < self + BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze + + private_constant(*constants(false)) + + handle :hash + + def emit_last_argument_hash + if children.empty? + write('{}') + else + emit_hash_body + end + end + + def emit_heredoc_reminders + children.each(&method(:emit_heredoc_reminder_member)) + end + + private + + def dispatch + if children.empty? + write('{}') + else + parentheses('{', '}') do + write(' ') + emit_hash_body + write(' ') + end + end + end + + def emit_heredoc_reminder_member(node) + emitter(node.children.last).emit_heredoc_reminders + end + + def emit_hash_body + delimited(children, &method(:emit_hash_member)) + end + + def emit_hash_member(node) + if n_kwsplat?(node) + visit(node) + else + emit_pair(node) + end + end + + def emit_pair(pair) + key, value = *pair.children + + if colon?(key) + write(key.children.first.to_s, ': ') + else + visit(key) + write(' => ') + end + + visit(value) + end + + def colon?(key) + n_sym?(key) && BAREWORD.match?(key.children.first) + end + + end # Hash + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/hash_pattern.rb b/lib/unparser/emitter/hash_pattern.rb new file mode 100644 index 00000000..9e68dffb --- /dev/null +++ b/lib/unparser/emitter/hash_pattern.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for hash patterns + class HashPattern < self + + handle :hash_pattern + + def emit_const_pattern + parentheses do + emit_hash_body + end + end + + private + + def dispatch + parentheses('{', '}') do + emit_hash_body + end + end + + def emit_hash_body + delimited(children, &method(:emit_member)) + end + + def emit_member(node) + case node.type + when :pair + emit_pair(node) + when :match_var + emit_match_var(node) + when :match_rest + writer_with(MatchRest, node).emit_hash_pattern + else + visit(node) + end + end + + def emit_match_var(node) + write_symbol_body(node.children.first) + write(':') + end + + def emit_pair(node) + key, value = node.children + + if n_sym?(key) + write_symbol_body(key.children.first) + else + visit(s(:dstr, *key)) + end + + write(':') + + ws + + visit(value) + end + + def write_symbol_body(symbol) + write(symbol.inspect[1..-1]) + end + end # Pin + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/hookexe.rb b/lib/unparser/emitter/hookexe.rb index bb71d1e9..de2a2e8d 100644 --- a/lib/unparser/emitter/hookexe.rb +++ b/lib/unparser/emitter/hookexe.rb @@ -6,8 +6,8 @@ class Emitter class Hookexe < self MAP = { - preexe: K_PREEXE, - postexe: K_POSTEXE + preexe: 'BEGIN', + postexe: 'END' }.freeze handle(*MAP.keys) @@ -16,16 +16,10 @@ class Hookexe < self private - # Perfrom dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(MAP.fetch(node.type), WS) - parentheses(*BRACKETS_CURLY) do - emit_body + write(MAP.fetch(node.type), ' ') + parentheses('{', '}') do + emit_body(body) end end diff --git a/lib/unparser/emitter/if.rb b/lib/unparser/emitter/if.rb index 11827c3a..91ce510b 100644 --- a/lib/unparser/emitter/if.rb +++ b/lib/unparser/emitter/if.rb @@ -3,25 +3,13 @@ module Unparser class Emitter # Emitter if nodes - # - # ignore :reek:RepeatedConditional class If < self handle :if children :condition, :if_branch, :else_branch - def terminated? - !postcondition? - end - private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch if postcondition? emit_postcondition @@ -30,105 +18,53 @@ def dispatch end end - # Test for postcondition - # - # @return [Boolean] - # - # @api private - # def postcondition? return false unless if_branch.nil? ^ else_branch.nil? body = if_branch || else_branch - local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition) + local_variable_scope.first_assignment_in?(body, condition) end - # Emit in postcondition style - # - # @return [undefined] - # - # @api private - # def emit_postcondition - visit_plain(if_branch || else_branch) - write(WS, keyword, WS) + visit(if_branch || else_branch) + write(' ', keyword, ' ') emit_condition end - # Emit in normal style - # - # @return [undefined] - # - # @api private - # def emit_normal - write(keyword, WS) + write(keyword, ' ') emit_condition emit_if_branch emit_else_branch k_end end - # Test if AST can be emitted as unless - # - # @return [Boolean] - # - # @api private - # def unless? !if_branch && else_branch end - # Return keyword - # - # @return [String] - # - # @api private - # def keyword - unless? ? K_UNLESS : K_IF + unless? ? 'unless' : 'if' end - # Emit condition - # - # @return [undefined] - # - # @api private - # def emit_condition - if condition.type.equal?(:match_current_line) - visit_plain(condition) - else - visit(condition) - end + visit(condition) end - # Emit if branch - # - # @return [undefined] - # - # @api private - # def emit_if_branch if if_branch - visit_indented(if_branch) + emit_body(if_branch) end nl if !if_branch && !else_branch end - # Emit else branch - # - # @return [undefined] - # - # @api private - # def emit_else_branch return unless else_branch - write(K_ELSE) unless unless? - visit_indented(else_branch) + write('else') unless unless? + emit_body(else_branch) end end # If diff --git a/lib/unparser/emitter/in_match.rb b/lib/unparser/emitter/in_match.rb new file mode 100644 index 00000000..5027fa0e --- /dev/null +++ b/lib/unparser/emitter/in_match.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class InMatch < self + + handle :in_match + + children :target, :pattern + + private + + def dispatch + visit(target) + write(' in ') + visit(pattern) + end + end # InMatch + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/in_pattern.rb b/lib/unparser/emitter/in_pattern.rb new file mode 100644 index 00000000..27a090ea --- /dev/null +++ b/lib/unparser/emitter/in_pattern.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class InPattern < self + + handle :in_pattern + + children :target, :unless_guard, :branch, :else_branch + + private + + def dispatch + write('in') + + ws + + visit(target) + + if unless_guard + ws + visit(unless_guard) + end + + if branch + ws + write('then') + emit_body(branch) + end + end + end # InPattern + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/index.rb b/lib/unparser/emitter/index.rb index 42efeebe..e509827c 100644 --- a/lib/unparser/emitter/index.rb +++ b/lib/unparser/emitter/index.rb @@ -3,61 +3,29 @@ module Unparser class Emitter # Emitter for send to index references - # - # ignore :reek:RepeatedConditional class Index < self - # Perform dispatch - # - # @return [undefined] - # - # @api private - # + private + def dispatch emit_receiver emit_operation end - private - - # Emit receiver - # - # @return [undefined] - # - # @api private - # def emit_receiver visit(first_child) end - # Test for mlhs - # - # @return [Boolean] - # - # @api private - # - def mlhs? - parent_type.equal?(:mlhs) - end - class Reference < self - include Terminated - define_group(:indices, 1..-1) handle :index private - # Emit arguments - # - # @return [undefined] - # - # @api private - # def emit_operation - parentheses(*BRACKETS_SQUARE) do - delimited_plain(indices) + parentheses('[', ']') do + delimited(indices) end end end # Reference @@ -70,65 +38,30 @@ class Assign < self VALUE_RANGE = (1..-2).freeze NO_VALUE_PARENT = IceNine.deep_freeze(%i[and_asgn op_asgn or_asgn].to_set) - # Test if assign will be emitted terminated - # - # @return [Boolean] - # - # @api private - # - def terminated? - !emit_value? - end - - private + private_constant(*constants(false)) - # Emit arguments - # - # @return [undefined] - # - # @api private - # - def emit_operation - parentheses(*BRACKETS_SQUARE) do - delimited_plain(indices) - end - - if emit_value? - write(WS, T_ASN, WS) - visit(children.last) - end + def emit_heredoc_reminders + emitter(children.last).emit_heredoc_reminders end - # The indices - # - # @return [Array] - # - def indices - if emit_value? - children[VALUE_RANGE] - else - children.drop(1) - end + def dispatch + emit_receiver + emit_operation(children[VALUE_RANGE]) + write(' = ') + visit(children.last) end - # Test if value should be emitted - # - # @return [Boolean] - # - # @api private - # - def emit_value? - !mlhs? && !no_value_parent? + def emit_mlhs + emit_receiver + emit_operation(children.drop(1)) end - # Test for no value parent - # - # @return [Boolean] - # - # @api private - # - def no_value_parent? - NO_VALUE_PARENT.include?(parent_type) + private + + def emit_operation(indices) + parentheses('[', ']') do + delimited(indices) + end end end # Assign end # Index diff --git a/lib/unparser/emitter/kwbegin.rb b/lib/unparser/emitter/kwbegin.rb new file mode 100644 index 00000000..06fd7122 --- /dev/null +++ b/lib/unparser/emitter/kwbegin.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for explicit begins + class KWBegin < self + handle :kwbegin + + private + + def dispatch + write('begin') + + if children.one? + emit_body_ensure_rescue(children.first) + else + indented do + emit_multiple_body + end + end + + k_end + end + + def emit_multiple_body + emit_join(children, method(:emit_body_member), method(:nl)) + end + + end # KWBegin + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/lambda.rb b/lib/unparser/emitter/lambda.rb index 2bcb4323..4e3bb735 100644 --- a/lib/unparser/emitter/lambda.rb +++ b/lib/unparser/emitter/lambda.rb @@ -4,18 +4,10 @@ module Unparser class Emitter # Emitter for lambda nodes class Lambda < self - include Terminated - handle :lambda private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write('->') end diff --git a/lib/unparser/emitter/literal.rb b/lib/unparser/emitter/literal.rb deleted file mode 100644 index 504a348d..00000000 --- a/lib/unparser/emitter/literal.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - # Namespace class for literal emiters - class Literal < self - include Terminated - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/array.rb b/lib/unparser/emitter/literal/array.rb deleted file mode 100644 index a55345aa..00000000 --- a/lib/unparser/emitter/literal/array.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Array literal emitter - class Array < self - handle :array - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - parentheses(*BRACKETS_SQUARE) do - delimited(children) - end - end - - end # Array - - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/dynamic.rb b/lib/unparser/emitter/literal/dynamic.rb deleted file mode 100644 index 24d89c03..00000000 --- a/lib/unparser/emitter/literal/dynamic.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Base class for dynamic literal emitters - class Dynamic < self - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - util = self.class - visit_parentheses(dynamic_body, util::OPEN, util::CLOSE) - end - - # Return dynamic body - # - # @return [Parser::AST::Node] - # - # @api private - # - def dynamic_body - Parser::AST::Node.new(:dyn_str_body, children) - end - - # Dynamic string literal emitter - class String < self - - OPEN = CLOSE = '"'.freeze - handle :dstr - - end # String - - # Dynamic symbol literal emitter - class Symbol < self - - OPEN = ':"'.freeze - CLOSE = '"'.freeze - - handle :dsym - - end # Symbol - end # Dynamic - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/dynamic_body.rb b/lib/unparser/emitter/literal/dynamic_body.rb deleted file mode 100644 index 8bdf559f..00000000 --- a/lib/unparser/emitter/literal/dynamic_body.rb +++ /dev/null @@ -1,132 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Emitter for dynamic bodies - class DynamicBody < self - - OPEN = '#{'.freeze - CLOSE = '}'.freeze - - # Emitter for interpolated nodes - class Interpolation < self - - handle :interpolated - - children :subject - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(OPEN) - visit_plain(subject) - write(CLOSE) - end - - end - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - children.each(&method(:emit_segment)) - end - - # Emit segment - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def emit_segment(node) - emit_interpolated_segment(node) - end - - pairs = Parser::Lexer::ESCAPES.invert.map do |key, value| - [key, "\\#{value}"] unless key.eql?(WS) - end.compact - - pairs << ['#{', '\#{'] - - ESCAPES = ::Hash[pairs] - - REPLACEMENTS = ::Regexp.union(ESCAPES.keys) - - # Emit str segment - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def emit_str_segment(node) - util = self.class - string = node.children.first - segment = string - .gsub(REPLACEMENTS, ESCAPES) - .gsub(util::DELIMITER, util::REPLACEMENT) - write(segment) - end - - # Emit interpolated segment - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def emit_interpolated_segment(node) - visit_parentheses(node, OPEN, CLOSE) - end - - # Dynamic string body - class String < self - - handle :dyn_str_body - - DELIMITER = '"'.freeze - REPLACEMENT = '\"'.freeze - - end # String - - # Dynamic regexp body - class Regexp < self - - handle :dyn_regexp_body - - DELIMITER = '/'.freeze - REPLACEMENT = '\/'.freeze - - end # Regexp - - # Dynamic regexp body - class ExecuteString < self - - handle :dyn_xstr_body - - DELIMITER = '`'.freeze - REPLACEMENT = '\`'.freeze - - end # ExecuteString - - end # DynamicBody - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/execute_string.rb b/lib/unparser/emitter/literal/execute_string.rb deleted file mode 100644 index 64b41764..00000000 --- a/lib/unparser/emitter/literal/execute_string.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - # Emitter for execute strings (xstr) nodes - class ExecuteString < self - - OPEN = CLOSE = '`'.freeze - - handle :xstr - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - visit_parentheses(dynamic_body, OPEN, CLOSE) - end - - # Return dynamic body - # - # @return [Parser::AST::Node] - # - # @api private - # - def dynamic_body - Parser::AST::Node.new(:dyn_xstr_body, children) - end - - end # ExecuteString - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/hash.rb b/lib/unparser/emitter/literal/hash.rb deleted file mode 100644 index fb958ce4..00000000 --- a/lib/unparser/emitter/literal/hash.rb +++ /dev/null @@ -1,156 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Abstract namespace class for hash pair emitters - class HashPair < self - - children :key, :value - - private - - # Emit value - # - # @return [undefined] - # - # @api private - # - def emit_value - value_type = value.type - conditional_parentheses(value_type.equal?(:if)) do - visit(value) - end - end - - # Pair emitter that emits hash-rocket separated key values - class Rocket < self - HASHROCKET = ' => '.freeze - - handle :pair_rocket, :pair - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - visit(key) - write(HASHROCKET) - emit_value - end - - end # Rocket - - # Pair emitter that emits colon separated key values - class Colon < self - COLON = ': '.freeze - - handle :pair_colon - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(key.children.first.to_s, COLON) - emit_value - end - - end # Colon - - end # HashPair - - # Emitter for hash bodies - class HashBody < self - - BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze - - handle :hash_body - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - delimited(effective_body) - end - - # Return effective body - # - # @return [Enumerable] - # - # @api private - # - def effective_body - children.map do |pair| - if pair.type.equal?(:kwsplat) - pair - else - key, _value = *pair - if key.type.equal?(:sym) && key.children.first.to_s =~ BAREWORD - n(:pair_colon, pair.children) - else - n(:pair_rocket, pair.children) - end - end - end - end - - end # HashBody - - # Emitter for Hash literals - class Hash < self - - DELIMITER = ', '.freeze - OPEN = '{'.freeze - CLOSE = '}'.freeze - - handle :hash - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - if children.empty? - write(OPEN, CLOSE) - else - emit_hash_body - end - end - - # Emit hash body - # - # @return [undefined] - # - # @api private - # - def emit_hash_body - parentheses(OPEN, CLOSE) do - write(WS) - run(HashBody) - write(WS) - end - end - - end # Hash - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/primitive.rb b/lib/unparser/emitter/literal/primitive.rb deleted file mode 100644 index a9b69ac6..00000000 --- a/lib/unparser/emitter/literal/primitive.rb +++ /dev/null @@ -1,145 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Base class for primitive emitters - class Primitive < self - - children :value - - # Emitter for primitives based on Object#inspect - class Inspect < self - - handle :sym, :str - - private - - # Dispatch value - # - # @return [undefined] - # - # @api private - # - def dispatch - write(value.inspect) - end - - end # Inspect - - # Emitter for complex literals - class Complex < self - - handle :complex - - RATIONAL_FORMAT = 'i'.freeze - - MAP = - if 0.class.equal?(Integer) - IceNine.deep_freeze( - Float => :float, - Rational => :rational, - Integer => :int - ) - else - IceNine.deep_freeze( - Float => :float, - Rational => :rational, - Fixnum => :int, - Bignum => :int - ) - end - - private - - # Dispatch value - # - # @return [undefined] - # - def dispatch - emit_imaginary - write(RATIONAL_FORMAT) - end - - # Emit imaginary component - # - # @return [undefined] - # - # @api private - # - def emit_imaginary - visit(imaginary_node) - end - - # Return imaginary node - # - # @return [Parser::AST::Node] - # - # @api private - # - def imaginary_node - imaginary = value.imaginary - s(MAP.fetch(imaginary.class), imaginary) - end - - end # Rational - - # Emitter for rational literals - class Rational < self - - handle :rational - - RATIONAL_FORMAT = 'r'.freeze - - private - - # Dispatch value - # - # @return [undefined] - # - def dispatch - integer = value.to_i - float = value.to_f - - write_rational(integer.to_f.eql?(float) ? integer : float) - end - - # Write rational format - # - # @param [#to_s] value - # - # @return [undefined] - # - # @api private - # - def write_rational(value) - write(value.to_s, RATIONAL_FORMAT) - end - - end # Rational - - # Emiter for numeric literals - class Numeric < self - - handle :int, :float - - private - - # Dispatch value - # - # @return [undefined] - # - # @api private - # - def dispatch - conditional_parentheses(parent.is_a?(Send) && value.negative?) do - write(value.inspect) - end - end - - end # Numeric - end # Primitive - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/range.rb b/lib/unparser/emitter/literal/range.rb deleted file mode 100644 index a1160ba6..00000000 --- a/lib/unparser/emitter/literal/range.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - # Abstract base class for literal range emitter - class Range < self - include Unterminated - - TOKENS = IceNine.deep_freeze( - irange: '..', - erange: '...' - ) - - handle(*TOKENS.keys) - - children :begin_node, :end_node - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - visit(begin_node) - write(TOKENS.fetch(node.type)) - visit(end_node) if end_node - end - - end # Range - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/regexp.rb b/lib/unparser/emitter/literal/regexp.rb deleted file mode 100644 index 13db5ea2..00000000 --- a/lib/unparser/emitter/literal/regexp.rb +++ /dev/null @@ -1,114 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - - # Emitter for regexp literals - class Regexp < self - DELIMITER = '/'.freeze - ESCAPED_DELIMITER = '\/'.freeze - - handle :regexp - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - parentheses(DELIMITER, DELIMITER) do - body.each(&method(:write_body)) - end - visit(children.last) - end - - # Return non regopt children - # - # @return [Array] - # - # @api private - # - def body - children[0..-2] - end - - # Write specific body component - # - # @param [Parser::AST::Node] node - # - # @return [undefined] - # - # @api private - # - def write_body(node) - case node.type - when :str - buffer.append_without_prefix(escape(node).children.first) - else - visit(s(:interpolated, node)) - end - end - - # Return dynamic body - # - # @return [undefined] - # - # @api private - # - def dynamic_body - Parser::AST::Node.new(:dyn_regexp_body, dynamic_body_children) - end - - # Return dynamic body children - # - # @return [Enumerable] - # - # @api private - # - def dynamic_body_children - children[0..-2].map do |child| - escape(child) - end - end - - # Return escaped child - # - # @param [Parser::AST::Node] child - # - # @return [Parser::AST::Node] - # - # @api private - # - def escape(child) - source = child.children.first - s(:str, source.gsub(DELIMITER, ESCAPED_DELIMITER)) - end - - end # Regexp - - # Emitter for regexp options - class Regopt < self - - handle :regopt - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(children.map(&:to_s).join) - end - - end # Regopt - - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/literal/singleton.rb b/lib/unparser/emitter/literal/singleton.rb deleted file mode 100644 index cf664d41..00000000 --- a/lib/unparser/emitter/literal/singleton.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Literal - # Emiter for literal singletons - class Singleton < self - - handle :self, :true, :false, :nil - - private - - # Perform dispatco - # - # @return [undefined] - # - # @api private - # - def dispatch - buffer.append(node.type.to_s) - end - - end # Singleton - end # Literal - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/masgn.rb b/lib/unparser/emitter/masgn.rb new file mode 100644 index 00000000..7eb2d49a --- /dev/null +++ b/lib/unparser/emitter/masgn.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for multiple assignment nodes + class MASGN < self + handle :masgn + + children :target, :source + + private + + def dispatch + visit(target) + write(' = ') + visit(source) + end + end # MLHS + end # Emitter +end # Unaprser diff --git a/lib/unparser/emitter/match.rb b/lib/unparser/emitter/match.rb index 1af64185..ec37bf38 100644 --- a/lib/unparser/emitter/match.rb +++ b/lib/unparser/emitter/match.rb @@ -5,10 +5,6 @@ class Emitter # Base class for special match node emitters class Match < self - include Unterminated - - OPERATOR = '=~'.freeze - # Emitter for match with local variable assignment class Lvasgn < self handle :match_with_lvasgn @@ -17,15 +13,9 @@ class Lvasgn < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch visit(regexp) - write(WS, OPERATOR, WS) + write(' =~ ') visit(lvasgn) end @@ -37,12 +27,8 @@ class CurrentLine < self children :regexp - # Perform dispatch - # - # @return [undefined] - # - # @api private - # + private + def dispatch visit(regexp) end diff --git a/lib/unparser/emitter/match_alt.rb b/lib/unparser/emitter/match_alt.rb new file mode 100644 index 00000000..ff877abe --- /dev/null +++ b/lib/unparser/emitter/match_alt.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class MatchAlt < self + + handle :match_alt + + children :left, :right + + private + + def dispatch + visit(left) + ws + write('|') + ws + visit(right) + end + end # MatchAlt + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/match_as.rb b/lib/unparser/emitter/match_as.rb new file mode 100644 index 00000000..59eb0d8d --- /dev/null +++ b/lib/unparser/emitter/match_as.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class MatchAs < self + + handle :match_as + + children :left, :right + + private + + def dispatch + visit(left) + write(' => ') + visit(right) + end + end # MatchAs + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/match_rest.rb b/lib/unparser/emitter/match_rest.rb new file mode 100644 index 00000000..311955aa --- /dev/null +++ b/lib/unparser/emitter/match_rest.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emiter for match rest nodes + class MatchRest < self + children :match_var + + def emit_array_pattern + write('*') + emit_match_var + end + + def emit_hash_pattern + write('**') + emit_match_var + end + + private + + def emit_match_var + visit(match_var) if match_var + end + end # MatchRest + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/match_var.rb b/lib/unparser/emitter/match_var.rb new file mode 100644 index 00000000..5c895901 --- /dev/null +++ b/lib/unparser/emitter/match_var.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class MatchVar < self + + handle :match_var + + children :name + + private + + def dispatch + write(name.to_s) + end + end # MatchVar + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/meta.rb b/lib/unparser/emitter/meta.rb deleted file mode 100644 index d84078d3..00000000 --- a/lib/unparser/emitter/meta.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - # Namespace class for meta emitters - class Meta < self - include Terminated - - handle(:__ENCODING__, :__FILE__, :__LINE__) - - def dispatch - write(node.type.to_s) - end - end # Meta - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/mlhs.rb b/lib/unparser/emitter/mlhs.rb new file mode 100644 index 00000000..c8835395 --- /dev/null +++ b/lib/unparser/emitter/mlhs.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for multiple assignment left hand side + class MLHS < self + handle :mlhs + + NO_COMMA = %i[arg splat mlhs restarg].freeze + + private_constant(*constants(false)) + + private + + def dispatch + if children.one? + emit_one_child_mlhs + else + emit_many + end + end + + def emit_one_child_mlhs + child = children.first + parentheses do + emitter(child).emit_mlhs + write(',') unless NO_COMMA.include?(child.type) + end + end + + def emit_many + parentheses do + delimited(children) do |node| + emitter(node).emit_mlhs + end + end + end + end # MLHS + end # Emitter +end # Unaprser diff --git a/lib/unparser/emitter/module.rb b/lib/unparser/emitter/module.rb index 364e2480..06b3d9b3 100644 --- a/lib/unparser/emitter/module.rb +++ b/lib/unparser/emitter/module.rb @@ -4,7 +4,7 @@ module Unparser class Emitter # Emitter for module nodes class Module < self - include LocalVariableRoot, Terminated + include LocalVariableRoot handle :module @@ -12,16 +12,10 @@ class Module < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_MODULE, WS) + write('module ') visit(name) - emit_body + emit_optional_body(body) k_end end diff --git a/lib/unparser/emitter/op_assign.rb b/lib/unparser/emitter/op_assign.rb index 0a2d91ec..b10d7a8d 100644 --- a/lib/unparser/emitter/op_assign.rb +++ b/lib/unparser/emitter/op_assign.rb @@ -5,8 +5,6 @@ class Emitter # Base class for and and or op-assign class BinaryAssign < self - include Unterminated - children :target, :expression MAP = IceNine.deep_freeze( @@ -16,17 +14,16 @@ class BinaryAssign < self handle(*MAP.keys) + def emit_heredoc_reminders + emitter(target).emit_heredoc_reminders + emitter(expression).emit_heredoc_reminders + end + private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - visit(target) - write(WS, MAP.fetch(node.type), WS) + emitter(target).emit_mlhs + write(' ', MAP.fetch(node.type), ' ') visit(expression) end @@ -34,32 +31,20 @@ def dispatch # Emitter for op assign class OpAssign < self - include Unterminated - handle :op_asgn + children :target, :operator, :value + private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - visit(first_child) + emitter(first_child).emit_mlhs emit_operator - visit(children[2]) + visit(value) end - # Emit operator - # - # @return [undefined] - # - # @api private - # def emit_operator - write(WS, children[1].to_s, T_ASN, WS) + write(' ', operator.to_s, '= ') end end # OpAssign diff --git a/lib/unparser/emitter/pin.rb b/lib/unparser/emitter/pin.rb new file mode 100644 index 00000000..444469ea --- /dev/null +++ b/lib/unparser/emitter/pin.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for pin nodes + class Pin < self + handle :pin + + children :target + + private + + def dispatch + write('^') + visit(target) + end + end # Pin + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb new file mode 100644 index 00000000..5774f38f --- /dev/null +++ b/lib/unparser/emitter/primitive.rb @@ -0,0 +1,91 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Base class for primitive emitters + class Primitive < self + + children :value + + # Emitter for primitives based on Object#inspect + class Inspect < self + + handle :sym, :str + + private + + def dispatch + write(value.inspect) + end + + end # Inspect + + # Emitter for complex literals + class Complex < self + + handle :complex + + RATIONAL_FORMAT = 'i'.freeze + + MAP = + IceNine.deep_freeze( + ::Float => :float, + ::Rational => :rational, + ::Integer => :int + ) + + private + + def dispatch + emit_imaginary + write(RATIONAL_FORMAT) + end + + def emit_imaginary + visit(imaginary_node) + end + + def imaginary_node + imaginary = value.imaginary + s(MAP.fetch(imaginary.class), imaginary) + end + + end # Rational + + # Emitter for rational literals + class Rational < self + + handle :rational + + RATIONAL_FORMAT = 'r'.freeze + + private + + def dispatch + integer = Integer(value) + float = value.to_f + + write_rational(integer.to_f.equal?(float) ? integer : float) + end + + def write_rational(value) + write(value.to_s, RATIONAL_FORMAT) + end + + end # Rational + + # Emiter for numeric literals + class Numeric < self + + handle :int + + private + + def dispatch + write(value.inspect) + end + + end # Numeric + end # Primitive + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/range.rb b/lib/unparser/emitter/range.rb new file mode 100644 index 00000000..8db822ef --- /dev/null +++ b/lib/unparser/emitter/range.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Range emitters + class Range < self + TOKENS = { + irange: '..', + erange: '...' + }.freeze + + SYMBOLS = { + erange: :tDOT3, + irange: :tDOT2 + }.freeze + + def symbol_name + true + end + + handle(*TOKENS.keys) + + children :begin_node, :end_node + + private + + def dispatch + visit(begin_node) if begin_node + write(TOKENS.fetch(node.type)) + visit(end_node) if end_node + end + + end # Range + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/redo.rb b/lib/unparser/emitter/redo.rb deleted file mode 100644 index 93faa1ae..00000000 --- a/lib/unparser/emitter/redo.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - # Emitter for redo nodes - class Redo < self - include Terminated - - handle :redo - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_REDO) - end - - end # Redo - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/regexp.rb b/lib/unparser/emitter/regexp.rb new file mode 100644 index 00000000..a9bdc94e --- /dev/null +++ b/lib/unparser/emitter/regexp.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for regexp literals + class Regexp < self + handle :regexp + + define_group(:body, 0..-2) + + private + + def dispatch + parentheses('/', '/') do + body.each(&method(:emit_body)) + end + emit_options + end + + def emit_options + write(children.last.children.join) + end + + def emit_body(node) + if n_begin?(node) + write('#{') + node.children.each(&method(:visit)) + write('}') + else + buffer.append_without_prefix(node.children.first.gsub('/', '\/')) + end + end + end # Regexp + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/repetition.rb b/lib/unparser/emitter/repetition.rb index 870028f9..9ecb9934 100644 --- a/lib/unparser/emitter/repetition.rb +++ b/lib/unparser/emitter/repetition.rb @@ -5,39 +5,29 @@ class Emitter # Emitter for postconditions class Post < self - include Unterminated - - handle :while_post, :until_post - children :condition, :body MAP = { - while_post: K_WHILE, - until_post: K_UNTIL + while_post: 'while', + until_post: 'until' }.freeze handle(*MAP.keys) - # Perform dispatch - # - # @return [undefined] - # - # @api private - # + private + def dispatch visit(body) - write(WS, MAP.fetch(node.type), WS) + write(' ', MAP.fetch(node.type), ' ') visit(condition) end end - # Base class for while and until emitters + # Emitter for while and until nodes class Repetition < self - include Terminated - MAP = { - while: K_WHILE, - until: K_UNTIL + while: 'while', + until: 'until' }.freeze handle(*MAP.keys) @@ -46,12 +36,6 @@ class Repetition < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch if postcontrol? emit_postcontrol @@ -60,54 +44,30 @@ def dispatch end end - # Test if node must be emitted in postcontrol form - # - # @return [Boolean] - # - # @api private - # def postcontrol? - return nil unless body # greez from falsyness - - local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition) + body && local_variable_scope.first_assignment_in?(body, condition) end - # Emit keyword - # - # @return [undefined] - # - # @api private - # def emit_keyword - write(MAP.fetch(node.type), WS) + write(MAP.fetch(node.type), ' ') end - # Emit embedded - # - # @return [undefned] - # - # @api private - # def emit_normal emit_keyword - conditional_parentheses(condition.type.equal?(:block)) do - visit(condition) + visit(condition) + if body + emit_body(body) + else + nl end - emit_body k_end end - # Emit postcontrol - # - # @return [undefined] - # - # @api private - # def emit_postcontrol - visit_plain(body) + visit(body) ws emit_keyword - visit_plain(condition) + visit(condition) end end # Repetition diff --git a/lib/unparser/emitter/resbody.rb b/lib/unparser/emitter/resbody.rb deleted file mode 100644 index 33494ebc..00000000 --- a/lib/unparser/emitter/resbody.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - # Emitter for rescue body nodes - class Resbody < self - - children :exception, :assignment, :body - - # Emitter for resbody in standalone form - class Standalone < self - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_RESCUE, WS) - visit_plain(body) - end - end - - # Emitter for resbody in keyworkd-embedded form - class Embedded < self - - handle :resbody - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_RESCUE) - emit_exception - emit_assignment - emit_body - end - - # Emit exception - # - # @return [undefined] - # - # @api private - # - def emit_exception - return unless exception - - ws - delimited(exception.children) - end - - # Emit assignment - # - # @return [undefined] - # - # @api private - # - def emit_assignment - return unless assignment - - write(WS, T_ASR, WS) - visit(assignment) - end - - end # Resbody - end - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/rescue.rb b/lib/unparser/emitter/rescue.rb index 0846aa36..e4ca0e32 100644 --- a/lib/unparser/emitter/rescue.rb +++ b/lib/unparser/emitter/rescue.rb @@ -4,109 +4,13 @@ module Unparser class Emitter # Emitter for rescue nodes class Rescue < self - include Unterminated - handle :rescue - children :body, :rescue_body - - define_group :rescue_bodies, 1..-2 - - EMBEDDED_TYPES = %i[block def defs kwbegin ensure].to_set.freeze - - NOINDENT_STANDALONE_RESCUE = %i[root begin pair_rocket pair_colon lvasgn ivasgn].to_set.freeze - private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - if standalone? - if NOINDENT_STANDALONE_RESCUE.include?(parent_type) - emit_standalone - else - indented { emit_standalone } - end - else - emit_embedded - end - end - - # Test if rescue node ist standalone - # - # @return [Boolean] - # - # @api private - # - def standalone? - if parent_type.equal?(:ensure) - !parent.node.children.first.equal?(node) - else - !EMBEDDED_TYPES.include?(parent_type) - end - end - - # Emit standalone form - # - # @return [undefined] - # - # @api private - # - def emit_standalone - visit_plain(body) - ws - run(Resbody::Standalone, rescue_body) + emit_rescue_postcontrol(node) end - - # Emit embedded form - # - # @return [undefined] - # - # @api private - # - def emit_embedded - if body - visit_indented(body) - else - nl - end - rescue_bodies.each do |child| - run(Resbody::Embedded, child) - end - emit_else - end - - # Emit else - # - # @return [undefined] - # - # @api private - # - def emit_else - return unless else_branch - - write(K_ELSE) - visit_indented(else_branch) - end - - # Return else body - # - # @return [Parser::AST::Node] - # if else body is present - # - # @return [nil] - # otherwise - # - # @api private - # - def else_branch - children.last - end - end # Rescue end # Emitter end # Unparser diff --git a/lib/unparser/emitter/retry.rb b/lib/unparser/emitter/retry.rb deleted file mode 100644 index c8ab4bfd..00000000 --- a/lib/unparser/emitter/retry.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - # Emitter for retry nodes - class Retry < self - include Terminated - - handle :retry - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_RETRY) - end - - end # Break - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/root.rb b/lib/unparser/emitter/root.rb index 5e0bb019..bf62973a 100644 --- a/lib/unparser/emitter/root.rb +++ b/lib/unparser/emitter/root.rb @@ -4,8 +4,19 @@ module Unparser class Emitter # Root emitter a special case class Root < self - include Concord::Public.new(:node, :buffer, :comments) + include Concord::Public.new(:buffer, :node, :comments) include LocalVariableRoot + + def dispatch + if children.any? + emit_body(node, indent: false) + else + visit_deep(node) + end + + emit_eof_comments + nl + end end # Root end # Emitter end # Unparser diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 76518ecb..7c598d40 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -3,236 +3,27 @@ module Unparser class Emitter # Emitter for send - # ignore :reek:TooManyMethods class Send < self + handle :csend, :send - handle :send - - ASSIGN_SUFFIX = '='.freeze - INDEX_ASSIGN = :'[]=' - INDEX_REFERENCE = :'[]' - NON_ASSIGN_RANGE = (0..-2).freeze - - children :receiver, :selector + def emit_mlhs + writer.emit_mlhs + end - def terminated? - effective_emitter.terminated? + def emit_heredoc_reminders + writer.emit_heredoc_reminders end private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - effective_emitter.write_to_buffer - end - - # Return effective emitter - # - # @return [Emitter] - # - # @api private - # - def effective_emitter - effective_emitter_class.new(node, parent) - end - - # Return effective emitter - # - # @return [Class:Emitter] - # - # @api private - # - def effective_emitter_class - if binary_operator? - Binary - elsif unary_operator? - Unary - elsif attribute_assignment? - AttributeAssignment - else - Regular - end - end - - # Return string selector - # - # @return [String] - # - # @api private - # - def string_selector - selector.to_s - end - - # Test for unary operator implemented as method - # - # @return [Boolean] - # - # @api private - # - def unary_operator? - UNARY_OPERATORS.include?(selector) - end - - # Test for binary operator implemented as method - # - # @return [Boolean] - # - # @api private - # - def binary_operator? - BINARY_OPERATORS.include?(selector) && arguments.one? && !arguments.first.type.equal?(:splat) - end - - # Emit selector - # - # @return [undefined] - # - # @api private - # - def emit_selector - write(mlhs? ? non_assignment_selector : string_selector) - end - - # Test for mlhs - # - # @return [Boolean] - # - # @api private - # - def mlhs? - parent_type.equal?(:mlhs) - end - - # Test for assignment - # - # FIXME: This also returns true for <= operator! - # - # @return [Boolean] - # - # @api private - # - def assignment? - string_selector[-1].eql?(ASSIGN_SUFFIX) - end - - # Test for attribute assignment - # - # @return [Boolean] - # - # @api private - # - def attribute_assignment? - !BINARY_OPERATORS.include?(selector) && !UNARY_OPERATORS.include?(selector) && assignment? && !mlhs? - end - - # Test for empty arguments - # - # @return [Boolean] - # - # @api private - # - def arguments? - arguments.any? - end - - # Return argument nodes - # - # @return [Array] - # - # @api private - # - def arguments - children[2..-1] - end - memoize :arguments - - # Emit arguments - # - # @return [undefined] - # - # @api private - # - def emit_arguments - if arguments.empty? - write('()') if receiver.nil? && avoid_clash? - else - normal_arguments - end - end - - # Emit normal arguments - # - # @return [undefined] - # - # @api private - # - def normal_arguments - parentheses do - delimited_plain(effective_arguments) - end - end - - # The effective arguments - # - # @return [Parser::AST::Node] - # - # @api private - # - def effective_arguments - last = arguments.length - 1 - arguments.each_with_index.map do |argument, index| - if last.equal?(index) && argument.type.equal?(:hash) && argument.children.any? - argument.updated(:hash_body) - else - argument - end - end - end - - # Test if clash with local variable or constant needs to be avoided - # - # @return [Boolean] - # - # @api private - # - def avoid_clash? - local_variable_clash? || parses_as_constant? - end - - # Test for local variable clash - # - # @return [Boolean] - # - # @api private - # - def local_variable_clash? - local_variable_scope.local_variable_defined_for_node?(node, selector) - end - - # The non assignment selector - # - # @return [String] - # - # @api private - def non_assignment_selector - string_selector[NON_ASSIGN_RANGE] + writer.dispatch end - # Test if selector parses as constant - # - # @return [Boolean] - # - # @api private - # - def parses_as_constant? - Unparser.parse(selector.to_s).type.equal?(:const) + def writer + writer_with(Writer::Send, node) end + memoize :writer end # Send end # Emitter end # Unparser diff --git a/lib/unparser/emitter/send/binary.rb b/lib/unparser/emitter/send/binary.rb deleted file mode 100644 index 8de416fa..00000000 --- a/lib/unparser/emitter/send/binary.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Send - # Emitter for binary sends - class Binary < self - include Unterminated - - private - - # Return undefined - # - # @return [undefined] - # - # @api private - # - def dispatch - visit(receiver) - emit_operator - emit_right - end - - # Emit operator - # - # @return [undefined] - # - # @api private - # - def emit_operator - write(WS, string_selector, WS) - end - - # Return right node - # - # @return [Parser::AST::Node] - # - # @api private - # - def right_node - children[2] - end - - # Emit right - # - # @return [undefined] - # - # @api private - # - def emit_right - visit(right_node) - end - - end # Binary - end # Send - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/send/conditional.rb b/lib/unparser/emitter/send/conditional.rb deleted file mode 100644 index ac29782f..00000000 --- a/lib/unparser/emitter/send/conditional.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Send - # Emitter for "conditional" receiver&.selector(arguments...) case - class Conditional < self - include Terminated - - handle :csend - - private - - # Perform regular dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - emit_receiver - emit_selector - emit_arguments - end - - # Emit receiver - # - # @return [undefined] - # - # @api private - # - def emit_receiver - visit(receiver) - write(T_AMP, T_DOT) - end - - end # Regular - end # Send - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/send/regular.rb b/lib/unparser/emitter/send/regular.rb deleted file mode 100644 index 4ed6145d..00000000 --- a/lib/unparser/emitter/send/regular.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class Emitter - class Send - # Emitter for "regular" receiver.selector(arguments...) case - class Regular < self - include Terminated - - private - - # Perform regular dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - emit_receiver - emit_selector - emit_arguments - end - - # Emit receiver - # - # @return [undefined] - # - # @api private - # - def emit_receiver - return unless first_child - - visit(receiver) - write(T_DOT) - end - - end # Regular - end # Send - end # Emitter -end # Unparser diff --git a/lib/unparser/emitter/simple.rb b/lib/unparser/emitter/simple.rb new file mode 100644 index 00000000..39b44e82 --- /dev/null +++ b/lib/unparser/emitter/simple.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for simple nodes that generate a single token + class Simple < self + MAP = IceNine.deep_freeze( + __ENCODING__: '__ENCODING__', + __FILE__: '__FILE__', + __LINE__: '__LINE__', + false: 'false', + forward_arg: '...', + forwarded_args: '...', + kwnilarg: '**nil', + match_nil_pattern: '**nil', + nil: 'nil', + redo: 'redo', + retry: 'retry', + self: 'self', + true: 'true', + zsuper: 'super' + ) + + handle(*MAP.keys) + + private + + def dispatch + write(MAP.fetch(node_type)) + end + end # Simple + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/splat.rb b/lib/unparser/emitter/splat.rb index c0944bca..cb22c9b9 100644 --- a/lib/unparser/emitter/splat.rb +++ b/lib/unparser/emitter/splat.rb @@ -4,44 +4,28 @@ module Unparser class Emitter # Emitter for splats class KwSplat < self - include Terminated - handle :kwsplat children :subject private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(T_SPLAT, T_SPLAT) + write('**') visit(subject) end end # Emitter for splats class Splat < self - include Terminated - handle :splat children :subject private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(T_SPLAT) + write('*') visit(subject) if subject end end diff --git a/lib/unparser/emitter/super.rb b/lib/unparser/emitter/super.rb index 669d3761..f78729f3 100644 --- a/lib/unparser/emitter/super.rb +++ b/lib/unparser/emitter/super.rb @@ -3,42 +3,14 @@ module Unparser class Emitter - # Emitter for zsuper nodes - class ZSuper < self - include Terminated - - handle :zsuper - - private - - # Perform dispatch - # - # @return [undefined] - # - # @api private - # - def dispatch - write(K_SUPER) - end - - end # ZSuper - # Emitter for super nodes class Super < self - include Terminated - handle :super private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_SUPER) + write('super') parentheses do delimited(children) end diff --git a/lib/unparser/emitter/undef.rb b/lib/unparser/emitter/undef.rb index fef3c383..cda2297b 100644 --- a/lib/unparser/emitter/undef.rb +++ b/lib/unparser/emitter/undef.rb @@ -4,20 +4,12 @@ module Unparser class Emitter # Emitter for undef nodes class Undef < self - include Unterminated - handle :undef private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_UNDEF, WS) + write('undef ') delimited(children) end diff --git a/lib/unparser/emitter/variable.rb b/lib/unparser/emitter/variable.rb index 905e7673..e4915bac 100644 --- a/lib/unparser/emitter/variable.rb +++ b/lib/unparser/emitter/variable.rb @@ -5,20 +5,12 @@ class Emitter # Emitter for various variable accesses class Variable < self - include Terminated - handle :ivar, :lvar, :cvar, :gvar, :back_ref children :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write(name.to_s) end @@ -27,43 +19,27 @@ def dispatch # Emitter for constant access class Const < self - include Terminated - handle :const children :scope, :name private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch emit_scope write(name.to_s) end - # Emit parent - # - # @return [undefined] - # - # @api private - # def emit_scope return unless scope visit(scope) - write(T_DCL) unless scope.type.equal?(:cbase) + write('::') unless n_cbase?(scope) end end # Emitter for nth_ref nodes (regexp captures) class NthRef < self - include Terminated - PREFIX = '$'.freeze handle :nth_ref @@ -71,12 +47,6 @@ class NthRef < self private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch write(PREFIX) write(name.to_s) diff --git a/lib/unparser/emitter/xstr.rb b/lib/unparser/emitter/xstr.rb new file mode 100644 index 00000000..3655924e --- /dev/null +++ b/lib/unparser/emitter/xstr.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Dynamic execute string literal emitter + class XStr < self + + handle :xstr + + private + + def dispatch + if heredoc? + emit_heredoc + else + emit_xstr + end + end + + def heredoc? + children.any? { |node| node.eql?(s(:str, '')) } + end + + def emit_heredoc + write(%(<<~`HEREDOC`)) + buffer.indent + nl + children.each do |child| + if n_str?(child) + write(child.children.first) + else + emit_begin(child) + end + end + buffer.unindent + write("HEREDOC\n") + end + + def emit_xstr + write('`') + children.each do |child| + if n_begin?(child) + emit_begin(child) + else + emit_string(child) + end + end + write('`') + end + + def emit_string(value) + write(escape_xstr(value.children.first)) + end + + def escape_xstr(input) + input.chars.map do |char| + if char.eql?('`') + '\\`' + else + char + end + end.join + end + + def emit_begin(component) + write('#{') + visit(unwrap_single_begin(component)) + write('}') + end + end # XStr + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/yield.rb b/lib/unparser/emitter/yield.rb index a457b122..4b6f8df3 100644 --- a/lib/unparser/emitter/yield.rb +++ b/lib/unparser/emitter/yield.rb @@ -5,20 +5,12 @@ class Emitter # Emitter for yield node class Yield < self - include Terminated - handle :yield private - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch - write(K_YIELD) + write('yield') return if children.empty? parentheses do diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb new file mode 100644 index 00000000..33529c7a --- /dev/null +++ b/lib/unparser/generation.rb @@ -0,0 +1,250 @@ +# frozen_string_literal: true + +module Unparser + # rubocop:disable Metrics/ModuleLength + module Generation + EXTRA_NL = %i[kwbegin def defs module class sclass].freeze + + private_constant(*constants(false)) + + def emit_heredoc_reminders; end + + def symbol_name; end + + def write_to_buffer + with_comments { dispatch } + self + end + + private + + def delimited(nodes, delimiter = ', ', &block) + return if nodes.empty? + + emit_join(nodes, block || method(:visit), -> { write(delimiter) }) + end + + def emit_join(nodes, emit_node, emit_delimiter) + return if nodes.empty? + + head, *tail = nodes + emit_node.call(head) + + tail.each do |node| + emit_delimiter.call + emit_node.call(node) + end + end + + def nl + emit_eol_comments + buffer.nl + end + + def with_comments + emit_comments_before if buffer.fresh_line? + yield + comments.consume(node) + end + + def ws + write(' ') + end + + def emit_eol_comments + comments.take_eol_comments.each do |comment| + write(' ', comment.text) + end + end + + def emit_eof_comments + emit_eol_comments + comments_left = comments.take_all + return if comments_left.empty? + + buffer.nl + emit_comments(comments_left) + end + + def emit_comments_before(source_part = :expression) + comments_before = comments.take_before(node, source_part) + return if comments_before.empty? + + emit_comments(comments_before) + buffer.nl + end + + def emit_comments(comments) + max = comments.size - 1 + comments.each_with_index do |comment, index| + if comment.type.equal?(:document) + buffer.append_without_prefix(comment.text.chomp) + else + write(comment.text) + end + buffer.nl if index < max + end + end + + def write(*strings) + strings.each(&buffer.method(:append)) + end + + def k_end + buffer.indent + emit_comments_before(:end) + buffer.unindent + write('end') + end + + def parentheses(open = '(', close = ')') + write(open) + yield + write(close) + end + + def indented + buffer = buffer() + buffer.indent + nl + yield + nl + buffer.unindent + end + + def emit_optional_body(node, indent: true) + if node + emit_body(node, indent: indent) + else + nl + end + end + + def emit_body(node, indent: true) + if indent + buffer.indent + nl + end + + if n_begin?(node) + if node.children.one? + visit_deep(node) + else + emit_body_inner(node) + end + else + visit_deep(node) + end + + if indent + buffer.unindent + nl + end + end + + def emit_body_inner(node) + head, *tail = node.children + emit_body_member(head) + + tail.each do |child| + nl + + nl if EXTRA_NL.include?(child.type) + + emit_body_member(child) + end + end + + def emit_body_member(node) + if n_rescue?(node) + emit_rescue_postcontrol(node) + else + visit_deep(node) + end + end + + def emit_ensure(node) + body, ensure_body = node.children + + if body + emit_body_rescue(body) + else + nl + end + + write('ensure') + + emit_optional_body(ensure_body) + end + + def emit_body_rescue(node) + if n_rescue?(node) + emit_rescue_regular(node) + else + emit_body(node) + end + end + + def emit_optional_body_ensure_rescue(node) + if node + emit_body_ensure_rescue(node) + else + nl + end + end + + def emit_body_ensure_rescue(node) + if n_ensure?(node) + emit_ensure(node) + elsif n_rescue?(node) + emit_rescue_regular(node) + else + emit_body(node) + end + end + + def emit_rescue_postcontrol(node) + writer_with(Writer::Rescue, node).emit_postcontrol + end + + def emit_rescue_regular(node) + writer_with(Writer::Rescue, node).emit_regular + end + + def writer_with(klass, node) + klass.new(to_h.merge(node: node)) + end + + def emitter(node) + Emitter.emitter(**to_h.merge(node: node)) + end + + def visit(node) + emitter(node).write_to_buffer + end + + def visit_deep(node) + emitter(node).tap do |emitter| + emitter.write_to_buffer + emitter.emit_heredoc_reminders + end + end + + def first_child + children.first + end + + def conditional_parentheses(flag) + if flag + parentheses { yield } + else + yield + end + end + + def children + node.children + end + end # Generation + # rubocop:enable Metrics/ModuleLength +end # Unparser diff --git a/lib/unparser/node_details.rb b/lib/unparser/node_details.rb new file mode 100644 index 00000000..00708189 --- /dev/null +++ b/lib/unparser/node_details.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Unparser + module NodeDetails + include Constants, NodeHelpers + + def self.included(descendant) + descendant.class_eval do + include Adamantium::Flat, Concord.new(:node) + + extend DSL + end + end + + private + + def children + node.children + end + end # NodeDetails +end # Unparser diff --git a/lib/unparser/node_details/send.rb b/lib/unparser/node_details/send.rb new file mode 100644 index 00000000..ac36f2f7 --- /dev/null +++ b/lib/unparser/node_details/send.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +module Unparser + module NodeDetails + class Send + include NodeDetails + + ASSIGN_SUFFIX = '='.freeze + NON_ASSIGN_RANGE = (0..-2).freeze + + private_constant(*constants(false)) + + children :receiver, :selector + + public :receiver, :selector + + def selector_binary_operator? + BINARY_OPERATORS.include?(selector) + end + + def binary_syntax_allowed? + selector_binary_operator? && arguments.one? && !n_splat?(arguments.first) + end + + def selector_unary_operator? + UNARY_OPERATORS.include?(selector) + end + + def assignment_operator? + assignment? && !selector_binary_operator? && !selector_unary_operator? + end + + def arguments? + arguments.any? + end + + def non_assignment_selector + if assignment? + string_selector[NON_ASSIGN_RANGE] + else + string_selector + end + end + + def assignment? + string_selector[-1].eql?(ASSIGN_SUFFIX) + end + memoize :assignment? + + def arguments + children[2..-1] + end + memoize :arguments + + def string_selector + selector.to_s + end + memoize :string_selector + + end # Send + end # NodeDetails +end # Unparser diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 926feae9..1d76742c 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -11,9 +11,6 @@ module NodeHelpers # @return [Parser::AST::Node] # # @api private - # - # ignore :reek:UncommunicativeMethodName - # ignore :reek:UtilityFunction def s(type, *children) Parser::AST::Node.new(type, children) end @@ -26,12 +23,54 @@ def s(type, *children) # @param [Array] children # # @api private - # - # ignore :reek:UncommunicativeMethodName - # ignore :reek:UtilityFunction def n(type, children = []) Parser::AST::Node.new(type, children) end + def n?(type, node) + node.type.equal?(type) + end + + %i[ + arg + args + array + array_pattern + empty_else + begin + block + cbase + const + dstr + ensure + hash + hash_pattern + in_pattern + int + kwsplat + lambda + match_rest + pair + rescue + send + shadowarg + splat + str + sym + ].each do |type| + name = "n_#{type}?" + define_method(name) do |node| + n?(type, node) + end + private(name) + end + + def unwrap_single_begin(node) + if n_begin?(node) && node.children.one? + node.children.first + else + node + end + end end # NodeHelpers end # Unparser diff --git a/lib/unparser/preprocessor.rb b/lib/unparser/preprocessor.rb deleted file mode 100644 index 7400661a..00000000 --- a/lib/unparser/preprocessor.rb +++ /dev/null @@ -1,159 +0,0 @@ -# frozen_string_literal: true - -module Unparser - # Preprocessor to normalize AST generated by parser - class Preprocessor - include Adamantium::Flat, NodeHelpers, AbstractType, Concord.new(:node, :parent_type), Procto.call(:result) - - # Return preprocessor result - # - # @return [Parser::AST::Node] - # - # @api private - # - abstract_method :result - - EMPTY = Parser::AST::Node.new(:empty) - - # Run preprocessor for node - # - # @param [Parser::AST::Node, nil] node - # - # @return [Parser::AST::Node, nil] - # - # @api private - # - def self.run(node, parent_type = nil) - return EMPTY if node.nil? - - REGISTRY.fetch(node.type, [Noop]).reduce(node) do |current, processor| - processor.call(current, parent_type) - end - end - - REGISTRY = Hash.new { |hash, key| hash[key] = [] } - - # Register preprocessor - # - # @param [Symbol] type - # - # @return [undefined] - # - # @api private - # - def self.register(type) - REGISTRY[type] << self - end - private_class_method :register - - private - - # Visit node - # - # @param [Parser::AST::Node] child - # - # @return [undefined] - # - # @api private - # - def visit(child) - self.class.run(child, node.type) - end - - # Return children - # - # @return [Array] - # - # @api private - # - def children - node.children - end - - # Return visited children - # - # @return [Array] - # - # @api private - # - def visited_children - children.map do |node| - if node.is_a?(Parser::AST::Node) - visit(node) - else - node - end - end - end - - # Noop preprocessor that just passes node through. - class Noop < self - - register :int - register :str - - # Return preprocessor result - # - # @return [Parser::AST::Node] - # - # @api private - # - def result - node.updated(nil, visited_children) - end - - end # Noop - - # Preprocessor transforming numeric nodes with infinity as value to round trippable equivalent. - class Infinity < self - - register :float - register :int - - NEG_INFINITY = -(Float::INFINITY - 1) - - # Return preprocessor result - # - # @return [Parser::AST::Node] - # - # @api private - # - def result - value = node.children.first - case value - when Float::INFINITY - s(:const, s(:const, nil, :Float), :INFINITY) - when NEG_INFINITY - s(:send, s(:const, s(:const, nil, :Float), :INFINITY), :-@) - else - node - end - end - end - - # Preprocessor for begin nodes. Removes begin nodes with one child. - # - # This reduces the amount of complex logic needed inside unparser to emit "nice" syntax with minimal - # tokens. - # - class Begin < self - - register :begin - - # Return preprocessor result - # - # @return [Parser::AST::Node] - # - # @api private - # - def result - if children.one? && !parent_type.equal?(:regexp) - visit(children.first) - else - Noop.call(node, parent_type) - end - end - - end # Begin - end # Preprocessor -end # Unparser diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index c0315624..36f9c4e9 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -2,8 +2,6 @@ module Unparser # Validation of unparser results - # - # ignore :reek:TooManyMethods class Validation include Adamantium::Flat, Anima.new( :generated_node, @@ -25,7 +23,7 @@ def success? original_node, generated_source, generated_node - ].all?(&:right?) && generated_node.from_right.eql?(original_node.from_right) + ].all?(&:right?) && generated_node.from_right.==(original_node.from_right) end # Return error report @@ -55,16 +53,14 @@ def report def self.from_string(original_source) original_node = Unparser .parse_either(original_source) - .fmap(&Preprocessor.method(:run)) generated_source = original_node .lmap(&method(:const_unit)) - .bind(&method(:unparse_either)) + .bind(&Unparser.method(:unparse_either)) generated_node = generated_source .lmap(&method(:const_unit)) .bind(&Unparser.method(:parse_either)) - .fmap(&Preprocessor.method(:run)) new( identification: '(string)', @@ -86,21 +82,10 @@ def self.from_path(path) private - # Create a labeled report from - # - # @param [String] label - # @param [Symbol] attribute_name - # - # @return [Array] def make_report(label, attribute_name) ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] })) end - # Report optional exception - # - # @param [Exception, nil] exception - # - # @return [Array] def report_exception(exception) if exception [exception.inspect].concat(exception.backtrace.take(20)) @@ -109,9 +94,6 @@ def report_exception(exception) end end - # Report the node diff - # - # @return [Array] def node_diff_report diff = nil @@ -127,23 +109,43 @@ def node_diff_report diff ? ['Node-Diff:', diff] : [] end - # Create unit represented as nil - # - # @param [Object] _value - # - # @return [nil] def self.const_unit(_value); end private_class_method :const_unit - # Unparse capturing errors - # - # @param [Parser::AST::Node] node - # - # @return [Either] - def self.unparse_either(node) - MPrelude::Either - .wrap_error(RuntimeError) { Unparser.unparse(node) } - end - private_class_method :unparse_either + class Literal < self + def success? + original_source.eql?(generated_source) + end + + def report + message = [identification] + + message.concat(make_report('Original-Source', :original_source)) + message.concat(make_report('Generated-Source', :generated_source)) + message.concat(make_report('Original-Node', :original_node)) + message.concat(make_report('Generated-Node', :generated_node)) + message.concat(node_diff_report) + message.concat(source_diff_report) + + message.join("\n") + end + + private + + def source_diff_report + diff = nil + + original_source.fmap do |original| + generated_source.fmap do |generated| + diff = Diff.new( + original.to_s.lines.map(&:chomp), + generated.to_s.lines.map(&:chomp) + ).colorized_diff + end + end + + diff ? ['Source-Diff:', diff] : [] + end + end # Literal end # Validation end # Unparser diff --git a/lib/unparser/writer.rb b/lib/unparser/writer.rb new file mode 100644 index 00000000..94b95bfd --- /dev/null +++ b/lib/unparser/writer.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Unparser + module Writer + include Generation, NodeHelpers + + def self.included(descendant) + descendant.class_eval do + include Anima.new(:buffer, :comments, :node, :local_variable_scope) + + extend DSL + end + end + end # Writer +end # Unparser diff --git a/lib/unparser/writer/binary.rb b/lib/unparser/writer/binary.rb new file mode 100644 index 00000000..1152ee04 --- /dev/null +++ b/lib/unparser/writer/binary.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Binary + include Writer, Adamantium::Flat + + children :left, :right + + OPERATOR_TOKENS = + { + and: '&&', + or: '||' + }.freeze + + KEYWORD_TOKENS = + { + and: 'and', + or: 'or' + }.freeze + + KEYWORD_SYMBOLS = + { + and: :kAND, + or: :kOR + }.freeze + + OPERATOR_SYMBOLS = + { + and: :tANDOP, + or: :tOROP + }.freeze + + MAP = + { + kAND: 'and', + kOR: 'or', + tOROP: '||', + tANDOP: '&&' + }.freeze + + NEED_KEYWORD = %i[return break next].freeze + + private_constant(*constants(false)) + + def emit_operator + emit_with(OPERATOR_TOKENS) + end + + def symbol_name + true + end + + def dispatch + left_emitter.write_to_buffer + write(' ', MAP.fetch(effective_symbol), ' ') + visit(right) + end + + private + + def effective_symbol + if NEED_KEYWORD.include?(right.type) || NEED_KEYWORD.include?(left.type) + return keyword_symbol + end + + unless left_emitter.symbol_name + return operator_symbol + end + + keyword_symbol + end + + def emit_with(map) + visit(left) + write(' ', map.fetch(node.type), ' ') + visit(right) + end + + def keyword_symbol + KEYWORD_SYMBOLS.fetch(node.type) + end + + def operator_symbol + OPERATOR_SYMBOLS.fetch(node.type) + end + + def left_emitter + emitter(left) + end + memoize :left_emitter + + def right_emitter + emitter(right) + end + memoize :right_emitter + end # Binary + end # Writer +end # Unparser diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb new file mode 100644 index 00000000..b1f614a6 --- /dev/null +++ b/lib/unparser/writer/dynamic_string.rb @@ -0,0 +1,229 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class DynamicString + include Writer, Adamantium::Flat + + PATTERNS_2 = IceNine.deep_freeze( + [ + %i[str_empty begin], + %i[begin str_nl] + ] + ) + + PATTERNS_3 = IceNine.deep_freeze( + [ + %i[begin str_nl_eol str_nl_eol], + %i[str_nl_eol begin str_nl_eol], + %i[str_ws begin str_nl_eol] + ] + ) + + FLAT_INTERPOLATION = %i[ivar cvar gvar].to_set.freeze + + private_constant(*constants(false)) + + def emit_heredoc_reminder + return unless heredoc? + + emit_heredoc_body + emit_heredoc_footer + end + + def dispatch + if heredoc? + emit_heredoc_header + else + emit_dstr + end + end + + private + + def heredoc_header + need_squiggly? ? '<<~HEREDOC' : '<<-HEREDOC' + end + + def heredoc? + children.empty? || (nl_last_child? && heredoc_pattern?) + end + + def emit_heredoc_header + write(heredoc_header) + end + + def emit_heredoc_body + nl + if need_squiggly? + emit_squiggly_heredoc_body + else + emit_normal_heredoc_body + end + end + + def emit_heredoc_footer + write('HEREDOC') + end + + def classify(node) + if n_str?(node) + classify_str(node) + else + node.type + end + end + + def classify_str(node) + if str_nl?(node) + :str_nl + elsif node.children.first.end_with?("\n") + :str_nl_eol + elsif str_ws?(node) + :str_ws + elsif str_empty?(node) + :str_empty + end + end + + def str_nl?(node) + node.eql?(s(:str, "\n")) + end + + def str_empty?(node) + node.eql?(s(:str, '')) + end + + def str_ws?(node) + /\A( |\t)+\z/.match?(node.children.first) + end + + def heredoc_pattern? + heredoc_pattern_2? || heredoc_pattern_3? + end + + def heredoc_pattern_3? + children.each_cons(3).any? do |group| + PATTERNS_3.include?(group.map(&method(:classify))) + end + end + + def heredoc_pattern_2? + children.each_cons(2).any? do |group| + PATTERNS_2.include?(group.map(&method(:classify))) + end + end + + def nl_last_child? + last = children.last + n_str?(last) && last.children.first[-1].eql?("\n") + end + + def need_squiggly? + children.any?(s(:str, '')) + end + + def emit_squiggly_heredoc_body + buffer.indent + children.each do |child| + if n_str?(child) + write(escape_dynamic(child.children.first)) + else + emit_dynamic(child) + end + end + buffer.unindent + end + + def emit_normal_heredoc_body + buffer.root_indent do + children.each do |child| + if n_str?(child) + write(escape_dynamic(child.children.first)) + else + emit_dynamic(child) + end + end + end + end + + def escape_dynamic(string) + string.gsub('#', '\#') + end + + def emit_dynamic(child) + if FLAT_INTERPOLATION.include?(child.type) + write('#') + visit(child) + elsif n_dstr?(child) + emit_body(child.children) + else + write('#{') + emit_dynamic_component(child.children.first) + write('}') + end + end + + def emit_dynamic_component(node) + visit(node) if node + end + + def emit_dstr + segments.each_with_index do |children, index| + emit_segment(children, index) + end + end + + def breakpoint?(child, current) + last_type = current.last&.type + + [ + n_str?(child) && last_type.equal?(:str) && current.none?(&method(:n_begin?)), + last_type.equal?(:dstr), + n_dstr?(child) && last_type + ].any? + end + + def segments + segments = [] + + segments << current = [] + + children.each do |child| + if breakpoint?(child, current) + segments << current = [] + end + + current << child + end + + segments + end + + def emit_segment(children, index) + write(' ') unless index.zero? + + write('"') + emit_body(children) + write('"') + end + + def emit_body(children) + buffer.root_indent do + children.each_with_index do |child, index| + if n_str?(child) + string = child.children.first + if string.eql?("\n") && children.fetch(index.pred).type.equal?(:begin) + write("\n") + else + write(string.inspect[1..-2]) + end + else + emit_dynamic(child) + end + end + end + end + end # DynamicString + end # Writer +end # Unparser diff --git a/lib/unparser/writer/resbody.rb b/lib/unparser/writer/resbody.rb new file mode 100644 index 00000000..bc386169 --- /dev/null +++ b/lib/unparser/writer/resbody.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +module Unparser + module Writer + # Writer for rescue bodies + class Resbody + include Writer + + children :exception, :assignment, :body + + def emit_postcontrol + write(' rescue ') + visit(body) + end + + def emit_regular + write('rescue') + emit_exception + emit_assignment + emit_optional_body(body) + end + + private + + def emit_exception + return unless exception + + ws + delimited(exception.children) + end + + def emit_assignment + return unless assignment + + write(' => ') + visit(assignment) + end + end # Resbody + end # Writer +end # Unparser diff --git a/lib/unparser/writer/rescue.rb b/lib/unparser/writer/rescue.rb new file mode 100644 index 00000000..b2075cd2 --- /dev/null +++ b/lib/unparser/writer/rescue.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Rescue + include Writer, Adamantium::Flat + + children :body, :rescue_body + + define_group :rescue_bodies, 1..-2 + + def emit_regular + emit_optional_body(body) + + rescue_bodies.each(&method(:emit_rescue_body)) + + if else_node + write('else') + emit_body(else_node) + end + end + + def emit_postcontrol + visit(body) + writer_with(Resbody, rescue_body).emit_postcontrol + end + + private + + def else_node + children.last + end + + def emit_rescue_body(node) + writer_with(Resbody, node).emit_regular + end + end # Rescue + end # Writer +end # Unparser diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb new file mode 100644 index 00000000..14487e9d --- /dev/null +++ b/lib/unparser/writer/send.rb @@ -0,0 +1,124 @@ +# frozen_string_literal: true + +module Unparser + module Writer + # Writer for send + class Send + include Writer, Adamantium::Flat, Constants, Generation + + INDEX_ASSIGN = :'[]=' + INDEX_REFERENCE = :'[]' + + OPERATORS = { + csend: '&.', + send: '.' + }.freeze + + private_constant(*constants(false)) + + children :receiver, :selector + + def dispatch + effective_writer.dispatch + end + + def emit_mlhs + effective_writer.emit_send_mlhs + end + + def emit_selector + write(details.string_selector) + end + + def emit_heredoc_reminders + emitter(receiver).emit_heredoc_reminders if receiver + arguments.each(&method(:emit_heredoc_reminder)) + end + + private + + def effective_writer + writer_with(effective_writer_class, node) + end + memoize :effective_writer + + def effective_writer_class + if details.binary_syntax_allowed? + Binary + elsif details.selector_unary_operator? + Unary + elsif write_as_attribute_assignment? + AttributeAssignment + else + Regular + end + end + + def write_as_attribute_assignment? + details.assignment_operator? + end + + def emit_operator + write(OPERATORS.fetch(node.type)) + end + + def emit_arguments + if arguments.empty? + write('()') if receiver.nil? && avoid_clash? + else + emit_normal_arguments + end + end + + def arguments + details.arguments + end + + def emit_normal_arguments + parentheses do + arguments.each_with_index do |node, index| + write(', ') unless index.zero? + emit_argument(node, index.succ.equal?(arguments.length)) + end + end + end + + def emit_argument(argument, last) + if n_hash?(argument) && last + writer_with(Emitter::Hash, argument).emit_last_argument_hash + else + visit(argument) + end + end + + def emit_heredoc_reminder(argument) + emitter(argument).emit_heredoc_reminders + end + + def avoid_clash? + local_variable_clash? || parses_as_constant? + end + + def local_variable_clash? + local_variable_scope.local_variable_defined_for_node?(node, selector) + end + + def parses_as_constant? + n_const?(Unparser.parse(selector.to_s)) + end + + def details + NodeDetails::Send.new(node) + end + memoize :details + + def emit_send_regular(node) + if n_send?(node) + writer_with(Regular, node).dispatch + else + visit(node) + end + end + end # Send + end # Writer +end # Unparser diff --git a/lib/unparser/emitter/send/attribute_assignment.rb b/lib/unparser/writer/send/attribute_assignment.rb similarity index 51% rename from lib/unparser/emitter/send/attribute_assignment.rb rename to lib/unparser/writer/send/attribute_assignment.rb index 569d577c..fe404c55 100644 --- a/lib/unparser/emitter/send/attribute_assignment.rb +++ b/lib/unparser/writer/send/attribute_assignment.rb @@ -1,24 +1,16 @@ # frozen_string_literal: true module Unparser - class Emitter + module Writer class Send - # Emitter for send as attribute assignment + # Writer for send as attribute assignment class AttributeAssignment < self - include Unterminated - children :receiver, :selector, :first_argument - # Perform regular dispatch - # - # @return [undefined] - # - # @api private - # def dispatch emit_receiver emit_attribute - write(T_ASN) + write('=') if arguments.one? visit(first_argument) @@ -27,29 +19,22 @@ def dispatch end end + def emit_send_mlhs + emit_receiver + write(details.non_assignment_selector) + end + private - # Emit receiver - # - # @return [Parser::AST::Node] - # - # @api private - # def emit_receiver visit(receiver) - write(T_DOT) + emit_operator end - # Emit attribute - # - # @return [undefined] - # - # @api private - # def emit_attribute - write(non_assignment_selector) + write(details.non_assignment_selector) end end # AttributeAssignment end # Send - end # Emitter + end # Writer end # Unparser diff --git a/lib/unparser/writer/send/binary.rb b/lib/unparser/writer/send/binary.rb new file mode 100644 index 00000000..c3cc46be --- /dev/null +++ b/lib/unparser/writer/send/binary.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Send + # Writer for binary sends + class Binary < self + def dispatch + visit(receiver) + emit_operator + emit_right + end + + private + + def emit_operator + write(' ', details.string_selector, ' ') + end + + def emit_right + emit_send_regular(children.fetch(2)) + end + + end # Binary + end # Send + end # Writer +end # Unparser diff --git a/lib/unparser/writer/send/conditional.rb b/lib/unparser/writer/send/conditional.rb new file mode 100644 index 00000000..c3ddcf68 --- /dev/null +++ b/lib/unparser/writer/send/conditional.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Send + # Writer for "conditional" receiver&.selector(arguments...) case + class Conditional < self + + private + + def dispatch + emit_receiver + emit_selector + emit_arguments + end + + def emit_receiver + visit(receiver) + write('&.') + end + + end # Regular + end # Send + end # Writer +end # Unparser diff --git a/lib/unparser/writer/send/regular.rb b/lib/unparser/writer/send/regular.rb new file mode 100644 index 00000000..f5642b74 --- /dev/null +++ b/lib/unparser/writer/send/regular.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Send + # Writer for "regular" receiver.selector(arguments...) case + class Regular < self + def dispatch + emit_receiver + emit_selector + emit_arguments + end + + def emit_send_mlhs + dispatch + end + + def emit_arguments_without_heredoc_body + emit_normal_arguments if arguments.any? + end + + def emit_receiver + return unless receiver + + emit_send_regular(receiver) + + emit_operator + end + + end # Regular + end # Send + end # Writer +end # Unparser diff --git a/lib/unparser/emitter/send/unary.rb b/lib/unparser/writer/send/unary.rb similarity index 51% rename from lib/unparser/emitter/send/unary.rb rename to lib/unparser/writer/send/unary.rb index 906b9e3b..c7ff4f1f 100644 --- a/lib/unparser/emitter/send/unary.rb +++ b/lib/unparser/writer/send/unary.rb @@ -1,36 +1,29 @@ # frozen_string_literal: true module Unparser - class Emitter + module Writer class Send - # Emitter for unary sends + # Writer for unary sends class Unary < self - include Unterminated - - private - - MAP = IceNine.deep_freeze( + MAP = { '-@': '-', '+@': '+' - ) + }.freeze + + private_constant(*constants(false)) - # Perform dispatch - # - # @return [undefined] - # - # @api private - # def dispatch name = selector + write(MAP.fetch(name, name).to_s) - if receiver.type.equal?(:int) && selector.equal?(:'+@') + + if n_int?(receiver) && selector.equal?(:'+@') write('+') end visit(receiver) end - end # Unary end # Send - end # Emitter + end # Writer end # Unparser diff --git a/mutant.sh b/mutant.sh index 3dbd8e28..36effea6 100755 --- a/mutant.sh +++ b/mutant.sh @@ -1,7 +1,30 @@ #/usr/bin/bash -ex -bundle exec mutant \ - --zombie \ - --ignore-subject 'Unparser::CLI*' \ - --ignore-subject 'Unparser::Validation.from_string' \ +bundle exec mutant \ + --zombie \ + --ignore-subject 'Unparser::CLI*' \ + --ignore-subject 'Unparser::Emitter#emit_comments' \ + --ignore-subject 'Unparser::Emitter#emit_comments_before' \ + --ignore-subject 'Unparser::Emitter#emit_eol_comments' \ + --ignore-subject 'Unparser::Emitter.handle' \ + --ignore-subject 'Unparser::Emitter::Args#normal_arguments' \ + --ignore-subject 'Unparser::Emitter::Args#shadowargs' \ + --ignore-subject 'Unparser::Emitter::Array#emitters' \ + --ignore-subject 'Unparser::Emitter::Binary#writer' \ + --ignore-subject 'Unparser::Emitter::Block#target_writer' \ + --ignore-subject 'Unparser::Emitter::Class#dispatch' \ + --ignore-subject 'Unparser::Emitter::Class#local_variable_scope' \ + --ignore-subject 'Unparser::Emitter::Def#local_variable_scope' \ + --ignore-subject 'Unparser::Emitter::HashPattern#write_symbol_body' \ + --ignore-subject 'Unparser::Emitter::LocalVariableRoot*' \ + --ignore-subject 'Unparser::Emitter::LocalVariableRoot.included' \ + --ignore-subject 'Unparser::Emitter::Module#local_variable_scope' \ + --ignore-subject 'Unparser::Emitter::Root#local_variable_scope' \ + --ignore-subject 'Unparser::Emitter::Send#writer' \ + --ignore-subject 'Unparser::Validation.from_string' \ + --ignore-subject 'Unparser::Writer.included' \ + --ignore-subject 'Unparser::Writer::Binary#left_emitter' \ + --ignore-subject 'Unparser::Writer::Binary#right_emitter' \ + --ignore-subject 'Unparser::Writer::Send#details' \ + --ignore-subject 'Unparser::Writer::Send#effective_writer' \ $* diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb index 16645446..b61ff5a2 100644 --- a/spec/integration/unparser/corpus_spec.rb +++ b/spec/integration/unparser/corpus_spec.rb @@ -55,22 +55,10 @@ def checkout private - # Return repository path - # - # @return [Pathname] - # - # @api private - # def repo_path TMP.join(name) end - # Helper method to execute system commands - # - # @param [Array] arguments - # - # @api private - # def system(arguments) return if Kernel.system(*arguments) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 3e90850e..9208dfe2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,7 +8,7 @@ require 'parser/current' RSpec.configuration.around(file_path: %r{spec/unit}) do |example| - Timeout.timeout(0.1, &example) + Timeout.timeout(5, &example) end RSpec.shared_examples_for 'a command method' do diff --git a/spec/unit/unparser/buffer/append_spec.rb b/spec/unit/unparser/buffer/append_spec.rb deleted file mode 100644 index 1c315b63..00000000 --- a/spec/unit/unparser/buffer/append_spec.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#append' do - subject { object.append(string) } - - let(:object) { described_class.new } - let(:string) { 'foo' } - - specify do - expect { subject }.to change { object.content }.from('').to('foo') - end - - # Yeah duplicate, mutant will be improved ;) - it 'should prefix with indentation if line is empty' do - object.append('foo') - object.nl - object.indent - object.append('bar') - object.append('baz') - expect(object.content).to eql("foo\n barbaz") - end - - it_should_behave_like 'a command method' -end diff --git a/spec/unit/unparser/buffer/append_without_prefix_spec.rb b/spec/unit/unparser/buffer/append_without_prefix_spec.rb deleted file mode 100644 index 695c1bb4..00000000 --- a/spec/unit/unparser/buffer/append_without_prefix_spec.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#append_without_prefix' do - subject { object.append_without_prefix(string) } - - let(:object) { described_class.new } - let(:string) { 'foo' } - - specify do - expect { subject }.to change { object.content }.from('').to('foo') - end - - it 'should not prefix with indentation' do - object.append_without_prefix('foo') - object.nl - object.indent - object.append_without_prefix('bar') - object.append_without_prefix('baz') - expect(object.content).to eql("foo\nbarbaz") - end - - it_should_behave_like 'a command method' -end diff --git a/spec/unit/unparser/buffer/capture_content_spec.rb b/spec/unit/unparser/buffer/capture_content_spec.rb deleted file mode 100644 index 8937e6df..00000000 --- a/spec/unit/unparser/buffer/capture_content_spec.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#capture_content' do - - let(:object) { described_class.new } - - it 'should capture only the content appended within the block' do - object.append('foo') - object.nl - object.indent - captured = object.capture_content do - object.append('bar') - object.nl - end - expect(captured).to eql(" bar\n") - end -end diff --git a/spec/unit/unparser/buffer/content_spec.rb b/spec/unit/unparser/buffer/content_spec.rb deleted file mode 100644 index 067d9269..00000000 --- a/spec/unit/unparser/buffer/content_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#content' do - subject { object.content } - - let(:object) { described_class.new } - - shared_examples_for 'buffer content' do - it 'contains expected content' do - should eql(expected_content) - end - - it { should be_frozen } - - it 'returns fresh string copies' do - first = object.content - second = object.content - expect(first).to eql(second) - expect(first).not_to be(second) - end - end - - context 'with empty buffer' do - let(:expected_content) { '' } - - it_should_behave_like 'buffer content' - end - - context 'with filled buffer' do - before do - object.append('foo') - end - - let(:expected_content) { 'foo' } - - it_behaves_like 'buffer content' - end -end diff --git a/spec/unit/unparser/buffer/fresh_line_spec.rb b/spec/unit/unparser/buffer/fresh_line_spec.rb deleted file mode 100644 index dc01499e..00000000 --- a/spec/unit/unparser/buffer/fresh_line_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#fresh_line?' do - let(:object) { described_class.new } - - it 'should return true while buffer is empty' do - expect(object.fresh_line?).to eql(true) - end - - it 'should return false after content has been appended' do - object.append('foo') - expect(object.fresh_line?).to eql(false) - end - - it 'should return true after a nl has been appended' do - object.append('foo') - object.nl - expect(object.fresh_line?).to eql(true) - end -end diff --git a/spec/unit/unparser/buffer/indent_spec.rb b/spec/unit/unparser/buffer/indent_spec.rb deleted file mode 100644 index e38084b4..00000000 --- a/spec/unit/unparser/buffer/indent_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#indent' do - let(:object) { described_class.new } - - subject { object.indent } - - it 'should indent with two spaces' do - object.append('foo') - object.nl - object.indent - object.append('bar') - object.nl - object.indent - object.append('baz') - expect(object.content).to eql("foo\n bar\n baz") - end - - it_should_behave_like 'a command method' -end diff --git a/spec/unit/unparser/buffer/nl_spec.rb b/spec/unit/unparser/buffer/nl_spec.rb deleted file mode 100644 index 1917c047..00000000 --- a/spec/unit/unparser/buffer/nl_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#nl' do - let(:object) { described_class.new } - - subject { object.nl } - - it 'writes a newline' do - object.append('foo') - subject - object.append('bar') - expect(object.content).to eql("foo\nbar") - end - - it_should_behave_like 'a command method' -end diff --git a/spec/unit/unparser/buffer/unindent_spec.rb b/spec/unit/unparser/buffer/unindent_spec.rb deleted file mode 100644 index cc8202e4..00000000 --- a/spec/unit/unparser/buffer/unindent_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'spec_helper' - -describe Unparser::Buffer, '#unindent' do - let(:object) { described_class.new } - - subject { object.unindent } - - it 'unindents two chars' do - object.append('foo') - object.nl - object.indent - object.append('bar') - object.nl - object.unindent - object.append('baz') - expect(object.content).to eql("foo\n bar\nbaz") - end - - it_should_behave_like 'a command method' -end diff --git a/spec/unit/unparser/buffer_spec.rb b/spec/unit/unparser/buffer_spec.rb new file mode 100644 index 00000000..93f07704 --- /dev/null +++ b/spec/unit/unparser/buffer_spec.rb @@ -0,0 +1,171 @@ +require 'spec_helper' + +describe Unparser::Buffer do + describe '#append' do + subject { object.append(string) } + + let(:object) { described_class.new } + let(:string) { 'foo' } + + specify do + expect { subject }.to change { object.content }.from('').to('foo') + end + + it 'should prefix with indentation if line is empty' do + object.append('foo') + object.nl + object.indent + object.append('bar') + object.append('baz') + expect(object.content).to eql("foo\n barbaz") + end + + it_should_behave_like 'a command method' + end + + describe '#append_without_prefix' do + subject { object.append_without_prefix(string) } + + let(:object) { described_class.new } + let(:string) { 'foo' } + + specify do + expect { subject }.to change { object.content }.from('').to('foo') + end + + it 'should not prefix with indentation' do + object.append_without_prefix('foo') + object.nl + object.indent + object.append_without_prefix('bar') + object.append_without_prefix('baz') + expect(object.content).to eql("foo\nbarbaz") + end + + it_should_behave_like 'a command method' + end + + describe '#capture_content' do + let(:object) { described_class.new } + + it 'should capture only the content appended within the block' do + object.append('foo') + object.nl + object.indent + captured = object.capture_content do + object.append('bar') + object.nl + end + expect(captured).to eql(" bar\n") + end + end + + describe '#content' do + subject { object.content } + + let(:object) { described_class.new } + + shared_examples_for 'buffer content' do + it 'contains expected content' do + should eql(expected_content) + end + + it { should be_frozen } + + it 'returns fresh string copies' do + first = object.content + second = object.content + expect(first).to eql(second) + expect(first).not_to be(second) + end + end + + context 'with empty buffer' do + let(:expected_content) { '' } + + it_should_behave_like 'buffer content' + end + + context 'with filled buffer' do + before do + object.append('foo') + end + + let(:expected_content) { 'foo' } + + it_behaves_like 'buffer content' + end + end + + describe '#fresh_line?' do + let(:object) { described_class.new } + + it 'should return true while buffer is empty' do + expect(object.fresh_line?).to eql(true) + end + + it 'should return false after content has been appended' do + object.append('foo') + expect(object.fresh_line?).to eql(false) + end + + it 'should return true after a nl has been appended' do + object.append('foo') + object.nl + expect(object.fresh_line?).to eql(true) + end + end + + describe '#indent' do + let(:object) { described_class.new } + + subject { object.indent } + + it 'should indent with two spaces' do + object.append('foo') + object.nl + object.indent + object.append('bar') + object.nl + object.indent + object.append('baz') + expect(object.content).to eql("foo\n bar\n baz") + end + + it_should_behave_like 'a command method' + end + + describe '#nl' do + let(:object) { described_class.new } + + subject { object.nl } + + it 'writes a newline' do + object.append('foo') + subject + object.append('bar') + expect(object.content).to eql("foo\nbar") + end + + it_should_behave_like 'a command method' + end + + describe '#unindent' do + let(:object) { described_class.new } + + subject { object.unindent } + + it 'unindents two chars' do + object.append('foo') + object.nl + object.indent + object.append('bar') + object.nl + object.unindent + object.append('baz') + expect(object.content).to eql("foo\n bar\nbaz") + end + + it_should_behave_like 'a command method' + end +end diff --git a/spec/unit/unparser/emitter/class_methods/handle_spec.rb b/spec/unit/unparser/emitter/class_methods/handle_spec.rb index 026697f7..6fa42a98 100644 --- a/spec/unit/unparser/emitter/class_methods/handle_spec.rb +++ b/spec/unit/unparser/emitter/class_methods/handle_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Unparser::Emitter, '.handle', mutant_expression: 'Unparser::Emitter*' do +describe Unparser::Emitter, '.handle', mutant_expression: 'Unparser*' do subject { class_under_test.class_eval { handle :foo } } let(:class_under_test) do diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index b0e639c1..e5e6f3bf 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -236,7 +236,7 @@ def apply end let(:path) { instance_double(Pathname, read: source, to_s: '/some/file') } - let(:source) { 'true' } + let(:source) { "true\n" } it 'returns expected validator' do expect(apply).to eql( @@ -267,7 +267,7 @@ def apply end context 'on valid original source' do - let(:source) { 'true' } + let(:source) { "true\n" } it 'returns expected validator' do expect(apply).to eql(described_class.new(attributes)) @@ -291,26 +291,6 @@ def apply end end - # These are actually specifying the wrong behavior as the normalization is a conceptual mistake - # But for now we specify them to get this PR through. - # - # Removal in followup. - context 'on denormalized valid original source' do - let(:source) { '(true)' } - - it 'returns expected validator' do - expect(apply).to eql(described_class.new(attributes.merge(generated_source: right('true')))) - end - end - - context 'on very denormalized valid original source' do - let(:source) { '((true))' } - - it 'returns expected validator' do - expect(apply).to eql(described_class.new(attributes.merge(generated_source: right('true')))) - end - end - context 'on invalid original source' do let(:source) { '(' } diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 7b296048..42a3edad 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Unparser, mutant_expression: 'Unparser::Emitter*' do +describe Unparser, mutant_expression: 'Unparser*' do describe '.buffer' do let(:source) { 'a + b' } @@ -15,6 +15,16 @@ def apply it 'returns parser buffer with pre-filled source' do expect(apply.source).to eql(source) end + + context 'on non default identification' do + def apply + described_class.buffer(source, '(foo)') + end + + it 'returns parser buffer with expected name' do + expect(apply.name).to eql('(foo)') + end + end end describe '.parser' do @@ -137,14 +147,8 @@ def apply end describe '.unparse' do - let(:builder_options) { {} } - def parser - Unparser.parser.tap do |parser| - builder_options.each do |name, value| - parser.builder.public_send(:"#{name}=", value) - end - end + Unparser.parser end def buffer(input) @@ -155,43 +159,16 @@ def parse_with_comments(string) parser.parse_with_comments(buffer(string)) end - def self.with_builder_options(options, &block) - context "with #{options}" do - let(:builder_options) { options } - - class_eval(&block) - end - end - - def assert_round_trip(string, parser) - ast, comments = parse_with_comments(string) - generated = Unparser.unparse(ast, comments) - expect(generated).to eql(string.chomp) - generated_ast, _comments = parse_with_comments(generated) - expect(ast == generated_ast).to be(true) - end - def assert_generates_from_string(parser, string, expected) ast_with_comments = parse_with_comments(string) assert_generates_from_ast(parser, ast_with_comments, expected.chomp) end def assert_generates_from_ast(parser, ast_with_comments, expected) - generated = Unparser.unparse(*ast_with_comments) + generated = Unparser.unparse(*ast_with_comments).chomp expect(generated).to eql(expected) ast, comments = parse_with_comments(generated) - expect(Unparser.unparse(ast, comments)).to eql(expected) - end - - def self.assert_unterminated(expression) - assert_source(expression) - assert_source("(#{expression}).foo") - end - - def self.assert_terminated(expression) - assert_source(expression) - assert_source("foo(#{expression})") - assert_source("#{expression}.foo") + expect(Unparser.unparse(ast, comments).chomp).to eql(expected) end def self.assert_generates(input, expected) @@ -204,1409 +181,51 @@ def self.assert_generates(input, expected) end end - def self.assert_round_trip(input) - it "should round trip #{input}" do - assert_round_trip(input, parser) - end - end - - def self.assert_source(input) - assert_round_trip(input) - end - - context 'kwargs' do - assert_source <<~RUBY - def foo(bar:, baz:) - end - RUBY - - assert_source <<~RUBY - foo(**bar) - RUBY - - assert_source <<~RUBY - def foo(bar:, baz: "value") - end - RUBY - end - - context 'literal' do - context 'int' do - assert_generates '-0', '0' - assert_source '++1' - assert_terminated '1' - assert_unterminated '-1' - assert_generates '0x1', '1' - assert_generates '1_000', '1000' - assert_generates '1e10', '10000000000.0' - assert_generates '10e10000000000', 'Float::INFINITY' - assert_generates '-10e10000000000', '-Float::INFINITY' - end - - context 'rational' do - assert_terminated '1r' - assert_generates '1.0r', '1r' - assert_generates '-0r', '0r' - - assert_terminated '1.5r' - assert_terminated '1.3r' - end - - context 'complex' do - %w( - 5i - -5i - 0.6i - -0.6i - 1000000000000000000000000000000i - 1ri - ).each do |expression| - assert_terminated(expression) - end - end - - context 'string' do - assert_generates '?c', '"c"' - assert_generates '"foo" "bar"', '"#{"foo"}#{"bar"}"' - assert_generates '"foo" "bar #{baz}"', '"#{"foo"}#{"#{"bar "}#{baz}"}"' - assert_generates '%Q(foo"#{@bar})', '"#{"foo\\""}#{@bar}"' - assert_generates '"foo#{1}bar"', '"#{"foo"}#{1}#{"bar"}"' - assert_generates '"\\\\#{}"', '"#{"\\\\"}#{}"' - assert_generates '"#{}\#{}"', '"#{}#{"\#{}"}"' - assert_generates '"\#{}#{}"', '"#{"\#{}"}#{}"' - assert_terminated '"\""' - assert_terminated '"foo bar"' - assert_terminated '"foo\nbar"' - # Within indentation - assert_generates <<~'RUBY', <<~'RUBY' - if foo - " - #{foo} - " - end - RUBY - if foo - "#{"\n"}#{" "}#{foo}#{"\n"}#{" "}" - end - RUBY - end - - context 'execute string' do - assert_generates '`foo`', '`#{"foo"}`' - assert_generates '`foo#{@bar}`', '`#{"foo"}#{@bar}`' - assert_generates '%x(\))', '`#{")"}`' - assert_generates '%x(`)', '`#{"`"}`' - assert_generates '`"`', '`#{"\\""}`' - end - - context 'symbol' do - assert_generates s(:sym, :foo), ':foo' - assert_generates s(:sym, :"A B"), ':"A B"' - assert_terminated ':foo' - assert_terminated ':"A B"' - assert_terminated ':"A\"B"' - assert_terminated ':""' - end - - context 'regexp' do - assert_terminated '/foo/' - assert_terminated %q(/[^-+',.\/:@[:alnum:]\[\]]+/) - assert_terminated '/foo#{@bar}/' - assert_terminated '/foo#{@bar}/imx' - assert_terminated '/#{"\u0000"}/' - assert_terminated "/\n/" - assert_terminated '/\n/' - assert_terminated "/\n/x" - # Within indentation - assert_source <<~RUBY - if foo - / - / - end - RUBY - assert_generates '%r(/)', '/\//' - assert_generates '%r(\))', '/\)/' - assert_generates '%r(#{@bar}baz)', '/#{@bar}baz/' - assert_terminated '/\/\//x' - end - - context 'dynamic symbol' do - assert_generates ':"foo#{bar}baz"', ':"#{"foo"}#{bar}#{"baz"}"' - assert_source ':"#{"foo"}"' - end - - context 'irange' do - assert_unterminated '1..' - assert_unterminated '1..2' - assert_unterminated '(0.0 / 0.0)..1' - assert_unterminated '1..(0.0 / 0.0)' - assert_unterminated '(0.0 / 0.0)..100' - end - - context 'erange' do - assert_unterminated '1...' - assert_unterminated '1...2' - end - - context 'float' do - assert_source '-0.1' - assert_terminated '0.1' - assert_terminated '0.1' - assert_generates '10.2e10000000000', 'Float::INFINITY' - assert_generates '-10.2e10000000000', '-Float::INFINITY' - assert_generates s(:float, -0.1), '-0.1' - assert_generates s(:float, 0.1), '0.1' - end - - context 'array' do - assert_terminated '[1, 2]' - assert_terminated '[1, (), n2]' - assert_terminated '[1]' - assert_terminated '[]' - assert_terminated '[1, *@foo]' - assert_terminated '[*@foo, 1]' - assert_terminated '[*@foo, *@baz]' - assert_generates '%w(foo bar)', '["foo", "bar"]' - end - - context 'hash' do - assert_terminated '{}' - assert_source '{ () => () }' - assert_source '{ 1 => 2 }' - assert_source '{ 1 => 2, 3 => 4 }' - - # special case for 2.1.3 - assert_source "{ foo: (if true\nend) }" - - context 'with symbol keys' do - assert_source '{ a: (1 rescue foo), b: 2 }' - assert_source '{ a: 1, b: 2 }' - assert_source '{ a: :a }' - assert_source '{ :"a b" => 1 }' - assert_source '{ :-@ => 1 }' - end + def self.assert_source(string) + it 'round trips' do + ast, comments = parse_with_comments(string) + generated = Unparser.unparse(ast, comments).chomp + expect(generated).to eql(string.chomp) + generated_ast, _comments = parse_with_comments(generated) + expect(ast == generated_ast).to be(true) end end - context 'access' do - %w(@a @@a $a $1 $` CONST SCOPED::CONST ::TOPLEVEL ::TOPLEVEL::CONST).each do |expression| - assert_terminated(expression) - end - end - - context 'control keywords' do - %w(retry redo).each do |expression| - assert_terminated(expression) - end - end - - context 'singletons' do - %w(self true false nil).each do |expression| - assert_terminated(expression) - end - end - - context 'magic keywords' do - assert_source '__ENCODING__' - - # These two assertions don't actually need to be wrapped in this block since `true` is the default, - # but it is helpful to contrast with the assertions farther down. - with_builder_options(emit_file_line_as_literals: true) do - assert_generates '__FILE__', '"(string)"' - assert_generates '__LINE__', '1' - end - - with_builder_options(emit_file_line_as_literals: false) do - assert_source '__FILE__' - assert_source '__LINE__' - end - end - - context 'assignment' do - context 'single' do - assert_unterminated 'a = 1' - assert_unterminated '@a = 1' - assert_unterminated '@@a = 1' - assert_unterminated '$a = 1' - assert_unterminated 'CONST = 1' - assert_unterminated 'Name::Spaced::CONST = 1' - assert_unterminated '::Foo = ::Bar' - end - - context 'lvar assigned from method with same name' do - assert_unterminated 'foo = foo()' - end - - context 'lvar introduction from condition' do - assert_source 'foo = bar while foo' - assert_source 'foo = bar until foo' - assert_source <<~'RUBY' - foo = exp - while foo - foo = bar - end - RUBY - - # Ugly I know. But its correct :D - # - # if foo { |pair| } - # pair = :foo - # foo - # end - assert_source <<~'RUBY' - if foo do |pair| - pair - end - pair = :foo - foo - end - RUBY - - assert_source <<~'RUBY' - while foo - foo = bar - end - RUBY - - assert_source <<~'RUBY' - each do |bar| - while foo - foo = bar - end - end - RUBY - - assert_source <<~'RUBY' - def foo - foo = bar while foo != baz - end - RUBY - - assert_source <<~'RUBY' - each do |baz| - while foo - foo = bar - end - end - RUBY - - assert_source <<~'RUBY' - each do |foo| - while foo - foo = bar - end - end - RUBY - end - - context 'multiple' do - assert_source 'a, * = [1, 2]' - assert_source 'a, *foo = [1, 2]' - assert_source '*a = []' - assert_source '*foo = [1, 2]' - assert_source 'a, = foo' - assert_unterminated 'a, b = [1, 2]' - assert_unterminated '@a, @b = [1, 2]' - assert_unterminated 'a.foo, a.bar = [1, 2]' - assert_unterminated 'a[0], a[1] = [1, 2]' - assert_unterminated 'a[*foo], a[1] = [1, 2]' - assert_unterminated '@@a, @@b = [1, 2]' - assert_unterminated '$a, $b = [1, 2]' - assert_unterminated 'a, b = foo' - assert_unterminated 'a, (b, c) = [1, [2, 3]]' - assert_unterminated 'a = (b, c = 1)' - assert_unterminated '(a,), b = 1' - end + context 'on empty source' do + assert_source '' end %w(next return break).each do |keyword| - context keyword do - assert_terminated keyword.to_s - assert_unterminated "#{keyword} 1" - assert_unterminated "#{keyword} 2, 3" - assert_unterminated "#{keyword} *nil" - assert_unterminated "#{keyword} *foo, bar" + assert_source "#{keyword} 1" + assert_source "#{keyword} 2, 3" + assert_source "#{keyword} *nil" + assert_source "#{keyword} *foo, bar" - assert_generates <<~RUBY, <<~RUBY - foo do |bar| - bar =~ // || #{keyword} - baz - end - RUBY - foo do |bar| - (bar =~ //) || #{keyword} + assert_source <<~RUBY + foo { |bar| + bar =~ // or #{keyword} baz - end - RUBY - - assert_generates <<~RUBY, <<~RUBY - #{keyword}(a ? b : c) - RUBY - #{keyword} (if a - b - else - c - end) + } RUBY end end - context 'conditional send (csend)' do - assert_terminated 'a&.b' - assert_terminated 'a&.b(c)' - end - - context 'send' do - assert_terminated 'foo' - assert_terminated 'self.foo' - assert_terminated 'a.foo' - assert_terminated 'A.foo' - assert_terminated 'foo[]' - assert_terminated 'foo[1]' - assert_terminated 'foo[*baz]' - assert_terminated 'foo(1)' - assert_terminated 'foo(bar)' - assert_terminated 'foo(&block)' - assert_terminated 'foo(&(foo || bar))' - assert_terminated 'foo(*arguments)' - assert_terminated 'foo(*arguments)' - - assert_source <<~'RUBY' - foo do - end - RUBY - - assert_source <<~'RUBY' - foo do |a| - end - RUBY - - assert_source <<~'RUBY' - foo do |a, | - end - RUBY - - assert_source <<~'RUBY' - foo do |a, b| - end - RUBY - - assert_source <<~'RUBY' - foo(1) do - nil - end - RUBY - - assert_source <<~'RUBY' - foo do |a, *b| - nil - end - RUBY - - assert_source <<~'RUBY' - foo do |a, *| - nil - end - RUBY - - assert_source <<~'RUBY' - foo do - bar - end - RUBY - - assert_source <<~'RUBY' - foo.bar(*args) - RUBY - - assert_source <<~'RUBY' - foo.bar do |(a, b), c| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |*a; b| - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |a; b| - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |; a, b| - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |*| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |(*)| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |((*))| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |(a, (*))| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do |(a, b)| - d - end - RUBY - - assert_source <<~'RUBY' - foo.bar do - end.baz - RUBY - - assert_source <<~'RUBY' - FOO() - RUBY - - assert_terminated '(1..2).max' - assert_terminated '1..2.max' - assert_unterminated 'a || return' - assert_unterminated 'foo << (bar * baz)' - - assert_source <<~'RUBY' - foo ||= (a, _ = b) - RUBY - - assert_source <<~'RUBY' - begin - rescue - end.bar - RUBY - - assert_source <<~'RUBY' - case (def foo - end - :bar) - when bar - end.baz - RUBY - - assert_source <<~'RUBY' - case foo - when bar - end.baz - RUBY - - assert_source <<~'RUBY' - class << self - end.bar - RUBY - - assert_source <<~'RUBY' - def self.foo - end.bar - RUBY - - assert_source <<~'RUBY' - def foo - end.bar - RUBY - - assert_source <<~'RUBY' - until foo - end.bar - RUBY - - assert_source <<~'RUBY' - while foo - end.bar - RUBY - - assert_source <<~'RUBY' - loop do - end.bar - RUBY - - assert_source <<~'RUBY' - class Foo - end.bar - RUBY - - assert_source <<~'RUBY' - module Foo - end.bar - RUBY - - assert_source <<~'RUBY' - if foo - end.baz - RUBY - - assert_source <<~'RUBY' - local = 1 - local.bar - RUBY - - assert_terminated 'foo.bar(*args)' - assert_terminated 'foo.bar(*arga, foo, *argb)' - assert_terminated 'foo.bar(*args, foo)' - assert_terminated 'foo.bar(foo, *args)' - assert_terminated 'foo.bar(foo, *args, &block)' - assert_source <<~'RUBY' - foo(bar, *args) - RUBY - - assert_terminated 'foo(*args, &block)' - assert_terminated 'foo.bar(&baz)' - assert_terminated 'foo.bar(:baz, &baz)' - assert_terminated 'foo.bar=:baz' - - assert_unterminated 'self.foo=:bar' - - assert_terminated 'foo.bar(baz: boz)' - assert_terminated 'foo.bar(foo, "baz" => boz)' - assert_terminated 'foo.bar({ foo: boz }, boz)' - assert_terminated 'foo.bar(foo, {})' - end - - context 'begin; end' do - assert_generates s(:begin), '' - - assert_source <<~'RUBY' - begin - end - RUBY - - assert_source <<~'RUBY' - foo - bar - RUBY - - assert_source <<~'RUBY' - begin - foo - bar - end.blah - RUBY - end - - context 'begin / rescue / ensure' do - assert_source <<~'RUBY' - begin - foo - ensure - bar - baz - end - RUBY - - assert_source <<~'RUBY' - begin - foo - rescue - baz - end - RUBY - - assert_source <<~'RUBY' - begin - begin - foo - bar - rescue - end - rescue - baz - bar - end - RUBY - - assert_source <<~'RUBY' - begin - raise(Exception) rescue foo = bar - rescue Exception - end - RUBY - - assert_source <<~'RUBY' - begin - foo - bar - rescue - baz - bar - end - RUBY - - assert_source <<~'RUBY' - begin - foo - rescue Exception - bar - end - RUBY - - assert_source <<~'RUBY' - begin - foo - rescue => bar - bar - end - RUBY - - assert_source <<~'RUBY' - begin - foo - rescue Exception, Other => bar - bar - end - RUBY - - assert_source <<~'RUBY' - class << self - undef :bar rescue nil - end - RUBY - - assert_source <<~'RUBY' - module Foo - undef :bar rescue nil - end - RUBY - - assert_source <<~'RUBY' - class Foo - undef :bar rescue nil - end - RUBY - - assert_source <<~'RUBY' - begin - rescue Exception => e - end - RUBY - - assert_source <<~'RUBY' - begin - ensure - end - RUBY - - assert_source <<~'RUBY' - begin - rescue - ensure - end - RUBY - - assert_source <<~'RUBY' - begin - foo - rescue Exception => bar - bar - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue SomeError, *bar - baz - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue SomeError, *bar => exception - baz - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue *bar - baz - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue LoadError - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue - else - baz - end - RUBY - - assert_source <<~'RUBY' - begin - bar - rescue *bar => exception - baz - end - RUBY - - assert_source <<~'RUBY' - m do - rescue Exception => e - end - RUBY - - assert_source <<~'RUBY' - m do - ensure - end - RUBY - - assert_source <<~'RUBY' - m do - rescue - ensure - end - RUBY - - assert_source <<~'RUBY' - m do - foo - rescue Exception => bar - bar - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue SomeError, *bar - baz - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue SomeError, *bar => exception - baz - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue *bar - baz - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue LoadError - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue - else - baz - end - RUBY - - assert_source <<~'RUBY' - m do - bar - rescue *bar => exception - baz - end - RUBY - - assert_source 'foo rescue bar' - assert_source 'foo rescue return bar' - assert_source 'x = (foo rescue return bar)' - - %w(while until if).each do |keyword| - assert_source <<~RUBY - #{keyword} ( - foo rescue false - ) - end - RUBY - - assert_generates <<~RUBY, <<~GENERATED - foo rescue false #{keyword} true - RUBY - #{keyword} true - foo rescue false - end - GENERATED + context 'op assign' do + %w(|= ||= &= &&= += -= *= /= **= %=).each do |op| + assert_source "self.foo #{op} bar" + assert_source "foo[key] #{op} bar" + assert_source "a #{op} (true; false)" end - - assert_generates <<~'RUBY', <<~GENERATED - case (foo rescue false) - when true - end - RUBY - case ( - foo rescue false - ) - when true - end - GENERATED end - context 'super' do - assert_source 'super' - - assert_source 'super()' - assert_source 'super(a)' - assert_source 'super(a, b)' - assert_source 'super(&block)' - assert_source 'super(a, &block)' - - assert_source <<~'RUBY' - super(a do - foo - end) - RUBY - - assert_source <<~'RUBY' - super do - foo - end - RUBY - - assert_source <<~'RUBY' - super(a) do - foo - end - RUBY - - assert_source <<~'RUBY' - super() do - foo + context 'element assignment' do + %w(+ - * / % & | || &&).each do |operator| + context "with #{operator}" do + assert_source "foo[index] #{operator}= 2" + assert_source "foo[] #{operator}= 2" end - RUBY - - assert_source <<~'RUBY' - super(a, b) do - foo - end - RUBY - - end - - context 'undef' do - assert_source 'undef :foo' - assert_source 'undef :foo, :bar' - end - - context 'BEGIN' do - assert_source <<~'RUBY' - BEGIN { - foo - } - RUBY - end - - context 'END' do - assert_source <<~'RUBY' - END { - foo - } - RUBY - end - - context 'alias' do - assert_source <<~'RUBY' - alias $foo $bar - RUBY - - assert_source <<~'RUBY' - alias :foo :bar - RUBY - end - - context 'yield' do - context 'without arguments' do - assert_source 'yield' - end - - context 'with argument' do - assert_source 'yield(a)' end - - context 'with arguments' do - assert_source 'yield(a, b)' - end - end - - context 'if statement' do - assert_source <<~'RUBY' - if /foo/ - bar - end - RUBY - - assert_source <<~'RUBY' - if 3 - 9 - end - RUBY - - assert_source <<~'RUBY' - if 4 - 5 - else - 6 - end - RUBY - - assert_source <<~'RUBY' - unless 3 - nil - end - RUBY - - assert_source <<~'RUBY' - unless 3 - 9 - end - RUBY - - assert_source <<~'RUBY' - if foo - end - RUBY - - assert_source <<~'RUBY' - foo = bar if foo - RUBY - - assert_source <<~'RUBY' - foo = bar unless foo - RUBY - - assert_source <<~'RUBY' - def foo(*foo) - unless foo - foo = bar - end - end - RUBY - - assert_source <<~'RUBY' - each do |foo| - unless foo - foo = bar - end - end - RUBY - end - - context 'def' do - context 'on instance' do - - assert_source <<~'RUBY' - def foo - end - RUBY - - assert_source <<~'RUBY' - def foo - bar - end - RUBY - - assert_source <<~'RUBY' - def foo - foo - rescue - bar - ensure - baz - end - RUBY - - assert_source <<~'RUBY' - begin - foo - ensure - bar rescue nil - end - RUBY - - assert_source <<~'RUBY' - def foo - bar - ensure - baz - end - RUBY - - assert_source <<~'RUBY' - def self.foo - bar - rescue - baz - end - RUBY - - assert_source <<~'RUBY' - def foo - bar - rescue - baz - end - RUBY - - assert_source <<~'RUBY' - def foo(bar) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar, baz) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar = ()) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar = (baz - nil)) - end - RUBY - - assert_source <<~'RUBY' - def foo(bar = true) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar, baz = true) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar: 1) - end - RUBY - - assert_source <<~'RUBY' - def foo(bar: bar) - end - RUBY - - assert_source <<~'RUBY' - def foo(bar: bar()) - end - RUBY - - assert_source <<~'RUBY' - def foo(*) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(*bar) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar, *baz) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(baz = true, *bor) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(baz = true, *bor, &block) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar, baz = true, *bor) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(&block) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo(bar, &block) - bar - end - RUBY - - assert_source <<~'RUBY' - def foo - bar - baz - end - RUBY - - assert_source <<~'RUBY' - def (foo do |bar| - end).bar - bar - end - RUBY - - assert_source <<~'RUBY' - def (foo(1)).bar - bar - end - RUBY - - assert_source <<~'RUBY' - def (Foo::Bar.baz).bar - baz - end - RUBY - - assert_source <<~'RUBY' - def (Foo::Bar).bar - baz - end - RUBY - - assert_source <<~'RUBY' - def Foo.bar - baz - end - RUBY - - assert_source <<~'RUBY' - def foo.bar - baz - end - RUBY - end - - context 'on singleton' do - assert_source <<~'RUBY' - def self.foo - end - RUBY - - assert_source <<~'RUBY' - def self.foo - bar - end - RUBY - - assert_source <<~'RUBY' - def self.foo - bar - baz - end - RUBY - - assert_source <<~'RUBY' - def Foo.bar - bar - end - RUBY - - end - - context 'class' do - assert_source <<~'RUBY' - class TestClass - end - RUBY - - assert_source <<~'RUBY' - class << some_object - end - RUBY - - assert_source <<~'RUBY' - class << some_object - the_body - end - RUBY - - assert_source <<~'RUBY' - class SomeNameSpace::TestClass - end - RUBY - - assert_source <<~'RUBY' - class Some::Name::Space::TestClass - end - RUBY - - assert_source <<~'RUBY' - class TestClass < Object - end - RUBY - - assert_source <<~'RUBY' - class TestClass < SomeNameSpace::Object - end - RUBY - - assert_source <<~'RUBY' - class TestClass - def foo - :bar - end - end - RUBY - - assert_source <<~'RUBY' - class ::TestClass - end - RUBY - end - - context 'module' do - - assert_source <<~'RUBY' - module TestModule - end - RUBY - - assert_source <<~'RUBY' - module SomeNameSpace::TestModule - end - RUBY - - assert_source <<~'RUBY' - module Some::Name::Space::TestModule - end - RUBY - - assert_source <<~'RUBY' - module TestModule - def foo - :bar - end - end - RUBY - - end - - context 'op assign' do - %w(|= ||= &= &&= += -= *= /= **= %=).each do |op| - assert_source "self.foo #{op} bar" - assert_source "foo[key] #{op} bar" - assert_source "a #{op} (true\nfalse)" - end - end - - context 'element assignment' do - assert_source 'foo[index] = value' - assert_source 'foo[*index] = value' - assert_source 'foo[a, b] = value' - assert_source 'foo[1..2] = value' - assert_source 'foo.[]=()' - assert_source 'foo.[]=true' - assert_source 'foo.[]=(1, 2)' - assert_source 'foo[] = 1' - assert_unterminated 'foo[] = 1' - - %w(+ - * / % & | || &&).each do |operator| - context "with #{operator}" do - assert_source "foo[index] #{operator}= 2" - assert_source "foo[] #{operator}= 2" - end - end - end - - context 'defined?' do - assert_source <<~'RUBY' - defined?(@foo) - RUBY - - assert_source <<~'RUBY' - defined?(Foo) - RUBY - - assert_source <<~'RUBY' - defined?((a, b = [1, 2])) - RUBY - end - end - - context 'lambda' do - assert_source <<~'RUBY' - lambda do - end - RUBY - - assert_source <<~'RUBY' - lambda do |a, b| - a - end - RUBY - - assert_source <<~'RUBY' - ->() do - end - RUBY - - assert_source <<~'RUBY' - ->(a) do - end - RUBY - - assert_source <<~'RUBY' - ->(a, b) do - end - RUBY - end - - context 'match operators' do - assert_source '/bar/ =~ foo' - assert_source '/bar/ =~ :foo' - assert_source '(/bar/ =~ :foo).foo' - assert_source 'foo =~ /bar/' - assert_source 'foo(foo =~ /bar/)' - assert_source '(foo =~ /bar/).foo' end context 'binary operator methods' do @@ -1623,223 +242,6 @@ def foo assert_source 'left / right' end - context 'nested binary operators' do - assert_source '(a + b) / (c - d)' - assert_source '(a + b) / c.-(e, f)' - assert_source '(a + b) / c.-(*f)' - end - - context 'binary operator' do - assert_source 'a || (return foo)' - assert_source '(return foo) || a' - assert_source 'a || (break foo)' - assert_source '(break foo) || a' - assert_source '(a || b).foo' - assert_source 'a || (b || c)' - end - - { or: :'||', and: :'&&' }.each do |word, symbol| - assert_generates "a #{word} return foo", "a #{symbol} (return foo)" - assert_generates "a #{word} break foo", "a #{symbol} (break foo)" - assert_generates "a #{word} next foo", "a #{symbol} (next foo)" - end - - context 'expansion of shortcuts' do - assert_source 'a += 2' - assert_source 'a -= 2' - assert_source 'a **= 2' - assert_source 'a *= 2' - assert_source 'a /= 2' - end - - context 'shortcuts' do - assert_source 'a &&= b' - assert_source 'a ||= 2' - assert_source '(a ||= 2).bar' - assert_source '(h ||= {})[k] = v' - end - - context 'flip flops' do - context 'inclusive' do - assert_source <<~'RUBY' - if ((i == 4)..(i == 4)) - foo - end - RUBY - end - - context 'exclusive' do - assert_source <<~'RUBY' - if ((i == 4)...(i == 4)) - foo - end - RUBY - end - end - - context 'case statement' do - assert_source <<~'RUBY' - case - when bar - baz - when baz - bar - end - RUBY - - assert_source <<~'RUBY' - case foo - when bar - when baz - bar - end - RUBY - - assert_source <<~'RUBY' - case foo - when bar - baz - when baz - bar - end - RUBY - - assert_source <<~'RUBY' - case foo - when bar, baz - :other - end - RUBY - - assert_source <<~'RUBY' - case foo - when *bar - :value - end - RUBY - - assert_source <<~'RUBY' - case foo - when bar - baz - else - :foo - end - RUBY - end - - context 'for' do - assert_source <<~'RUBY' - bar(for a in bar do - baz - end) - RUBY - assert_source <<~'RUBY' - for a in bar do - baz - end - RUBY - - assert_source <<~'RUBY' - for a, *b in bar do - baz - end - RUBY - - assert_source <<~'RUBY' - for a, b in bar do - baz - end - RUBY - end - - context 'unary operators' do - assert_source '!1' - assert_source '!(!1)' - assert_source '!(!(foo || bar))' - assert_source '!(!1).baz' - assert_source '~a' - assert_source '-a' - assert_source '+a' - assert_source '-(-a).foo' - end - - context 'loop' do - assert_source <<~'RUBY' - loop do - foo - end - RUBY - end - - context 'post conditions' do - assert_source <<~'RUBY' - x = (begin - foo - end while baz) - RUBY - - assert_source <<~'RUBY' - begin - foo - end while baz - RUBY - - assert_source <<~'RUBY' - begin - foo - bar - end until baz - RUBY - - assert_source <<~'RUBY' - begin - foo - bar - end while baz - RUBY - end - - context 'while' do - assert_source <<~'RUBY' - while false - end - RUBY - - assert_source <<~'RUBY' - while false - 3 - end - RUBY - - assert_source <<~'RUBY' - while (foo do - end) - :body - end - RUBY - end - - context 'until' do - assert_source <<~'RUBY' - until false - end - RUBY - - assert_source <<~'RUBY' - until false - 3 - end - RUBY - - assert_source <<~'RUBY' - until (foo do - end) - :body - end - RUBY - end - assert_source <<~'RUBY' # comment before a_line_of_code @@ -1850,11 +252,11 @@ def foo RUBY assert_source <<~'RUBY' - nested do # first + nested { + # first # second something # comment - # another - end + } # another # last RUBY @@ -1878,15 +280,15 @@ def noop =begin block comment =end - nested do + nested { =begin another block comment =end something + } =begin last block comment =end - end RUBY assert_generates(<<~'RUBY', <<~'RUBY') @@ -1917,4 +319,25 @@ def noop =end RUBY end + + describe 'corpus' do + let(:version_excludes) do + if RUBY_VERSION < '2.7.' + %w[ + --ignore test/corpus/literal/pattern.rb + --ignore test/corpus/literal/since/27.rb + ] + else + [] + end + end + + it 'passes the literal corpus' do + expect(Unparser::CLI.run(%w[test/corpus/literal --literal] + version_excludes)).to be(0) + end + + it 'passes the semantic corpus' do + expect(Unparser::CLI.run(%w[test/corpus/semantic] + version_excludes)).to be(0) + end + end end diff --git a/test/corpus/literal/alias.rb b/test/corpus/literal/alias.rb new file mode 100644 index 00000000..fb06a295 --- /dev/null +++ b/test/corpus/literal/alias.rb @@ -0,0 +1,2 @@ +alias $foo $bar +alias :foo :bar diff --git a/test/corpus/literal/assignment.rb b/test/corpus/literal/assignment.rb new file mode 100644 index 00000000..c74b6855 --- /dev/null +++ b/test/corpus/literal/assignment.rb @@ -0,0 +1,42 @@ +$a = 1 +($a, $b) = [1, 2] +((a,), b) = 1 +(*a) = [] +(*foo) = [1, 2] +(@@a, @@b) = [1, 2] +(@a, @b) = [1, 2] +(a, (b, c)) = [1, [2, 3]] +(a, *) = [1, 2] +(a, *foo) = [1, 2] +(a, b) = [1, 2] +(a, b) = foo +(a,) = foo +(a.foo, a.bar) = [1, 2] +(a[*foo], a[1]) = [1, 2] +(a[0], a[1]) = [1, 2] +::Foo = ::Bar +@@a = 1 +@a = 1 +CONST = 1 +Name::Spaced::CONST = 1 +a = ((b, c) = 1) +a = 1 +foo = foo() +foo.[]=() +foo.[]=(1, 2) +foo.[]=true +foo[*index] = value +foo[1..2] = value +foo[] = 1 +foo[a, b] = value +foo[index] = value +x = <<-HEREDOC +HEREDOC +x.x=<<-HEREDOC +HEREDOC +x[] = <<-HEREDOC +HEREDOC +a[<<-HEREDOC] ||= bar +HEREDOC +@a ||= <<-HEREDOC +HEREDOC diff --git a/test/corpus/literal/binary.rb b/test/corpus/literal/binary.rb new file mode 100644 index 00000000..c5c18c4b --- /dev/null +++ b/test/corpus/literal/binary.rb @@ -0,0 +1,20 @@ +(a || b).foo +(break foo) || a +(return foo) || a +a && b && c +a = b || break +a = b || next +a = b || return +a and return foo +a or return +a or return foo +a || (b || c) +a || (break foo) +a || (return foo) +b or break +b or next +break or b +next or b +return or a +a = b or c +a = b and c diff --git a/test/corpus/literal/block.rb b/test/corpus/literal/block.rb new file mode 100644 index 00000000..b2baf1dc --- /dev/null +++ b/test/corpus/literal/block.rb @@ -0,0 +1,96 @@ +foo { +} +foo { |a| +} +foo { |a,| +} +foo { |a,; x| +} +foo { |a, b| +} +foo(1) { + nil +} +foo { |a, *b| + nil +} +foo { |a, *| + nil +} +foo { + bar +} +foo.bar { |(a, b), c| + d +} +foo.bar { |*a; b| +} +foo.bar { |a; b| +} +foo.bar { |; a, b| +} +foo.bar { |*| + d +} +foo.bar { |(*)| + d +} +foo.bar { |((*))| + d +} +foo.bar { |(a, (*))| + d +} +foo.bar { |(a, b)| + d +} +foo.bar { +}.baz +m do +rescue Exception => e +end +m do + foo +rescue Exception => bar + bar +end +m do + bar +rescue SomeError, *bar + baz +end +m do + bar +rescue SomeError, *bar => exception + baz +end +m do + bar +rescue *bar + baz +end +m do + bar +rescue LoadError +end +m do + bar +rescue +else + baz +end +m do + bar +rescue *bar => exception + baz +end +m do +ensure +end +m do +rescue +ensure +end +bar { + _1 + _2 +} diff --git a/test/corpus/literal/case.rb b/test/corpus/literal/case.rb new file mode 100644 index 00000000..9634ff49 --- /dev/null +++ b/test/corpus/literal/case.rb @@ -0,0 +1,31 @@ +case +when bar + baz +when baz + bar +end +case foo +when bar +when baz + bar +end +case foo +when bar + baz +when baz + bar +end +case foo +when bar, baz + :other +end +case foo +when *bar + :value +end +case foo +when bar + baz +else + :foo +end diff --git a/test/corpus/literal/class.rb b/test/corpus/literal/class.rb new file mode 100644 index 00000000..f0198625 --- /dev/null +++ b/test/corpus/literal/class.rb @@ -0,0 +1,35 @@ +class A +end + +class << a +end + +class << a + b +end + +class A::B +end + +class A::B::C +end + +class A < B +end + +class A < B::C +end + +class A::B < C::D +end + +class A + include(B.new) + + def foo + :bar + end +end + +class ::A +end diff --git a/test/corpus/literal/control.rb b/test/corpus/literal/control.rb new file mode 100644 index 00000000..281b47a4 --- /dev/null +++ b/test/corpus/literal/control.rb @@ -0,0 +1,5 @@ +next +return +break +retry +redo diff --git a/test/corpus/literal/def.rb b/test/corpus/literal/def.rb new file mode 100644 index 00000000..e2648f8e --- /dev/null +++ b/test/corpus/literal/def.rb @@ -0,0 +1,129 @@ +def foo + a +rescue + b +else + c +ensure + d +end + +def foo + a rescue b +rescue + b +else + c +ensure + d +end + +def foo(bar:, baz:) +end + +def foo +end + +def foo + bar +end + +def foo + foo +rescue + bar +ensure + baz +end + +def foo + bar +ensure + baz +end + +def foo + bar +rescue + baz +end + +def foo(bar) + bar +end + +def foo(bar, baz) + bar +end + +def foo(bar = ()) + bar +end + +def foo(bar = (baz; nil)) +end + +def foo(bar = true) + bar +end + +def foo(bar, baz = true) + bar +end + +def foo(bar: 1) +end + +def foo(bar: baz) +end + +def foo(bar: bar()) +end + +def foo(*) + bar +end + +def foo(*bar) + bar +end + +def foo(bar, *baz) + bar +end + +def foo(baz = true, *bor) + bar +end + +def foo(baz = true, *bor, &block) + bar +end + +def foo(bar, baz = true, *bor) + bar +end + +def foo(&block) + bar +end + +def foo(bar, &block) + bar +end + +def foo + bar + baz +end + +def f(((a))) +end + +def foo(bar:, baz: "value") +end + +def f + (<<-HEREDOC) + HEREDOC +end diff --git a/test/corpus/literal/defined.rb b/test/corpus/literal/defined.rb new file mode 100644 index 00000000..65e7c370 --- /dev/null +++ b/test/corpus/literal/defined.rb @@ -0,0 +1,3 @@ +defined?(@foo) +defined?(Foo) +defined?(((a, b) = [1, 2])) diff --git a/test/corpus/literal/defs.rb b/test/corpus/literal/defs.rb new file mode 100644 index 00000000..b70aa9ef --- /dev/null +++ b/test/corpus/literal/defs.rb @@ -0,0 +1,40 @@ +def self.foo +end + +def self.foo + bar +end + +def self.foo + bar + baz +end + +def Foo.bar + bar +end + +def (foo { |bar| +}).bar + bar +end + +def (foo(1)).bar + bar +end + +def (Foo::Bar.baz).bar + baz +end + +def (Foo::Bar).bar + baz +end + +def Foo.bar + baz +end + +def foo.bar + baz +end diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb new file mode 100644 index 00000000..c2e83c9e --- /dev/null +++ b/test/corpus/literal/dstr.rb @@ -0,0 +1,19 @@ +if true + <<~HEREDOC + #{}a + HEREDOC +end +if true + <<~HEREDOC + a + #{}a + b + HEREDOC + x +end +<<~HEREDOC + \#{}\#{} + #{} + #{} + #{} +HEREDOC diff --git a/test/corpus/literal/empty_begin.rb b/test/corpus/literal/empty_begin.rb new file mode 100644 index 00000000..6a452c18 --- /dev/null +++ b/test/corpus/literal/empty_begin.rb @@ -0,0 +1 @@ +() diff --git a/test/corpus/literal/flipflop.rb b/test/corpus/literal/flipflop.rb new file mode 100644 index 00000000..8badd39d --- /dev/null +++ b/test/corpus/literal/flipflop.rb @@ -0,0 +1,6 @@ +if ((i == 4)..(i == 4)) + foo +end +if ((i == 4)...(i == 4)) + foo +end diff --git a/test/corpus/literal/for.rb b/test/corpus/literal/for.rb new file mode 100644 index 00000000..4c19a352 --- /dev/null +++ b/test/corpus/literal/for.rb @@ -0,0 +1,12 @@ +bar(for a in bar do + baz +end) +for a in bar do + baz +end +for (a, *b) in bar do + baz +end +for (a, b) in bar do + baz +end diff --git a/test/corpus/literal/hookexe.rb b/test/corpus/literal/hookexe.rb new file mode 100644 index 00000000..08f14f47 --- /dev/null +++ b/test/corpus/literal/hookexe.rb @@ -0,0 +1,7 @@ +BEGIN { + foo +} +bar +END { + baz +} diff --git a/test/corpus/literal/if.rb b/test/corpus/literal/if.rb new file mode 100644 index 00000000..0c13801f --- /dev/null +++ b/test/corpus/literal/if.rb @@ -0,0 +1,36 @@ +if /foo/ + bar +end +if 3 + 9 +end +if 4 + 5 +else + 6 +end +unless 3 + nil +end +unless 3 + 9 +end +if foo +end + +module A + foo = bar if foo +end + +module B + foo = bar unless foo +end +unless foo + foo = bar +end +if foo { |pair| + pair +} + pair = :foo + foo +end diff --git a/test/corpus/literal/kwbegin.rb b/test/corpus/literal/kwbegin.rb new file mode 100644 index 00000000..6cc1e74c --- /dev/null +++ b/test/corpus/literal/kwbegin.rb @@ -0,0 +1,80 @@ +begin +rescue +end + +begin +ensure +end + +begin + a +end + +begin + a +rescue + b +end + +begin + a + b +rescue + b +end + +begin +rescue A +end + +begin +rescue A => foo +end + +begin + a +rescue A + b +rescue B + c +ensure + d +end + +begin + begin + foo + bar + rescue + end +rescue + baz + bar +end + +begin + raise(Exception) rescue foo = bar +rescue Exception +end + +begin + foo +rescue => bar + bar +end + +begin + foo +rescue Exception, Other => bar + bar +end + +begin + bar +rescue SomeError, *bar => exception + baz +end + +class << self + undef :bar rescue nil +end diff --git a/test/corpus/literal/lambda.rb b/test/corpus/literal/lambda.rb new file mode 100644 index 00000000..4eb722da --- /dev/null +++ b/test/corpus/literal/lambda.rb @@ -0,0 +1,13 @@ +lambda { +} +lambda { |a, b| + a +} +->() { +} +->(a) { +} +->(a, b) { +} +->(a, b; c) { +} diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb new file mode 100644 index 00000000..880c4c2f --- /dev/null +++ b/test/corpus/literal/literal.rb @@ -0,0 +1,92 @@ +{ "foo" => <<-HEREDOC, "bar" => :baz } +HEREDOC +["foo", <<-HEREDOC] +HEREDOC +a(<<-HEREDOC).a +HEREDOC +{ "foo" => <<-HEREDOC, **baz } +HEREDOC +"#@a #@@a #$a" +0 +++1 +1 +1 +1r +1.5r +1.3r +5i +-5i +0.6i +-0.6i +1000000000000000000000000000000i +1ri +"foo" "bar" +"foobar #{baz}" +"foo#{1}bar" +"\\\\#{}" +"#{}\#{}" +"\#{}#{}" +"foo\\\#{@bar}" +"\"" +"foo bar" +"foo\nbar" +`foo` +`foo#{@bar}` +`)` +`\`` +`"` +:foo +:"A B" +:foo +:"A B" +:"A\"B" +:"" +/foo/ +/[^-+',.\/:@[:alnum:]\[\]]+/ +/foo#{@bar}/ +/foo#{@bar}/imx +/#{"\u0000"}/ +/\n/ +/\n/ +/\n/x +/\/\//x +:"foo#{bar}baz" +:"#{"foo"}" +(1..) +1..2 +(0.0 / 0.0)..1 +1..(0.0 / 0.0) +(0.0 / 0.0)..100 +(1...) +1...2 +-0.1 +0.1 +[1, 2] +[1, (), n2] +[1] +[] +[1, *@foo] +[*@foo, 1] +[*@foo, *@baz] +{} +{ () => () } +{ 1 => 2 } +{ 1 => 2, 3 => 4 } +{ a: (1 rescue foo), b: 2 } +{ a: 1, b: 2 } +{ a: :a } +{ :"a b" => 1 } +{ :-@ => 1 } +"#{} +#{}\na" +foo { + "#{} +#{}\na" +} +:"a\\ +b" +<<~`HEREDOC` + x + #{foo} +HEREDOC + diff --git a/test/corpus/literal/module.rb b/test/corpus/literal/module.rb new file mode 100644 index 00000000..cec03f3b --- /dev/null +++ b/test/corpus/literal/module.rb @@ -0,0 +1,16 @@ +module A +end + +module A::B +end + +module A::B::C +end + +module A + include(B.new) + + def foo + :bar + end +end diff --git a/test/corpus/literal/opasgn.rb b/test/corpus/literal/opasgn.rb new file mode 100644 index 00000000..5858d773 --- /dev/null +++ b/test/corpus/literal/opasgn.rb @@ -0,0 +1,24 @@ +a += 2 +a -= 2 +a **= 2 +a *= 2 +a /= 2 +a &&= b +a ||= 2 +(a ||= 2).bar +(h ||= {})[k] = v +a.b += 2 +a.b -= 2 +a.b **= 2 +a.b *= 2 +a.b /= 2 +a.b &&= b +a.b ||= 2 +a[b] += 2 +a[b] -= 2 +a[b] **= 2 +a[b] *= 2 +a[b] /= 2 +a[b] &&= b +a[b] ||= 2 +foo.A += 1 diff --git a/test/corpus/literal/pattern.rb b/test/corpus/literal/pattern.rb new file mode 100644 index 00000000..589a8903 --- /dev/null +++ b/test/corpus/literal/pattern.rb @@ -0,0 +1,38 @@ +case foo +in A[1, 2, *a, 3] then + true +in [1, 2, ] then + y +in A(x:) then + true +in {**a} then + true +in {} if true then + true +in [x, y, *] then + true +in {a: 1, aa: 2} then + true +in {} then + true +in {**nil} then + true +in {"#{"a"}": 1} then + true +in 1 | 2 then + true +in 1 => a then + true +in ^x then + true +else + true +end +case foo +in A[1, 2, *a, 3] +end +case foo +in A +else +end +1 in [a] diff --git a/test/corpus/literal/pragma.rb b/test/corpus/literal/pragma.rb new file mode 100644 index 00000000..4f6dd71b --- /dev/null +++ b/test/corpus/literal/pragma.rb @@ -0,0 +1,4 @@ +__ENCODING__ +__FILE__ +__LINE__ +__dir__ diff --git a/test/corpus/literal/rescue.rb b/test/corpus/literal/rescue.rb new file mode 100644 index 00000000..a7878168 --- /dev/null +++ b/test/corpus/literal/rescue.rb @@ -0,0 +1,3 @@ +foo rescue bar +foo rescue return bar +x = (foo rescue return bar) diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb new file mode 100644 index 00000000..06543ebc --- /dev/null +++ b/test/corpus/literal/send.rb @@ -0,0 +1,79 @@ +module A + foo ||= ((a, _) = b) +end + +module A + local = 1 + local.bar +end +class A +end.bar +module A +end.bar +begin +rescue +end.bar +case (def foo +end; :bar) +when bar +end.baz +case foo +when bar +end.baz +class << self +end.bar +def self.foo +end.bar +def foo +end.bar +until foo +end.bar +while foo +end.bar +loop { +}.bar +if foo +end.baz +(/bar/ =~ :foo).foo +(1..2).max +(foo =~ /bar/).foo +/bar/ =~ :foo +/bar/ =~ foo +1..2.max +A.foo +FOO() +a&.b +a.foo +foo +foo << (bar * baz) +foo =~ /bar/ +foo(&(foo || bar)) +foo(&block) +foo(*args, &block) +foo(*arguments) +foo(1, 2) +foo(bar) +foo(bar, *args) +foo(foo =~ /bar/) +foo.bar(&baz) +foo.bar(*arga, foo, *argb) +foo.bar(*args) +foo.bar(*args, foo) +foo.bar(:baz, &baz) +foo.bar(baz: boz) +foo.bar(foo, "baz" => boz) +foo.bar(foo, *args) +foo.bar(foo, *args, &block) +foo.bar(foo, {}) +foo.bar({ foo: boz }, boz) +foo.bar=:baz +foo(a: b) +foo[*baz] +foo[1, 2] +foo[] +self.foo +self.foo=:bar +(a + b) / (c - d) +(a + b) / c.-(e, f) +(a + b) / c.-(*f) +x(**foo) diff --git a/test/corpus/literal/since/27.rb b/test/corpus/literal/since/27.rb new file mode 100644 index 00000000..c332f9e4 --- /dev/null +++ b/test/corpus/literal/since/27.rb @@ -0,0 +1,4 @@ +-> { + _1 + _2 +} +(..1) diff --git a/test/corpus/literal/singletons.rb b/test/corpus/literal/singletons.rb new file mode 100644 index 00000000..496e6a41 --- /dev/null +++ b/test/corpus/literal/singletons.rb @@ -0,0 +1,4 @@ +false +nil +self +true diff --git a/test/corpus/literal/super.rb b/test/corpus/literal/super.rb new file mode 100644 index 00000000..0e73e6f0 --- /dev/null +++ b/test/corpus/literal/super.rb @@ -0,0 +1,21 @@ +super +super() +super(a) +super(a, b) +super(&block) +super(a, &block) +super(a { + foo +}) +super { + foo +} +super(a) { + foo +} +super() { + foo +} +super(a, b) { + foo +} diff --git a/test/corpus/literal/unary.rb b/test/corpus/literal/unary.rb new file mode 100644 index 00000000..77685cb7 --- /dev/null +++ b/test/corpus/literal/unary.rb @@ -0,0 +1,8 @@ +!1 +!(!1) +!(!(foo || bar)) +!(!1).baz +~a +-a ++a +-(-a).foo diff --git a/test/corpus/literal/undef.rb b/test/corpus/literal/undef.rb new file mode 100644 index 00000000..a65d8d0c --- /dev/null +++ b/test/corpus/literal/undef.rb @@ -0,0 +1,2 @@ +undef :foo +undef :foo, :bar diff --git a/test/corpus/literal/variables.rb b/test/corpus/literal/variables.rb new file mode 100644 index 00000000..1de938f3 --- /dev/null +++ b/test/corpus/literal/variables.rb @@ -0,0 +1,10 @@ +a +@a +@@a +$a +$1 +$` +CONST +SCOPED::CONST +::TOPLEVEL +::TOPLEVEL::CONST diff --git a/test/corpus/literal/while.rb b/test/corpus/literal/while.rb new file mode 100644 index 00000000..19a60ef5 --- /dev/null +++ b/test/corpus/literal/while.rb @@ -0,0 +1,73 @@ +module A + foo { |bar| + while foo + foo = bar + end + } +end + +def foo + foo = bar while foo != baz +end + +module A + foo = bar while foo +end + +module A + foo = bar until foo +end + +module A + while foo + foo = bar + end +end + +module A + each { |baz| + while foo + foo = bar + end + } +end + +module A + each { |foo| + while foo + foo = bar + end + } +end +x = (begin + foo +end while baz) +begin + foo +end while baz +begin + foo + bar +end until baz +begin + foo + bar +end while baz +while false +end +while false + 3 +end +while (foo { +}) + :body +end +until false +end +until false + 3 +end +until (foo { +}) + :body +end diff --git a/test/corpus/literal/yield.rb b/test/corpus/literal/yield.rb new file mode 100644 index 00000000..c0b58208 --- /dev/null +++ b/test/corpus/literal/yield.rb @@ -0,0 +1,3 @@ +yield +yield(a) +yield(a, b) diff --git a/test/corpus/semantic/and.rb b/test/corpus/semantic/and.rb new file mode 100644 index 00000000..43d17124 --- /dev/null +++ b/test/corpus/semantic/and.rb @@ -0,0 +1,8 @@ +a...b or c...d +a...b and c...d + +if a...b or c...d +end + +if a...b and c...d +end diff --git a/test/corpus/semantic/block.rb b/test/corpus/semantic/block.rb new file mode 100644 index 00000000..1ffc65c8 --- /dev/null +++ b/test/corpus/semantic/block.rb @@ -0,0 +1,26 @@ +foo do +end + +foo do +rescue +end + +foo do + nil rescue nil + nil +end + +foo do |_1| +end + +foo(<<-DOC) do |_1| + b +DOC + a +end + +foo(<<-DOC) do + b +DOC + a +end diff --git a/test/corpus/semantic/def.rb b/test/corpus/semantic/def.rb new file mode 100644 index 00000000..75746193 --- /dev/null +++ b/test/corpus/semantic/def.rb @@ -0,0 +1,7 @@ +def foo + (a - b) +end + +def foo + a rescue Exception +end diff --git a/test/corpus/semantic/dstr.rb b/test/corpus/semantic/dstr.rb new file mode 100644 index 00000000..919e7360 --- /dev/null +++ b/test/corpus/semantic/dstr.rb @@ -0,0 +1,127 @@ +< Date: Wed, 10 Jun 2020 14:35:25 +0300 Subject: [PATCH 052/256] Change to raise unparser specific error on unknown node type * This unparser specific exception type makes it easy to catch errors originating from the specific case unparser is exposed to a node type it does not understand. --- Changelog.md | 1 + lib/unparser/emitter.rb | 3 ++- spec/unit/unparser_spec.rb | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 4b01ad40..716ac054 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,6 @@ # v0.4.8 unreleased +* Change to specific node type when unparser fails on an unknown node type: [#150](https://github.com/mbj/unparser/pull/150) * Significantly improve verifier (only useful for debugging) * Add `Unparser::Color` module for colorized source diffs diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 8aa9c3ca..473dbe12 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true module Unparser + UnknownNodeError = Class.new(ArgumentError) # Emitter base class # @@ -116,7 +117,7 @@ def write_to_buffer def self.emitter(node, parent) type = node.type klass = REGISTRY.fetch(type) do - raise ArgumentError, "No emitter for node: #{type.inspect}" + raise UnknownNodeError, "Unknown node type: #{type.inspect}" end klass.new(node, parent) end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 502d80f9..7b296048 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -119,6 +119,23 @@ def apply end end + describe '.unparse' do + context 'on unknown node type' do + def apply + Unparser.unparse(node) + end + + let(:node) { s(:example_node) } + + it 'raises UnknownNodeError' do + expect { apply }.to raise_error( + Unparser::UnknownNodeError, + 'Unknown node type: :example_node' + ) + end + end + end + describe '.unparse' do let(:builder_options) { {} } From a67eca0e6a8734a2e9e751dd95e7e0b6492acb3e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Aug 2020 15:57:03 +0000 Subject: [PATCH 053/256] Add github actions CI --- .github/workflows/ci.yml | 84 ++++++++++++++++++++++++++++++++++++++++ README.md | 3 +- 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..8d95f843 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,84 @@ +name: CI + +on: push + +jobs: + base: + name: Base steps + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Check Whitespace + run: git diff --check -- HEAD~1 + ruby-unit-spec: + name: Unit Specs + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + ruby: ['2.6'] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - run: | + gem install bundler + bundle install + - run: bundle exec rspec spec/unit + ruby-integration-spec: + name: Integration Specs + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + ruby: ['2.6'] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - run: | + gem install bundler + bundle install + - run: bundle exec rspec spec/integration + ruby-mutant: + name: Mutation coverage + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + ruby: ['2.6'] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - run: | + gem install bundler + bundle install + - run: bundle exec mutant --since HEAD~1 --zombie -- 'Mutant*' + ruby-rubocop: + name: Rubocop + runs-on: ${{ matrix.os }} + timeout-minutes: 5 + strategy: + fail-fast: false + matrix: + ruby: ['2.6'] + os: [ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + - run: bundle install + - run: bundle exec rake metrics:rubocop diff --git a/README.md b/README.md index 4d171ba6..26c8d709 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ unparser ======== -[![Build Status](https://secure.travis-ci.org/mbj/unparser.svg?branch=master)](http://travis-ci.org/mbj/unparser) -[![Code Climate](https://codeclimate.com/github/mbj/unparser.svg)](https://codeclimate.com/github/mbj/unparser) +[![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg) [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). From b57bd0dcebf704f9b38f30a8f10af7516fc2e684 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Aug 2020 16:08:00 +0000 Subject: [PATCH 054/256] Remove CircleCI config --- .circleci/config.yml | 49 -------------------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 226ac393..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,49 +0,0 @@ -defaults: &defaults - working_directory: ~/unparser - docker: - - image: circleci/ruby:2.6.5 -version: 2 -jobs: - unit_specs: - <<: *defaults - steps: - - checkout - - run: bundle install - - run: bundle exec rspec spec/unit - integration_specs: - <<: *defaults - steps: - - checkout - - run: bundle install - - run: bundle exec rspec spec/integration - metrics: - <<: *defaults - steps: - - checkout - - run: bundle install - - run: bundle exec rake metrics:rubocop - - run: bundle exec rake metrics:reek - - run: bundle exec rake metrics:flay - - run: bundle exec rake metrics:flog - mutant: - <<: *defaults - steps: - - checkout - - run: bundle install - - run: | - bundle \ - exec \ - mutant \ - --ignore-subject 'Unparser::AST::LocalVariableScope*' \ - --since origin/master \ - --zombie \ - -- \ - 'Unparser*' -workflows: - version: 2 - test: - jobs: - - unit_specs - - integration_specs - - metrics - - mutant From 2c8dc18355cab40002656b4708f66ad431f6186b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Aug 2020 16:44:04 +0000 Subject: [PATCH 055/256] Remove morpher as development dependency --- spec/integration/unparser/corpus_spec.rb | 52 +++++++++++++++--------- spec/spec_helper.rb | 8 ++-- unparser.gemspec | 1 - 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb index 58cc86fb..16645446 100644 --- a/spec/integration/unparser/corpus_spec.rb +++ b/spec/integration/unparser/corpus_spec.rb @@ -81,26 +81,40 @@ def system(arguments) end end - LOADER = Morpher.build do - s(:block, - s(:guard, s(:primitive, Array)), - s(:map, - s(:block, - s(:guard, s(:primitive, Hash)), - s(:hash_transform, - s(:key_symbolize, :repo_uri, s(:guard, s(:primitive, String))), - s(:key_symbolize, :repo_ref, s(:guard, s(:primitive, String))), - s(:key_symbolize, :name, s(:guard, s(:primitive, String))), - s(:key_symbolize, :exclude, s(:map, s(:guard, s(:primitive, String))))), - s(:load_attribute_hash, - # NOTE: The domain param has no DSL currently! - Morpher::Evaluator::Transformer::Domain::Param.new( - Project, - [:repo_uri, :repo_ref, :name, :exclude] - ))))) - end + transform = Mutant::Transform + string = transform::Primitive.new(String) + string_array = transform::Array.new(string) + path = ROOT.join('spec', 'integrations.yml') + + loader = + transform::Named.new( + path.to_s, + transform::Sequence.new( + [ + transform::Exception.new(SystemCallError, :read.to_proc), + transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)), + transform::Array.new( + transform::Sequence.new( + [ + transform::Hash.new( + optional: [], + required: [ + transform::Hash::Key.new('exclude', string_array), + transform::Hash::Key.new('name', string), + transform::Hash::Key.new('repo_ref', string), + transform::Hash::Key.new('repo_uri', string) + ] + ), + transform::Hash::Symbolize.new, + transform::Exception.new(Anima::Error, Project.public_method(:new)) + ] + ) + ) + ] + ) + ) - ALL = LOADER.call(YAML.load_file(ROOT.join('spec', 'integrations.yml'))) + ALL = loader.apply(path).lmap(&:compact_message).from_right end Project::ALL.each do |project| diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 83cb9b49..6d60eff9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,9 @@ -require 'yaml' -require 'pathname' -require 'unparser' require 'anima' -require 'morpher' require 'devtools/spec_helper' +require 'mutant' +require 'pathname' +require 'unparser' +require 'yaml' require 'parser/current' diff --git a/unparser.gemspec b/unparser.gemspec index bd9a2010..98da260c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,7 +26,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('anima', '~> 0.3.1') gem.add_development_dependency('devtools', '~> 0.1.23') - gem.add_development_dependency('morpher', '~> 0.2.6') gem.add_development_dependency('mutant', '~> 0.9.5') gem.add_development_dependency('mutant-rspec', '~> 0.9.5') end From 09a3b9c7c3cebce9b88c5b220f7fcdd5d727e3a0 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Aug 2020 21:34:22 +0000 Subject: [PATCH 056/256] Add Unparser::{Color,Diff} --- .github/workflows/ci.yml | 2 +- lib/unparser.rb | 8 +- lib/unparser/cli.rb | 4 - lib/unparser/cli/differ.rb | 152 ------------------------- lib/unparser/cli/source.rb | 4 +- lib/unparser/{cli => }/color.rb | 10 -- lib/unparser/diff.rb | 115 +++++++++++++++++++ spec/unit/unparser/color_spec.rb | 40 +++++++ spec/unit/unparser/diff_spec.rb | 189 +++++++++++++++++++++++++++++++ 9 files changed, 353 insertions(+), 171 deletions(-) delete mode 100644 lib/unparser/cli/differ.rb rename lib/unparser/{cli => }/color.rb (84%) create mode 100644 lib/unparser/diff.rb create mode 100644 spec/unit/unparser/color_spec.rb create mode 100644 spec/unit/unparser/diff_spec.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8d95f843..e4e0e948 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - run: | gem install bundler bundle install - - run: bundle exec mutant --since HEAD~1 --zombie -- 'Mutant*' + - run: bundle exec mutant --since HEAD~1 --zombie --ignore-subject 'Unparser::CLI*' -- 'Unparser*' ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/lib/unparser.rb b/lib/unparser.rb index 18fd26a9..2f936aa2 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -1,10 +1,12 @@ # frozen_string_literal: true -require 'set' require 'abstract_type' -require 'procto' require 'concord' +require 'diff/lcs' +require 'diff/lcs/hunk' require 'parser/current' +require 'procto' +require 'set' # Library namespace module Unparser @@ -100,6 +102,8 @@ def self.buffer(source) require 'unparser/dsl' require 'unparser/ast' require 'unparser/ast/local_variable_scope' +require 'unparser/diff' +require 'unparser/color' require 'unparser/emitter' require 'unparser/emitter/literal' require 'unparser/emitter/literal/primitive' diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index a5b1c155..8f8e71b7 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -2,12 +2,8 @@ require 'unparser' require 'optparse' -require 'diff/lcs' -require 'diff/lcs/hunk' require 'unparser/cli/source' -require 'unparser/cli/differ' -require 'unparser/cli/color' module Unparser # Unparser CLI implementation diff --git a/lib/unparser/cli/differ.rb b/lib/unparser/cli/differ.rb deleted file mode 100644 index cf6beb46..00000000 --- a/lib/unparser/cli/differ.rb +++ /dev/null @@ -1,152 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class CLI - # Class to create diffs from source code - class Differ - include Adamantium::Flat, Concord.new(:old, :new), Procto.call(:colorized_diff) - - CONTEXT_LINES = 5 - - # Return new object - # - # @param [String] old - # @param [String] new - # - # @return [Differ] - # - # @api private - # - def self.build(old, new) - new(lines(old), lines(new)) - end - - # Return colorized diff line - # - # @param [String] line - # - # @return [String] - # - # @api private - # - def self.colorize_line(line) - case line[0] - when '+' - Color::GREEN - when '-' - Color::RED - else - Color::NONE - end.format(line) - end - - # Break up source into lines - # - # @param [String] source - # - # @return [Array] - # - # @api private - # - def self.lines(source) - source.lines.map(&:chomp) - end - private_class_method :lines - - # Return hunks - # - # @return [Array] - # - # @api private - # - def hunks - file_length_difference = new.length - old.length - diffs.map do |piece| - hunk = Diff::LCS::Hunk.new(old, new, piece, CONTEXT_LINES, file_length_difference) - file_length_difference = hunk.file_length_difference - hunk - end - end - - # Return collapsed hunks - # - # @return [Enumerable] - # - # @api private - # - def collapsed_hunks - hunks.each_with_object([]) do |hunk, output| - last = output.last - - if last && hunk.merge(last) - output.pop - end - - output << hunk - end - end - - # Return source diff - # - # @return [String] - # if there is a diff - # - # @return [nil] - # otherwise - # - # @api private - # - def diff - output = +'' - - collapsed_hunks.each do |hunk| - output << hunk.diff(:unified) << "\n" - end - - output - end - memoize :diff - - # Return colorized source diff - # - # @return [String] - # if there is a diff - # - # @return [nil] - # otherwise - # - # @api private - # - def colorized_diff - diff.lines.map do |line| - self.class.colorize_line(line) - end.join - end - memoize :colorized_diff - - private - - # Return diffs - # - # @return [Array] - # - # @api private - # - def diffs - Diff::LCS.diff(old, new) - end - memoize :diffs - - # Return max length - # - # @return [Fixnum] - # - # @api private - # - def max_length - [old, new].map(&:length).max - end - - end # CLI - end # Differ -end # Unparser diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb index 7195960d..e0060948 100644 --- a/lib/unparser/cli/source.rb +++ b/lib/unparser/cli/source.rb @@ -165,10 +165,10 @@ def report_with_ast_diff # @api private # def ast_diff - Differ.call( + Diff.new( original_ast.inspect.lines.map(&:chomp), generated_ast.inspect.lines.map(&:chomp) - ) + ).colorized_diff end # Return generated AST diff --git a/lib/unparser/cli/color.rb b/lib/unparser/color.rb similarity index 84% rename from lib/unparser/cli/color.rb rename to lib/unparser/color.rb index 9a1ca63e..483d1501 100644 --- a/lib/unparser/cli/color.rb +++ b/lib/unparser/color.rb @@ -10,9 +10,6 @@ class Color # @param [String] text # # @return [String] - # - # @api private - # def format(text) "\e[#{code}m#{text}\e[0m" end @@ -25,9 +22,6 @@ def format(text) # # @return [String] # the argument string - # - # @api private - # def format(text) text end @@ -37,16 +31,12 @@ def format(text) # Initialize null color # # @return [undefined] - # - # @api private - # def initialize; end end.new RED = Color.new(31) GREEN = Color.new(32) - BLUE = Color.new(34) end # Color end # Unparser diff --git a/lib/unparser/diff.rb b/lib/unparser/diff.rb new file mode 100644 index 00000000..2ef2d6c0 --- /dev/null +++ b/lib/unparser/diff.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +module Unparser + # Class to create diffs from source code + class Diff + include Adamantium::Flat, Concord.new(:old, :new) + + ADDITION = '+' + DELETION = '-' + NEWLINE = "\n" + + # Unified source diff between old and new + # + # @return [String] + # if there is exactly one diff + # + # @return [nil] + # otherwise + def diff + return if diffs.empty? + + minimized_hunk.diff(:unified) + NEWLINE + end + memoize :diff + + # Colorized unified source diff between old and new + # + # @return [String] + # if there is a diff + # + # @return [nil] + # otherwise + def colorized_diff + return unless diff + + diff.lines.map(&self.class.method(:colorize_line)).join + end + memoize :colorized_diff + + # Build new object from source strings + # + # @param [String] old + # @param [String] new + # + # @return [Diff] + def self.build(old, new) + new(lines(old), lines(new)) + end + + # Break up source into lines + # + # @param [String] source + # + # @return [Array] + def self.lines(source) + source.lines.map(&:chomp) + end + private_class_method :lines + + private + + # Diffs between old and new + # + # @return [Array] + def diffs + ::Diff::LCS.diff(old, new) + end + + # Raw diff-lcs hunks + # + # @return [Array] + def hunks + diffs.map do |diff| + ::Diff::LCS::Hunk.new(old.map(&:dup), new, diff, max_length, 0) + end + end + + # Minimized hunk + # + # @return Diff::LCS::Hunk + def minimized_hunk + head, *tail = hunks + + tail.reduce(head) do |left, right| + right.merge(left) + right + end + end + + # Max length of source line in new and old + # + # @return [Integer] + def max_length + [old, new].map(&:length).max + end + + # Colorized a unified diff line + # + # @param [String] line + # + # @return [String] + def self.colorize_line(line) + case line[0] + when ADDITION + Color::GREEN + when DELETION + Color::RED + else + Color::NONE + end.format(line) + end + private_class_method :colorize_line + + end # Diff +end # Unparser diff --git a/spec/unit/unparser/color_spec.rb b/spec/unit/unparser/color_spec.rb new file mode 100644 index 00000000..348384bc --- /dev/null +++ b/spec/unit/unparser/color_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe Unparser::Color do + shared_examples 'actual color' do |code| + describe '#format' do + + it 'returns formatted string' do + expect(apply).to eql("\e[#{code}mexample-string\e[0m") + end + end + end + + describe '#format' do + let(:input) { 'example-string' } + + def apply + object.format(input) + end + + context 'RED' do + let(:object) { described_class::RED } + + include_examples 'actual color', 31 + end + + context 'GREEN' do + let(:object) { described_class::GREEN } + + include_examples 'actual color', 32 + end + + context 'NONE' do + let(:object) { described_class::NONE } + + it 'returns original input' do + expect(apply).to be(input) + end + end + end +end diff --git a/spec/unit/unparser/diff_spec.rb b/spec/unit/unparser/diff_spec.rb new file mode 100644 index 00000000..15657871 --- /dev/null +++ b/spec/unit/unparser/diff_spec.rb @@ -0,0 +1,189 @@ +# frozen_string_literal: true + +RSpec.describe Unparser::Diff do + let(:object) { described_class } + + describe '.build' do + + subject { object.build(old_string, new_string) } + + let(:old_string) { "foo\nbar" } + let(:new_string) { "bar\nbaz" } + + it { should eql(described_class.new(%w[foo bar], %w[bar baz])) } + + end + + describe '#colorized_diff' do + let(:object) { described_class.new(old, new) } + + subject { object.colorized_diff } + + context 'when there is a diff at begin of hunk' do + let(:old) { %w[foo bar] } + let(:new) { %w[baz bar] } + + let(:expectation) do + [ + "@@ -1,3 +1,3 @@\n", + Mutant::Color::RED.format("-foo\n"), + Mutant::Color::GREEN.format("+baz\n"), + " bar\n" + ].join + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when there is no diff' do + let(:old) { '' } + let(:new) { '' } + + it { should be(nil) } + + it_should_behave_like 'an idempotent method' + end + end + + describe '#diff' do + let(:object) { described_class.new(old, new) } + + subject { object.diff } + + context 'when there is a diff at begin and end' do + let(:old) { %w[foo bar foo] } + let(:new) { %w[baz bar baz] } + + let(:expectation) do + <<~STR + @@ -1,4 +1,4 @@ + -foo + +baz + bar + -foo + +baz + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when there is a diff at begin of hunk' do + let(:old) { %w[foo bar] } + let(:new) { %w[baz bar] } + + let(:expectation) do + <<~STR + @@ -1,3 +1,3 @@ + -foo + +baz + bar + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when there is a diff NOT at begin of hunk' do + let(:old) { %w[foo bar] } + let(:new) { %w[foo baz bar] } + + let(:expectation) do + <<~STR + @@ -1,3 +1,4 @@ + foo + +baz + bar + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when the diff has a long context at begin' do + let(:old) { %w[foo bar baz boz a b c] } + let(:new) { %w[foo bar baz boz a b c other] } + + let(:expectation) do + <<~STR + @@ -1,8 +1,9 @@ + foo + bar + baz + boz + a + b + c + +other + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when the diff has a long context at end, deleting' do + let(:old) { %w[other foo bar baz boz a b c] } + let(:new) { %w[foo bar baz boz a b c] } + + let(:expectation) do + <<~STR + @@ -1,9 +1,8 @@ + -other + foo + bar + baz + boz + a + b + c + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when the diff has a long context at end, inserting' do + let(:old) { %w[foo bar baz boz a b c] } + let(:new) { %w[other foo bar baz boz a b c] } + + let(:expectation) do + <<~STR + @@ -1,8 +1,9 @@ + +other + foo + bar + baz + boz + a + b + c + STR + end + + it { should eql(expectation) } + + it_should_behave_like 'an idempotent method' + end + + context 'when there is no diff' do + let(:old) { '' } + let(:new) { '' } + + it { should be(nil) } + + it_should_behave_like 'an idempotent method' + end + end +end From 0f7c9a24f027dac8106aafde3466b1327e945686 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Aug 2020 01:32:57 +0000 Subject: [PATCH 057/256] Fix Gemfile.lock --- Gemfile.lock | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5a61d775..14fa12b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,15 +72,6 @@ GEM kwalify (0.7.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) - morpher (0.2.6) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.0) - ast (~> 2.2) - concord (~> 0.1.5) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.0) - procto (~> 0.0.2) mprelude (0.1.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) @@ -169,7 +160,6 @@ PLATFORMS DEPENDENCIES anima (~> 0.3.1) devtools (~> 0.1.23) - morpher (~> 0.2.6) mutant (~> 0.9.5) mutant-license! mutant-rspec (~> 0.9.5) From ce17e479ecc3cb2c2edf2e1091880418b6f4d9a7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Aug 2020 01:32:58 +0000 Subject: [PATCH 058/256] Add mprelude dependency --- Gemfile.lock | 1 + unparser.gemspec | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 14fa12b1..b44bd5d6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,6 +7,7 @@ PATH concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) + mprelude (~> 0.1.0) parser (>= 2.6.5) procto (~> 0.0.2) diff --git a/unparser.gemspec b/unparser.gemspec index 98da260c..36222dad 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -18,9 +18,10 @@ Gem::Specification.new do |gem| gem.add_dependency('abstract_type', '~> 0.0.7') gem.add_dependency('adamantium', '~> 0.2.0') - gem.add_dependency('equalizer', '~> 0.0.9') - gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('concord', '~> 0.1.5') + gem.add_dependency('diff-lcs', '~> 1.3') + gem.add_dependency('equalizer', '~> 0.0.9') + gem.add_dependency('mprelude', '~> 0.1.0') gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') From c324876b84a00d1fd6adf71ec3cda8f8eab1ba6b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Aug 2020 01:32:59 +0000 Subject: [PATCH 059/256] Add Unparser::Validation --- .github/workflows/ci.yml | 8 +- Gemfile.lock | 19 +- config/rubocop.yml | 3 + lib/unparser.rb | 16 +- lib/unparser/cli.rb | 106 +++++---- lib/unparser/cli/source.rb | 267 --------------------- lib/unparser/validation.rb | 149 ++++++++++++ spec/unit/unparser/validation_spec.rb | 327 ++++++++++++++++++++++++++ spec/unit/unparser_spec.rb | 60 ++++- unparser.gemspec | 4 +- 10 files changed, 636 insertions(+), 323 deletions(-) delete mode 100644 lib/unparser/cli/source.rb create mode 100644 lib/unparser/validation.rb create mode 100644 spec/unit/unparser/validation_spec.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4e0e948..6213c2ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,13 @@ jobs: - run: | gem install bundler bundle install - - run: bundle exec mutant --since HEAD~1 --zombie --ignore-subject 'Unparser::CLI*' -- 'Unparser*' + - run: | + bundle exec mutant \ + --since HEAD~1 \ + --zombie \ + --ignore-subject 'Unparser::CLI*' \ + --ignore-subject 'Unparser::Validation.from_string' \ + -- 'Unparser*' ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/Gemfile.lock b/Gemfile.lock index b44bd5d6..bf3f8e57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ PATH unparser (0.4.7) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) + anima (~> 0.3.1) concord (~> 0.1.5) diff-lcs (~> 1.3) equalizer (~> 0.0.9) @@ -23,7 +24,7 @@ GEM abstract_type (~> 0.0.7) adamantium (~> 0.2) equalizer (~> 0.0.11) - ast (2.4.0) + ast (2.4.1) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -80,27 +81,28 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.5) + mutant (0.9.8) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) ast (~> 2.2) concord (~> 0.1.5) - diff-lcs (~> 1.3) + diff-lcs (= 1.3) equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) mprelude (~> 0.1.0) - parser (~> 2.7.0.2) + parser (~> 2.7.1) procto (~> 0.0.2) unparser (~> 0.4.6) + variable (~> 0.0.1) mutant-license (0.1.0) mutant-rspec (0.9.5) mutant (~> 0.9.5) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.1) - parser (2.7.0.2) - ast (~> 2.4.0) + parser (2.7.1.4) + ast (~> 2.4.1) path_expander (1.1.0) procto (0.0.3) psych (3.1.0) @@ -146,6 +148,8 @@ GEM simplecov-html (0.10.2) thread_safe (0.3.6) unicode-display_width (1.6.1) + variable (0.0.1) + equalizer (~> 0.0.11) virtus (1.0.5) axiom-types (~> 0.1) coercible (~> 1.0) @@ -159,9 +163,8 @@ PLATFORMS ruby DEPENDENCIES - anima (~> 0.3.1) devtools (~> 0.1.23) - mutant (~> 0.9.5) + mutant (~> 0.9.8) mutant-license! mutant-rspec (~> 0.9.5) unparser! diff --git a/config/rubocop.yml b/config/rubocop.yml index 2a41104c..052e6fc4 100644 --- a/config/rubocop.yml +++ b/config/rubocop.yml @@ -123,3 +123,6 @@ Layout/HashAlignment: Naming/RescuedExceptionsVariableName: Enabled: false + +Layout/MultilineMethodCallIndentation: + Enabled: false diff --git a/lib/unparser.rb b/lib/unparser.rb index 2f936aa2..f82c4a01 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true require 'abstract_type' +require 'anima' require 'concord' require 'diff/lcs' require 'diff/lcs/hunk' +require 'mprelude' require 'parser/current' require 'procto' require 'set' @@ -42,11 +44,22 @@ def self.unparse(node, comment_array = []) # # @param [String] source # - # @return [Parser::AST::Node] + # @return [Parser::AST::Node, nil] def self.parse(source) parser.parse(buffer(source)) end + # Parse string into either syntax error or AST + # + # @param [String] source + # + # @return [MPrelude::Either] + def self.parse_either(source) + MPrelude::Either.wrap_error(Parser::SyntaxError) do + parser.parse(buffer(source)) + end + end + # Parse string into AST, with comments # # @param [String] source @@ -156,5 +169,6 @@ def self.buffer(source) require 'unparser/emitter/ensure' require 'unparser/emitter/index' require 'unparser/emitter/lambda' +require 'unparser/validation' # make it easy for zombie require 'unparser/finalize' diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 8f8e71b7..a611a19a 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -3,8 +3,6 @@ require 'unparser' require 'optparse' -require 'unparser/cli/source' - module Unparser # Unparser CLI implementation # @@ -15,6 +13,36 @@ class CLI EXIT_SUCCESS = 0 EXIT_FAILURE = 1 + class Target + include AbstractType + + # Path target + class Path < self + include Concord.new(:path) + + # Validation for this target + # + # @return [Validation] + def validation + Validation.from_path(path) + end + end + + # String target + class String + include Concord.new(:string) + + # Validation for this target + # + # @return [Validation] + def validation + Validation.from_string(string) + end + end # String + end # Target + + private_constant(*constants(false)) + # Run CLI # # @param [Array] arguments @@ -38,8 +66,8 @@ def self.run(*arguments) # # ignore :reek:TooManyStatements def initialize(arguments) - @sources = [] - @ignore = Set.new + @ignore = Set.new + @targets = [] @success = true @fail_fast = false @@ -50,7 +78,7 @@ def initialize(arguments) end opts.parse!(arguments).each do |name| - @sources.concat(sources(name)) + @targets.concat(targets(name)) end end @@ -67,16 +95,16 @@ def add_options(builder) builder.banner = 'usage: unparse [options] FILE [FILE]' builder.separator('') builder.on('-e', '--evaluate SOURCE') do |source| - @sources << Source::String.new(source) + @targets << Target::String.new(source) end - builder.on('--start-with FILE') do |file| - @start_with = sources(file).first + builder.on('--start-with FILE') do |path| + @start_with = targets(path).first end builder.on('-v', '--verbose') do @verbose = true end builder.on('--ignore FILE') do |file| - @ignore.merge(sources(file)) + @ignore.merge(targets(file)) end builder.on('--fail-fast') do @fail_fast = true @@ -90,10 +118,8 @@ def add_options(builder) # @api private # def exit_status - effective_sources.each do |source| - next if @ignore.include?(source) - - process_source(source) + effective_targets.each do |target| + process_target(target) break if @fail_fast && !@success end @@ -102,66 +128,64 @@ def exit_status private - # Process source + # Process target # - # @param [CLI::Source] source + # @param [Target] target # # @return [undefined] # # @api private # - def process_source(source) - if source.success? - puts source.report if @verbose - puts "Success: #{source.identification}" + def process_target(target) + validation = target.validation + if validation.success? + puts validation.report if @verbose + puts "Success: #{validation.identification}" else - puts source.report - puts "Error: #{source.identification}" + puts validation.report + puts "Error: #{validation.identification}" @success = false end end - # Return effective sources + # Return effective targets # - # @return [Enumerable] + # @return [Enumerable] # # @api private # - def effective_sources + def effective_targets if @start_with reject = true - @sources.reject do |source| - if reject && source.eql?(@start_with) + @targets.reject do |targets| + if reject && targets.eql?(@start_with) reject = false end reject end else - @sources - end + @targets + end.reject(&@ignore.method(:include?)) end - # Return sources for file name + # Return targets for file name # # @param [String] file_name # - # @return [Enumerable] + # @return [Enumerable] # # @api private # # ignore :reek:UtilityFunction - def sources(file_name) - files = - if File.directory?(file_name) - Dir.glob(File.join(file_name, '**/*.rb')).sort - elsif File.file?(file_name) - [file_name] - else - Dir.glob(file_name).sort - end - - files.map(&Source::File.method(:new)) + def targets(file_name) + if File.directory?(file_name) + Dir.glob(File.join(file_name, '**/*.rb')).sort + elsif File.file?(file_name) + [file_name] + else + Dir.glob(file_name).sort + end.map { |file| Target::Path.new(Pathname.new(file)) } end end # CLI diff --git a/lib/unparser/cli/source.rb b/lib/unparser/cli/source.rb deleted file mode 100644 index e0060948..00000000 --- a/lib/unparser/cli/source.rb +++ /dev/null @@ -1,267 +0,0 @@ -# frozen_string_literal: true - -module Unparser - class CLI - # Source representation for CLI sources - # - # ignore :reek:TooManyMethods - class Source - include AbstractType, Adamantium::Flat, NodeHelpers - - # Source state generated after first unparse - class Generated - include Concord::Public.new(:source, :ast, :error) - - # Test if source was generated successfully - # - # @return [Boolean] - # - # @api private - # - def success? - !error - end - - # Build generated source - # - # @param [Parser::AST::Node] ast - # - # @api private - # - def self.build(ast) - source = Unparser.unparse(ast) - new(source, ast, nil) - rescue StandardError => exception - new(nil, ast, exception) - end - end - - # Test if source could be unparsed successfully - # - # @return [Boolean] - # - # @api private - # - def success? - generated.success? && original_ast && generated_ast && original_ast.eql?(generated_ast) - end - - # Return error report - # - # @return [String] - # - # @api private - # - def report - if original_ast && generated_ast - report_with_ast_diff - elsif !original_ast - report_original - elsif !generated.success? - report_unparser - elsif !generated_ast - report_generated - else - raise - end - end - memoize :report - - private - - # Return generated source - # - # @return [String] - # - # @api private - # - def generated - Source::Generated.build(original_ast) - end - memoize :generated - - # Return stripped source - # - # @param [String] source - # - # @return [String] - # - # @api private - # - # ignore :reek:UtilityFunction - def strip(source) - source = source.rstrip - indent = source.scan(/^\s*/).first - source.gsub(/^#{indent}/, '') - end - - # Return error report for parsing original - # - # @return [String] - # - # @api private - # - def report_original - strip(<<-MESSAGE) - Parsing of original source failed: - #{original_source} - MESSAGE - end - - # Report unparser bug - # - # @return [String] - # - # @api private - # - def report_unparser - message = ['Unparsing parsed AST failed'] - error = generated.error - message << error - error.backtrace.take(20).each(&message.method(:<<)) - message << 'Original-AST:' - message << original_ast.inspect - message.join("\n") - end - - # Return error report for parsing generated - # - # @return [String] - # - # @api private - # - def report_generated - strip(<<-MESSAGE) - Parsing of generated source failed: - Original-source: - #{original_source} - Original-AST: - #{original_ast.inspect} - Source: - #{generated.source} - MESSAGE - end - - # Return error report with AST difference - # - # @return [String] - # - # @api private - # - def report_with_ast_diff - strip(<<-MESSAGE) - #{ast_diff} - Original-Source:\n#{original_source} - Original-AST:\n#{original_ast.inspect} - Generated-Source:\n#{generated.source} - Generated-AST:\n#{generated_ast.inspect} - MESSAGE - end - - # Return ast diff - # - # @return [String] - # - # @api private - # - def ast_diff - Diff.new( - original_ast.inspect.lines.map(&:chomp), - generated_ast.inspect.lines.map(&:chomp) - ).colorized_diff - end - - # Return generated AST - # - # @return [Parser::AST::Node] - # if parser was sucessful for generated ast - # - # @return [nil] - # otherwise - # - # @api private - # - def generated_ast - generated.success? && Preprocessor.run(Unparser.parse(generated.source)) - rescue Parser::SyntaxError - nil - end - memoize :generated_ast - - # Return original AST - # - # @return [Parser::AST::Node] - # - # @api private - # - def original_ast - Preprocessor.run(Unparser.parse(original_source)) - rescue Parser::SyntaxError - nil - end - memoize :original_ast - - # CLI source from string - class String < self - include Concord.new(:original_source) - - # Return identification - # - # @return [String] - # - # @api private - # - def identification - '(string)' - end - - end # String - - # CLI source from file - class File < self - include Concord.new(:file_name) - - # Return identification - # - # @return [String] - # - # @api private - # - def identification - "(#{file_name})" - end - - private - - # Return original source - # - # @return [String] - # - # @api private - # - def original_source - ::File.read(file_name) - end - memoize :original_source - - end # File - - # Source passed in as node - class Node < self - include Concord.new(:original_ast) - - # Return original source - # - # @return [String] - # - # @api private - # - def original_source - Unparser.unparse(original_ast) - end - memoize :original_source - end # Node - - end # Source - end # CLI -end # Unparser diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb new file mode 100644 index 00000000..c0315624 --- /dev/null +++ b/lib/unparser/validation.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +module Unparser + # Validation of unparser results + # + # ignore :reek:TooManyMethods + class Validation + include Adamantium::Flat, Anima.new( + :generated_node, + :generated_source, + :identification, + :original_node, + :original_source + ) + + # Test if source could be unparsed successfully + # + # @return [Boolean] + # + # @api private + # + def success? + [ + original_source, + original_node, + generated_source, + generated_node + ].all?(&:right?) && generated_node.from_right.eql?(original_node.from_right) + end + + # Return error report + # + # @return [String] + # + # @api private + # + def report + message = [identification] + + message.concat(make_report('Original-Source', :original_source)) + message.concat(make_report('Generated-Source', :generated_source)) + message.concat(make_report('Original-Node', :original_node)) + message.concat(make_report('Generated-Node', :generated_node)) + message.concat(node_diff_report) + + message.join("\n") + end + memoize :report + + # Create validator from string + # + # @param [String] original_source + # + # @return [Validator] + def self.from_string(original_source) + original_node = Unparser + .parse_either(original_source) + .fmap(&Preprocessor.method(:run)) + + generated_source = original_node + .lmap(&method(:const_unit)) + .bind(&method(:unparse_either)) + + generated_node = generated_source + .lmap(&method(:const_unit)) + .bind(&Unparser.method(:parse_either)) + .fmap(&Preprocessor.method(:run)) + + new( + identification: '(string)', + original_source: MPrelude::Either::Right.new(original_source), + original_node: original_node, + generated_source: generated_source, + generated_node: generated_node + ) + end + + # Create validator from file + # + # @param [Pathname] path + # + # @return [Validator] + def self.from_path(path) + from_string(path.read).with(identification: path.to_s) + end + + private + + # Create a labeled report from + # + # @param [String] label + # @param [Symbol] attribute_name + # + # @return [Array] + def make_report(label, attribute_name) + ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] })) + end + + # Report optional exception + # + # @param [Exception, nil] exception + # + # @return [Array] + def report_exception(exception) + if exception + [exception.inspect].concat(exception.backtrace.take(20)) + else + ['undefined'] + end + end + + # Report the node diff + # + # @return [Array] + def node_diff_report + diff = nil + + original_node.fmap do |original| + generated_node.fmap do |generated| + diff = Diff.new( + original.to_s.lines.map(&:chomp), + generated.to_s.lines.map(&:chomp) + ).colorized_diff + end + end + + diff ? ['Node-Diff:', diff] : [] + end + + # Create unit represented as nil + # + # @param [Object] _value + # + # @return [nil] + def self.const_unit(_value); end + private_class_method :const_unit + + # Unparse capturing errors + # + # @param [Parser::AST::Node] node + # + # @return [Either] + def self.unparse_either(node) + MPrelude::Either + .wrap_error(RuntimeError) { Unparser.unparse(node) } + end + private_class_method :unparse_either + end # Validation +end # Unparser diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb new file mode 100644 index 00000000..b0e639c1 --- /dev/null +++ b/spec/unit/unparser/validation_spec.rb @@ -0,0 +1,327 @@ +require 'spec_helper' + +describe Unparser::Validation do + let(:object) do + described_class.new( + identification: identification, + generated_node: generated_node, + generated_source: generated_source, + original_node: original_node, + original_source: original_source + ) + end + + def right(value) + MPrelude::Either::Right.new(value) + end + + def left(value) + MPrelude::Either::Left.new(value) + end + + let(:generated_node) { right(s(:send, s(:int, 1), :foo)) } + let(:generated_source) { right('1.foo') } + let(:identification) { 'example-identification' } + let(:original_node) { right(s(:send, s(:int, 1), :foo)) } + let(:original_source) { right('1.foo') } + + let(:exception) do + left( + instance_double( + RuntimeError, + message: 'foo', + backtrace: Array.new(21, &'line-%02d'.method(:%)) + ) + ) + end + + let(:exception_report) do + <<~'REPORT'.strip + # + line-00 + line-01 + line-02 + line-03 + line-04 + line-05 + line-06 + line-07 + line-08 + line-09 + line-10 + line-11 + line-12 + line-13 + line-14 + line-15 + line-16 + line-17 + line-18 + line-19 + REPORT + end + + def report + object.report + end + + shared_examples 'not successful' do + it 'is not successful' do + expect(object.success?).to be(false) + end + end + + context 'on success' do + it 'is successful' do + expect(object.success?).to be(true) + end + + it 'returns expected report' do + expect(report).to eql(<<~'REPORT'.strip) + example-identification + Original-Source: + 1.foo + Generated-Source: + 1.foo + Original-Node: + (send + (int 1) :foo) + Generated-Node: + (send + (int 1) :foo) + REPORT + end + end + + context 'on failing to generate original source with exception' do + let(:original_source) { exception } + + include_examples 'not successful' + + it 'returns expected report' do + expect(report).to eql(<<~REPORT.strip) + example-identification + Original-Source: + #{exception_report} + Generated-Source: + 1.foo + Original-Node: + (send + (int 1) :foo) + Generated-Node: + (send + (int 1) :foo) + REPORT + end + end + + context 'on failing to parse generated source due precondition error' do + let(:generated_node) { left(nil) } + + include_examples 'not successful' + + it 'returns expected report' do + expect(report).to eql(<<~REPORT.strip) + example-identification + Original-Source: + 1.foo + Generated-Source: + 1.foo + Original-Node: + (send + (int 1) :foo) + Generated-Node: + undefined + REPORT + end + end + + context 'on failing to parse original source' do + let(:original_node) { exception } + + include_examples 'not successful' + + it 'returns expected report' do + expect(report).to eql(<<~REPORT.strip) + example-identification + Original-Source: + 1.foo + Generated-Source: + 1.foo + Original-Node: + #{exception_report} + Generated-Node: + (send + (int 1) :foo) + REPORT + end + end + + context 'on failing to generate generated source' do + let(:generated_source) { exception } + + include_examples 'not successful' + + it 'returns expected report' do + expect(report).to eql(<<~REPORT.strip) + example-identification + Original-Source: + 1.foo + Generated-Source: + #{exception_report} + Original-Node: + (send + (int 1) :foo) + Generated-Node: + (send + (int 1) :foo) + REPORT + end + end + + context 'on failing to parse generated source' do + let(:generated_node) { exception } + + include_examples 'not successful' + + it 'returns expected report' do + expect(report).to eql(<<~REPORT.strip) + example-identification + Original-Source: + 1.foo + Generated-Source: + 1.foo + Original-Node: + (send + (int 1) :foo) + Generated-Node: + #{exception_report} + REPORT + end + end + + context 'on generating different node' do + let(:generated_node) { right(s(:send, s(:int, 1), :bar)) } + + include_examples 'not successful' + + it 'returns expected report' do + diff = [ + Unparser::Color::NONE.format(" (send\n"), + Unparser::Color::RED.format("- (int 1) :foo)\n"), + Unparser::Color::GREEN.format("+ (int 1) :bar)\n") + ] + + expect(report).to eql(<<~'REPORT' + diff.join) + example-identification + Original-Source: + 1.foo + Generated-Source: + 1.foo + Original-Node: + (send + (int 1) :foo) + Generated-Node: + (send + (int 1) :bar) + Node-Diff: + @@ -1,3 +1,3 @@ + REPORT + end + end + + describe '.from_path' do + def apply + described_class.from_path(path) + end + + let(:path) { instance_double(Pathname, read: source, to_s: '/some/file') } + let(:source) { 'true' } + + it 'returns expected validator' do + expect(apply).to eql( + described_class.new( + generated_node: right(s(:true)), + generated_source: right(source), + identification: '/some/file', + original_node: right(s(:true)), + original_source: right(source) + ) + ) + end + end + + describe '.from_string' do + def apply + described_class.from_string(source) + end + + let(:attributes) do + { + generated_node: right(s(:true)), + generated_source: right(source), + identification: '(string)', + original_node: right(s(:true)), + original_source: right(source) + } + end + + context 'on valid original source' do + let(:source) { 'true' } + + it 'returns expected validator' do + expect(apply).to eql(described_class.new(attributes)) + end + + context 'with unparsing error' do + let(:exception) { RuntimeError.new('example-error') } + + before do + allow(Unparser).to receive(:unparse).and_raise(exception) + end + + it 'returns expected validator' do + validator = apply + + expect(validator.generated_node).to eql(left(nil)) + expect(validator.generated_source.from_left.class).to be(RuntimeError) + expect(validator.original_source).to eql(right(source)) + expect(validator.original_node).to eql(right(s(:true))) + end + end + end + + # These are actually specifying the wrong behavior as the normalization is a conceptual mistake + # But for now we specify them to get this PR through. + # + # Removal in followup. + context 'on denormalized valid original source' do + let(:source) { '(true)' } + + it 'returns expected validator' do + expect(apply).to eql(described_class.new(attributes.merge(generated_source: right('true')))) + end + end + + context 'on very denormalized valid original source' do + let(:source) { '((true))' } + + it 'returns expected validator' do + expect(apply).to eql(described_class.new(attributes.merge(generated_source: right('true')))) + end + end + + context 'on invalid original source' do + let(:source) { '(' } + + it 'returns expected validator' do + validator = apply + + expect(validator.generated_node).to eql(left(nil)) + expect(validator.generated_source).to eql(left(nil)) + expect(validator.original_source).to eql(right(source)) + expect(validator.original_node.from_left.class).to be(Parser::SyntaxError) + end + end + end +end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index d92a1855..502d80f9 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -57,11 +57,65 @@ def apply describe '.parse' do def apply - described_class.parse('self[1]=2') + described_class.parse(source) end - it 'returns expected AST' do - expect(apply).to eql(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2))) + context 'on present source' do + let(:source) { 'self[1]=2' } + + it 'returns expected AST' do + expect(apply).to eql(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2))) + end + end + + context 'on empty source' do + let(:source) { '' } + + it 'returns ni' do + expect(apply).to be(nil) + end + end + + context 'on syntax error' do + let(:source) { '[' } + + it 'raises error' do + expect { apply }.to raise_error(Parser::SyntaxError) + end + end + end + + describe '.parse_either' do + def apply + described_class.parse_either(source) + end + + context 'on present source' do + let(:source) { 'self[1]=2' } + + it 'returns right value with expected AST' do + expect(apply).to eql(MPrelude::Either::Right.new(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2)))) + end + end + + context 'on empty source' do + let(:source) { '' } + + it 'returns right value with nil' do + expect(apply).to eql(MPrelude::Either::Right.new(nil)) + end + end + + context 'on syntax error' do + let(:source) { '[' } + + it 'returns left value with syntax error' do + result = apply + + # Syntax errors that compare nicely under #eql? are hard to construct + expect(result).to be_instance_of(MPrelude::Either::Left) + expect(result.from_left).to be_instance_of(Parser::SyntaxError) + end end end diff --git a/unparser.gemspec b/unparser.gemspec index 36222dad..468c14a2 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -18,6 +18,7 @@ Gem::Specification.new do |gem| gem.add_dependency('abstract_type', '~> 0.0.7') gem.add_dependency('adamantium', '~> 0.2.0') + gem.add_dependency('anima', '~> 0.3.1') gem.add_dependency('concord', '~> 0.1.5') gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('equalizer', '~> 0.0.9') @@ -25,8 +26,7 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('anima', '~> 0.3.1') gem.add_development_dependency('devtools', '~> 0.1.23') - gem.add_development_dependency('mutant', '~> 0.9.5') + gem.add_development_dependency('mutant', '~> 0.9.8') gem.add_development_dependency('mutant-rspec', '~> 0.9.5') end From f929cb1bb58a4a93db7d39688cc85360e606b4dd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 00:45:34 +0000 Subject: [PATCH 060/256] Add missing changelog entries --- Changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Changelog.md b/Changelog.md index eca2e644..4b01ad40 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# v0.4.8 unreleased + +* Significantly improve verifier (only useful for debugging) +* Add `Unparser::Color` module for colorized source diffs + # v0.4.7 2020-01-03 * Add support for endless ranges From 702a4e4dc2f96924ba186fd8bbd9b970920f6e69 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 01:15:54 +0000 Subject: [PATCH 061/256] Remove devtools dependency --- .github/workflows/ci.yml | 2 +- .rubocop.yml | 127 ++++++++++++++++++++++++++++++++++++-- Gemfile.lock | 104 +++++++------------------------ config/rubocop.yml | 128 --------------------------------------- spec/spec_helper.rb | 24 +++++++- unparser.gemspec | 9 ++- 6 files changed, 173 insertions(+), 221 deletions(-) delete mode 100644 config/rubocop.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6213c2ab..52f03c57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,4 +87,4 @@ jobs: with: ruby-version: ${{ matrix.ruby }} - run: bundle install - - run: bundle exec rake metrics:rubocop + - run: bundle exec rubocop diff --git a/.rubocop.yml b/.rubocop.yml index edae6504..1b402596 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,9 +1,126 @@ AllCops: Include: + - 'lib/unparser.rb' + - 'lib/unparser/**/*.rb' + - '**/*.rake' - 'Gemfile' + - 'Gemfile.triage' + +# Avoid parameter lists longer than five parameters. +ParameterLists: + Max: 3 + CountKeywordArgs: true + +MethodLength: + CountComments: false + Max: 17 + +AbcSize: + Max: 18 + +# Avoid more than `Max` levels of nesting. +BlockNesting: + Max: 3 + +# Align with the style guide. +CollectionMethods: + PreferredMethods: + collect: 'map' + inject: 'reduce' + find: 'detect' + find_all: 'select' + +# Limit line length +LineLength: + Max: 113 # TODO: lower to 79 once the rubocop branch in shared/Gemfile is removed + +ClassLength: + Max: 204 + +# Prefer modifiers and explicit if statements over returning early for small methods +GuardClause: + Enabled: false + +Metrics/BlockLength: Exclude: - - 'Gemfile.devtools' - - 'vendor/**/*' - - 'benchmarks/**/*' - - 'tmp/**/*' - TargetRubyVersion: 2.5 + # Ignore RSpec DSL + - spec/**/* + +# Flags freezes for singletons that could still be mutated like Regexps +RedundantFreeze: + Enabled: false + +# Allow Fixnum and Bignum. This Gem supports versions before 2.4 +UnifiedInteger: + Enabled: false + +# Disabled because of indenting with private keyword in class bodies. +IndentationWidth: + Enabled: false + +# I like raise more +SignalException: + Enabled: false + +# False positive in unparser source +OneLineConditional: + Enabled: false + +Documentation: + Enabled: false + +# Disable documentation checking until a class needs to be documented once +Documentation: + Enabled: false + +# Do not favor modifier if/unless usage when you have a single-line body +IfUnlessModifier: + Enabled: false + +# Allow case equality operator (in limited use within the specs) +CaseEquality: + Enabled: false + +# Constants do not always have to use SCREAMING_SNAKE_CASE +ConstantName: + Enabled: false + +# Not all trivial readers/writers can be defined with attr_* methods +TrivialAccessors: + Enabled: false + +# I like to have an empty line before closing the currently opened body +EmptyLinesAroundBlockBody: + Enabled: false + +EmptyLinesAroundClassBody: + Enabled: false + +EmptyLinesAroundModuleBody: + Enabled: false + +# I like my style more +AccessModifierIndentation: + Enabled: false + +Style/CommentedKeyword: + Enabled: false + +Style/MixinGrouping: + Enabled: false + +Lint/BooleanSymbol: + Enabled: false + +Style/AccessModifierDeclarations: + Enabled: false + +Layout/HashAlignment: + EnforcedColonStyle: table + EnforcedHashRocketStyle: table + +Naming/RescuedExceptionsVariableName: + Enabled: false + +Layout/MultilineMethodCallIndentation: + Enabled: false diff --git a/Gemfile.lock b/Gemfile.lock index bf3f8e57..6c44554d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,53 +25,13 @@ GEM adamantium (~> 0.2) equalizer (~> 0.0.11) ast (2.4.1) - axiom-types (0.1.1) - descendants_tracker (~> 0.0.4) - ice_nine (~> 0.11.0) - thread_safe (~> 0.3, >= 0.3.1) - codeclimate-engine-rb (0.4.1) - virtus (~> 1.0) - coercible (1.0.0) - descendants_tracker (~> 0.0.1) concord (0.1.5) adamantium (~> 0.2.0) equalizer (~> 0.0.9) - descendants_tracker (0.0.4) - thread_safe (~> 0.3, >= 0.3.1) - devtools (0.1.25) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.0) - concord (~> 0.1.5) - flay (~> 2.12.0) - flog (~> 4.6.2) - procto (~> 0.0.3) - rake (~> 12.3.0) - reek (~> 5.6.0) - rspec (~> 3.8.0) - rspec-core (~> 3.8.0) - rspec-its (~> 1.2.0) - rubocop (~> 0.79.0) - simplecov (~> 0.16.1) - yard (~> 0.9.16) - yardstick (~> 0.9.9) diff-lcs (1.3) - docile (1.3.2) equalizer (0.0.11) - erubis (2.7.0) - flay (2.12.1) - erubis (~> 2.7.0) - path_expander (~> 1.0) - ruby_parser (~> 3.0) - sexp_processor (~> 4.0) - flog (4.6.4) - path_expander (~> 1.0) - ruby_parser (~> 3.1, > 3.1.0) - sexp_processor (~> 4.8) ice_nine (0.11.2) jaro_winkler (1.5.4) - json (2.3.0) - kwalify (0.7.2) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) mprelude (0.1.0) @@ -81,7 +41,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.8) + mutant (0.9.9) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -97,39 +57,30 @@ GEM unparser (~> 0.4.6) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.9.5) - mutant (~> 0.9.5) + mutant-rspec (0.9.9) + mutant (~> 0.9.9) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.19.1) + parallel (1.19.2) parser (2.7.1.4) ast (~> 2.4.1) - path_expander (1.1.0) procto (0.0.3) - psych (3.1.0) rainbow (3.0.0) - rake (12.3.3) - reek (5.6.0) - codeclimate-engine-rb (~> 0.4.0) - kwalify (~> 0.7.0) - parser (>= 2.5.0.0, < 2.8, != 2.5.1.1) - psych (~> 3.1.0) - rainbow (>= 2.0, < 4.0) - rspec (3.8.0) - rspec-core (~> 3.8.0) - rspec-expectations (~> 3.8.0) - rspec-mocks (~> 3.8.0) - rspec-core (3.8.2) - rspec-support (~> 3.8.0) - rspec-expectations (3.8.6) + rspec (3.9.0) + rspec-core (~> 3.9.0) + rspec-expectations (~> 3.9.0) + rspec-mocks (~> 3.9.0) + rspec-core (3.9.2) + rspec-support (~> 3.9.3) + rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) + rspec-support (~> 3.9.0) rspec-its (1.2.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.8.2) + rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.8.0) - rspec-support (3.8.3) + rspec-support (~> 3.9.0) + rspec-support (3.9.3) rubocop (0.79.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) @@ -138,35 +89,22 @@ GEM ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) ruby-progressbar (1.10.1) - ruby_parser (3.14.1) - sexp_processor (~> 4.9) - sexp_processor (4.13.0) - simplecov (0.16.1) - docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) thread_safe (0.3.6) unicode-display_width (1.6.1) variable (0.0.1) equalizer (~> 0.0.11) - virtus (1.0.5) - axiom-types (~> 0.1) - coercible (~> 1.0) - descendants_tracker (~> 0.0, >= 0.0.3) - equalizer (~> 0.0, >= 0.0.9) - yard (0.9.24) - yardstick (0.9.9) - yard (~> 0.8, >= 0.8.7.2) PLATFORMS ruby DEPENDENCIES - devtools (~> 0.1.23) - mutant (~> 0.9.8) + mutant (~> 0.9.9) mutant-license! - mutant-rspec (~> 0.9.5) + mutant-rspec (~> 0.9.9) + rspec (~> 3.9) + rspec-core (~> 3.9) + rspec-its (~> 1.2.0) + rubocop (~> 0.79.0) unparser! BUNDLED WITH diff --git a/config/rubocop.yml b/config/rubocop.yml deleted file mode 100644 index 052e6fc4..00000000 --- a/config/rubocop.yml +++ /dev/null @@ -1,128 +0,0 @@ -inherit_from: ../.rubocop.yml - -AllCops: - Include: - - 'lib/unparser.rb' - - 'lib/unparser/**/*.rb' - - '**/*.rake' - - 'Gemfile' - - 'Gemfile.triage' - -# Avoid parameter lists longer than five parameters. -ParameterLists: - Max: 3 - CountKeywordArgs: true - -MethodLength: - CountComments: false - Max: 17 - -AbcSize: - Max: 18 - -# Avoid more than `Max` levels of nesting. -BlockNesting: - Max: 3 - -# Align with the style guide. -CollectionMethods: - PreferredMethods: - collect: 'map' - inject: 'reduce' - find: 'detect' - find_all: 'select' - -# Limit line length -LineLength: - Max: 113 # TODO: lower to 79 once the rubocop branch in shared/Gemfile is removed - -ClassLength: - Max: 204 - -# Prefer modifiers and explicit if statements over returning early for small methods -GuardClause: - Enabled: false - -Metrics/BlockLength: - Exclude: - # Ignore RSpec DSL - - spec/**/* - -# Flags freezes for singletons that could still be mutated like Regexps -RedundantFreeze: - Enabled: false - -# Allow Fixnum and Bignum. This Gem supports versions before 2.4 -UnifiedInteger: - Enabled: false - -# Disabled because of indenting with private keyword in class bodies. -IndentationWidth: - Enabled: false - -# I like raise more -SignalException: - Enabled: false - -# False positive in unparser source -OneLineConditional: - Enabled: false - -Documentation: - Enabled: false - -# Disable documentation checking until a class needs to be documented once -Documentation: - Enabled: false - -# Do not favor modifier if/unless usage when you have a single-line body -IfUnlessModifier: - Enabled: false - -# Allow case equality operator (in limited use within the specs) -CaseEquality: - Enabled: false - -# Constants do not always have to use SCREAMING_SNAKE_CASE -ConstantName: - Enabled: false - -# Not all trivial readers/writers can be defined with attr_* methods -TrivialAccessors: - Enabled: false - -# I like to have an empty line before closing the currently opened body -EmptyLinesAroundBlockBody: - Enabled: false - -EmptyLinesAroundClassBody: - Enabled: false - -EmptyLinesAroundModuleBody: - Enabled: false - -# I like my style more -AccessModifierIndentation: - Enabled: false - -Style/CommentedKeyword: - Enabled: false - -Style/MixinGrouping: - Enabled: false - -Lint/BooleanSymbol: - Enabled: false - -Style/AccessModifierDeclarations: - Enabled: false - -Layout/HashAlignment: - EnforcedColonStyle: table - EnforcedHashRocketStyle: table - -Naming/RescuedExceptionsVariableName: - Enabled: false - -Layout/MultilineMethodCallIndentation: - Enabled: false diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6d60eff9..3e90850e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,12 +1,34 @@ require 'anima' -require 'devtools/spec_helper' require 'mutant' require 'pathname' +require 'timeout' require 'unparser' require 'yaml' require 'parser/current' +RSpec.configuration.around(file_path: %r{spec/unit}) do |example| + Timeout.timeout(0.1, &example) +end + +RSpec.shared_examples_for 'a command method' do + it 'returns self' do + should equal(object) + end +end + +RSpec.shared_examples_for 'an idempotent method' do + it 'is idempotent' do + first = subject + fail 'RSpec not configured for threadsafety' unless RSpec.configuration.threadsafe? + mutex = __memoized.instance_variable_get(:@mutex) + memoized = __memoized.instance_variable_get(:@memoized) + + mutex.synchronize { memoized.delete(:subject) } + should equal(first) + end +end + module SpecHelper def s(type, *children) Parser::AST::Node.new(type, children) diff --git a/unparser.gemspec b/unparser.gemspec index 468c14a2..71ca52ed 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,7 +26,10 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('devtools', '~> 0.1.23') - gem.add_development_dependency('mutant', '~> 0.9.8') - gem.add_development_dependency('mutant-rspec', '~> 0.9.5') + gem.add_development_dependency('mutant', '~> 0.9.9') + gem.add_development_dependency('mutant-rspec', '~> 0.9.9') + gem.add_development_dependency('rspec', '~> 3.9') + gem.add_development_dependency('rspec-core', '~> 3.9') + gem.add_development_dependency('rspec-its', '~> 1.2.0') + gem.add_development_dependency('rubocop', '~> 0.79.0') end From 0bdc12bec0086046195dfc52968705f05b9bc217 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 01:37:57 +0000 Subject: [PATCH 062/256] Change to use Unparser::Color in specs --- spec/unit/unparser/diff_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/unparser/diff_spec.rb b/spec/unit/unparser/diff_spec.rb index 15657871..4c1142c4 100644 --- a/spec/unit/unparser/diff_spec.rb +++ b/spec/unit/unparser/diff_spec.rb @@ -26,8 +26,8 @@ let(:expectation) do [ "@@ -1,3 +1,3 @@\n", - Mutant::Color::RED.format("-foo\n"), - Mutant::Color::GREEN.format("+baz\n"), + Unparser::Color::RED.format("-foo\n"), + Unparser::Color::GREEN.format("+baz\n"), " bar\n" ].join end From 8a740036b269612f5a7ae720f6b300059bacbafe Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 01:39:51 +0000 Subject: [PATCH 063/256] Change to version to v0.4.8 --- Changelog.md | 2 +- unparser.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 716ac054..77f29354 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# v0.4.8 unreleased +# v0.4.8 2020-05-25 * Change to specific node type when unparser fails on an unknown node type: [#150](https://github.com/mbj/unparser/pull/150) * Significantly improve verifier (only useful for debugging) diff --git a/unparser.gemspec b/unparser.gemspec index 71ca52ed..4abf2140 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.7' + gem.version = '0.4.8' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From bb9d481ed94cd23d05c6bcbe34b46adea549b6f3 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 02:42:30 +0000 Subject: [PATCH 064/256] Fix Gemfile.lock --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6c44554d..6adcf79b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.4.7) + unparser (0.4.8) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From a4d5235fb0410e01fcbab7ac73b7e1bd3002e517 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 02:40:48 +0000 Subject: [PATCH 065/256] Change to use mutant.sh --- .github/workflows/ci.yml | 8 +------- mutant.sh | 8 ++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100755 mutant.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52f03c57..5724f11b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,13 +65,7 @@ jobs: - run: | gem install bundler bundle install - - run: | - bundle exec mutant \ - --since HEAD~1 \ - --zombie \ - --ignore-subject 'Unparser::CLI*' \ - --ignore-subject 'Unparser::Validation.from_string' \ - -- 'Unparser*' + - run: ./mutant.sh ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/mutant.sh b/mutant.sh new file mode 100755 index 00000000..6014efbf --- /dev/null +++ b/mutant.sh @@ -0,0 +1,8 @@ +#/usr/bin/bash -ex + +bundle exec mutant \ + --since HEAD~1 \ + --zombie \ + --ignore-subject 'Unparser::CLI*' \ + --ignore-subject 'Unparser::Validation.from_string' \ + -- 'Unparser*' From 8761111c93d8e32f8ee90918718da78186e3385e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 25 Aug 2020 02:47:14 +0000 Subject: [PATCH 066/256] Fix README markdown syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 26c8d709..58053e24 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ unparser ======== -[![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg) +![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg) [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). From 5e4b95644834fb20fd429efe32a535845e479c1c Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 10 Sep 2020 15:12:34 +0000 Subject: [PATCH 067/256] Fix 0.4.8 release date --- Changelog.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Changelog.md b/Changelog.md index 77f29354..ce5a0b6a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.4.9 2020-09-10 + +* Change packaging to avoid git in gemspec. + # v0.4.8 2020-05-25 * Change to specific node type when unparser fails on an unknown node type: [#150](https://github.com/mbj/unparser/pull/150) From 582e6af790e3a27da12f102787f22bf2c16c3d03 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 10 Sep 2020 15:12:35 +0000 Subject: [PATCH 068/256] Change gemspec to not rely on git --- unparser.gemspec | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/unparser.gemspec b/unparser.gemspec index 4abf2140..b7ee90c8 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.4.8' + gem.version = '0.4.9' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -10,8 +10,7 @@ Gem::Specification.new do |gem| gem.homepage = 'http://github.com/mbj/unparser' gem.license = 'MIT' - gem.files = `git ls-files`.split("\n") - gem.test_files = `git ls-files -- {spec,features}/*`.split("\n") + gem.files = Dir.glob('lib/**/*') gem.require_paths = %w[lib] gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] From f0dc82d3c506438f9f536532d63acb7619b344f3 Mon Sep 17 00:00:00 2001 From: Daniel Gollahon Date: Wed, 9 Sep 2020 00:44:16 -0600 Subject: [PATCH 069/256] Remove unused `let` definition --- spec/unit/unparser/comments/take_all_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/unit/unparser/comments/take_all_spec.rb b/spec/unit/unparser/comments/take_all_spec.rb index 594f6729..0f807f12 100644 --- a/spec/unit/unparser/comments/take_all_spec.rb +++ b/spec/unit/unparser/comments/take_all_spec.rb @@ -8,7 +8,6 @@ def hi # EOL 1 end # EOL 2 RUBY end - let(:ast) { ast_and_comments[0] } let(:comments) { ast_and_comments[1] } let(:object) { described_class.new(comments) } From c01dc3a44bd4880ff6b721d6ee889b87c18bc8dd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 6 Oct 2020 03:02:24 +0000 Subject: [PATCH 070/256] Upgrade dependencies --- .github/workflows/ci.yml | 4 +++- .rspec | 1 + Gemfile.lock | 24 ++++++++++++------------ spec/unit/unparser/diff_spec.rb | 24 +++++------------------- 4 files changed, 21 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5724f11b..8195b590 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,5 +80,7 @@ jobs: - uses: actions/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - run: bundle install + - run: | + gem install bundler + bundle install - run: bundle exec rubocop diff --git a/.rspec b/.rspec index 2ac79342..4df29cc9 100644 --- a/.rspec +++ b/.rspec @@ -2,3 +2,4 @@ --format progress --warnings --order random +--require spec_helper diff --git a/Gemfile.lock b/Gemfile.lock index 6adcf79b..962b228c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.4.8) + unparser (0.4.9) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -20,15 +20,15 @@ GEM adamantium (0.2.0) ice_nine (~> 0.11.0) memoizable (~> 0.4.0) - anima (0.3.1) + anima (0.3.2) abstract_type (~> 0.0.7) adamantium (~> 0.2) equalizer (~> 0.0.11) ast (2.4.1) - concord (0.1.5) + concord (0.1.6) adamantium (~> 0.2.0) equalizer (~> 0.0.9) - diff-lcs (1.3) + diff-lcs (1.4.4) equalizer (0.0.11) ice_nine (0.11.2) jaro_winkler (1.5.4) @@ -41,27 +41,27 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.9) + mutant (0.9.12) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) ast (~> 2.2) concord (~> 0.1.5) - diff-lcs (= 1.3) + diff-lcs (~> 1.3) equalizer (~> 0.0.9) ice_nine (~> 0.11.1) memoizable (~> 0.4.2) mprelude (~> 0.1.0) parser (~> 2.7.1) procto (~> 0.0.2) - unparser (~> 0.4.6) + unparser (~> 0.4.8) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.9.9) - mutant (~> 0.9.9) + mutant-rspec (0.9.10) + mutant (~> 0.9.10) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) - parser (2.7.1.4) + parser (2.7.1.5) ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) @@ -69,7 +69,7 @@ GEM rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) rspec-mocks (~> 3.9.0) - rspec-core (3.9.2) + rspec-core (3.9.3) rspec-support (~> 3.9.3) rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) @@ -108,4 +108,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 1.17.3 + 2.1.4 diff --git a/spec/unit/unparser/diff_spec.rb b/spec/unit/unparser/diff_spec.rb index 4c1142c4..83b0bc43 100644 --- a/spec/unit/unparser/diff_spec.rb +++ b/spec/unit/unparser/diff_spec.rb @@ -25,10 +25,9 @@ let(:expectation) do [ - "@@ -1,3 +1,3 @@\n", + "@@ -1 +1 @@\n", Unparser::Color::RED.format("-foo\n"), Unparser::Color::GREEN.format("+baz\n"), - " bar\n" ].join end @@ -78,10 +77,9 @@ let(:expectation) do <<~STR - @@ -1,3 +1,3 @@ + @@ -1 +1 @@ -foo +baz - bar STR end @@ -96,7 +94,7 @@ let(:expectation) do <<~STR - @@ -1,3 +1,4 @@ + @@ -1,2 +1,3 @@ foo +baz bar @@ -137,15 +135,9 @@ let(:expectation) do <<~STR - @@ -1,9 +1,8 @@ + @@ -1,2 +1 @@ -other foo - bar - baz - boz - a - b - c STR end @@ -160,15 +152,9 @@ let(:expectation) do <<~STR - @@ -1,8 +1,9 @@ + @@ -1 +1,2 @@ +other foo - bar - baz - boz - a - b - c STR end From 9cd31679444ab6109982b0a90b29ddd81077f404 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 6 Oct 2020 03:11:51 +0000 Subject: [PATCH 071/256] Fix rubocop shadowing --- .rubocop.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 1b402596..25e92294 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -66,9 +66,6 @@ SignalException: OneLineConditional: Enabled: false -Documentation: - Enabled: false - # Disable documentation checking until a class needs to be documented once Documentation: Enabled: false From 8d4496af7883411c82680702f7d9988a356598c8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 6 Oct 2020 03:22:07 +0000 Subject: [PATCH 072/256] Fix rubocop department name warnings --- .rubocop.yml | 56 +++++++++++++---------------------------- lib/unparser/emitter.rb | 10 ++++---- 2 files changed, 23 insertions(+), 43 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 25e92294..c75fe8d6 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,23 +7,23 @@ AllCops: - 'Gemfile.triage' # Avoid parameter lists longer than five parameters. -ParameterLists: +Metrics/ParameterLists: Max: 3 CountKeywordArgs: true -MethodLength: +Metrics/MethodLength: CountComments: false Max: 17 -AbcSize: +Metrics/AbcSize: Max: 18 # Avoid more than `Max` levels of nesting. -BlockNesting: +Metrics/BlockNesting: Max: 3 # Align with the style guide. -CollectionMethods: +Style/CollectionMethods: PreferredMethods: collect: 'map' inject: 'reduce' @@ -31,14 +31,14 @@ CollectionMethods: find_all: 'select' # Limit line length -LineLength: +Layout/LineLength: Max: 113 # TODO: lower to 79 once the rubocop branch in shared/Gemfile is removed -ClassLength: - Max: 204 +Metrics/ClassLength: + Max: 175 # Prefer modifiers and explicit if statements over returning early for small methods -GuardClause: +Style/GuardClause: Enabled: false Metrics/BlockLength: @@ -47,57 +47,37 @@ Metrics/BlockLength: - spec/**/* # Flags freezes for singletons that could still be mutated like Regexps -RedundantFreeze: +Style/RedundantFreeze: Enabled: false # Allow Fixnum and Bignum. This Gem supports versions before 2.4 -UnifiedInteger: +Lint/UnifiedInteger: Enabled: false # Disabled because of indenting with private keyword in class bodies. -IndentationWidth: - Enabled: false - -# I like raise more -SignalException: - Enabled: false - -# False positive in unparser source -OneLineConditional: +Layout/IndentationWidth: Enabled: false # Disable documentation checking until a class needs to be documented once -Documentation: +Style/Documentation: Enabled: false # Do not favor modifier if/unless usage when you have a single-line body -IfUnlessModifier: - Enabled: false - -# Allow case equality operator (in limited use within the specs) -CaseEquality: - Enabled: false - -# Constants do not always have to use SCREAMING_SNAKE_CASE -ConstantName: - Enabled: false - -# Not all trivial readers/writers can be defined with attr_* methods -TrivialAccessors: +Style/IfUnlessModifier: Enabled: false # I like to have an empty line before closing the currently opened body -EmptyLinesAroundBlockBody: +Layout/EmptyLinesAroundBlockBody: Enabled: false -EmptyLinesAroundClassBody: +Layout/EmptyLinesAroundClassBody: Enabled: false -EmptyLinesAroundModuleBody: +Layout/EmptyLinesAroundModuleBody: Enabled: false # I like my style more -AccessModifierIndentation: +Layout/AccessModifierIndentation: Enabled: false Style/CommentedKeyword: diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 473dbe12..7abc884c 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -14,7 +14,7 @@ class Emitter extend DSL # Registry for node emitters - REGISTRY = {} # rubocop:disable MutableConstant + REGISTRY = {} # rubocop:disable Style/MutableConstant NOINDENT = %i[rescue ensure].to_set.freeze @@ -437,7 +437,7 @@ def indented # # @api private # - # rubocop:disable MethodCallWithoutArgsParentheses + # rubocop:disable Style/MethodCallWithoutArgsParentheses def emit_body(body = body()) unless body buffer.indent @@ -447,7 +447,7 @@ def emit_body(body = body()) end visit_indented(body) end - # rubocop:enable MethodCallWithoutArgsParentheses + # rubocop:enable Style/MethodCallWithoutArgsParentheses # Visit indented node # @@ -487,11 +487,11 @@ def parent_type # # @api private # - # rubocop:disable MethodCallWithoutArgsParentheses + # rubocop:disable Style/MethodCallWithoutArgsParentheses def run(emitter, node = node()) emitter.new(node, self).write_to_buffer end - # rubocop:enable MethodCallWithoutArgsParentheses + # rubocop:enable Style/MethodCallWithoutArgsParentheses end # Emitter end # Unparser From 434474d70df6a88685c21fdd24b1dfa45eee7726 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 7 Oct 2020 15:46:46 +0000 Subject: [PATCH 073/256] Upgrade mutant --- Gemfile.lock | 12 ++++++------ unparser.gemspec | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 962b228c..1792ed63 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,7 +41,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.12) + mutant (0.9.13) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -57,11 +57,11 @@ GEM unparser (~> 0.4.8) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.9.10) - mutant (~> 0.9.10) + mutant-rspec (0.9.13) + mutant (~> 0.9.13) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) - parser (2.7.1.5) + parser (2.7.2.0) ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) @@ -98,9 +98,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.9.9) + mutant (~> 0.9.13) mutant-license! - mutant-rspec (~> 0.9.9) + mutant-rspec (~> 0.9.13) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.2.0) diff --git a/unparser.gemspec b/unparser.gemspec index b7ee90c8..d3efac4b 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -25,8 +25,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.9.9') - gem.add_development_dependency('mutant-rspec', '~> 0.9.9') + gem.add_development_dependency('mutant', '~> 0.9.13') + gem.add_development_dependency('mutant-rspec', '~> 0.9.13') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.2.0') From d85cf466eb810006b3b2ff09ba6c4a08d05e3b44 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 7 Oct 2020 15:47:48 +0000 Subject: [PATCH 074/256] Change mutant.sh to be usable for local dev --- .github/workflows/ci.yml | 2 +- mutant.sh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8195b590..ff03a029 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - run: | gem install bundler bundle install - - run: ./mutant.sh + - run: ./mutant.sh --since HEAD~1 -- 'Unparser*' ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/mutant.sh b/mutant.sh index 6014efbf..3dbd8e28 100755 --- a/mutant.sh +++ b/mutant.sh @@ -1,8 +1,7 @@ #/usr/bin/bash -ex bundle exec mutant \ - --since HEAD~1 \ --zombie \ --ignore-subject 'Unparser::CLI*' \ --ignore-subject 'Unparser::Validation.from_string' \ - -- 'Unparser*' + $* From 8202d3afad5d6dd7c0496da607e38819744a98f7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 7 Oct 2020 21:49:04 +0000 Subject: [PATCH 075/256] Upgrade gemfile --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1792ed63..4418bb40 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.4.9) + unparser (0.5.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From 2d8fcf132829e52208df92164fbed36a4e79541e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 8 Oct 2020 04:57:56 +0000 Subject: [PATCH 076/256] Change to only trailing newlines on certain root nodes --- lib/unparser/emitter/root.rb | 7 ++++++- lib/unparser/validation.rb | 4 ++-- mutant.sh | 1 + spec/unit/unparser/validation_spec.rb | 4 ++-- test/corpus/literal/empty.rb | 0 test/corpus/literal/literal.rb | 1 - 6 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 test/corpus/literal/empty.rb diff --git a/lib/unparser/emitter/root.rb b/lib/unparser/emitter/root.rb index bf62973a..e335f446 100644 --- a/lib/unparser/emitter/root.rb +++ b/lib/unparser/emitter/root.rb @@ -7,6 +7,10 @@ class Root < self include Concord::Public.new(:buffer, :node, :comments) include LocalVariableRoot + END_NL = %i[class sclass module begin].freeze + + private_constant(*constants(false)) + def dispatch if children.any? emit_body(node, indent: false) @@ -15,7 +19,8 @@ def dispatch end emit_eof_comments - nl + + nl if END_NL.include?(node.type) && !buffer.fresh_line? end end # Root end # Emitter diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index 36f9c4e9..7bc1dc97 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -138,8 +138,8 @@ def source_diff_report original_source.fmap do |original| generated_source.fmap do |generated| diff = Diff.new( - original.to_s.lines.map(&:chomp), - generated.to_s.lines.map(&:chomp) + original.split("\n", -1), + generated.split("\n", -1) ).colorized_diff end end diff --git a/mutant.sh b/mutant.sh index 36effea6..1422f114 100755 --- a/mutant.sh +++ b/mutant.sh @@ -22,6 +22,7 @@ bundle exec mutant \ --ignore-subject 'Unparser::Emitter::Root#local_variable_scope' \ --ignore-subject 'Unparser::Emitter::Send#writer' \ --ignore-subject 'Unparser::Validation.from_string' \ + --ignore-subject 'Unparser::Validation::Literal*' \ --ignore-subject 'Unparser::Writer.included' \ --ignore-subject 'Unparser::Writer::Binary#left_emitter' \ --ignore-subject 'Unparser::Writer::Binary#right_emitter' \ diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index e5e6f3bf..59d4c492 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -236,7 +236,7 @@ def apply end let(:path) { instance_double(Pathname, read: source, to_s: '/some/file') } - let(:source) { "true\n" } + let(:source) { 'true' } it 'returns expected validator' do expect(apply).to eql( @@ -267,7 +267,7 @@ def apply end context 'on valid original source' do - let(:source) { "true\n" } + let(:source) { 'true' } it 'returns expected validator' do expect(apply).to eql(described_class.new(attributes)) diff --git a/test/corpus/literal/empty.rb b/test/corpus/literal/empty.rb new file mode 100644 index 00000000..e69de29b diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 880c4c2f..f3ef96ac 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -89,4 +89,3 @@ x #{foo} HEREDOC - From 22dfdd6e303a72a257fc9282d0b8f9135574f6f8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 03:54:39 +0000 Subject: [PATCH 077/256] Change to initialize builder --- lib/unparser.rb | 2 ++ mutant.sh | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/unparser.rb b/lib/unparser.rb index 95d1552a..f5f9d18a 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -18,6 +18,8 @@ class Builder < Parser::Builders::Default modernize def initialize + super + self.emit_file_line_as_literals = false end end diff --git a/mutant.sh b/mutant.sh index 1422f114..217f80c2 100755 --- a/mutant.sh +++ b/mutant.sh @@ -2,6 +2,7 @@ bundle exec mutant \ --zombie \ + --ignore-subject 'Unparser::Builder#initialize' \ --ignore-subject 'Unparser::CLI*' \ --ignore-subject 'Unparser::Emitter#emit_comments' \ --ignore-subject 'Unparser::Emitter#emit_comments_before' \ From ed7a4885ded5b445086d9eff17bdd25c26b2c284 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 03:54:40 +0000 Subject: [PATCH 078/256] Change to aggregate boolean expression --- lib/unparser/emitter/args.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/unparser/emitter/args.rb b/lib/unparser/emitter/args.rb index 9a3cdd6f..5bc34d71 100644 --- a/lib/unparser/emitter/args.rb +++ b/lib/unparser/emitter/args.rb @@ -7,9 +7,7 @@ class Args < self def emit_block_arguments delimited(normal_arguments) - if normal_arguments.one? - write(',') if n_arg?(normal_arguments.first) - end + write(',') if normal_arguments.one? && n_arg?(normal_arguments.first) emit_shadowargs end From 207ea3abacfc153538c51bf7a1b6b4d739ddeac9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 03:54:41 +0000 Subject: [PATCH 079/256] Change to prefer block pass over yield --- lib/unparser/generation.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index 33529c7a..099d8543 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -234,11 +234,11 @@ def first_child children.first end - def conditional_parentheses(flag) + def conditional_parentheses(flag, &block) if flag - parentheses { yield } + parentheses(&block) else - yield + block.call end end From f73368aeff140d34b7579c44dc9955d81d946051 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 03:54:42 +0000 Subject: [PATCH 080/256] Change to case over if else --- lib/unparser/emitter/float.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/unparser/emitter/float.rb b/lib/unparser/emitter/float.rb index 4be6023c..e03c934c 100644 --- a/lib/unparser/emitter/float.rb +++ b/lib/unparser/emitter/float.rb @@ -14,9 +14,10 @@ class Float < self private def dispatch - if value.eql?(INFINITY) + case value + when INFINITY write('10e1000000000000000000') - elsif value.eql?(NEG_INFINITY) + when NEG_INFINITY write('-10e1000000000000000000') else write(value.inspect) From e7ee9edddd1e2c5ea6d044f84faf07f70e4903fc Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 03:54:43 +0000 Subject: [PATCH 081/256] Upgrade rubocop to 0.92 --- .rubocop.yml | 1 + Gemfile.lock | 21 +++++++++++++-------- lib/unparser/emitter/primitive.rb | 2 ++ unparser.gemspec | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 7fac64f6..064b4510 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -8,6 +8,7 @@ AllCops: Exclude: - lib/unparser/precedence/data.rb - tmp + NewCops: enable # Avoid parameter lists longer than five parameters. Metrics/ParameterLists: diff --git a/Gemfile.lock b/Gemfile.lock index 4418bb40..4e93c110 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,7 +31,6 @@ GEM diff-lcs (1.4.4) equalizer (0.0.11) ice_nine (0.11.2) - jaro_winkler (1.5.4) memoizable (0.4.2) thread_safe (~> 0.3, >= 0.3.1) mprelude (0.1.0) @@ -65,6 +64,8 @@ GEM ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) + regexp_parser (1.8.1) + rexml (3.2.4) rspec (3.9.0) rspec-core (~> 3.9.0) rspec-expectations (~> 3.9.0) @@ -81,16 +82,20 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) - rubocop (0.79.0) - jaro_winkler (~> 1.5.1) + rubocop (0.92.0) parallel (~> 1.10) - parser (>= 2.7.0.1) + parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.7) + rexml + rubocop-ast (>= 0.5.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-ast (0.7.1) + parser (>= 2.7.1.5) ruby-progressbar (1.10.1) thread_safe (0.3.6) - unicode-display_width (1.6.1) + unicode-display_width (1.7.0) variable (0.0.1) equalizer (~> 0.0.11) @@ -104,8 +109,8 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.2.0) - rubocop (~> 0.79.0) + rubocop (~> 0.92.0) unparser! BUNDLED WITH - 2.1.4 + 2.2.0.rc.1 diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb index 5774f38f..0604abd5 100644 --- a/lib/unparser/emitter/primitive.rb +++ b/lib/unparser/emitter/primitive.rb @@ -61,12 +61,14 @@ class Rational < self private + # rubocop:disable Lint/FloatComparison def dispatch integer = Integer(value) float = value.to_f write_rational(integer.to_f.equal?(float) ? integer : float) end + # rubocop:enable Lint/FloatComparison def write_rational(value) write(value.to_s, RATIONAL_FORMAT) diff --git a/unparser.gemspec b/unparser.gemspec index 40a5a717..7422e984 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -30,5 +30,5 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.2.0') - gem.add_development_dependency('rubocop', '~> 0.79.0') + gem.add_development_dependency('rubocop', '~> 0.92.0') end From 562d369c84f045298d46186c86b73876a5b758b1 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 04:03:48 +0000 Subject: [PATCH 082/256] Add rubocop packaging extension --- Gemfile.lock | 3 +++ unparser.gemspec | 13 +++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 4e93c110..1b7b4c8f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -93,6 +93,8 @@ GEM unicode-display_width (>= 1.4.0, < 2.0) rubocop-ast (0.7.1) parser (>= 2.7.1.5) + rubocop-packaging (0.5.0) + rubocop (~> 0.89) ruby-progressbar (1.10.1) thread_safe (0.3.6) unicode-display_width (1.7.0) @@ -110,6 +112,7 @@ DEPENDENCIES rspec-core (~> 3.9) rspec-its (~> 1.2.0) rubocop (~> 0.92.0) + rubocop-packaging (~> 0.5.0) unparser! BUNDLED WITH diff --git a/unparser.gemspec b/unparser.gemspec index 7422e984..e6b550ef 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -25,10 +25,11 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.9.13') - gem.add_development_dependency('mutant-rspec', '~> 0.9.13') - gem.add_development_dependency('rspec', '~> 3.9') - gem.add_development_dependency('rspec-core', '~> 3.9') - gem.add_development_dependency('rspec-its', '~> 1.2.0') - gem.add_development_dependency('rubocop', '~> 0.92.0') + gem.add_development_dependency('mutant', '~> 0.9.13') + gem.add_development_dependency('mutant-rspec', '~> 0.9.13') + gem.add_development_dependency('rspec', '~> 3.9') + gem.add_development_dependency('rspec-core', '~> 3.9') + gem.add_development_dependency('rspec-its', '~> 1.2.0') + gem.add_development_dependency('rubocop', '~> 0.92.0') + gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 66e83cb14ac1067de2596226ee511f1080b2b859 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 20:07:45 +0000 Subject: [PATCH 083/256] Fix 0.5.0 release date --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index d455a1fc..a91af3f4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# v0.5.0 unreleased +# v0.5.0 2020-10-08 * Add 2.7 syntax support * Fix lots of edge cases via leveraging parser specs From e123038ec8b8001ca0ed90fec09d7582cfc70179 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 20:07:21 +0000 Subject: [PATCH 084/256] Change to emit empty dstr as %() --- Changelog.md | 4 ++++ lib/unparser/writer/dynamic_string.rb | 10 +++++++--- test/corpus/literal/assignment.rb | 10 ++++++++++ test/corpus/literal/def.rb | 7 ++++++- test/corpus/literal/literal.rb | 9 +++++++-- 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Changelog.md b/Changelog.md index a91af3f4..9988b58d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.0 2020-10-09 + +* Change to emit empty `dstr` as `%()` + # v0.5.0 2020-10-08 * Add 2.7 syntax support diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index b1f614a6..a5392f47 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -46,7 +46,7 @@ def heredoc_header end def heredoc? - children.empty? || (nl_last_child? && heredoc_pattern?) + !children.empty? && (nl_last_child? && heredoc_pattern?) end def emit_heredoc_header @@ -169,8 +169,12 @@ def emit_dynamic_component(node) end def emit_dstr - segments.each_with_index do |children, index| - emit_segment(children, index) + if children.empty? + write('%()') + else + segments.each_with_index do |children, index| + emit_segment(children, index) + end end end diff --git a/test/corpus/literal/assignment.rb b/test/corpus/literal/assignment.rb index c74b6855..9ac0c80c 100644 --- a/test/corpus/literal/assignment.rb +++ b/test/corpus/literal/assignment.rb @@ -30,13 +30,23 @@ foo[] = 1 foo[a, b] = value foo[index] = value +x = %() +x.x=%() +x[%()] = bar +a[%()] ||= bar +@a ||= %() x = <<-HEREDOC + #{} HEREDOC x.x=<<-HEREDOC + #{} HEREDOC x[] = <<-HEREDOC + #{} HEREDOC a[<<-HEREDOC] ||= bar + #{} HEREDOC @a ||= <<-HEREDOC + #{} HEREDOC diff --git a/test/corpus/literal/def.rb b/test/corpus/literal/def.rb index e2648f8e..61339bd4 100644 --- a/test/corpus/literal/def.rb +++ b/test/corpus/literal/def.rb @@ -124,6 +124,11 @@ def foo(bar:, baz: "value") end def f - (<<-HEREDOC) + <<-HEREDOC + #{} HEREDOC end + +def f + %() +end diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index f3ef96ac..22a83abb 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -1,11 +1,16 @@ { "foo" => <<-HEREDOC, "bar" => :baz } + #{} HEREDOC -["foo", <<-HEREDOC] -HEREDOC +{ "foo" => %(), "bar" => :baz } +["foo", %()] a(<<-HEREDOC).a + #{} HEREDOC +a(%()).a { "foo" => <<-HEREDOC, **baz } + #{} HEREDOC +{ "foo" => %(), **baz } "#@a #@@a #$a" 0 ++1 From 2be8cb29a326d9298e9ffc97b241c6aa340b3ff0 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 9 Oct 2020 20:12:37 +0000 Subject: [PATCH 085/256] Change version to 0.5.1 --- Gemfile.lock | 2 +- unparser.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1b7b4c8f..e8522372 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.0) + unparser (0.5.1) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) diff --git a/unparser.gemspec b/unparser.gemspec index e6b550ef..982fe784 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.0' + gem.version = '0.5.1' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From a6b007f05d005d481b28237932eae7c90d4ca010 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Oct 2020 01:29:27 +0000 Subject: [PATCH 086/256] Fix changelog version --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9988b58d..6a241ff5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# v0.5.0 2020-10-09 +# v0.5.1 2020-10-09 * Change to emit empty `dstr` as `%()` From 3ca3ae2279048170f04784205d8c2233c593ae3c Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Oct 2020 01:29:28 +0000 Subject: [PATCH 087/256] Fix unary csend --- Changelog.md | 4 ++++ Gemfile.lock | 2 +- lib/unparser/writer/send.rb | 2 +- test/corpus/literal/send.rb | 1 + unparser.gemspec | 2 +- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6a241ff5..20ecb833 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.2 2020-10-16 + +* Fix unary csends to emit correctly. + # v0.5.1 2020-10-09 * Change to emit empty `dstr` as `%()` diff --git a/Gemfile.lock b/Gemfile.lock index e8522372..9cd9cd0a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.1) + unparser (0.5.2) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index 14487e9d..2209f111 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -45,7 +45,7 @@ def effective_writer def effective_writer_class if details.binary_syntax_allowed? Binary - elsif details.selector_unary_operator? + elsif details.selector_unary_operator? && n_send?(node) Unary elsif write_as_attribute_assignment? AttributeAssignment diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index 06543ebc..78ec7112 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -77,3 +77,4 @@ def foo (a + b) / c.-(e, f) (a + b) / c.-(*f) x(**foo) +foo&.! diff --git a/unparser.gemspec b/unparser.gemspec index 982fe784..0fa79a44 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.1' + gem.version = '0.5.2' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From a4d9dc255f7847e31aec76b56887a0d2a1e262da Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Oct 2020 01:29:29 +0000 Subject: [PATCH 088/256] Add primitive devloop --- scripts/devloop.sh | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 scripts/devloop.sh diff --git a/scripts/devloop.sh b/scripts/devloop.sh new file mode 100755 index 00000000..4cc3aeee --- /dev/null +++ b/scripts/devloop.sh @@ -0,0 +1,5 @@ +while inotifywait **/*.rb Gemfile unparser.gemspec; do + bundle exec rspec spec/unit -fd --fail-fast --order default \ + && bundle exec ./mutant.sh --since master -- 'Unparser*' \ + && bundle exec rubocop +done From 3ccb1c2cf5f16c359eb8658e9fb7529c539c1b51 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Oct 2020 01:29:30 +0000 Subject: [PATCH 089/256] Add Unparser.unparse_validate interface --- Changelog.md | 1 + lib/unparser.rb | 17 ++++++++++++++++ spec/unit/unparser_spec.rb | 40 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/Changelog.md b/Changelog.md index 20ecb833..5c68b4ff 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ # v0.5.2 2020-10-16 * Fix unary csends to emit correctly. +* Add `Unparser.unparse_validate` interface # v0.5.1 2020-10-09 diff --git a/lib/unparser.rb b/lib/unparser.rb index f5f9d18a..099a1211 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -50,6 +50,23 @@ def self.unparse(node, comment_array = []) end.content end + # Unparse with validation + # + # @param [Parser::AST::Node, nil] node + # @param [Array] comment_array + # + # @return [Either] + def self.unparse_validate(node, comment_array = []) + generated = unparse(node, comment_array) + validation = Validation.from_string(generated) + + if validation.success? + MPrelude::Either::Right.new(generated) + else + MPrelude::Either::Left.new(validation) + end + end + # Unparse capturing errors # # This is mostly useful for writing testing tools against unparser. diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 42a3edad..98b767d4 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -129,6 +129,46 @@ def apply end end + describe '.unparse_validate' do + def apply + Unparser.unparse_validate(s(:true)) + end + + context 'on successful validation' do + context 'with comments' do + def apply + Unparser.unparse_validate( + *Unparser.parser.parse_with_comments(Unparser.buffer('true # foo')) + ) + end + + it 'returns right value with generated source' do + expect(apply).to eql(MPrelude::Either::Right.new('true # foo')) + end + end + + context 'without comments' do + it 'returns right value with generated source' do + expect(apply).to eql(MPrelude::Either::Right.new('true')) + end + end + end + + context 'on unsuccessful validation' do + before do + allow(Unparser::Validation).to receive_messages(from_string: validation) + end + + let(:validation) do + instance_double(Unparser::Validation, success?: false) + end + + it 'returns left value with validation' do + expect(apply).to eql(MPrelude::Either::Left.new(validation)) + end + end + end + describe '.unparse' do context 'on unknown node type' do def apply From db81801a08237296f523b3325f2d4dcba1a37c13 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 16 Oct 2020 03:30:31 +0000 Subject: [PATCH 090/256] Add Unparser::Validation.from_node --- lib/unparser/validation.rb | 21 +++++++++++++++ spec/unit/unparser/validation_spec.rb | 37 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index 7bc1dc97..18fe224f 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -71,6 +71,27 @@ def self.from_string(original_source) ) end + # Create validator from node + # + # @param [Parser::AST::Node] original_node + # + # @return [Validator] + def self.from_node(original_node) + generated_source = Unparser.unparse_either(original_node) + + generated_node = generated_source + .lmap(&method(:const_unit)) + .bind(&Unparser.public_method(:parse_either)) + + new( + identification: '(string)', + original_source: generated_source, + original_node: MPrelude::Either::Right.new(original_node), + generated_source: generated_source, + generated_node: generated_node + ) + end + # Create validator from file # # @param [Pathname] path diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index 59d4c492..14f17582 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -304,4 +304,41 @@ def apply end end end + + describe '.from_node' do + def apply + described_class.from_node(node) + end + + let(:attributes) do + { + generated_node: right(s(:true)), + generated_source: right('true'), + identification: '(string)', + original_node: right(node), + original_source: right('true') + } + end + + context 'on valid original node' do + let(:node) { s(:true) } + + it 'returns expected validator' do + expect(apply).to eql(described_class.new(attributes)) + end + end + + context 'on invalid original node' do + let(:node) { s(:foo) } + + it 'returns expected validator' do + validator = apply + + expect(validator.generated_node).to eql(left(nil)) + expect(validator.generated_source.lmap(&:inspect)).to eql(left(Unparser::UnknownNodeError.new('Unknown node type: :foo').inspect)) + expect(validator.original_source).to eql(validator.generated_source) + expect(validator.original_node).to eql(right(node)) + end + end + end end From 3dfe4f1e4200c5f3b80941551e871492b6f363c9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 02:55:09 +0000 Subject: [PATCH 091/256] Change to ruby/setup-ruby action --- .github/workflows/ci.yml | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f39d3a63..1b46058b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,16 +17,14 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6', '2.7'] + ruby: [ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - - uses: actions/setup-ruby@v1 + - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - run: | - gem install bundler - bundle install + - run: bundle install - run: bundle exec rspec spec/unit ruby-integration-spec: name: Integration Specs @@ -35,16 +33,14 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6', '2.7'] + ruby: [ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - - uses: actions/setup-ruby@v1 + - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - run: | - gem install bundler - bundle install + - run: bundle install - run: bundle exec rspec spec/integration ruby-rubocop: name: Rubocop @@ -53,14 +49,12 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.6', '2.7'] + ruby: [ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 - - uses: actions/setup-ruby@v1 + - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - - run: | - gem install bundler - bundle install + - run: bundle install - run: bundle exec rubocop From 6bdad7e69aabb960f688db92f59cb9d2780987d9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:04:39 +0000 Subject: [PATCH 092/256] Change tests to pass on ruby-2.5 --- spec/unit/unparser_spec.rb | 20 ++++++++++++++------ test/corpus/literal/literal.rb | 4 ---- test/corpus/literal/since/26.rb | 4 ++++ 3 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 test/corpus/literal/since/26.rb diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 98b767d4..c2c00806 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -362,14 +362,22 @@ def noop describe 'corpus' do let(:version_excludes) do + excludes = [] + if RUBY_VERSION < '2.7.' - %w[ - --ignore test/corpus/literal/pattern.rb - --ignore test/corpus/literal/since/27.rb - ] - else - [] + excludes.concat( + %w[ + test/corpus/literal/pattern.rb + test/corpus/literal/since/27.rb + ] + ) end + + if RUBY_VERSION < '2.6.' + excludes << 'test/corpus/literal/since/26.rb' + end + + excludes.flat_map { |file| ['--ignore', file] } end it 'passes the literal corpus' do diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 22a83abb..2b59c99c 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -57,13 +57,9 @@ /\/\//x :"foo#{bar}baz" :"#{"foo"}" -(1..) -1..2 (0.0 / 0.0)..1 1..(0.0 / 0.0) (0.0 / 0.0)..100 -(1...) -1...2 -0.1 0.1 [1, 2] diff --git a/test/corpus/literal/since/26.rb b/test/corpus/literal/since/26.rb new file mode 100644 index 00000000..eb1f3874 --- /dev/null +++ b/test/corpus/literal/since/26.rb @@ -0,0 +1,4 @@ +(1..) +1..2 +(1...) +1...2 From ecbad40dbd48ec30e2f5dda2475b29fff934ee73 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:04:40 +0000 Subject: [PATCH 093/256] Add ruby-2.5 to CI matrix --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b46058b..93bde889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 From a093a1efd9281c5466dcd643cdef7d7ba199449d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:11:11 +0000 Subject: [PATCH 094/256] Change to bundle with 2.1.4 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9cd9cd0a..81fd2d1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,4 +116,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.2.0.rc.1 + 2.1.4 From 8587964de238ce6faa01d544e420845ceadb3095 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:12:17 +0000 Subject: [PATCH 095/256] Upgrade to mutant-0.9.14 --- Gemfile.lock | 12 ++++++------ unparser.gemspec | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 81fd2d1c..509d480d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.13) + mutant (0.9.14) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -53,11 +53,11 @@ GEM mprelude (~> 0.1.0) parser (~> 2.7.1) procto (~> 0.0.2) - unparser (~> 0.4.8) + unparser (~> 0.5.2) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.9.13) - mutant (~> 0.9.13) + mutant-rspec (0.9.14) + mutant (~> 0.9.14) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) @@ -105,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.9.13) + mutant (~> 0.9.14) mutant-license! - mutant-rspec (~> 0.9.13) + mutant-rspec (~> 0.9.14) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.2.0) diff --git a/unparser.gemspec b/unparser.gemspec index 0fa79a44..95fb0159 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -25,8 +25,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.9.13') - gem.add_development_dependency('mutant-rspec', '~> 0.9.13') + gem.add_development_dependency('mutant', '~> 0.9.14') + gem.add_development_dependency('mutant-rspec', '~> 0.9.14') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.2.0') From 5373f23ee7fc5a280d200770987f55ba5680e9ba Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:20:34 +0000 Subject: [PATCH 096/256] Add minor README improvements --- README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 58053e24..d608836d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ unparser ![CI](https://github.com/mbj/unparser/workflows/CI/badge.svg) [![Gem Version](https://img.shields.io/gem/v/unparser.svg)](https://rubygems.org/gems/unparser) -Generate equivalent source for ASTs from whitequarks [parser](https://github.com/whitequark/parser). +Generate equivalent source for ASTs from [parser](https://github.com/whitequark/parser). The following constraints apply: @@ -12,8 +12,13 @@ The following constraints apply: * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format * Only support for Ruby >= 2.5 -It serves well for [mutant](https://github.com/mbj/mutant) mutators and the in-memory vendoring for self hosting, -and other tooling. +Notable Users: + +* [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing. +* [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports. +* May other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies). + +(if you want your tool to be mentioned here please PR the addition with a TLDR of your use case). Public API: ----------- @@ -86,12 +91,22 @@ RUBY generated = Unparser.unparse(node) # ["foo", "bar"], NOT %w[foo bar] ! -code == generated # false, not identical code +code == generated # false, not identical code Unparser.parse(generated) == node # true, but identical AST ``` Summary: unparser does not reproduce your source! It produces equivalent source. +Ruby Versions: +-------------- + +Unparsers primay reason for existance is mutant and its +supported [Ruby-Versions](https://github.com/mbj/mutant#ruby-versions). + +Basically: All non EOL MRI releases. + +If you need to generate Ruby Syntax outside of this band feel free to contact me (email in gemspec). + Testing: -------- From 31931c41b447224bfd49e4237aa60e3a7f16903b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 03:30:10 +0000 Subject: [PATCH 097/256] Add required_ruby_version >= 2.5 to gemspec --- Changelog.md | 4 ++++ Gemfile.lock | 2 +- unparser.gemspec | 4 +++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5c68b4ff..69b39823 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.3 2020-10-18 + +* Add required ruby version '>= 2.5' to gemspec. + # v0.5.2 2020-10-16 * Fix unary csends to emit correctly. diff --git a/Gemfile.lock b/Gemfile.lock index 509d480d..cc6cce87 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.2) + unparser (0.5.3) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) diff --git a/unparser.gemspec b/unparser.gemspec index 95fb0159..0550410b 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.2' + gem.version = '0.5.3' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -15,6 +15,8 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] + gem.required_ruby_version = '>= 2.5' + gem.add_dependency('abstract_type', '~> 0.0.7') gem.add_dependency('adamantium', '~> 0.2.0') gem.add_dependency('anima', '~> 0.3.1') From e74f27c49eb0df03a91373981912ea0cbbcdfc0e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 18 Oct 2020 04:33:30 +0000 Subject: [PATCH 098/256] Add dependabot config --- .github/dependabot.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..e077018f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: / + schedule: + interval: daily From 785d97304e325f9b91e4a64b6fb43bb56a57bb6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2020 04:38:01 +0000 Subject: [PATCH 099/256] Update rspec-its requirement from ~> 1.2.0 to ~> 1.3.0 Updates the requirements on [rspec-its](https://github.com/rspec/rspec-its) to permit the latest version. - [Release notes](https://github.com/rspec/rspec-its/releases) - [Changelog](https://github.com/rspec/rspec-its/blob/main/Changelog.md) - [Commits](https://github.com/rspec/rspec-its/compare/v1.2.0...v1.3.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- unparser.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index cc6cce87..a0ebf6a9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,7 +75,7 @@ GEM rspec-expectations (3.9.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) - rspec-its (1.2.0) + rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) rspec-mocks (3.9.1) @@ -110,7 +110,7 @@ DEPENDENCIES mutant-rspec (~> 0.9.14) rspec (~> 3.9) rspec-core (~> 3.9) - rspec-its (~> 1.2.0) + rspec-its (~> 1.3.0) rubocop (~> 0.92.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 0550410b..71bac689 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -31,7 +31,7 @@ Gem::Specification.new do |gem| gem.add_development_dependency('mutant-rspec', '~> 0.9.14') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') - gem.add_development_dependency('rspec-its', '~> 1.2.0') + gem.add_development_dependency('rspec-its', '~> 1.3.0') gem.add_development_dependency('rubocop', '~> 0.92.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 14d45d6125cd6cb32af513c2ae6a6db920f69025 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Oct 2020 04:44:07 +0000 Subject: [PATCH 100/256] Update rubocop requirement from ~> 0.92.0 to ~> 0.93.1 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.92.0...v0.93.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 12 ++++++------ unparser.gemspec | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a0ebf6a9..2990cfbd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,7 +64,7 @@ GEM ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) - regexp_parser (1.8.1) + regexp_parser (1.8.2) rexml (3.2.4) rspec (3.9.0) rspec-core (~> 3.9.0) @@ -82,16 +82,16 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) - rubocop (0.92.0) + rubocop (0.93.1) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.7) + regexp_parser (>= 1.8) rexml - rubocop-ast (>= 0.5.0) + rubocop-ast (>= 0.6.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (0.7.1) + rubocop-ast (0.8.0) parser (>= 2.7.1.5) rubocop-packaging (0.5.0) rubocop (~> 0.89) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 0.92.0) + rubocop (~> 0.93.1) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 71bac689..7805d975 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 0.92.0') + gem.add_development_dependency('rubocop', '~> 0.93.1') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 0929e107a14eba63de606b6abb7d5abe3e96db3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Oct 2020 05:02:26 +0000 Subject: [PATCH 101/256] Bump rubocop-packaging from 0.5.0 to 0.5.1 Bumps [rubocop-packaging](https://github.com/utkarsh2102/rubocop-packaging) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/utkarsh2102/rubocop-packaging/releases) - [Commits](https://github.com/utkarsh2102/rubocop-packaging/compare/v0.5.0...v0.5.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2990cfbd..ed5a10a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,10 +91,10 @@ GEM rubocop-ast (>= 0.6.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (0.8.0) + rubocop-ast (1.0.0) parser (>= 2.7.1.5) - rubocop-packaging (0.5.0) - rubocop (~> 0.89) + rubocop-packaging (0.5.1) + rubocop (>= 0.89, < 2.0) ruby-progressbar (1.10.1) thread_safe (0.3.6) unicode-display_width (1.7.0) From 28dd3dadb8958c5dafa2005aec11976d0efa427b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Oct 2020 05:07:48 +0000 Subject: [PATCH 102/256] Update rubocop requirement from ~> 0.93.1 to ~> 1.0.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v0.93.1...v1.0.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- unparser.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ed5a10a4..daec1be5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,7 +82,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.3) - rubocop (0.93.1) + rubocop (1.0.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -91,7 +91,7 @@ GEM rubocop-ast (>= 0.6.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.0.0) + rubocop-ast (1.0.1) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 0.93.1) + rubocop (~> 1.0.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 7805d975..b4701d6b 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 0.93.1') + gem.add_development_dependency('rubocop', '~> 1.0.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From ce9d4b0f9955c7a468db796e765df52519aa3618 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Oct 2020 13:52:33 +0000 Subject: [PATCH 103/256] Upgrade mutant --- Gemfile.lock | 18 +++++++++--------- unparser.gemspec | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index daec1be5..7a5196ce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.9.14) + mutant (0.10.0) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -53,11 +53,11 @@ GEM mprelude (~> 0.1.0) parser (~> 2.7.1) procto (~> 0.0.2) - unparser (~> 0.5.2) + unparser (~> 0.5.3) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.9.14) - mutant (~> 0.9.14) + mutant-rspec (0.10.0) + mutant (~> 0.10.0) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) @@ -72,7 +72,7 @@ GEM rspec-mocks (~> 3.9.0) rspec-core (3.9.3) rspec-support (~> 3.9.3) - rspec-expectations (3.9.2) + rspec-expectations (3.9.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-its (1.3.0) @@ -81,7 +81,7 @@ GEM rspec-mocks (3.9.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) - rspec-support (3.9.3) + rspec-support (3.9.4) rubocop (1.0.0) parallel (~> 1.10) parser (>= 2.7.1.5) @@ -91,7 +91,7 @@ GEM rubocop-ast (>= 0.6.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.0.1) + rubocop-ast (1.1.0) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) @@ -105,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.9.14) + mutant (~> 0.10.0) mutant-license! - mutant-rspec (~> 0.9.14) + mutant-rspec (~> 0.10.0) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index b4701d6b..23e8a204 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.9.14') - gem.add_development_dependency('mutant-rspec', '~> 0.9.14') + gem.add_development_dependency('mutant', '~> 0.10.0') + gem.add_development_dependency('mutant-rspec', '~> 0.10.0') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 565a0e487048b4593ace745afd3b38918e87f57d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 29 Oct 2020 13:56:33 +0000 Subject: [PATCH 104/256] Add macsosx to CI matrix --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93bde889..319af54a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.5, ruby-2.6, ruby-2.7] - os: [ubuntu-latest] + os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 @@ -50,7 +50,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.5, ruby-2.6, ruby-2.7] - os: [ubuntu-latest] + os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 From 05cc30f0a832bfd472bf45ba197854b26ff7d71e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2020 05:03:29 +0000 Subject: [PATCH 105/256] Bump mutant from 0.10.0 to 0.10.1 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.0 to 0.10.1. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.0...v0.10.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 7a5196ce..19811e25 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.0) + mutant (0.10.1) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From d7e9cd3ef499bffcb2ef3ba7c6e5e6347df85e0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2020 05:09:57 +0000 Subject: [PATCH 106/256] Bump rspec-core from 3.9.3 to 3.10.0 Bumps [rspec-core](https://github.com/rspec/rspec-core) from 3.9.3 to 3.10.0. - [Release notes](https://github.com/rspec/rspec-core/releases) - [Changelog](https://github.com/rspec/rspec-core/blob/main/Changelog.md) - [Commits](https://github.com/rspec/rspec-core/compare/v3.9.3...v3.10.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 19811e25..c8f4a232 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -66,22 +66,22 @@ GEM rainbow (3.0.0) regexp_parser (1.8.2) rexml (3.2.4) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.3) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.3) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.0) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) + rspec-support (~> 3.10.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.9.1) + rspec-mocks (3.10.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.4) + rspec-support (~> 3.10.0) + rspec-support (3.10.0) rubocop (1.0.0) parallel (~> 1.10) parser (>= 2.7.1.5) From a9f8ace49a831525ec23f6bb5eb4b6473c18d398 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2020 13:54:27 +0000 Subject: [PATCH 107/256] Update rubocop requirement from ~> 1.0.0 to ~> 1.1.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.0.0...v1.1.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- unparser.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c8f4a232..10c85cbf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,13 +82,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.0.0) + rubocop (1.1.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8) rexml - rubocop-ast (>= 0.6.0) + rubocop-ast (>= 1.0.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) rubocop-ast (1.1.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.0.0) + rubocop (~> 1.1.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 23e8a204..857de5d3 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.0.0') + gem.add_development_dependency('rubocop', '~> 1.1.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From f05d0b1a7cc9b4854c5d1392c98dd34ec4c54963 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Nov 2020 14:01:24 +0000 Subject: [PATCH 108/256] Bump mutant-rspec from 0.10.0 to 0.10.1 Bumps [mutant-rspec](https://github.com/mbj/mutant) from 0.10.0 to 0.10.1. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.0...v0.10.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 10c85cbf..23f247fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.3) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.10.0) - mutant (~> 0.10.0) + mutant-rspec (0.10.1) + mutant (~> 0.10.1) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) From e44cd446a911193e22d3d682b18119b2abd8c8a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Nov 2020 05:04:13 +0000 Subject: [PATCH 109/256] Bump mutant from 0.10.1 to 0.10.4 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.1 to 0.10.4. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.1...v0.10.4) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 23f247fb..e66eefce 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.1) + mutant (0.10.4) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From a22df3efc89b582d6ff91a90b342023d1e834dc7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Nov 2020 05:04:08 +0000 Subject: [PATCH 110/256] Bump mutant-rspec from 0.10.1 to 0.10.4 Bumps [mutant-rspec](https://github.com/mbj/mutant) from 0.10.1 to 0.10.4. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.1...v0.10.4) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e66eefce..63348493 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.3) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.10.1) - mutant (~> 0.10.1) + mutant-rspec (0.10.4) + mutant (~> 0.10.4) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) From ffbcd13312a47f8335a51586cd9fcb4252f4fce4 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 4 Nov 2020 05:41:17 +0000 Subject: [PATCH 111/256] Fix forced ternary on control keyword [fix #189] --- Changelog.md | 4 ++++ Gemfile.lock | 8 ++++---- lib/unparser/emitter/flow_modifier.rb | 7 +++++-- lib/unparser/emitter/if.rb | 8 ++++++++ lib/unparser/node_helpers.rb | 1 + test/corpus/literal/control.rb | 10 ++++++++++ unparser.gemspec | 2 +- 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index 69b39823..3890e2fe 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.4 2020-11-04 + +* Fix forced ternary on control keyword [#191](https://github.com/mbj/unparser/pull/191) + # v0.5.3 2020-10-18 * Add required ruby version '>= 2.5' to gemspec. diff --git a/Gemfile.lock b/Gemfile.lock index 63348493..39bf3887 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.3) + unparser (0.5.4) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.4) + mutant (0.10.5) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.3) variable (~> 0.0.1) mutant-license (0.1.0) - mutant-rspec (0.10.4) - mutant (~> 0.10.4) + mutant-rspec (0.10.5) + mutant (~> 0.10.5) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index 93c81f4d..c1835972 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -19,8 +19,11 @@ class FlowModifier < self def dispatch write(MAP.fetch(node.type)) - unless children.empty? - emit_arguments + if children.one? && n_if?(children.first) + ws + emitter(children.first).emit_ternary + else + emit_arguments unless children.empty? end end diff --git a/lib/unparser/emitter/if.rb b/lib/unparser/emitter/if.rb index 91ce510b..4919a0f7 100644 --- a/lib/unparser/emitter/if.rb +++ b/lib/unparser/emitter/if.rb @@ -8,6 +8,14 @@ class If < self children :condition, :if_branch, :else_branch + def emit_ternary + visit(condition) + write(' ? ') + visit(if_branch) + write(' : ') + visit(else_branch) + end + private def dispatch diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 1d76742c..83645a66 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -45,6 +45,7 @@ def n?(type, node) ensure hash hash_pattern + if in_pattern int kwsplat diff --git a/test/corpus/literal/control.rb b/test/corpus/literal/control.rb index 281b47a4..648782bc 100644 --- a/test/corpus/literal/control.rb +++ b/test/corpus/literal/control.rb @@ -3,3 +3,13 @@ break retry redo +return 1 +return 1, 2 +return true ? 1 : 2 +break true ? 1 : 2 +next true ? 1 : 2 +return true, if true + 1 +else + 2 +end diff --git a/unparser.gemspec b/unparser.gemspec index 857de5d3..f08d42a9 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.3' + gem.version = '0.5.4' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From ae5f137e2077401fb622ee44ae1690916d4af11a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2020 05:03:24 +0000 Subject: [PATCH 112/256] Bump mutant-license Bumps mutant-license from 0.1.0 to 0.1.1.2.2355046999240944981729280251890364410689.0. Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 39bf3887..ab5eefc9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -55,7 +55,7 @@ GEM procto (~> 0.0.2) unparser (~> 0.5.3) variable (~> 0.0.1) - mutant-license (0.1.0) + mutant-license (0.1.1.2.2355046999240944981729280251890364410689.0) mutant-rspec (0.10.5) mutant (~> 0.10.5) rspec-core (>= 3.8.0, < 4.0.0) From b903f3f1b0dfdd7db055b7819cd4e4459de7fb1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Nov 2020 05:03:39 +0000 Subject: [PATCH 113/256] Update rubocop requirement from ~> 1.1.0 to ~> 1.2.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.1.0...v1.2.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- unparser.gemspec | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 39bf3887..cfd7f73e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,7 +82,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.1.0) + rubocop (1.2.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -91,7 +91,7 @@ GEM rubocop-ast (>= 1.0.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.1.0) + rubocop-ast (1.1.1) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.1.0) + rubocop (~> 1.2.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index f08d42a9..7ab344c2 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.1.0') + gem.add_development_dependency('rubocop', '~> 1.2.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 78251f5bcd895372053ce8c0251bb0fa9e3dab1f Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 9 Nov 2020 03:36:30 +0000 Subject: [PATCH 114/256] Change to mutant-0.10.x CLI --- mutant.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mutant.sh b/mutant.sh index 217f80c2..01f8f260 100755 --- a/mutant.sh +++ b/mutant.sh @@ -1,6 +1,6 @@ #/usr/bin/bash -ex -bundle exec mutant \ +bundle exec mutant run \ --zombie \ --ignore-subject 'Unparser::Builder#initialize' \ --ignore-subject 'Unparser::CLI*' \ From afbcf5959d68277c90fc05c278f59d0dfd292ab6 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 9 Nov 2020 03:36:31 +0000 Subject: [PATCH 115/256] Fix mlhs to splat [Fix #194] --- lib/unparser/emitter/splat.rb | 12 +++++++++++- test/corpus/literal/assignment.rb | 1 + test/corpus/literal/case.rb | 6 ++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/unparser/emitter/splat.rb b/lib/unparser/emitter/splat.rb index cb22c9b9..5129fa5a 100644 --- a/lib/unparser/emitter/splat.rb +++ b/lib/unparser/emitter/splat.rb @@ -22,12 +22,22 @@ class Splat < self children :subject + def emit_mlhs + write('*') + subject_emitter.emit_mlhs if subject + end + private def dispatch write('*') - visit(subject) if subject + subject_emitter.write_to_buffer + end + + def subject_emitter + emitter(subject) end + memoize :subject_emitter end end end # Unparser diff --git a/test/corpus/literal/assignment.rb b/test/corpus/literal/assignment.rb index 9ac0c80c..84a74e89 100644 --- a/test/corpus/literal/assignment.rb +++ b/test/corpus/literal/assignment.rb @@ -14,6 +14,7 @@ (a.foo, a.bar) = [1, 2] (a[*foo], a[1]) = [1, 2] (a[0], a[1]) = [1, 2] +(*c.foo) = 1 ::Foo = ::Bar @@a = 1 @a = 1 diff --git a/test/corpus/literal/case.rb b/test/corpus/literal/case.rb index 9634ff49..c455fd7c 100644 --- a/test/corpus/literal/case.rb +++ b/test/corpus/literal/case.rb @@ -29,3 +29,9 @@ else :foo end +case foo +when *bar | baz +end +case foo +when *bar.baz=1 +end From 1b15d3d9b2d2a706b6df5bff73ae0cd3047090b8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 9 Nov 2020 03:36:32 +0000 Subject: [PATCH 116/256] Fix n-th-ref dstring interpolation --- lib/unparser/writer/dynamic_string.rb | 2 +- test/corpus/literal/dstr.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index a5392f47..37357803 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -20,7 +20,7 @@ class DynamicString ] ) - FLAT_INTERPOLATION = %i[ivar cvar gvar].to_set.freeze + FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze private_constant(*constants(false)) diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index c2e83c9e..7dfa5762 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -17,3 +17,7 @@ #{} #{} HEREDOC +"a#$1" +"a#$a" +"a#@a" +"a#@@a" From d6b63249a55144025aeb48a0b32426888df0bb34 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 9 Nov 2020 03:36:33 +0000 Subject: [PATCH 117/256] Fix to emit heredoc reminders on rescue postcontrol --- lib/unparser/generation.rb | 4 +++- lib/unparser/writer/rescue.rb | 4 ++++ test/corpus/literal/dstr.rb | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index 099d8543..e553c58d 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -204,7 +204,9 @@ def emit_body_ensure_rescue(node) end def emit_rescue_postcontrol(node) - writer_with(Writer::Rescue, node).emit_postcontrol + writer = writer_with(Writer::Rescue, node) + writer.emit_postcontrol + writer.emit_heredoc_reminders end def emit_rescue_regular(node) diff --git a/lib/unparser/writer/rescue.rb b/lib/unparser/writer/rescue.rb index b2075cd2..889ec974 100644 --- a/lib/unparser/writer/rescue.rb +++ b/lib/unparser/writer/rescue.rb @@ -20,6 +20,10 @@ def emit_regular end end + def emit_heredoc_reminders + emitter(body).emit_heredoc_reminders + end + def emit_postcontrol visit(body) writer_with(Resbody, rescue_body).emit_postcontrol diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index 7dfa5762..2c1ed53c 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -17,6 +17,10 @@ #{} #{} HEREDOC +<<-HEREDOC rescue nil +#{} +a +HEREDOC "a#$1" "a#$a" "a#@a" From 22279ee57104c8d8628a39e0b1bdcd685e8791c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Nov 2020 05:16:32 +0000 Subject: [PATCH 118/256] Bump mutant from 0.10.5 to 0.10.6 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.5 to 0.10.6. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.5...v0.10.6) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ea08d2c6..068f60c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.5) + mutant (0.10.6) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -53,7 +53,7 @@ GEM mprelude (~> 0.1.0) parser (~> 2.7.1) procto (~> 0.0.2) - unparser (~> 0.5.3) + unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.0) mutant-rspec (0.10.5) From fa0babcfd1b6148799f6ce9b8e6b60f9d2e7a2d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2020 05:04:20 +0000 Subject: [PATCH 119/256] Bump mutant-license Bumps mutant-license from 0.1.1.2.2355046999240944981729280251890364410689.0 to 0.1.1.2.2355046999240944981729280251890364410689.1. Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index ea08d2c6..659c6309 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -55,7 +55,7 @@ GEM procto (~> 0.0.2) unparser (~> 0.5.3) variable (~> 0.0.1) - mutant-license (0.1.1.2.2355046999240944981729280251890364410689.0) + mutant-license (0.1.1.2.2355046999240944981729280251890364410689.1) mutant-rspec (0.10.5) mutant (~> 0.10.5) rspec-core (>= 3.8.0, < 4.0.0) From eff676e2d96be2db58ffc3ed527c6ffe73181b36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Nov 2020 05:06:26 +0000 Subject: [PATCH 120/256] Bump mutant-rspec from 0.10.5 to 0.10.6 Bumps [mutant-rspec](https://github.com/mbj/mutant) from 0.10.5 to 0.10.6. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.5...v0.10.6) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 98609e03..a134df4e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.1) - mutant-rspec (0.10.5) - mutant (~> 0.10.5) + mutant-rspec (0.10.6) + mutant (~> 0.10.6) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.19.2) parser (2.7.2.0) From b578237a57ba901814253058fa6f87a35d5d5ebd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Nov 2020 05:03:15 +0000 Subject: [PATCH 121/256] Update rubocop requirement from ~> 1.2.0 to ~> 1.3.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.2.0...v1.3.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 8 ++++---- unparser.gemspec | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index a134df4e..73f56433 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,7 +59,7 @@ GEM mutant-rspec (0.10.6) mutant (~> 0.10.6) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.19.2) + parallel (1.20.0) parser (2.7.2.0) ast (~> 2.4.1) procto (0.0.3) @@ -82,13 +82,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.2.0) + rubocop (1.3.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8) rexml - rubocop-ast (>= 1.0.1) + rubocop-ast (>= 1.1.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) rubocop-ast (1.1.1) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.2.0) + rubocop (~> 1.3.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 7ab344c2..15c94ada 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.2.0') + gem.add_development_dependency('rubocop', '~> 1.3.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 214ec437918af27cc3911ca4f776577637acdef9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 30 Nov 2020 10:10:24 +0000 Subject: [PATCH 122/256] Upgrade to mutant ~> 0.10.9 --- Gemfile.lock | 10 +++++----- spec/integration/unparser/corpus_spec.rb | 2 +- unparser.gemspec | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 73f56433..833f2d60 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.6) + mutant (0.10.9) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.1) - mutant-rspec (0.10.6) - mutant (~> 0.10.6) + mutant-rspec (0.10.9) + mutant (~> 0.10.9) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.0) parser (2.7.2.0) @@ -105,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.0) + mutant (~> 0.10.9) mutant-license! - mutant-rspec (~> 0.10.0) + mutant-rspec (~> 0.10.9) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb index b61ff5a2..18bfb73f 100644 --- a/spec/integration/unparser/corpus_spec.rb +++ b/spec/integration/unparser/corpus_spec.rb @@ -102,7 +102,7 @@ def system(arguments) ) ) - ALL = loader.apply(path).lmap(&:compact_message).from_right + ALL = loader.call(path).lmap(&:compact_message).from_right end Project::ALL.each do |project| diff --git a/unparser.gemspec b/unparser.gemspec index 15c94ada..add5fee9 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.10.0') - gem.add_development_dependency('mutant-rspec', '~> 0.10.0') + gem.add_development_dependency('mutant', '~> 0.10.9') + gem.add_development_dependency('mutant-rspec', '~> 0.10.9') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 869e5fa7deaddd5a44d2ce39926817573a958c94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Nov 2020 10:22:40 +0000 Subject: [PATCH 123/256] Update rubocop requirement from ~> 1.3.0 to ~> 1.4.2 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.3.0...v1.4.2) Signed-off-by: dependabot[bot] --- Gemfile.lock | 10 +++++----- unparser.gemspec | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 833f2d60..5625a868 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -59,12 +59,12 @@ GEM mutant-rspec (0.10.9) mutant (~> 0.10.9) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.20.0) + parallel (1.20.1) parser (2.7.2.0) ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) - regexp_parser (1.8.2) + regexp_parser (2.0.0) rexml (3.2.4) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -82,7 +82,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.3.0) + rubocop (1.4.2) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -91,7 +91,7 @@ GEM rubocop-ast (>= 1.1.1) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.1.1) + rubocop-ast (1.2.0) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.3.0) + rubocop (~> 1.4.2) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index add5fee9..89050bf8 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.3.0') + gem.add_development_dependency('rubocop', '~> 1.4.2') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 5bb74d7e522f946e38bc5a8c6718a1b1f01708e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Dec 2020 05:09:58 +0000 Subject: [PATCH 124/256] Bump mutant from 0.10.9 to 0.10.10 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.9 to 0.10.10. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/commits/v0.10.10) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5625a868..6a16a2df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.9) + mutant (0.10.10) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From 4818cc385632b7a4be3e00804e5f224ecb4bc5ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:03:03 +0000 Subject: [PATCH 125/256] Bump mutant from 0.10.10 to 0.10.11 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.10 to 0.10.11. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.10...v0.10.11) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6a16a2df..ccb4a2b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.10) + mutant (0.10.11) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From 7316105614ac2d8718eb417f22914139581e390c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:03:18 +0000 Subject: [PATCH 126/256] Update rubocop requirement from ~> 1.4.2 to ~> 1.5.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.4.2...v1.5.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 10 +++++----- unparser.gemspec | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6a16a2df..3b0eb25f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,16 +82,16 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.4.2) + rubocop (1.5.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8) + regexp_parser (>= 2.0) rexml - rubocop-ast (>= 1.1.1) + rubocop-ast (>= 1.2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.2.0) + rubocop-ast (1.3.0) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.4.2) + rubocop (~> 1.5.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 89050bf8..d8728bfe 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.4.2') + gem.add_development_dependency('rubocop', '~> 1.5.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 2770c705045ed8b743a685d98df4cdd8289bcf22 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Dec 2020 05:02:48 +0000 Subject: [PATCH 127/256] Bump mutant-rspec from 0.10.9 to 0.10.11 Bumps [mutant-rspec](https://github.com/mbj/mutant) from 0.10.9 to 0.10.11. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/commits/v0.10.11) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 236760b9..e7d272c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.1) - mutant-rspec (0.10.9) - mutant (~> 0.10.9) + mutant-rspec (0.10.11) + mutant (~> 0.10.11) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (2.7.2.0) From 58374e5ec3b71978b4cb9e80c1f92ac6ca63c4a0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Dec 2020 12:23:07 +0000 Subject: [PATCH 128/256] Bump mutant from 0.10.11 to 0.10.12 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.11 to 0.10.12. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.11...v0.10.12) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e7d272c9..7e63c435 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.11) + mutant (0.10.12) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From b7f9848884d9d66bff4edd1d3389f1d6e758020a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 7 Dec 2020 13:59:28 +0000 Subject: [PATCH 129/256] Upgrade mutant dependency --- .github/workflows/ci.yml | 4 ++-- Gemfile.lock | 18 +++++++++--------- unparser.gemspec | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 319af54a..36e46c7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: ruby-integration-spec: name: Integration Specs runs-on: ${{ matrix.os }} - timeout-minutes: 5 + timeout-minutes: 10 strategy: fail-fast: false matrix: @@ -45,7 +45,7 @@ jobs: ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} - timeout-minutes: 5 + timeout-minutes: 10 strategy: fail-fast: false matrix: diff --git a/Gemfile.lock b/Gemfile.lock index 7e63c435..5b174d26 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.12) + mutant (0.10.15) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -55,9 +55,9 @@ GEM procto (~> 0.0.2) unparser (~> 0.5.4) variable (~> 0.0.1) - mutant-license (0.1.1.2.2355046999240944981729280251890364410689.1) - mutant-rspec (0.10.11) - mutant (~> 0.10.11) + mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) + mutant-rspec (0.10.15) + mutant (~> 0.10.15) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (2.7.2.0) @@ -82,13 +82,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.5.0) + rubocop (1.5.2) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.0) + regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.2.0) + rubocop-ast (>= 1.2.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 2.0) rubocop-ast (1.3.0) @@ -105,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.9) + mutant (~> 0.10.15) mutant-license! - mutant-rspec (~> 0.10.9) + mutant-rspec (~> 0.10.15) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index d8728bfe..538e8039 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.10.9') - gem.add_development_dependency('mutant-rspec', '~> 0.10.9') + gem.add_development_dependency('mutant', '~> 0.10.15') + gem.add_development_dependency('mutant-rspec', '~> 0.10.15') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 216123e10ada95d464cd77b53cf0355455bb29c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 9 Dec 2020 05:02:35 +0000 Subject: [PATCH 130/256] Bump mutant from 0.10.15 to 0.10.17 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.15 to 0.10.17. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.15...v0.10.17) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5b174d26..8dd65734 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.15) + mutant (0.10.17) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From e23713659476c63bc8bd07881efa402ebc09f717 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Dec 2020 05:04:47 +0000 Subject: [PATCH 131/256] Update rubocop requirement from ~> 1.5.0 to ~> 1.6.0 Updates the requirements on [rubocop](https://github.com/rubocop-hq/rubocop) to permit the latest version. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.5.2...v1.6.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- unparser.gemspec | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5b174d26..697f1e1d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,7 +82,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.5.2) + rubocop (1.6.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -111,7 +111,7 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.5.0) + rubocop (~> 1.6.0) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 538e8039..29fb3466 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.5.0') + gem.add_development_dependency('rubocop', '~> 1.6.0') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 26aa8874fee2c9e228d4c73d58282fdf8c4c9b33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Dec 2020 05:04:31 +0000 Subject: [PATCH 132/256] Bump mutant from 0.10.17 to 0.10.19 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.17 to 0.10.19. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.17...v0.10.19) Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index f3c85b84..bc3eb0fb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.17) + mutant (0.10.19) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) From 0785ecdf8dde3efec61e97765d3de8652012b0c7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 16 Dec 2020 05:05:20 +0000 Subject: [PATCH 133/256] Upgrade dependencies --- Gemfile.lock | 12 ++++++------ unparser.gemspec | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index bc3eb0fb..84f23b3c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.15) - mutant (~> 0.10.15) + mutant-rspec (0.10.19) + mutant (= 0.10.19) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (2.7.2.0) @@ -82,7 +82,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.6.0) + rubocop (1.6.1) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -105,13 +105,13 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.15) + mutant (~> 0.10.19) mutant-license! - mutant-rspec (~> 0.10.15) + mutant-rspec (~> 0.10.19) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.6.0) + rubocop (~> 1.6.1) rubocop-packaging (~> 0.5.0) unparser! diff --git a/unparser.gemspec b/unparser.gemspec index 29fb3466..0e063bba 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,11 +27,11 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.10.15') - gem.add_development_dependency('mutant-rspec', '~> 0.10.15') + gem.add_development_dependency('mutant', '~> 0.10.19') + gem.add_development_dependency('mutant-rspec', '~> 0.10.19') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.6.0') + gem.add_development_dependency('rubocop', '~> 1.6.1') gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') end From 6b856e57d23150d986f015265c11be0863685e47 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Dec 2020 05:04:44 +0000 Subject: [PATCH 134/256] Bump mutant from 0.10.19 to 0.10.20 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.19 to 0.10.20. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.19...v0.10.20) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 84f23b3c..679d89db 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.19) + mutant (0.10.20) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.19) - mutant (= 0.10.19) + mutant-rspec (0.10.20) + mutant (= 0.10.20) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (2.7.2.0) From 34b5d9aa138742ca0efc18d2e2a27ce66247a0d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Dec 2020 05:03:50 +0000 Subject: [PATCH 135/256] Bump mutant from 0.10.20 to 0.10.21 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.20 to 0.10.21. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.20...v0.10.21) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 679d89db..ff570a75 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.20) + mutant (0.10.21) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.4) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.20) - mutant (= 0.10.20) + mutant-rspec (0.10.21) + mutant (= 0.10.21) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (2.7.2.0) From e01c27216cc303222c470d84bba5df2eb89eb009 Mon Sep 17 00:00:00 2001 From: Vladimir Dementyev Date: Thu, 24 Dec 2020 22:22:28 +0000 Subject: [PATCH 136/256] Fix in-pattern without body --- lib/unparser/emitter/in_pattern.rb | 2 ++ test/corpus/literal/pattern.rb | 3 +++ 2 files changed, 5 insertions(+) diff --git a/lib/unparser/emitter/in_pattern.rb b/lib/unparser/emitter/in_pattern.rb index 27a090ea..b1f0d43d 100644 --- a/lib/unparser/emitter/in_pattern.rb +++ b/lib/unparser/emitter/in_pattern.rb @@ -27,6 +27,8 @@ def dispatch ws write('then') emit_body(branch) + else + nl end end end # InPattern diff --git a/test/corpus/literal/pattern.rb b/test/corpus/literal/pattern.rb index 589a8903..ae1ba429 100644 --- a/test/corpus/literal/pattern.rb +++ b/test/corpus/literal/pattern.rb @@ -25,6 +25,9 @@ true in ^x then true +in 1 +in 2 then + true else true end From d967bd56b84af29e8b1555c946b77a3336087f6b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 24 Dec 2020 22:25:48 +0000 Subject: [PATCH 137/256] Change version to 0.5.5 --- Changelog.md | 4 ++++ Gemfile.lock | 2 +- unparser.gemspec | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3890e2fe..597346ca 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.5 2020-12-24 + +* Fix in-pattern without body [#231](https://github.com/mbj/unparser/pull/231) + # v0.5.4 2020-11-04 * Fix forced ternary on control keyword [#191](https://github.com/mbj/unparser/pull/191) diff --git a/Gemfile.lock b/Gemfile.lock index ff570a75..c6d607d8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.4) + unparser (0.5.5) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) diff --git a/unparser.gemspec b/unparser.gemspec index 0e063bba..4d4780c2 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.4' + gem.version = '0.5.5' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From d2b80c70fb49f6b590305917178ef34694cac538 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 24 Dec 2020 23:02:02 +0000 Subject: [PATCH 138/256] Change to depend on mutant ~> 0.10.21 --- Gemfile.lock | 6 +++--- unparser.gemspec | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c6d607d8..99c2af6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -64,7 +64,7 @@ GEM ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) - regexp_parser (2.0.0) + regexp_parser (2.0.1) rexml (3.2.4) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -105,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.19) + mutant (~> 0.10.21) mutant-license! - mutant-rspec (~> 0.10.19) + mutant-rspec (~> 0.10.21) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index 4d4780c2..57f6832e 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,8 +27,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 2.6.5') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.10.19') - gem.add_development_dependency('mutant-rspec', '~> 0.10.19') + gem.add_development_dependency('mutant', '~> 0.10.21') + gem.add_development_dependency('mutant-rspec', '~> 0.10.21') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From a0055ce6789edb61c63cc8e21b8732aef9ca753d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 25 Dec 2020 20:49:51 +0000 Subject: [PATCH 139/256] Add 3.0 syntax support [fix #168] --- .github/workflows/ci.yml | 6 +-- Changelog.md | 4 ++ Gemfile | 3 ++ Gemfile.lock | 56 +++++++++++++++------------ lib/unparser.rb | 14 ++----- lib/unparser/emitter/hash.rb | 32 +-------------- lib/unparser/emitter/kwargs.rb | 13 +++++++ lib/unparser/emitter/match_pattern.rb | 22 +++++++++++ lib/unparser/emitter/pair.rb | 33 ++++++++++++++++ lib/unparser/writer/dynamic_string.rb | 12 +----- lib/unparser/writer/send.rb | 15 +------ spec/unit/unparser_spec.rb | 17 -------- test/corpus/literal/dstr.rb | 22 +++++------ test/corpus/literal/literal.rb | 7 ++-- test/corpus/semantic/block.rb | 4 +- unparser.gemspec | 4 +- 16 files changed, 133 insertions(+), 131 deletions(-) create mode 100644 lib/unparser/emitter/kwargs.rb create mode 100644 lib/unparser/emitter/match_pattern.rb create mode 100644 lib/unparser/emitter/pair.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36e46c7b..718c4d33 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -49,7 +49,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7] + ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/Changelog.md b/Changelog.md index 597346ca..e425f869 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.6 2020-12-25 + +* Add full Ruby 3.0 Syntax support [#233](https://github.com/mbj/unparser/pull/233) + # v0.5.5 2020-12-24 * Fix in-pattern without body [#231](https://github.com/mbj/unparser/pull/231) diff --git a/Gemfile b/Gemfile index 1419592e..120ce354 100644 --- a/Gemfile +++ b/Gemfile @@ -7,3 +7,6 @@ gemspec source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end + +gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.0' +gem 'mutant-rspec', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 99c2af6d..5f542e9f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,31 @@ +GIT + remote: https://github.com/mbj/mutant + revision: 656eb24431eae29975afa4b62b22a196b549963a + branch: add/ruby-3.0 + specs: + mutant (0.10.22) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.1) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + mprelude (~> 0.1.0) + parser (~> 3.0.0) + procto (~> 0.0.2) + unparser (~> 0.5.5) + variable (~> 0.0.1) + mutant-rspec (0.10.22) + mutant (= 0.10.22) + rspec-core (>= 3.8.0, < 4.0.0) + PATH remote: . specs: - unparser (0.5.5) + unparser (0.5.6) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -9,7 +33,7 @@ PATH diff-lcs (~> 1.3) equalizer (~> 0.0.9) mprelude (~> 0.1.0) - parser (>= 2.6.5) + parser (>= 3.0.0) procto (~> 0.0.2) GEM @@ -40,31 +64,13 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.21) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.1) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - mprelude (~> 0.1.0) - parser (~> 2.7.1) - procto (~> 0.0.2) - unparser (~> 0.5.4) - variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.21) - mutant (= 0.10.21) - rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) - parser (2.7.2.0) + parser (3.0.0.0) ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) - regexp_parser (2.0.1) + regexp_parser (2.0.2) rexml (3.2.4) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -105,9 +111,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.21) + mutant! mutant-license! - mutant-rspec (~> 0.10.21) + mutant-rspec! rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) @@ -116,4 +122,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.1.4 + 2.2.3 diff --git a/lib/unparser.rb b/lib/unparser.rb index 099a1211..96f1a59f 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -117,21 +117,10 @@ def self.parser Parser::CurrentRuby.new(Builder.new).tap do |parser| parser.diagnostics.tap do |diagnostics| diagnostics.all_errors_are_fatal = true - diagnostics.consumer = method(:consume_diagnostic) end end end - # Consume diagnostic - # - # @param [Parser::Diagnostic] diagnostic - # - # @return [undefined] - def self.consume_diagnostic(diagnostic) - Kernel.warn(diagnostic.render) - end - private_class_method :consume_diagnostic - # Construct a parser buffer from string # # @param [String] source @@ -209,6 +198,9 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/variable' require 'unparser/emitter/xstr' require 'unparser/emitter/yield' +require 'unparser/emitter/kwargs' +require 'unparser/emitter/pair' +require 'unparser/emitter/match_pattern' require 'unparser/writer' require 'unparser/writer/binary' require 'unparser/writer/dynamic_string' diff --git a/lib/unparser/emitter/hash.rb b/lib/unparser/emitter/hash.rb index 8d05dca0..f70e2b7a 100644 --- a/lib/unparser/emitter/hash.rb +++ b/lib/unparser/emitter/hash.rb @@ -4,10 +4,6 @@ module Unparser class Emitter # Emitter for Hash literals class Hash < self - BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze - - private_constant(*constants(false)) - handle :hash def emit_last_argument_hash @@ -41,34 +37,8 @@ def emit_heredoc_reminder_member(node) end def emit_hash_body - delimited(children, &method(:emit_hash_member)) - end - - def emit_hash_member(node) - if n_kwsplat?(node) - visit(node) - else - emit_pair(node) - end + delimited(children) end - - def emit_pair(pair) - key, value = *pair.children - - if colon?(key) - write(key.children.first.to_s, ': ') - else - visit(key) - write(' => ') - end - - visit(value) - end - - def colon?(key) - n_sym?(key) && BAREWORD.match?(key.children.first) - end - end # Hash end # Emitter end # Unparser diff --git a/lib/unparser/emitter/kwargs.rb b/lib/unparser/emitter/kwargs.rb new file mode 100644 index 00000000..bffffc93 --- /dev/null +++ b/lib/unparser/emitter/kwargs.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + class Kwargs < self + handle :kwargs + + def dispatch + delimited(children) + end + end # Kwargs + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/match_pattern.rb b/lib/unparser/emitter/match_pattern.rb new file mode 100644 index 00000000..27c04ed2 --- /dev/null +++ b/lib/unparser/emitter/match_pattern.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class MatchPattern < self + + handle :match_pattern + handle :match_pattern_p + + children :target, :pattern + + private + + def dispatch + visit(target) + write(' in ') + visit(pattern) + end + end # InPattern + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/pair.rb b/lib/unparser/emitter/pair.rb new file mode 100644 index 00000000..04c87d8e --- /dev/null +++ b/lib/unparser/emitter/pair.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for key value pairs in hash literals or kwargs + class Pair < self + BAREWORD = /\A[A-Za-z_][A-Za-z_0-9]*[?!]?\z/.freeze + + private_constant(*constants(false)) + + handle :pair + + children :key, :value + + private + + def dispatch + if colon?(key) + write(key.children.first.to_s, ': ') + else + visit(key) + write(' => ') + end + + visit(value) + end + + def colon?(key) + n_sym?(key) && BAREWORD.match?(key.children.first) + end + end + end +end diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index 37357803..a7760a11 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -42,7 +42,7 @@ def dispatch private def heredoc_header - need_squiggly? ? '<<~HEREDOC' : '<<-HEREDOC' + '<<-HEREDOC' end def heredoc? @@ -55,11 +55,7 @@ def emit_heredoc_header def emit_heredoc_body nl - if need_squiggly? - emit_squiggly_heredoc_body - else - emit_normal_heredoc_body - end + emit_normal_heredoc_body end def emit_heredoc_footer @@ -119,10 +115,6 @@ def nl_last_child? n_str?(last) && last.children.first[-1].eql?("\n") end - def need_squiggly? - children.any?(s(:str, '')) - end - def emit_squiggly_heredoc_body buffer.indent children.each do |child| diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index 2209f111..d03fb63a 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -75,20 +75,7 @@ def arguments end def emit_normal_arguments - parentheses do - arguments.each_with_index do |node, index| - write(', ') unless index.zero? - emit_argument(node, index.succ.equal?(arguments.length)) - end - end - end - - def emit_argument(argument, last) - if n_hash?(argument) && last - writer_with(Emitter::Hash, argument).emit_last_argument_hash - else - visit(argument) - end + parentheses { delimited(arguments) } end def emit_heredoc_reminder(argument) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index c2c00806..0eac474d 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -46,23 +46,6 @@ def apply .to raise_error(Parser::SyntaxError) end end - - context 'warnings' do - before do - allow(Kernel).to receive(:warn) - end - - it 'returns a parser that warns on diagnostics' do - expect { apply.parse(invalid_source_buffer) } - .to raise_error(Parser::SyntaxError) - - expect(Kernel).to have_received(:warn) - .with([ - "(string):1:4: error: unexpected token $end", - "(string):1: a +", "(string):1: " - ]) - end - end end describe '.parse' do diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index 2c1ed53c..43bd1546 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -1,21 +1,19 @@ if true - <<~HEREDOC - #{}a - HEREDOC + "#{}a" end if true - <<~HEREDOC - a - #{}a - b + <<-HEREDOC +a +#{}a +b HEREDOC x end -<<~HEREDOC - \#{}\#{} - #{} - #{} - #{} +<<-HEREDOC +\#{}\#{} +#{} +#{} +#{} HEREDOC <<-HEREDOC rescue nil #{} diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 2b59c99c..2fc7cd1d 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -86,7 +86,6 @@ } :"a\\ b" -<<~`HEREDOC` - x - #{foo} -HEREDOC +` x +#{foo} +#` diff --git a/test/corpus/semantic/block.rb b/test/corpus/semantic/block.rb index 1ffc65c8..58916900 100644 --- a/test/corpus/semantic/block.rb +++ b/test/corpus/semantic/block.rb @@ -10,10 +10,10 @@ nil end -foo do |_1| +foo do |a| end -foo(<<-DOC) do |_1| +foo(<<-DOC) do |a| b DOC a diff --git a/unparser.gemspec b/unparser.gemspec index 57f6832e..010bc015 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.5' + gem.version = '0.5.6' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -24,7 +24,7 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('equalizer', '~> 0.0.9') gem.add_dependency('mprelude', '~> 0.1.0') - gem.add_dependency('parser', '>= 2.6.5') + gem.add_dependency('parser', '>= 3.0.0') gem.add_dependency('procto', '~> 0.0.2') gem.add_development_dependency('mutant', '~> 0.10.21') From 551ea551e37ee65797a185bc036444cb9df477e2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 26 Dec 2020 01:10:29 +0000 Subject: [PATCH 140/256] Upgrade to rubocop-1.7.0 --- Gemfile.lock | 10 +++++----- unparser.gemspec | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5f542e9f..dfab6536 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/mbj/mutant - revision: 656eb24431eae29975afa4b62b22a196b549963a + revision: 1ecdf3344f65f21b757e38fcc8f51d10f4a878e6 branch: add/ruby-3.0 specs: mutant (0.10.22) @@ -16,7 +16,7 @@ GIT mprelude (~> 0.1.0) parser (~> 3.0.0) procto (~> 0.0.2) - unparser (~> 0.5.5) + unparser (~> 0.5.6) variable (~> 0.0.1) mutant-rspec (0.10.22) mutant (= 0.10.22) @@ -88,7 +88,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.0) - rubocop (1.6.1) + rubocop (1.7.0) parallel (~> 1.10) parser (>= 2.7.1.5) rainbow (>= 2.2.2, < 4.0) @@ -117,8 +117,8 @@ DEPENDENCIES rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) - rubocop (~> 1.6.1) - rubocop-packaging (~> 0.5.0) + rubocop (~> 1.7) + rubocop-packaging (~> 0.5) unparser! BUNDLED WITH diff --git a/unparser.gemspec b/unparser.gemspec index 010bc015..1ccdb893 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -32,6 +32,6 @@ Gem::Specification.new do |gem| gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') - gem.add_development_dependency('rubocop', '~> 1.6.1') - gem.add_development_dependency('rubocop-packaging', '~> 0.5.0') + gem.add_development_dependency('rubocop', '~> 1.7') + gem.add_development_dependency('rubocop-packaging', '~> 0.5') end From fc5c03356ce9713f0f59b0f52b8a4e98c7a40900 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 26 Dec 2020 02:15:19 +0000 Subject: [PATCH 141/256] Add gem metadata --- Gemfile.lock | 2 +- unparser.gemspec | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index dfab6536..a03fe824 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/mbj/mutant - revision: 1ecdf3344f65f21b757e38fcc8f51d10f4a878e6 + revision: a4fad01945bceb3e9d166e13d644fcd4081e70b7 branch: add/ruby-3.0 specs: mutant (0.10.22) diff --git a/unparser.gemspec b/unparser.gemspec index 1ccdb893..07d0446e 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -10,6 +10,12 @@ Gem::Specification.new do |gem| gem.homepage = 'http://github.com/mbj/unparser' gem.license = 'MIT' + gem.metadata = { + 'bug_tracker_uri' => 'https://github.com/mbj/unparser/issues', + 'changelog_uri' => 'https://github.com/mbj/unparser/blob/master/Changelog.md', + 'funding_uri' => 'https://github.com/sponsors/mbj' + } + gem.files = Dir.glob('lib/**/*') gem.require_paths = %w[lib] gem.extra_rdoc_files = %w[README.md] From aea8c1e4cfac939a58f3ff1584e3418f7aa3acd8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 26 Dec 2020 02:48:15 +0000 Subject: [PATCH 142/256] Upgrade to mutant-0.10.22 --- Gemfile | 3 --- Gemfile.lock | 46 ++++++++++++++++++++-------------------------- unparser.gemspec | 4 ++-- 3 files changed, 22 insertions(+), 31 deletions(-) diff --git a/Gemfile b/Gemfile index 120ce354..1419592e 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,3 @@ gemspec source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end - -gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.0' -gem 'mutant-rspec', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.0' diff --git a/Gemfile.lock b/Gemfile.lock index a03fe824..95564afd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,27 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant - revision: a4fad01945bceb3e9d166e13d644fcd4081e70b7 - branch: add/ruby-3.0 - specs: - mutant (0.10.22) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.1) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - mprelude (~> 0.1.0) - parser (~> 3.0.0) - procto (~> 0.0.2) - unparser (~> 0.5.6) - variable (~> 0.0.1) - mutant-rspec (0.10.22) - mutant (= 0.10.22) - rspec-core (>= 3.8.0, < 4.0.0) - PATH remote: . specs: @@ -64,7 +40,25 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) + mutant (0.10.22) + abstract_type (~> 0.0.7) + adamantium (~> 0.2.0) + anima (~> 0.3.1) + ast (~> 2.2) + concord (~> 0.1.5) + diff-lcs (~> 1.3) + equalizer (~> 0.0.9) + ice_nine (~> 0.11.1) + memoizable (~> 0.4.2) + mprelude (~> 0.1.0) + parser (~> 3.0.0) + procto (~> 0.0.2) + unparser (~> 0.5.6) + variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) + mutant-rspec (0.10.22) + mutant (= 0.10.22) + rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.0.0) ast (~> 2.4.1) @@ -111,9 +105,9 @@ PLATFORMS ruby DEPENDENCIES - mutant! + mutant (~> 0.10.22) mutant-license! - mutant-rspec! + mutant-rspec (~> 0.10.22) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index 07d0446e..63e98522 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -33,8 +33,8 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 3.0.0') gem.add_dependency('procto', '~> 0.0.2') - gem.add_development_dependency('mutant', '~> 0.10.21') - gem.add_development_dependency('mutant-rspec', '~> 0.10.21') + gem.add_development_dependency('mutant', '~> 0.10.22') + gem.add_development_dependency('mutant-rspec', '~> 0.10.22') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 945521e668c70e1b082af69821b419c0bad64d81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Dec 2020 05:14:09 +0000 Subject: [PATCH 143/256] Bump rspec-core from 3.10.0 to 3.10.1 Bumps [rspec-core](https://github.com/rspec/rspec-core) from 3.10.0 to 3.10.1. - [Release notes](https://github.com/rspec/rspec-core/releases) - [Changelog](https://github.com/rspec/rspec-core/blob/main/Changelog.md) - [Commits](https://github.com/rspec/rspec-core/compare/v3.10.0...v3.10.1) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 95564afd..1040dd5c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,7 +70,7 @@ GEM rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) rspec-mocks (~> 3.10.0) - rspec-core (3.10.0) + rspec-core (3.10.1) rspec-support (~> 3.10.0) rspec-expectations (3.10.0) diff-lcs (>= 1.2.0, < 2.0) @@ -81,7 +81,7 @@ GEM rspec-mocks (3.10.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-support (3.10.0) + rspec-support (3.10.1) rubocop (1.7.0) parallel (~> 1.10) parser (>= 2.7.1.5) From 1af1e51a2d06355e72502a38682dcf2818ab7d2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Dec 2020 05:04:31 +0000 Subject: [PATCH 144/256] Bump mutant from 0.10.22 to 0.10.23 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.22 to 0.10.23. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.22...v0.10.23) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1040dd5c..90352e89 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.22) + mutant (0.10.23) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -56,8 +56,8 @@ GEM unparser (~> 0.5.6) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.22) - mutant (= 0.10.22) + mutant-rspec (0.10.23) + mutant (= 0.10.23) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.0.0) From 0c6ce58dfcdba9852490107f1cd2456c6d5559cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jan 2021 05:08:17 +0000 Subject: [PATCH 145/256] Bump mutant-rspec from 0.10.23 to 0.10.25 Bumps [mutant-rspec](https://github.com/mbj/mutant) from 0.10.23 to 0.10.25. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.23...v0.10.25) Signed-off-by: dependabot[bot] --- Gemfile.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 90352e89..9858509a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,7 +40,7 @@ GEM equalizer (~> 0.0.9) ice_nine (~> 0.11.1) procto (~> 0.0.2) - mutant (0.10.23) + mutant (0.10.25) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) @@ -53,18 +53,19 @@ GEM mprelude (~> 0.1.0) parser (~> 3.0.0) procto (~> 0.0.2) + regexp_parser (~> 2.0, >= 2.0.3) unparser (~> 0.5.6) variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) - mutant-rspec (0.10.23) - mutant (= 0.10.23) + mutant-rspec (0.10.25) + mutant (= 0.10.25) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.0.0) ast (~> 2.4.1) procto (0.0.3) rainbow (3.0.0) - regexp_parser (2.0.2) + regexp_parser (2.0.3) rexml (3.2.4) rspec (3.10.0) rspec-core (~> 3.10.0) From 288317b6ca11cd5ddd7308d4aa6187ce5cacd901 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 8 Jan 2021 05:02:37 +0000 Subject: [PATCH 146/256] Bump rubocop from 1.7.0 to 1.8.0 Bumps [rubocop](https://github.com/rubocop-hq/rubocop) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/rubocop-hq/rubocop/releases) - [Changelog](https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop-hq/rubocop/compare/v1.7.0...v1.8.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9858509a..c7d9cb60 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,22 +83,22 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.1) - rubocop (1.7.0) + rubocop (1.8.0) parallel (~> 1.10) - parser (>= 2.7.1.5) + parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml rubocop-ast (>= 1.2.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.3.0) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.4.0) parser (>= 2.7.1.5) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) - ruby-progressbar (1.10.1) + ruby-progressbar (1.11.0) thread_safe (0.3.6) - unicode-display_width (1.7.0) + unicode-display_width (2.0.0) variable (0.0.1) equalizer (~> 0.0.11) From db7bc05cbe322790c0d72983eb65ff1b80c100e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 05:07:36 +0000 Subject: [PATCH 147/256] Bump mutant-license Bumps mutant-license from 0.1.1.2.2355046999240944981729280251890364410689.2 to 0.1.1.2.2355046999240944981729280251890364410689.4. Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index c7d9cb60..72be802e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -56,7 +56,7 @@ GEM regexp_parser (~> 2.0, >= 2.0.3) unparser (~> 0.5.6) variable (~> 0.0.1) - mutant-license (0.1.1.2.2355046999240944981729280251890364410689.2) + mutant-license (0.1.1.2.2355046999240944981729280251890364410689.4) mutant-rspec (0.10.25) mutant (= 0.10.25) rspec-core (>= 3.8.0, < 4.0.0) From 60410b31a9c067dc51ec8d6c14c48bd11b3bdb48 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 11 Jan 2021 14:57:45 +0000 Subject: [PATCH 148/256] Upgrade dependencies --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 72be802e..2594200c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -73,17 +73,17 @@ GEM rspec-mocks (~> 3.10.0) rspec-core (3.10.1) rspec-support (~> 3.10.0) - rspec-expectations (3.10.0) + rspec-expectations (3.10.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.10.0) + rspec-mocks (3.10.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.1) - rubocop (1.8.0) + rubocop (1.8.1) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) From a49eb18a451ced51447868c8c9f4e016b3d73bd8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 11 Jan 2021 17:01:44 +0000 Subject: [PATCH 149/256] Fix heredoc dispatch on returns [Fix #243] --- Changelog.md | 4 ++++ Gemfile.lock | 2 +- lib/unparser/emitter/flow_modifier.rb | 6 ++++++ test/corpus/literal/dstr.rb | 5 +++++ unparser.gemspec | 2 +- 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index e425f869..e5a5d2ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.5.7 2020-12-25 + +* Fix heredocs in return arguments [#244](https://github.com/mbj/unparser/pull/244) + # v0.5.6 2020-12-25 * Add full Ruby 3.0 Syntax support [#233](https://github.com/mbj/unparser/pull/233) diff --git a/Gemfile.lock b/Gemfile.lock index 2594200c..d0562e10 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.5.6) + unparser (0.5.7) abstract_type (~> 0.0.7) adamantium (~> 0.2.0) anima (~> 0.3.1) diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index c1835972..ef58c976 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -14,6 +14,12 @@ class FlowModifier < self handle(*MAP.keys) + def emit_heredoc_reminders + children.each do |node| + emitter(node).emit_heredoc_reminders + end + end + private def dispatch diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index 43bd1546..3df85072 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -23,3 +23,8 @@ "a#$a" "a#@a" "a#@@a" +if true + return <<-HEREDOC + #{42} + HEREDOC +end diff --git a/unparser.gemspec b/unparser.gemspec index 63e98522..b98fe8da 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.6' + gem.version = '0.5.7' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From ed132f179f4cdd0a78a1e01d4cd14f489f0bb0bd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 12 Jan 2021 02:54:09 +0000 Subject: [PATCH 150/256] Add Unparser::InvalidNodeError --- Changelog.md | 5 +++++ lib/unparser.rb | 15 ++++++++++++++- lib/unparser/writer/send.rb | 6 +++++- spec/spec_helper.rb | 1 + spec/unit/unparser/invalid_node_error_spec.rb | 12 ++++++++++++ spec/unit/unparser_spec.rb | 12 ++++++++++++ 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 spec/unit/unparser/invalid_node_error_spec.rb diff --git a/Changelog.md b/Changelog.md index e5a5d2ae..2edc02ee 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# v0.6.0 unreleased + +* Change to raise Unparser::InvalidNode error in some cases when unparsing invalid AST. +* Change `Unparser.unparse` into an official public API. + # v0.5.7 2020-12-25 * Fix heredocs in return arguments [#244](https://github.com/mbj/unparser/pull/244) diff --git a/lib/unparser.rb b/lib/unparser.rb index 96f1a59f..f2d2e4e8 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -29,6 +29,17 @@ def initialize private_constant(*constants(false)) + # Error raised when unparser encounters an invalid AST + class InvalidNodeError < RuntimeError + attr_reader :node + + def initialize(message, node) + super(message) + @node = node + freeze + end + end + # Unparse an AST (and, optionally, comments) into a string # # @param [Parser::AST::Node, nil] node @@ -36,8 +47,10 @@ def initialize # # @return [String] # - # @api private + # @raise InvalidNodeError + # if the node passed is invalid # + # @api public def self.unparse(node, comment_array = []) return '' if node.nil? diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index d03fb63a..d444d7cf 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -91,7 +91,11 @@ def local_variable_clash? end def parses_as_constant? - n_const?(Unparser.parse(selector.to_s)) + test = Unparser.parse_either(selector.to_s).from_right do + fail InvalidNodeError.new("Invalid selector for send node: #{selector.inspect}", node) + end + + n_const?(test) end def details diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9208dfe2..96a4933c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ require 'anima' require 'mutant' require 'pathname' +require 'rspec/its' require 'timeout' require 'unparser' require 'yaml' diff --git a/spec/unit/unparser/invalid_node_error_spec.rb b/spec/unit/unparser/invalid_node_error_spec.rb new file mode 100644 index 00000000..e723552c --- /dev/null +++ b/spec/unit/unparser/invalid_node_error_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +RSpec.describe Unparser::InvalidNodeError do + let(:node) { s(:some_node) } + let(:message) { 'message'.dup } + + subject { described_class.new(message, node) } + + its(:node) { should be(node) } + its(:message) { should be(message) } + its(:frozen?) { should be(true) } +end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 0eac474d..9174778e 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -218,6 +218,18 @@ def self.assert_source(string) assert_source '' end + context 'invalid send selector' do + let(:node) { s(:send, nil, :module) } + + it 'raises InvalidNode error' do + expect { Unparser.unparse(node) }.to raise_error do |error| + expect(error).to be_a(Unparser::InvalidNodeError) + expect(error.message).to eql('Invalid selector for send node: :module') + expect(error.node).to be(node) + end + end + end + %w(next return break).each do |keyword| context keyword do assert_source "#{keyword} 1" From 52b48b1c347e18234110d814d3d7113d8f8eca14 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 16 Jan 2021 03:22:36 +0000 Subject: [PATCH 151/256] Remove redundant bundle install CI step --- .github/workflows/ci.yml | 6 +++--- .rubocop.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 718c4d33..f8ed5801 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,8 @@ jobs: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: + bundler-cache: true ruby-version: ${{ matrix.ruby }} - - run: bundle install - run: bundle exec rspec spec/unit ruby-integration-spec: name: Integration Specs @@ -39,8 +39,8 @@ jobs: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: + bundler-cache: true ruby-version: ${{ matrix.ruby }} - - run: bundle install - run: bundle exec rspec spec/integration ruby-rubocop: name: Rubocop @@ -55,6 +55,6 @@ jobs: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: + bundler-cache: true ruby-version: ${{ matrix.ruby }} - - run: bundle install - run: bundle exec rubocop diff --git a/.rubocop.yml b/.rubocop.yml index 064b4510..0536f522 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,8 +6,8 @@ AllCops: - 'Gemfile' - 'Gemfile.triage' Exclude: - - lib/unparser/precedence/data.rb - - tmp + - tmp/**/* + - vendor/**/* NewCops: enable # Avoid parameter lists longer than five parameters. From 9860c0e2858634ea3c6d7ca06c853f8a619350a0 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 11 Jan 2021 00:47:55 +0000 Subject: [PATCH 152/256] Change to reduced vendored uncommon dependencies * Drop `ice_nine` usage entirely. * Drop `procto` usage. * Drop `variable` usage. * Drop all APIs unparser does not use. * Drop freezer configuration from adamantium (its now always flat). * Fix mutation coverage Sources: * [abstract_type](https://github.com/mbj/concord) -> Unparser::AbstractType * [adamantium](https://github.com/dkubb/adamantium) -> Unparser::Adamantium * [anima](https://github.com/mbj/concord) -> Unparser::Anima * [concord](https://github.com/mbj/concord) -> Unparser::Concord * [memoizable](https://github.com/dkubb/memoizable) -> Unparser::Adamantium * [mprelude](https://github.com/dkubb/memoizable) -> Unparser::Either --- Changelog.md | 3 + Gemfile | 2 + Gemfile.lock | 75 +--- README.md | 12 + config/mutant.yml | 35 ++ lib/unparser.rb | 29 +- lib/unparser/abstract_type.rb | 121 ++++++ lib/unparser/adamantium.rb | 150 +++++++ lib/unparser/adamantium/method_builder.rb | 110 +++++ lib/unparser/anima.rb | 184 +++++++++ lib/unparser/anima/attribute.rb | 59 +++ lib/unparser/anima/error.rb | 23 ++ lib/unparser/ast.rb | 3 +- lib/unparser/color.rb | 2 +- lib/unparser/concord.rb | 114 +++++ lib/unparser/diff.rb | 2 +- lib/unparser/either.rb | 153 +++++++ lib/unparser/emitter.rb | 2 +- lib/unparser/emitter/index.rb | 2 +- lib/unparser/emitter/op_assign.rb | 4 +- lib/unparser/emitter/primitive.rb | 4 +- lib/unparser/emitter/simple.rb | 4 +- lib/unparser/equalizer.rb | 98 +++++ lib/unparser/node_details.rb | 2 +- lib/unparser/validation.rb | 6 +- lib/unparser/writer/binary.rb | 2 +- lib/unparser/writer/dynamic_string.rb | 22 +- lib/unparser/writer/rescue.rb | 2 +- lib/unparser/writer/send.rb | 2 +- mutant.sh | 32 -- scripts/devloop.sh | 4 +- spec/integration/unparser/corpus_spec.rb | 5 +- spec/spec_helper.rb | 11 +- spec/unit/unparser/abstract_type_spec.rb | 174 ++++++++ spec/unit/unparser/adamantium/memory_spec.rb | 131 ++++++ spec/unit/unparser/adamantium_spec.rb | 414 +++++++++++++++++++ spec/unit/unparser/anima/attribute_spec.rb | 62 +++ spec/unit/unparser/anima/error_spec.rb | 16 + spec/unit/unparser/anima_spec.rb | 210 ++++++++++ spec/unit/unparser/concord_spec.rb | 143 +++++++ spec/unit/unparser/either_spec.rb | 378 +++++++++++++++++ spec/unit/unparser/equalizer_spec.rb | 165 ++++++++ spec/unit/unparser/validation_spec.rb | 8 - spec/unit/unparser_spec.rb | 12 +- test/run-parser-tests.rb | 6 +- unparser.gemspec | 17 +- 46 files changed, 2849 insertions(+), 166 deletions(-) create mode 100644 lib/unparser/abstract_type.rb create mode 100644 lib/unparser/adamantium.rb create mode 100644 lib/unparser/adamantium/method_builder.rb create mode 100644 lib/unparser/anima.rb create mode 100644 lib/unparser/anima/attribute.rb create mode 100644 lib/unparser/anima/error.rb create mode 100644 lib/unparser/concord.rb create mode 100644 lib/unparser/either.rb create mode 100644 lib/unparser/equalizer.rb delete mode 100755 mutant.sh create mode 100644 spec/unit/unparser/abstract_type_spec.rb create mode 100644 spec/unit/unparser/adamantium/memory_spec.rb create mode 100644 spec/unit/unparser/adamantium_spec.rb create mode 100644 spec/unit/unparser/anima/attribute_spec.rb create mode 100644 spec/unit/unparser/anima/error_spec.rb create mode 100644 spec/unit/unparser/anima_spec.rb create mode 100644 spec/unit/unparser/concord_spec.rb create mode 100644 spec/unit/unparser/either_spec.rb create mode 100644 spec/unit/unparser/equalizer_spec.rb diff --git a/Changelog.md b/Changelog.md index 2edc02ee..025fca82 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,10 @@ # v0.6.0 unreleased +[245](https://github.com/mbj/unparser/pull/245) + * Change to raise Unparser::InvalidNode error in some cases when unparsing invalid AST. * Change `Unparser.unparse` into an official public API. +* Remove lots of dependencies. # v0.5.7 2020-12-25 diff --git a/Gemfile b/Gemfile index 1419592e..0c4ba0c5 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ source 'https://rubygems.org' gemspec +gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'upgrade/unparser' + source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index d0562e10..b0d66a94 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,69 +1,35 @@ +GIT + remote: https://github.com/mbj/mutant + revision: d151dd5fdee91695fc0fde7591d498e6e27c7355 + branch: upgrade/unparser + specs: + mutant (0.10.25) + ast (~> 2.2) + diff-lcs (~> 1.3) + parser (~> 3.0.0) + regexp_parser (~> 2.0, >= 2.0.3) + unparser (~> 0.6.0) + mutant-rspec (0.10.25) + mutant (= 0.10.25) + rspec-core (>= 3.8.0, < 4.0.0) + PATH remote: . specs: - unparser (0.5.7) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.1) - concord (~> 0.1.5) + unparser (0.6.0) diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - mprelude (~> 0.1.0) parser (>= 3.0.0) - procto (~> 0.0.2) GEM remote: https://rubygems.org/ remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ specs: - abstract_type (0.0.7) - adamantium (0.2.0) - ice_nine (~> 0.11.0) - memoizable (~> 0.4.0) - anima (0.3.2) - abstract_type (~> 0.0.7) - adamantium (~> 0.2) - equalizer (~> 0.0.11) ast (2.4.1) - concord (0.1.6) - adamantium (~> 0.2.0) - equalizer (~> 0.0.9) diff-lcs (1.4.4) - equalizer (0.0.11) - ice_nine (0.11.2) - memoizable (0.4.2) - thread_safe (~> 0.3, >= 0.3.1) - mprelude (0.1.0) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - concord (~> 0.1.5) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - procto (~> 0.0.2) - mutant (0.10.25) - abstract_type (~> 0.0.7) - adamantium (~> 0.2.0) - anima (~> 0.3.1) - ast (~> 2.2) - concord (~> 0.1.5) - diff-lcs (~> 1.3) - equalizer (~> 0.0.9) - ice_nine (~> 0.11.1) - memoizable (~> 0.4.2) - mprelude (~> 0.1.0) - parser (~> 3.0.0) - procto (~> 0.0.2) - regexp_parser (~> 2.0, >= 2.0.3) - unparser (~> 0.5.6) - variable (~> 0.0.1) mutant-license (0.1.1.2.2355046999240944981729280251890364410689.4) - mutant-rspec (0.10.25) - mutant (= 0.10.25) - rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.0.0) ast (~> 2.4.1) - procto (0.0.3) rainbow (3.0.0) regexp_parser (2.0.3) rexml (3.2.4) @@ -97,18 +63,15 @@ GEM rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - thread_safe (0.3.6) unicode-display_width (2.0.0) - variable (0.0.1) - equalizer (~> 0.0.11) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.22) + mutant! mutant-license! - mutant-rspec (~> 0.10.22) + mutant-rspec (~> 0.10.25) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) @@ -117,4 +80,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.2.3 + 2.2.5 diff --git a/README.md b/README.md index d608836d..0550c0c0 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,18 @@ People Various people contributed to this repository. See [Contributors](https://github.com/mbj/unparser/graphs/contributors). +Included Libraries +------------------ + +For dependency reduction reasons unparser ships vendored (and reduced) versions of: + +* [abstract_type](https://github.com/mbj/concord) -> Unparser::AbstractType +* [adamantium](https://github.com/dkubb/adamantium) -> Unparser::Adamantium +* [anima](https://github.com/mbj/concord) -> Unparser::Anima +* [concord](https://github.com/mbj/concord) -> Unparser::Concord +* [memoizable](https://github.com/dkubb/memoizable) -> Unparser::Adamantium +* [mprelude](https://github.com/dkubb/memoizable) -> Unparser::Either + Contributing ------------- diff --git a/config/mutant.yml b/config/mutant.yml index 8c4cfe43..dc5750c5 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -4,3 +4,38 @@ includes: integration: rspec requires: - unparser +matcher: + subjects: + - 'Unparser*' + ignore: + # API changed between ruby versions and each of them + # have a different minimal form + - 'Unparser::Concord#define_readers' + - 'Unparser::Builder#initialize' + - 'Unparser::CLI*' + - 'Unparser::Emitter#emit_comments' + - 'Unparser::Emitter#emit_comments_before' + - 'Unparser::Emitter#emit_eol_comments' + - 'Unparser::Emitter.handle' + - 'Unparser::Emitter::Args#normal_arguments' + - 'Unparser::Emitter::Args#shadowargs' + - 'Unparser::Emitter::Array#emitters' + - 'Unparser::Emitter::Binary#writer' + - 'Unparser::Emitter::Block#target_writer' + - 'Unparser::Emitter::Class#dispatch' + - 'Unparser::Emitter::Class#local_variable_scope' + - 'Unparser::Emitter::Def#local_variable_scope' + - 'Unparser::Emitter::HashPattern#write_symbol_body' + - 'Unparser::Emitter::LocalVariableRoot*' + - 'Unparser::Emitter::LocalVariableRoot.included' + - 'Unparser::Emitter::Module#local_variable_scope' + - 'Unparser::Emitter::Root#local_variable_scope' + - 'Unparser::Emitter::Send#writer' + - 'Unparser::NodeDetails.included' + - 'Unparser::Validation.from_string' + - 'Unparser::Validation::Literal*' + - 'Unparser::Writer.included' + - 'Unparser::Writer::Binary#left_emitter' + - 'Unparser::Writer::Binary#right_emitter' + - 'Unparser::Writer::Send#details' + - 'Unparser::Writer::Send#effective_writer' diff --git a/lib/unparser.rb b/lib/unparser.rb index f2d2e4e8..ba22cef6 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -1,16 +1,22 @@ # frozen_string_literal: true -require 'abstract_type' -require 'anima' -require 'concord' require 'diff/lcs' require 'diff/lcs/hunk' -require 'mprelude' require 'optparse' require 'parser/current' -require 'procto' require 'set' +require 'unparser/equalizer' +require 'unparser/adamantium' +require 'unparser/adamantium/method_builder' +require 'unparser/abstract_type' + +require 'unparser/concord' +require 'unparser/either' +require 'unparser/anima' +require 'unparser/anima/attribute' +require 'unparser/anima/error' + # Library namespace module Unparser # Unparser specific AST builder defaulting to modern AST format @@ -27,7 +33,7 @@ def initialize EMPTY_STRING = ''.freeze EMPTY_ARRAY = [].freeze - private_constant(*constants(false)) + private_constant(*constants(false) - %i[Adamantium AbstractType Anima Concord Either Equalizer Memoizable]) # Error raised when unparser encounters an invalid AST class InvalidNodeError < RuntimeError @@ -74,9 +80,9 @@ def self.unparse_validate(node, comment_array = []) validation = Validation.from_string(generated) if validation.success? - MPrelude::Either::Right.new(generated) + Either::Right.new(generated) else - MPrelude::Either::Left.new(validation) + Either::Left.new(validation) end end @@ -88,8 +94,7 @@ def self.unparse_validate(node, comment_array = []) # # @return [Either] def self.unparse_either(node) - MPrelude::Either - .wrap_error(Exception) { unparse(node) } + Either.wrap_error(Exception) { unparse(node) } end # Parse string into AST @@ -105,9 +110,9 @@ def self.parse(source) # # @param [String] source # - # @return [MPrelude::Either] + # @return [Either] def self.parse_either(source) - MPrelude::Either.wrap_error(Parser::SyntaxError) do + Either.wrap_error(Parser::SyntaxError) do parser.parse(buffer(source)) end end diff --git a/lib/unparser/abstract_type.rb b/lib/unparser/abstract_type.rb new file mode 100644 index 00000000..2a91ed4f --- /dev/null +++ b/lib/unparser/abstract_type.rb @@ -0,0 +1,121 @@ +# frozen_string_literal: true + +module Unparser + # Module to allow class and methods to be abstract + # + # Original code before vendoring and reduction from: https://github.com/dkubb/abstract_type. + module AbstractType + + # Hook called when module is included + # + # @param [Module] descendant + # the module or class including AbstractType + # + # @return [undefined] + # + # @api private + def self.included(descendant) + super + create_new_method(descendant) + descendant.extend(AbstractMethodDeclarations) + end + + private_class_method :included + + # Define the new method on the abstract type + # + # Ensures that the instance cannot be of the abstract type + # and must be a descendant. + # + # @param [Class] abstract_class + # + # @return [undefined] + # + # @api private + def self.create_new_method(abstract_class) + abstract_class.define_singleton_method(:new) do |*args, &block| + if equal?(abstract_class) + fail NotImplementedError, "#{self} is an abstract type" + else + super(*args, &block) + end + end + end + + private_class_method :create_new_method + + module AbstractMethodDeclarations + + # Create abstract instance methods + # + # @example + # class Foo + # include AbstractType + # + # # Create an abstract instance method + # abstract_method :some_method + # end + # + # @param [Array<#to_s>] names + # + # @return [self] + # + # @api public + def abstract_method(*names) + names.each(&method(:create_abstract_instance_method)) + self + end + + # Create abstract singleton methods + # + # @example + # class Foo + # include AbstractType + # + # # Create an abstract instance method + # abstract_singleton_method :some_method + # end + # + # @param [Array<#to_s>] names + # + # @return [self] + # + # @api private + def abstract_singleton_method(*names) + names.each(&method(:create_abstract_singleton_method)) + self + end + + private + + # Create abstract singleton method + # + # @param [#to_s] name + # the name of the method to create + # + # @return [undefined] + # + # @api private + def create_abstract_singleton_method(name) + define_singleton_method(name) do |*| + fail NotImplementedError, "#{self}.#{name} is not implemented" + end + end + + # Create abstract instance method + # + # @param [#to_s] name + # the name of the method to create + # + # @return [undefined] + # + # @api private + def create_abstract_instance_method(name) + define_method(name) do |*| + fail NotImplementedError, "#{self.class}##{name} is not implemented" + end + end + + end # AbstractMethodDeclarations + end # AbstractType +end # Unparser diff --git a/lib/unparser/adamantium.rb b/lib/unparser/adamantium.rb new file mode 100644 index 00000000..f63c4461 --- /dev/null +++ b/lib/unparser/adamantium.rb @@ -0,0 +1,150 @@ +# frozen_string_literal: true + +module Unparser + # Allows objects to be made immutable + # + # Original code before vendoring and reduction from: https://github.com/dkubb/adamantium. + module Adamantium + module InstanceMethods + # A noop #dup for immutable objects + # + # @return [self] + # + # @api public + def dup + self + end + + # Freeze the object + # + # @return [Object] + # + # @api public + def freeze + memoized_method_cache + super() + end + + private + + def memoized_method_cache + @memoized_method_cache ||= Memory.new({}) + end + + end # InstanceMethods + + # Storage for memoized methods + class Memory + + # Initialize the memory storage for memoized methods + # + # @return [undefined] + # + # @api private + def initialize(values) + @values = values + @monitor = Monitor.new + freeze + end + + # Fetch the value from memory, or evaluate if it does not exist + # + # @param [Symbol] name + # + # @yieldreturn [Object] + # the value to memoize + # + # @api public + def fetch(name) + @values.fetch(name) do # check for the key + @monitor.synchronize do # acquire a lock if the key is not found + @values.fetch(name) do # recheck under lock + @values[name] = yield # set the value + end + end + end + end + end # Memory + + # Methods mixed in to adamantium classes + module ClassMethods + + # Instantiate a new frozen object + # + # @return [Object] + # + # @api public + def new(*) + super.freeze + end + + end # ClassMethods + + # Methods mixed in to adamantium modules + module ModuleMethods + + # Memoize a list of methods + # + # @param [Array<#to_s>] methods + # a list of methods to memoize + # + # @return [self] + # + # @api public + def memoize(*methods) + methods.each(&method(:memoize_method)) + self + end + + # Test if method is memoized + # + # @param [Symbol] name + # + # @return [Bool] + def memoized?(method_name) + memoized_methods.key?(method_name) + end + + # Return unmemoized instance method + # + # @param [Symbol] name + # + # @return [UnboundMethod] + # the memoized method + # + # @raise [NameError] + # raised if the method is unknown + # + # @api public + def unmemoized_instance_method(method_name) + memoized_methods.fetch(method_name) do + fail ArgumentError, "##{method_name} is not memoized" + end + end + + private + + def memoize_method(method_name) + if memoized_methods.key?(method_name) + fail ArgumentError, "##{method_name} is already memoized" + end + + memoized_methods[method_name] = MethodBuilder.new(self, method_name).call + end + + def memoized_methods + @memoized_methods ||= {} + end + + end # ModuleMethods + + def self.included(descendant) + descendant.class_eval do + include InstanceMethods + extend ModuleMethods + extend ClassMethods if instance_of?(Class) + end + end + private_class_method :included + end # Adamantium +end # Unparser diff --git a/lib/unparser/adamantium/method_builder.rb b/lib/unparser/adamantium/method_builder.rb new file mode 100644 index 00000000..71af396b --- /dev/null +++ b/lib/unparser/adamantium/method_builder.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module Unparser + module Adamantium + # Build the memoized method + class MethodBuilder + + # Raised when the method arity is invalid + class InvalidArityError < ArgumentError + + # Initialize an invalid arity exception + # + # @param [Module] descendant + # @param [Symbol] method + # @param [Integer] arity + # + # @api private + def initialize(descendant, method, arity) + super("Cannot memoize #{descendant}##{method}, its arity is #{arity}") + end + + end # InvalidArityError + + # Raised when a block is passed to a memoized method + class BlockNotAllowedError < ArgumentError + + # Initialize a block not allowed exception + # + # @param [Module] descendant + # @param [Symbol] method + # + # @api private + def initialize(descendant, method) + super("Cannot pass a block to #{descendant}##{method}, it is memoized") + end + + end # BlockNotAllowedError + + # Initialize an object to build a memoized method + # + # @param [Module] descendant + # @param [Symbol] method_name + # + # @return [undefined] + # + # @api private + def initialize(descendant, method_name) + @descendant = descendant + @method_name = method_name + @original_visibility = visibility + @original_method = @descendant.instance_method(@method_name) + assert_arity(@original_method.arity) + end + + # Build a new memoized method + # + # @example + # method_builder.call # => creates new method + # + # @return [UnboundMethod] + # + # @api public + def call + remove_original_method + create_memoized_method + set_method_visibility + @original_method + end + + private + + def assert_arity(arity) + if arity.nonzero? + fail InvalidArityError.new(@descendant, @method_name, arity) + end + end + + def remove_original_method + name = @method_name + @descendant.module_eval { undef_method(name) } + end + + def create_memoized_method + name = @method_name + method = @original_method + @descendant.module_eval do + define_method(name) do |&block| + fail BlockNotAllowedError.new(self.class, name) if block + + memoized_method_cache.fetch(name) do + method.bind(self).call.freeze + end + end + end + end + + def set_method_visibility + @descendant.__send__(@original_visibility, @method_name) + end + + def visibility + if @descendant.private_method_defined?(@method_name) then :private + elsif @descendant.protected_method_defined?(@method_name) then :protected + else :public + end + end + + end # MethodBuilder + end # Adamantium +end # Unparser diff --git a/lib/unparser/anima.rb b/lib/unparser/anima.rb new file mode 100644 index 00000000..8618c9fd --- /dev/null +++ b/lib/unparser/anima.rb @@ -0,0 +1,184 @@ +# frozen_string_literal: true + +module Unparser + # Original code before vendoring and reduction from: https://github.com/mbj/anima. + class Anima < Module + include Adamantium, Equalizer.new(:attributes) + + # Return names + # + # @return [AttributeSet] + attr_reader :attributes + + # Initialize object + # + # @return [undefined] + # + # rubocop:disable Lint/MissingSuper + def initialize(*names) + @attributes = names.uniq.map(&Attribute.public_method(:new)).freeze + end + # rubocop:enable Lint/MissingSuper + + # Return new anima with attributes added + # + # @return [Anima] + # + # @example + # anima = Anima.new(:foo) + # anima.add(:bar) # equals Anima.new(:foo, :bar) + # + def add(*names) + new(attribute_names + names) + end + + # Return new anima with attributes removed + # + # @return [Anima] + # + # @example + # anima = Anima.new(:foo, :bar) + # anima.remove(:bar) # equals Anima.new(:foo) + # + def remove(*names) + new(attribute_names - names) + end + + # Return attributes hash for instance + # + # @param [Object] object + # + # @return [Hash] + def attributes_hash(object) + attributes.each_with_object({}) do |attribute, attributes_hash| + attributes_hash[attribute.name] = attribute.get(object) + end + end + + # Return attribute names + # + # @return [Enumerable] + def attribute_names + attributes.map(&:name) + end + memoize :attribute_names + + # Initialize instance + # + # @param [Object] object + # + # @param [Hash] attribute_hash + # + # @return [self] + def initialize_instance(object, attribute_hash) + assert_known_attributes(object.class, attribute_hash) + attributes.each do |attribute| + attribute.load(object, attribute_hash) + end + self + end + + # Static instance methods for anima infected classes + module InstanceMethods + # Initialize an anima infected object + # + # @param [#to_h] attributes + # a hash that matches anima defined attributes + # + # @return [undefined] + # + # rubocop:disable Lint/MissingSuper + def initialize(attributes) + self.class.anima.initialize_instance(self, attributes) + end + # rubocop:enable Lint/MissingSuper + + # Return a hash representation of an anima infected object + # + # @example + # anima.to_h # => { :foo => : bar } + # + # @return [Hash] + # + # @api public + def to_h + self.class.anima.attributes_hash(self) + end + + # Return updated instance + # + # @example + # klass = Class.new do + # include Anima.new(:foo, :bar) + # end + # + # foo = klass.new(:foo => 1, :bar => 2) + # updated = foo.with(:foo => 3) + # updated.foo # => 3 + # updated.bar # => 2 + # + # @param [Hash] attributes + # + # @return [Anima] + # + # @api public + def with(attributes) + self.class.new(to_h.update(attributes)) + end + end # InstanceMethods + + private + + # Infect the instance with anima + # + # @param [Class, Module] scope + # + # @return [undefined] + def included(descendant) + descendant.instance_exec(self, attribute_names) do |anima, names| + # Define anima method + define_singleton_method(:anima) { anima } + + # Define instance methods + include InstanceMethods + + # Define attribute readers + attr_reader(*names) + + # Define equalizer + include Equalizer.new(*names) + end + end + + # Fail unless keys in +attribute_hash+ matches #attribute_names + # + # @param [Class] klass + # the class being initialized + # + # @param [Hash] attribute_hash + # the attributes to initialize +object+ with + # + # @return [undefined] + # + # @raise [Error] + def assert_known_attributes(klass, attribute_hash) + keys = attribute_hash.keys + + unknown = keys - attribute_names + missing = attribute_names - keys + + unless unknown.empty? && missing.empty? + fail Error.new(klass, missing, unknown) + end + end + + # Return new instance + # + # @param [Enumerable] attributes + # + # @return [Anima] + def new(attributes) + self.class.new(*attributes) + end + end # Anima +end # Unparser diff --git a/lib/unparser/anima/attribute.rb b/lib/unparser/anima/attribute.rb new file mode 100644 index 00000000..ff6f0c4b --- /dev/null +++ b/lib/unparser/anima/attribute.rb @@ -0,0 +1,59 @@ +# frozen_string_literal: true + +module Unparser + class Anima + # An attribute + class Attribute + include Adamantium, Equalizer.new(:name) + + # Initialize attribute + # + # @param [Symbol] name + def initialize(name) + @name = name + @instance_variable_name = :"@#{name}" + end + + # Return attribute name + # + # @return [Symbol] + attr_reader :name + + # Return instance variable name + # + # @return [Symbol] + attr_reader :instance_variable_name + + # Load attribute + # + # @param [Object] object + # @param [Hash] attributes + # + # @return [self] + def load(object, attributes) + set(object, attributes.fetch(name)) + end + + # Get attribute value from object + # + # @param [Object] object + # + # @return [Object] + def get(object) + object.public_send(name) + end + + # Set attribute value in object + # + # @param [Object] object + # @param [Object] value + # + # @return [self] + def set(object, value) + object.instance_variable_set(instance_variable_name, value) + + self + end + end # Attribute + end # Anima +end # Unparser diff --git a/lib/unparser/anima/error.rb b/lib/unparser/anima/error.rb new file mode 100644 index 00000000..f77daa86 --- /dev/null +++ b/lib/unparser/anima/error.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Unparser + class Anima + # Abstract base class for anima errors + class Error < RuntimeError + FORMAT = '%s attributes missing: %s, unknown: %s'.freeze + private_constant(*constants(false)) + + # Initialize object + # + # @param [Class] klass + # the class being initialized + # @param [Enumerable] missing + # @param [Enumerable] unknown + # + # @return [undefined] + def initialize(klass, missing, unknown) + super(format(FORMAT, klass, missing, unknown)) + end + end # Error + end # Anima +end # Unparser diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 3ac7b17c..976cd9b3 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -3,7 +3,6 @@ module Unparser # Namespace for AST processing tools module AST - FIRST_CHILD = ->(node) { node.children.first }.freeze TAUTOLOGY = ->(_node) { true }.freeze @@ -79,7 +78,7 @@ def self.local_variable_reads(node) # AST enumerator class Enumerator - include Adamantium::Flat, Concord.new(:node, :controller), Enumerable + include Adamantium, Concord.new(:node, :controller), Enumerable # Return new instance # diff --git a/lib/unparser/color.rb b/lib/unparser/color.rb index e4106582..0542be21 100644 --- a/lib/unparser/color.rb +++ b/lib/unparser/color.rb @@ -3,7 +3,7 @@ module Unparser # Class to colorize strings class Color - include Adamantium::Flat, Concord.new(:code) + include Adamantium, Concord.new(:code) # Format text with color # diff --git a/lib/unparser/concord.rb b/lib/unparser/concord.rb new file mode 100644 index 00000000..f43227dc --- /dev/null +++ b/lib/unparser/concord.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +module Unparser + # A mixin to define a composition + # + # Original code before vendoring and reduction from: https://github.com/mbj/concord. + class Concord < Module + include Adamantium, Equalizer.new(:names) + + # The maximum number of objects the hosting class is composed of + MAX_NR_OF_OBJECTS = 3 + + # Return names + # + # @return [Enumerable] + # + # @api private + # + attr_reader :names + + private + + # Initialize object + # + # @return [undefined] + # + # @api private + # + # rubocop:disable Lint/MissingSuper + def initialize(*names) + if names.length > MAX_NR_OF_OBJECTS + fail "Composition of more than #{MAX_NR_OF_OBJECTS} objects is not allowed" + end + + @names = names + define_initialize + define_readers + define_equalizer + end + # rubocop:enable Lint/MissingSuper + + # Define equalizer + # + # @return [undefined] + # + # @api private + # + def define_equalizer + include(Equalizer.new(*names)) + end + + # Define readers + # + # @return [undefined] + # + # @api private + # + def define_readers + attribute_names = names + attr_reader(*attribute_names) + + protected(*attribute_names) if attribute_names.any? + end + + # Define initialize method + # + # @return [undefined] + # + # @api private + # + # + def define_initialize + ivars = instance_variable_names + size = names.size + + define_method :initialize do |*args| + args_size = args.size + unless args_size.equal?(size) + fail ArgumentError, "wrong number of arguments (#{args_size} for #{size})" + end + + ivars.zip(args) { |ivar, arg| instance_variable_set(ivar, arg) } + end + end + + # Return instance variable names + # + # @return [String] + # + # @api private + # + def instance_variable_names + names.map { |name| "@#{name}" } + end + + # Mixin for public attribute readers + class Public < self + + # Hook called when module is included + # + # @param [Class,Module] descendant + # + # @return [undefined] + # + # @api private + # + def included(descendant) + names.each do |name| + descendant.__send__(:public, name) + end + end + end # Public + end # Concord +end # Unparser diff --git a/lib/unparser/diff.rb b/lib/unparser/diff.rb index f27053ab..52a29048 100644 --- a/lib/unparser/diff.rb +++ b/lib/unparser/diff.rb @@ -3,7 +3,7 @@ module Unparser # Class to create diffs from source code class Diff - include Adamantium::Flat, Concord.new(:old, :new) + include Adamantium, Concord.new(:old, :new) ADDITION = '+' DELETION = '-' diff --git a/lib/unparser/either.rb b/lib/unparser/either.rb new file mode 100644 index 00000000..924f731a --- /dev/null +++ b/lib/unparser/either.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +module Unparser + module RequireBlock + + private + + # Raise error unless block is provided + # + # @raise [MissingBlockError] + # if no block is given + # + # @return [self] + def require_block + fail LocalJumpError unless block_given? + + self + end + end # RequireBLock + + class Either + include( + Adamantium, + Concord.new(:value), + RequireBlock + ) + + # Execute block and wrap error in left + # + # @param [Class] exception + # + # @return [Either] + def self.wrap_error(*exceptions) + Right.new(yield) + rescue *exceptions => error + Left.new(error) + end + + # Test for left constructor + # + # @return [Boolean] + def left? + instance_of?(Left) + end + + # Test for right constructor + # + # @return [Boolean] + def right? + instance_of?(Right) + end + + class Left < self + # Evaluate functor block + # + # @return [Either::Left] + def fmap(&block) + require_block(&block) + end + + # Evaluate applicative block + # + # @return [Either::Left] + def bind(&block) + require_block(&block) + end + + # Unwrap value from left + # + # @return [Object] + def from_left + value + end + + # Unwrap value from right + # + # @return [Object] + # + def from_right + if block_given? + yield(value) + else + fail "Expected right value, got #{inspect}" + end + end + + # Map over left value + # + # @return [Either::Right] + def lmap + Left.new(yield(value)) + end + + # Evaluate left side of branch + # + # @param [#call] left + # @param [#call] _right + def either(left, _right) + left.call(value) + end + end # Left + + class Right < self + # Evaluate functor block + # + # @return [Either::Right] + def fmap + Right.new(yield(value)) + end + + # Evaluate applicative block + # + # @return [Either] + def bind + yield(value) + end + + # Unwrap value from left + # + # @return [Object] + # + def from_left + if block_given? + yield(value) + else + fail "Expected left value, got #{inspect}" + end + end + + # Unwrap value from right + # + # @return [Object] + def from_right + value + end + + # Map over left value + # + # @return [Either::Right] + def lmap(&block) + require_block(&block) + end + + # Evaluate right side of branch + # + # @param [#call] _left + # @param [#call] right + def either(_left, right) + right.call(value) + end + end # Right + end # Either +end # Unparser diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index 87b84d18..aedc5171 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -5,7 +5,7 @@ module Unparser # Emitter base class class Emitter - include Adamantium::Flat, AbstractType, Constants, Generation, NodeHelpers + include Adamantium, AbstractType, Constants, Generation, NodeHelpers include Anima.new(:buffer, :comments, :node, :local_variable_scope) public :node diff --git a/lib/unparser/emitter/index.rb b/lib/unparser/emitter/index.rb index e509827c..dd7a3a75 100644 --- a/lib/unparser/emitter/index.rb +++ b/lib/unparser/emitter/index.rb @@ -36,7 +36,7 @@ class Assign < self handle :indexasgn VALUE_RANGE = (1..-2).freeze - NO_VALUE_PARENT = IceNine.deep_freeze(%i[and_asgn op_asgn or_asgn].to_set) + NO_VALUE_PARENT = %i[and_asgn op_asgn or_asgn].to_set.freeze private_constant(*constants(false)) diff --git a/lib/unparser/emitter/op_assign.rb b/lib/unparser/emitter/op_assign.rb index b10d7a8d..52d3d117 100644 --- a/lib/unparser/emitter/op_assign.rb +++ b/lib/unparser/emitter/op_assign.rb @@ -7,10 +7,10 @@ class Emitter class BinaryAssign < self children :target, :expression - MAP = IceNine.deep_freeze( + MAP = { and_asgn: '&&=', or_asgn: '||=' - ) + }.freeze handle(*MAP.keys) diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb index 0604abd5..e173704d 100644 --- a/lib/unparser/emitter/primitive.rb +++ b/lib/unparser/emitter/primitive.rb @@ -28,11 +28,11 @@ class Complex < self RATIONAL_FORMAT = 'i'.freeze MAP = - IceNine.deep_freeze( + { ::Float => :float, ::Rational => :rational, ::Integer => :int - ) + }.freeze private diff --git a/lib/unparser/emitter/simple.rb b/lib/unparser/emitter/simple.rb index 39b44e82..b3dfe12b 100644 --- a/lib/unparser/emitter/simple.rb +++ b/lib/unparser/emitter/simple.rb @@ -4,7 +4,7 @@ module Unparser class Emitter # Emitter for simple nodes that generate a single token class Simple < self - MAP = IceNine.deep_freeze( + MAP = { __ENCODING__: '__ENCODING__', __FILE__: '__FILE__', __LINE__: '__LINE__', @@ -19,7 +19,7 @@ class Simple < self self: 'self', true: 'true', zsuper: 'super' - ) + }.freeze handle(*MAP.keys) diff --git a/lib/unparser/equalizer.rb b/lib/unparser/equalizer.rb new file mode 100644 index 00000000..ab7e1c75 --- /dev/null +++ b/lib/unparser/equalizer.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Unparser + # Define equality, equivalence and inspection methods + # + # Original code before vendoring and reduction from: https://github.com/dkubb/equalizer. + class Equalizer < Module + # Initialize an Equalizer with the given keys + # + # Will use the keys with which it is initialized to define #cmp?, + # #hash, and #inspect + # + # @param [Array] keys + # + # @return [undefined] + # + # @api private + # + # rubocop:disable Lint/MissingSuper + def initialize(*keys) + @keys = keys + define_methods + freeze + end + # rubocop:enable Lint/MissingSuper + + private + + def included(descendant) + descendant.include(Methods) + end + + def define_methods + define_cmp_method + define_hash_method + define_inspect_method + end + + def define_cmp_method + keys = @keys + define_method(:cmp?) do |comparator, other| + keys.all? do |key| + __send__(key).public_send(comparator, other.__send__(key)) + end + end + private :cmp? + end + + def define_hash_method + keys = @keys + define_method(:hash) do + keys.map(&public_method(:__send__)).push(self.class).hash + end + end + + def define_inspect_method + keys = @keys + define_method(:inspect) do + klass = self.class + name = klass.name || klass.inspect + "#<#{name}#{keys.map { |key| " #{key}=#{__send__(key).inspect}" }.join}>" + end + end + + # The comparison methods + module Methods + # Compare the object with other object for equality + # + # @example + # object.eql?(other) # => true or false + # + # @param [Object] other + # the other object to compare with + # + # @return [Boolean] + # + # @api public + def eql?(other) + instance_of?(other.class) && cmp?(__method__, other) + end + + # Compare the object with other object for equivalency + # + # @example + # object == other # => true or false + # + # @param [Object] other + # the other object to compare with + # + # @return [Boolean] + # + # @api public + def ==(other) + instance_of?(other.class) && cmp?(__method__, other) + end + end # module Methods + end # class Equalizer +end # Unparser diff --git a/lib/unparser/node_details.rb b/lib/unparser/node_details.rb index 00708189..51933040 100644 --- a/lib/unparser/node_details.rb +++ b/lib/unparser/node_details.rb @@ -6,7 +6,7 @@ module NodeDetails def self.included(descendant) descendant.class_eval do - include Adamantium::Flat, Concord.new(:node) + include Adamantium, Concord.new(:node) extend DSL end diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index 18fe224f..d0413428 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -3,7 +3,7 @@ module Unparser # Validation of unparser results class Validation - include Adamantium::Flat, Anima.new( + include Adamantium, Anima.new( :generated_node, :generated_source, :identification, @@ -64,7 +64,7 @@ def self.from_string(original_source) new( identification: '(string)', - original_source: MPrelude::Either::Right.new(original_source), + original_source: Either::Right.new(original_source), original_node: original_node, generated_source: generated_source, generated_node: generated_node @@ -86,7 +86,7 @@ def self.from_node(original_node) new( identification: '(string)', original_source: generated_source, - original_node: MPrelude::Either::Right.new(original_node), + original_node: Either::Right.new(original_node), generated_source: generated_source, generated_node: generated_node ) diff --git a/lib/unparser/writer/binary.rb b/lib/unparser/writer/binary.rb index 1152ee04..db2025ab 100644 --- a/lib/unparser/writer/binary.rb +++ b/lib/unparser/writer/binary.rb @@ -3,7 +3,7 @@ module Unparser module Writer class Binary - include Writer, Adamantium::Flat + include Writer, Adamantium children :left, :right diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index a7760a11..cec91745 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -3,22 +3,20 @@ module Unparser module Writer class DynamicString - include Writer, Adamantium::Flat + include Writer, Adamantium - PATTERNS_2 = IceNine.deep_freeze( + PATTERNS_2 = [ - %i[str_empty begin], - %i[begin str_nl] - ] - ) + %i[str_empty begin].freeze, + %i[begin str_nl].freeze + ].freeze - PATTERNS_3 = IceNine.deep_freeze( + PATTERNS_3 = [ - %i[begin str_nl_eol str_nl_eol], - %i[str_nl_eol begin str_nl_eol], - %i[str_ws begin str_nl_eol] - ] - ) + %i[begin str_nl_eol str_nl_eol].freeze, + %i[str_nl_eol begin str_nl_eol].freeze, + %i[str_ws begin str_nl_eol].freeze + ].freeze FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze diff --git a/lib/unparser/writer/rescue.rb b/lib/unparser/writer/rescue.rb index 889ec974..892b2fb4 100644 --- a/lib/unparser/writer/rescue.rb +++ b/lib/unparser/writer/rescue.rb @@ -3,7 +3,7 @@ module Unparser module Writer class Rescue - include Writer, Adamantium::Flat + include Writer, Adamantium children :body, :rescue_body diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index d444d7cf..cabef7b9 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -4,7 +4,7 @@ module Unparser module Writer # Writer for send class Send - include Writer, Adamantium::Flat, Constants, Generation + include Writer, Adamantium, Constants, Generation INDEX_ASSIGN = :'[]=' INDEX_REFERENCE = :'[]' diff --git a/mutant.sh b/mutant.sh deleted file mode 100755 index 01f8f260..00000000 --- a/mutant.sh +++ /dev/null @@ -1,32 +0,0 @@ -#/usr/bin/bash -ex - -bundle exec mutant run \ - --zombie \ - --ignore-subject 'Unparser::Builder#initialize' \ - --ignore-subject 'Unparser::CLI*' \ - --ignore-subject 'Unparser::Emitter#emit_comments' \ - --ignore-subject 'Unparser::Emitter#emit_comments_before' \ - --ignore-subject 'Unparser::Emitter#emit_eol_comments' \ - --ignore-subject 'Unparser::Emitter.handle' \ - --ignore-subject 'Unparser::Emitter::Args#normal_arguments' \ - --ignore-subject 'Unparser::Emitter::Args#shadowargs' \ - --ignore-subject 'Unparser::Emitter::Array#emitters' \ - --ignore-subject 'Unparser::Emitter::Binary#writer' \ - --ignore-subject 'Unparser::Emitter::Block#target_writer' \ - --ignore-subject 'Unparser::Emitter::Class#dispatch' \ - --ignore-subject 'Unparser::Emitter::Class#local_variable_scope' \ - --ignore-subject 'Unparser::Emitter::Def#local_variable_scope' \ - --ignore-subject 'Unparser::Emitter::HashPattern#write_symbol_body' \ - --ignore-subject 'Unparser::Emitter::LocalVariableRoot*' \ - --ignore-subject 'Unparser::Emitter::LocalVariableRoot.included' \ - --ignore-subject 'Unparser::Emitter::Module#local_variable_scope' \ - --ignore-subject 'Unparser::Emitter::Root#local_variable_scope' \ - --ignore-subject 'Unparser::Emitter::Send#writer' \ - --ignore-subject 'Unparser::Validation.from_string' \ - --ignore-subject 'Unparser::Validation::Literal*' \ - --ignore-subject 'Unparser::Writer.included' \ - --ignore-subject 'Unparser::Writer::Binary#left_emitter' \ - --ignore-subject 'Unparser::Writer::Binary#right_emitter' \ - --ignore-subject 'Unparser::Writer::Send#details' \ - --ignore-subject 'Unparser::Writer::Send#effective_writer' \ - $* diff --git a/scripts/devloop.sh b/scripts/devloop.sh index 4cc3aeee..b7bca114 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,5 +1,5 @@ -while inotifywait **/*.rb Gemfile unparser.gemspec; do +while inotifywait ../mutant/**/*.rb **/*.rb Gemfile unparser.gemspec; do bundle exec rspec spec/unit -fd --fail-fast --order default \ - && bundle exec ./mutant.sh --since master -- 'Unparser*' \ + && bundle exec mutant run --zombie --since master --fail-fast -- 'Unparser*' \ && bundle exec rubocop done diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb index 18bfb73f..9b4bf361 100644 --- a/spec/integration/unparser/corpus_spec.rb +++ b/spec/integration/unparser/corpus_spec.rb @@ -1,11 +1,12 @@ require 'spec_helper' + describe 'Unparser on ruby corpus', mutant: false do ROOT = Pathname.new(__FILE__).parent.parent.parent.parent TMP = ROOT.join('tmp') class Project - include Anima.new(:name, :repo_uri, :repo_ref, :exclude) + include Unparser::Anima.new(:name, :repo_uri, :repo_ref, :exclude) # Perform verification via unparser cli # @@ -94,7 +95,7 @@ def system(arguments) ] ), transform::Hash::Symbolize.new, - transform::Exception.new(Anima::Error, Project.public_method(:new)) + transform::Exception.new(Unparser::Anima::Error, Project.public_method(:new)) ] ) ) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 96a4933c..6bd6151c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,8 @@ -require 'anima' -require 'mutant' require 'pathname' require 'rspec/its' require 'timeout' require 'unparser' +require 'mutant' require 'yaml' require 'parser/current' @@ -34,6 +33,14 @@ module SpecHelper def s(type, *children) Parser::AST::Node.new(type, children) end + + def right(value) + Unparser::Either::Right.new(value) + end + + def left(value) + Unparser::Either::Left.new(value) + end end RSpec.configure do |config| diff --git a/spec/unit/unparser/abstract_type_spec.rb b/spec/unit/unparser/abstract_type_spec.rb new file mode 100644 index 00000000..8416ba50 --- /dev/null +++ b/spec/unit/unparser/abstract_type_spec.rb @@ -0,0 +1,174 @@ +RSpec.shared_examples 'AbstractType.create_new_method' do + context 'called on a subclass' do + let(:object) { Class.new(abstract_type) } + + it { should be_instance_of(object) } + end + + context 'called on the class' do + let(:object) { abstract_type } + + specify do + expect { subject }.to raise_error( + NotImplementedError, + "#{object} is an abstract type" + ) + end + end +end + +RSpec.describe Unparser::AbstractType::AbstractMethodDeclarations, '#abstract_method' do + subject { object.abstract_method(:some_method) } + + let(:object) { Class.new { include Unparser::AbstractType } } + let(:subclass) { Class.new(object) } + + before do + Subclass = subclass + end + + after do + Object.class_eval { remove_const(:Subclass) } + end + + it { should equal(object) } + + it 'creates an abstract method' do + expect { subject }.to change { subclass.method_defined?(:some_method) } + .from(false) + .to(true) + end + + it 'creates an abstract method with the expected arity' do + subject + expect(object.instance_method(:some_method).arity).to be(-1) + end + + it 'creates a method that raises an exception' do + subject + expect { subclass.new.some_method }.to raise_error( + NotImplementedError, + 'Subclass#some_method is not implemented' + ) + end +end + +RSpec.describe( + Unparser::AbstractType::AbstractMethodDeclarations, + '#abstract_singleton_method' +) do + subject { object.abstract_singleton_method(:some_method) } + + let(:object) { Class.new { include Unparser::AbstractType } } + let(:subclass) { Class.new(object) } + + before do + Subclass = subclass + end + + after do + Object.class_eval { remove_const(:Subclass) } + end + + it { should equal(object) } + + it 'creates an abstract method' do + expect { subject }.to change { subclass.respond_to?(:some_method) } + .from(false) + .to(true) + end + + it 'creates an abstract method with the expected arity' do + subject + expect(object.method(:some_method).arity).to be(-1) + end + + it 'creates a method that raises an exception' do + subject + expect { subclass.some_method }.to raise_error( + NotImplementedError, + 'Subclass.some_method is not implemented' + ) + end +end + +RSpec.describe Unparser::AbstractType, '.included' do + subject { object } + + let(:object) { described_class } + let(:klass) { Class.new } + + it 'extends the klass' do + expect(klass.singleton_class) + .to_not include(described_class::AbstractMethodDeclarations) + klass.send(:include, subject) + expect(klass.singleton_class) + .to include(described_class::AbstractMethodDeclarations) + end + + it 'overrides the new singleton method' do + expect(klass.method(:new).owner).to eq(Class) + klass.send(:include, subject) + expect(klass.method(:new).owner).to eq(klass.singleton_class) + end + + it 'delegates to the ancestor' do + included_ancestor = false + subject.extend Module.new { + define_method(:included) { |_| included_ancestor = true } + } + expect { klass.send(:include, subject) } + .to change { included_ancestor }.from(false).to(true) + end +end + +RSpec.describe Unparser::AbstractType, '.create_new_method' do + context 'with arguments' do + subject { object.new(:foo) } + + let(:abstract_type) do + Class.new do + include Unparser::AbstractType + + def initialize(foo) + @foo = foo + end + end + end + + it_behaves_like 'AbstractType.create_new_method' + end + + context 'with a block' do + subject { object.new(:foo) { nil } } + + let(:abstract_type) do + Class.new do + include Unparser::AbstractType + + def initialize(foo) + @foo = foo + yield + end + end + end + + it_behaves_like 'AbstractType.create_new_method' + end + + context 'without arguments' do + subject { object.new } + + let(:abstract_type) { Class.new { include Unparser::AbstractType } } + + it_behaves_like 'AbstractType.create_new_method' + end + + context 'on an class that doesn\'t have Object as its superclass' do + subject { object.new } + + let(:abstract_type) { Class.new(RuntimeError) { include Unparser::AbstractType } } + + it_behaves_like 'AbstractType.create_new_method' + end +end diff --git a/spec/unit/unparser/adamantium/memory_spec.rb b/spec/unit/unparser/adamantium/memory_spec.rb new file mode 100644 index 00000000..e1cc8603 --- /dev/null +++ b/spec/unit/unparser/adamantium/memory_spec.rb @@ -0,0 +1,131 @@ +RSpec.describe Unparser::Adamantium::Memory do + describe '#fetch' do + let(:events) { [] } + let(:monitor) { instance_double(Monitor) } + let(:name) { :some_name } + let(:object) { described_class.new(proxy) } + let(:values) { {} } + let(:value_was_read) { -> {} } + + let(:block) do + lambda do + events << :block_call + @counter += 1 + end + end + + let(:proxy) do + proxy = instance_double(Hash) + values = values() + + allow(proxy).to receive(:fetch) do |name, &block| + events << :fetch + values.fetch(name) do + value_was_read.call + block.call + end + end + + allow(proxy).to receive(:[]=) do |name, value| + events << :set + values[name] = value + end + + proxy + end + + + def apply + object.fetch(name, &block) + end + + before do + allow(Monitor).to receive_messages(new: monitor) + + allow(monitor).to receive(:synchronize) do |&block| + events << :synchronize_start + block.call.tap do + events << :synchronize_end + end + end + + @counter = 0 + end + + shared_examples 'expected events' do + it 'triggers expected events' do + expect { apply } + .to change(events, :to_a) + .from([]).to(expected_events) + end + + it 'returns expected value' do + expect(apply).to be(1) + end + + it 'creates frozen objects' do + expect(object.frozen?).to be(true) + end + end + + context 'when value is present in memory' do + let(:values) { { name => 1 } } + let(:expected_events) { %i[fetch] } + + include_examples 'expected events' + end + + context 'when value is not present in memory initially' do + let(:values) { {} } + + let(:expected_events) do + %i[ + fetch + synchronize_start + fetch + block_call + set + synchronize_end + ] + end + + include_examples 'expected events' + + context 'but is present inside the lock' do + let(:value_was_read) { ->() { values[name] = 1 } } + + let(:expected_events) do + %i[ + fetch + synchronize_start + fetch + synchronize_end + ] + end + + include_examples 'expected events' + end + + context 'and is re-read after initial generation' do + def apply + super() + super() + end + + let(:expected_events) do + %i[ + fetch + synchronize_start + fetch + block_call + set + synchronize_end + fetch + ] + end + + include_examples 'expected events' + end + end + end +end diff --git a/spec/unit/unparser/adamantium_spec.rb b/spec/unit/unparser/adamantium_spec.rb new file mode 100644 index 00000000..53780067 --- /dev/null +++ b/spec/unit/unparser/adamantium_spec.rb @@ -0,0 +1,414 @@ +RSpec.describe Unparser::Adamantium do + describe '.included' do + subject { descendant.instance_exec(object) { |mod| include mod } } + + let(:object) { described_class } + let(:superclass) { Module } + + around do |example| + # Restore included method after each example + superclass.class_eval do + alias_method :original_included, :included + example.call + undef_method :included + alias_method :included, :original_included + end + end + + shared_examples_for 'all descendant types' do + it 'delegates to the superclass #included method' do + # This is the most succinct approach I could think of to test whether the + # superclass#included method is called. All of the built-in rspec helpers + # did not seem to work for this. + included = 0 + superclass.class_eval { define_method(:included) { |_| included += 1 } } + expect(included).to be(0) + subject + expect(included).to be(1) + end + + it 'extends the descendant with Unparser::Adamantium::ModuleMethods' do + subject + expect(descendant.singleton_class.included_modules) + .to include(Unparser::Adamantium::ModuleMethods) + end + end + + context 'with a class descendant' do + let(:descendant) { Class.new } + + it_behaves_like 'all descendant types' + + it 'extends a class descendant with Unparser::Adamantium::ClassMethods' do + subject + expect(descendant.singleton_class.included_modules) + .to include(Unparser::Adamantium::ClassMethods) + end + end + + context 'with a module descendant' do + let(:descendant) { Module.new } + + it_behaves_like 'all descendant types' + + it 'does not extends a module descendant with Unparser::Adamantium::ClassMethods' do + subject + expect(descendant.singleton_class.included_modules) + .to_not include(Unparser::Adamantium::ClassMethods) + end + end + end + + describe '#new' do + let(:argument) { 'argument' } + + subject { class_under_test.new(argument) } + + let(:class_under_test) do + Class.new do + include Unparser::Adamantium + + attr_reader :argument + + def initialize(argument) + @argument = argument + end + end + end + + it { should be_frozen } + + its(:argument) { should be(argument) } + end + + describe '#dup' do + subject { object.dup } + + let(:object) do + Class.new do + include Unparser::Adamantium + end.new + end + + it { should equal(object) } + end + + describe '#freeze' do + subject { object.freeze } + + let(:class_under_test) do + Class.new do + include Unparser::Adamantium + + def test + end + end + end + + context 'with an unfrozen object' do + let(:object) { class_under_test.allocate } + + it_should_behave_like 'a command method' + + it 'freezes the object' do + expect { subject }.to change(object, :frozen?) + .from(false) + .to(true) + end + end + + context 'with a frozen object' do + let(:object) { class_under_test.new } + + it_should_behave_like 'a command method' + + it 'does not change the frozen state of the object' do + expect { subject }.to_not change(object, :frozen?) + end + end + end + + describe '#memoized?' do + subject { object.memoized?(method) } + + let(:object) do + Class.new do + include Unparser::Adamantium + + def some_method + end + + def some_memoized_method + end + memoize :some_memoized_method + end + end + + context 'when method is not memoized' do + let(:method) { :some_method } + + it { should be(false) } + end + + context 'when method is memoized' do + let(:method) { :some_memoized_method } + + it { should be(true) } + end + end + + describe '#unmemoized_instance_method' do + subject { object.unmemoized_instance_method(method) } + + let(:object) do + Class.new do + include Unparser::Adamantium + + def some_method + end + + def some_memoized_method + +'foo' + end + memoize :some_memoized_method + end + end + + context 'when method is not memoized' do + let(:method) { :some_method } + + it 'raises error' do + expect { subject }.to raise_error( + ArgumentError, + '#some_method is not memoized' + ) + end + end + + context 'when method is memoized' do + let(:method) { :some_memoized_method } + + it 'returns unmemoized method' do + unmemoized = subject + + expect(unmemoized.name).to eql(method) + + instance = object.new + + bound = unmemoized.bind(instance) + + first = bound.call + second = bound.call + + expect(first).to eql('foo') + expect(first).to_not be(second) + end + end + end + + describe '#memoize' do + subject { object.memoize(method) } + + let(:object) do + Class.new do + include Unparser::Adamantium + + def argumented(x) + end + + def some_state + +'' + end + + def some_other_state + end + memoize :some_other_state + + def public_method + end + + protected def protected_method + end + + private def private_method + end + end + end + + shared_examples_for 'memoizes method' do + it 'memoizes the instance method' do + subject + instance = object.new + expect(instance.send(method)).to be(instance.send(method)) + end + + let(:fake) do + Class.new do + attr_reader :messages + + def initialize + @messages = [] + end + + def write(message) + @messages << message + end + end + end + + it 'does not trigger warnings' do + begin + original = $stderr + $stderr = fake.new + subject + expect($stderr.messages).to eql([]) + ensure + $stderr = original + end + end + + it 'does not allow to call memoized method with blocks' do + subject + + expect do + object.new.send(method) { } + end.to raise_error do |error| + expect(error).to be_a( + Unparser::Adamantium::MethodBuilder::BlockNotAllowedError + ) + + expect(error.message).to eql( + "Cannot pass a block to #{object.inspect}##{method}, it is memoized" + ) + end + end + end + + shared_examples_for 'wraps original method' do + it 'creates a method with an arity of 0' do + subject + expect(object.new.method(method).arity).to be_zero + end + + context 'when the initializer calls the memoized method' do + it_should_behave_like 'memoizes method' + + before do + method = self.method + object.send(:define_method, :initialize) { send(method) } + end + + it 'allows the memoized method to be called within the initializer' do + subject + expect { object.new }.to_not raise_error + end + end + end + + context 'on method with arguments' do + let(:method) { :argumented } + + it 'should raise error' do + expect { subject }.to raise_error( + ArgumentError, "Cannot memoize #{object}#argumented, its arity is 1" + ) + end + end + + context 'memoized method that returns generated values' do + let(:method) { :some_state } + + it_should_behave_like 'a command method' + it_should_behave_like 'memoizes method' + it_should_behave_like 'wraps original method' + + it 'creates a method that returns a frozen value' do + subject + expect(object.new.send(method)).to be_frozen + end + + it 'creates a method that returns expected value' do + subject + + expect(object.new.some_state).to eql('') + end + + it 'creates a method that returns same value for each call' do + subject + + instance = object.new + + expect(instance.some_state).to be(instance.some_state) + end + + it 'does not get confused with sibling memoized methods' do + subject + + instance = object.new + instance.some_other_state + + expect(instance.some_other_state).to_not be(instance.some_state) + + expect(instance.some_state).to be(instance.some_state) + end + + it 'does not allow repated memoization' do + subject + + expect { subject.memoize(method) }.to raise_error( + ArgumentError, + "##{method} is already memoized" + ) + end + end + + context 'public method' do + let(:method) { :public_method } + + it_should_behave_like 'a command method' + it_should_behave_like 'memoizes method' + it_should_behave_like 'wraps original method' + + it 'is still a public method' do + should be_public_method_defined(method) + end + + it 'creates a method that returns a frozen value' do + subject + expect(object.new.send(method)).to be_frozen + end + end + + context 'protected method' do + let(:method) { :protected_method } + + it_should_behave_like 'a command method' + it_should_behave_like 'memoizes method' + + it 'is still a protected method' do + should be_protected_method_defined(method) + end + + it 'creates a method that returns a frozen value' do + subject + expect(object.new.send(method)).to be_frozen + end + end + + context 'private method' do + let(:method) { :private_method } + + it_should_behave_like 'a command method' + it_should_behave_like 'memoizes method' + + it 'is still a private method' do + should be_private_method_defined(method) + end + + it 'creates a method that returns a frozen value' do + subject + expect(object.new.send(method)).to be_frozen + end + end + end +end diff --git a/spec/unit/unparser/anima/attribute_spec.rb b/spec/unit/unparser/anima/attribute_spec.rb new file mode 100644 index 00000000..deead1a6 --- /dev/null +++ b/spec/unit/unparser/anima/attribute_spec.rb @@ -0,0 +1,62 @@ +describe Unparser::Anima::Attribute do + let(:object) { described_class.new(:foo) } + + describe '#get' do + subject { object.get(target) } + + let(:target_class) do + Class.new do + attr_reader :foo + + def initialize(foo) + @foo = foo + end + end + end + + let(:target) { target_class.new(value) } + let(:value) { double('Value') } + + it 'should return value' do + should be(value) + end + end + + describe '#load' do + subject { object.load(target, attribute_hash) } + + let(:target) { Object.new } + let(:value) { double('Value') } + let(:attribute_hash) { { foo: value } } + + it 'should set value as instance variable' do + subject + expect(target.instance_variable_get(:@foo)).to be(value) + end + + it_should_behave_like 'a command method' + end + + describe '#instance_variable_name' do + subject { object.instance_variable_name } + + it { should be(:@foo) } + + it_should_behave_like 'an idempotent method' + end + + describe '#set' do + subject { object.set(target, value) } + + let(:target) { Object.new } + + let(:value) { double('Value') } + + it_should_behave_like 'a command method' + + it 'should set value as instance variable' do + subject + expect(target.instance_variable_get(:@foo)).to be(value) + end + end +end diff --git a/spec/unit/unparser/anima/error_spec.rb b/spec/unit/unparser/anima/error_spec.rb new file mode 100644 index 00000000..4724b3a3 --- /dev/null +++ b/spec/unit/unparser/anima/error_spec.rb @@ -0,0 +1,16 @@ +describe Unparser::Anima::Error do + describe '#message' do + let(:object) { described_class.new(Unparser::Anima, missing, unknown) } + + let(:missing) { %i[missing] } + let(:unknown) { %i[unknown] } + + subject { object.message } + + it 'should return the message string' do + should eql('Unparser::Anima attributes missing: [:missing], unknown: [:unknown]') + end + + it_should_behave_like 'an idempotent method' + end +end diff --git a/spec/unit/unparser/anima_spec.rb b/spec/unit/unparser/anima_spec.rb new file mode 100644 index 00000000..ff3fd1cd --- /dev/null +++ b/spec/unit/unparser/anima_spec.rb @@ -0,0 +1,210 @@ +describe Unparser::Anima do + let(:object) { described_class.new(:foo) } + + describe '#attributes_hash' do + let(:value) { double('Value') } + let(:instance) { double(foo: value) } + + subject { object.attributes_hash(instance) } + + it { should eql(foo: value) } + end + + describe '#remove' do + let(:object) { described_class.new(:foo, :bar) } + + context 'with single attribute' do + subject { object.remove(:bar) } + + it { should eql(described_class.new(:foo)) } + end + + context 'with multiple attributes' do + subject { object.remove(:foo, :bar) } + + it { should eql(described_class.new) } + end + + context 'with inexisting attribute' do + subject { object.remove(:baz) } + + it { should eql(object) } + end + end + + describe '#add' do + context 'with single attribute' do + subject { object.add(:bar) } + + it { should eql(described_class.new(:foo, :bar)) } + end + + context 'with multiple attributes' do + subject { object.add(:bar, :baz) } + + it { should eql(described_class.new(:foo, :bar, :baz)) } + end + + context 'with duplicate attribute ' do + subject { object.add(:foo) } + + it { should eql(object) } + end + end + + describe '#attributes' do + subject { object.attributes } + + it { should eql([Unparser::Anima::Attribute.new(:foo)]) } + it { should be_frozen } + end + + describe '#included' do + let(:target) do + object = self.object + Class.new do + include object + end + end + + let(:value) { double('Value') } + let(:instance) { target.new(foo: value) } + let(:instance_b) { target.new(foo: value) } + let(:instance_c) { target.new(foo: double('Bar')) } + + context 'on instance' do + subject { instance } + + it { should eql(instance_b) } + it { should_not eql(instance_c) } + + it 'returns expected value' do + expect(instance.foo).to be(value) + end + end + + context 'on singleton' do + subject { target } + + it 'should define attribute hash reader' do + expect(instance.to_h).to eql(foo: value) + end + + specify { expect(subject.anima).to be(object) } + end + end + + describe '#initialize_instance' do + let(:object) { Unparser::Anima.new(:foo, :bar) } + let(:target) { Object.new } + + let(:foo) { double('Foo') } + let(:bar) { double('Bar') } + + subject { object.initialize_instance(target, attribute_hash) } + + context 'when all keys are present in attribute hash' do + let(:attribute_hash) { { foo: foo, bar: bar } } + + it 'should initialize target instance variables' do + subject + + expect( + target + .instance_variables + .map(&:to_sym) + .to_set + ).to eql(%i[@foo @bar].to_set) + expect(target.instance_variable_get(:@foo)).to be(foo) + expect(target.instance_variable_get(:@bar)).to be(bar) + end + + it_should_behave_like 'a command method' + end + + context 'when an extra key is present in attribute hash' do + let(:attribute_hash) { { foo: foo, bar: bar, baz: double('Baz') } } + + it 'should raise error' do + expect { subject }.to raise_error( + Unparser::Anima::Error, + Unparser::Anima::Error.new(target.class, [], [:baz]).message + ) + end + + context 'and the extra key is falsy' do + let(:attribute_hash) { { foo: foo, bar: bar, nil => double('Baz') } } + + it 'should raise error' do + expect { subject }.to raise_error( + Unparser::Anima::Error, + Unparser::Anima::Error.new(target.class, [], [nil]).message + ) + end + end + end + + context 'when a key is missing in attribute hash' do + let(:attribute_hash) { { bar: bar } } + + it 'should raise error' do + expect { subject }.to raise_error( + Unparser::Anima::Error.new(target.class, [:foo], []).message + ) + end + end + end + + describe 'using super in initialize' do + subject { klass.new } + + let(:klass) do + Class.new do + include Unparser::Anima.new(:foo) + def initialize(attributes = { foo: :bar }) + super + end + end + end + + specify { expect(subject.foo).to eql(:bar) } + end + + describe '#to_h on an anima infected instance' do + subject { instance.to_h } + + let(:instance) { klass.new(params) } + let(:params) { Hash[foo: :bar] } + let(:klass) do + Class.new do + include Unparser::Anima.new(:foo) + end + end + + it { should eql(params) } + end + + describe '#with' do + subject { object.with(attributes) } + + let(:klass) do + Class.new do + include Unparser::Anima.new(:foo, :bar) + end + end + + let(:object) { klass.new(foo: 1, bar: 2) } + + context 'with empty attributes' do + let(:attributes) { {} } + + it { should eql(object) } + end + + context 'with updated attribute' do + let(:attributes) { { foo: 3 } } + + it { should eql(klass.new(foo: 3, bar: 2)) } + end + end +end diff --git a/spec/unit/unparser/concord_spec.rb b/spec/unit/unparser/concord_spec.rb new file mode 100644 index 00000000..548b84a3 --- /dev/null +++ b/spec/unit/unparser/concord_spec.rb @@ -0,0 +1,143 @@ +RSpec.describe Unparser::Concord do + let(:class_under_test) do + Class.new do + include Unparser::Concord.new(:foo, :bar) + end + end + + let(:instance_a) { class_under_test.new(foo, bar) } + let(:instance_b) { class_under_test.new(foo, bar) } + let(:instance_c) { class_under_test.new(foo, double('Baz')) } + + let(:foo) { double('Foo') } + let(:bar) { double('Bar') } + + context 'initializer' do + it 'creates a private #initialize method' do + mod = Module.new + expect { mod.send(:include, Unparser::Concord.new) } + .to change { mod.private_method_defined?(:initialize) } + .from(false).to(true) + end + + it 'does not cause warnings' do + begin + original = $stderr + $stderr = StringIO.new + Class.new do + include Unparser::Concord.new + end + expect($stderr.tap(&:rewind).read).to eql('') + ensure + $stderr = original + end + end + + it 'creates an initializer that asserts the number of arguments' do + expect { class_under_test.new(1) } + .to raise_error(ArgumentError, 'wrong number of arguments (1 for 2)') + end + + it 'creates an initializer that allows 2 arguments' do + expect { class_under_test.new(1, 2) }.to_not raise_error + end + + it 'creates an initializer that is callable via super' do + class_under_test.class_eval do + attr_reader :baz + public :foo + public :bar + + def initialize(foo, bar) + @baz = foo + bar + super(foo, bar) + end + end + + instance = class_under_test.new(1, 2) + expect(instance.foo).to eql(1) + expect(instance.bar).to eql(2) + expect(instance.baz).to eql(3) + end + + it 'creates an initializer that is callable via zsuper' do + class_under_test.class_eval do + attr_reader :baz + public :foo + public :bar + + def initialize(foo, bar) + @baz = foo + bar + super + end + end + + instance = class_under_test.new(1, 2) + expect(instance.foo).to eql(1) + expect(instance.bar).to eql(2) + expect(instance.baz).to eql(3) + end + + it 'creates an initializer that sets the instance variables' do + instance = class_under_test.new(1, 2) + expect(instance.instance_variable_get(:@foo)).to be(1) + expect(instance.instance_variable_get(:@bar)).to be(2) + end + end + + context 'with no objects to compose' do + it 'assigns no ivars' do + instance = Class.new { include Unparser::Concord.new }.new + expect(instance.instance_variables).to be_empty + end + end + + context 'visibility' do + it 'should set attribute readers to protected' do + protected_methods = class_under_test.protected_instance_methods + expect(protected_methods).to match_array([:foo, :bar]) + end + end + + context 'attribute behavior' do + subject { instance_a } + + specify { expect(subject.send(:foo)).to be(foo) } + specify { expect(subject.send(:bar)).to be(bar) } + end + + context 'equalization behavior' do + specify 'composed objects are equalized on attributes' do + expect(instance_a).to eql(instance_b) + expect(instance_a.hash).to eql(instance_b.hash) + expect(instance_a).to eql(instance_b) + expect(instance_a).to_not be(instance_c) + expect(instance_a).to_not eql(instance_c) + end + end + + context 'when composing too many objects' do + specify 'it raises an error' do + expect do + Unparser::Concord.new(:a, :b, :c, :d) + end.to raise_error(RuntimeError, 'Composition of more than 3 objects is not allowed') + expect do + Unparser::Concord.new(:a, :b, :c) + end.to_not raise_error + end + end + + context Unparser::Concord::Public do + let(:class_under_test) do + Class.new do + include Unparser::Concord::Public.new(:foo, :bar) + end + end + + it 'should create public attr readers' do + object = class_under_test.new(:foo, :bar) + expect(object.foo).to eql(:foo) + expect(object.bar).to eql(:bar) + end + end +end diff --git a/spec/unit/unparser/either_spec.rb b/spec/unit/unparser/either_spec.rb new file mode 100644 index 00000000..5dd7bcc9 --- /dev/null +++ b/spec/unit/unparser/either_spec.rb @@ -0,0 +1,378 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'no block evaluation' do + context 'with block' do + let(:block) { -> { fail } } + + it 'does not evaluate block' do + apply + end + end +end + +RSpec.shared_examples 'requires block' do + context 'without block' do + let(:block) { nil } + + specify do + expect { apply }.to raise_error(LocalJumpError) + end + end +end + +RSpec.shared_examples 'returns self' do + it 'returns self' do + expect(apply).to be(subject) + end +end + +RSpec.shared_examples '#bind block evaluation' do + it 'evaluates block and returns its wrapped result' do + expect { expect(apply).to eql(block_result) } + .to change(yields, :to_a) + .from([]) + .to([value]) + end +end + +RSpec.shared_examples 'Functor#fmap block evaluation' do + it 'evaluates block and returns its wrapped result' do + expect { expect(apply).to eql(described_class.new(block_result)) } + .to change(yields, :to_a) + .from([]) + .to([value]) + end +end + +RSpec.describe Unparser::Either do + describe '.wrap_error' do + let(:block) { -> { fail error } } + let(:error) { exception.new } + let(:exception) { TestError } + let(:other_exception) { OtherTestError } + + class TestError < RuntimeError; end + + class OtherTestError < RuntimeError; end + + shared_examples 'block returns' do + let(:value) { instance_double(Object, 'value') } + let(:block) { -> { value } } + + it 'returns right wrapping block value' do + expect(apply).to eql(described_class::Right.new(value)) + end + end + + shared_examples 'covered exception' do + it 'returns left wrapping exception' do + expect(apply).to eql(described_class::Left.new(error)) + end + end + + shared_examples 'uncovered exception' do + let(:unexpected_exception) { StandardError } + + let(:block) { -> { fail unexpected_exception } } + + it 'returns raises error' do + expect { apply }.to raise_error(unexpected_exception) + end + end + + context 'on single exception argument' do + def apply + described_class.wrap_error(exception, &block) + end + + context 'when block returns' do + include_examples 'block returns' + end + + context 'when block raises' do + context 'with covered exception' do + include_examples 'covered exception' + end + + context 'with uncovered exception' do + include_examples 'uncovered exception' + end + end + end + + context 'on multiple exception arguments' do + def apply + described_class.wrap_error(exception, other_exception, &block) + end + + context 'when block returns' do + include_examples 'block returns' + end + + context 'when block raises' do + context 'with covered exception' do + include_examples 'covered exception' + end + + context 'with uncovered exception' do + include_examples 'uncovered exception' + end + + context 'with other covered exception' do + let(:block) { -> { fail other_error } } + let(:other_error) { other_exception.new } + + it 'returns left wrapping exception' do + expect(apply).to eql(described_class::Left.new(other_error)) + end + end + end + end + end +end + +RSpec.describe Unparser::Either::Left do + subject { described_class.new(value) } + + let(:block_result) { instance_double(Object, 'block result') } + let(:value) { instance_double(Object, 'value') } + let(:yields) { [] } + + let(:block) do + lambda do |value| + yields << value + block_result + end + end + + class TestError < RuntimeError; end + + describe '#fmap' do + def apply + subject.fmap(&block) + end + + include_examples 'no block evaluation' + include_examples 'requires block' + include_examples 'returns self' + end + + describe '#bind' do + def apply + subject.bind(&block) + end + + include_examples 'no block evaluation' + include_examples 'requires block' + include_examples 'returns self' + end + + describe '#from_left' do + def apply + subject.from_left(&block) + end + + it 'returns left value' do + expect(apply).to be(value) + end + + include_examples 'no block evaluation' + end + + describe '#from_right' do + def apply + subject.from_right(&block) + end + + context 'without block' do + let(:block) { nil } + + it 'raises RuntimeError error' do + expect { apply }.to raise_error( + RuntimeError, + "Expected right value, got #{subject.inspect}" + ) + end + end + + context 'with block' do + let(:yields) { [] } + let(:block_return) { instance_double(Object, 'block-return') } + + let(:block) do + lambda do |value| + yields << value + block_return + end + end + + it 'calls block with left value' do + expect { apply }.to change(yields, :to_a).from([]).to([value]) + end + + it 'returns block value' do + expect(apply).to be(block_return) + end + end + end + + describe '#lmap' do + def apply + subject.lmap(&block) + end + + include_examples 'requires block' + include_examples 'Functor#fmap block evaluation' + end + + describe '#either' do + def apply + subject.either(block, -> { fail }) + end + + include_examples '#bind block evaluation' + end + + describe '#left?' do + def apply + subject.left? + end + + it 'returns true' do + expect(apply).to be(true) + end + end + + describe '#right?' do + def apply + subject.right? + end + + it 'returns false' do + expect(apply).to be(false) + end + end +end + +RSpec.describe Unparser::Either::Right do + subject { described_class.new(value) } + + let(:block_result) { instance_double(Object, 'block result') } + let(:value) { instance_double(Object, 'value') } + let(:yields) { [] } + + let(:block) do + lambda do |value| + yields << value + block_result + end + end + + describe '#fmap' do + def apply + subject.fmap(&block) + end + + include_examples 'requires block' + include_examples 'Functor#fmap block evaluation' + end + + describe '#bind' do + def apply + subject.bind(&block) + end + + include_examples 'requires block' + include_examples '#bind block evaluation' + end + + describe '#from_left' do + def apply + subject.from_left(&block) + end + + context 'without block' do + let(:block) { nil } + + it 'raises RuntimeError error' do + expect { apply }.to raise_error( + RuntimeError, + "Expected left value, got #{subject.inspect}" + ) + end + end + + context 'with block' do + let(:yields) { [] } + let(:block_return) { instance_double(Object, 'block-return') } + + let(:block) do + lambda do |value| + yields << value + block_return + end + end + + it 'calls block with right value' do + expect { apply }.to change(yields, :to_a).from([]).to([value]) + end + + it 'returns block value' do + expect(apply).to be(block_return) + end + end + end + + describe '#from_right' do + def apply + subject.from_right(&block) + end + + it 'returns right value' do + expect(apply).to be(value) + end + + include_examples 'no block evaluation' + end + + describe '#lmap' do + def apply + subject.lmap(&block) + end + + include_examples 'requires block' + include_examples 'no block evaluation' + + it 'returns self' do + expect(apply).to be(subject) + end + end + + describe '#either' do + def apply + subject.either(-> { fail }, block) + end + + include_examples '#bind block evaluation' + end + + describe '#left?' do + def apply + subject.left? + end + + it 'returns false' do + expect(apply).to be(false) + end + end + + describe '#right?' do + def apply + subject.right? + end + + it 'returns true' do + expect(apply).to be(true) + end + end +end diff --git a/spec/unit/unparser/equalizer_spec.rb b/spec/unit/unparser/equalizer_spec.rb new file mode 100644 index 00000000..b4a272a3 --- /dev/null +++ b/spec/unit/unparser/equalizer_spec.rb @@ -0,0 +1,165 @@ +describe Unparser::Equalizer, '.new' do + let(:object) { described_class } + let(:name) { 'User' } + let(:klass) { ::Class.new } + + context 'with no keys' do + subject { object.new } + + before do + # specify the class #name method + allow(klass).to receive(:name).and_return(name) + klass.send(:include, subject) + end + + let(:instance) { klass.new } + + it { should be_instance_of(object) } + + it { should be_frozen } + + it 'defines #hash and #inspect methods dynamically' do + expect(subject.public_instance_methods(false).map(&:to_s).sort) + .to eql(%w[hash inspect]) + end + + describe '#eql?' do + context 'when the objects are similar' do + let(:other) { instance.dup } + + it { expect(instance.eql?(other)).to be(true) } + end + + context 'when the objects are different' do + let(:other) { double('other') } + + it { expect(instance.eql?(other)).to be(false) } + end + end + + describe '#==' do + context 'when the objects are similar' do + let(:other) { instance.dup } + + it { expect(instance == other).to be(true) } + end + + context 'when the objects are different' do + let(:other) { double('other') } + + it { expect(instance == other).to be(false) } + end + end + + describe '#hash' do + it 'has the expected arity' do + expect(klass.instance_method(:hash).arity).to be(0) + end + + it { expect(instance.hash).to eql([klass].hash) } + end + + describe '#inspect' do + it 'has the expected arity' do + expect(klass.instance_method(:inspect).arity).to be(0) + end + + it { expect(instance.inspect).to eql('#') } + end + end + + context 'with keys' do + subject { object.new(*keys) } + + let(:keys) { %i[firstname lastname].freeze } + let(:firstname) { 'John' } + let(:lastname) { 'Doe' } + let(:instance) { klass.new(firstname, lastname) } + + let(:klass) do + ::Class.new do + attr_reader :firstname, :lastname + private :firstname, :lastname + + def initialize(firstname, lastname) + @firstname = firstname + @lastname = lastname + end + end + end + + before do + # specify the class #inspect method + allow(klass).to receive_messages(name: nil, inspect: name) + klass.send(:include, subject) + end + + it { should be_instance_of(object) } + + it { should be_frozen } + + it 'defines #hash and #inspect methods dynamically' do + expect(subject.public_instance_methods(false).map(&:to_s).sort) + .to eql(%w[hash inspect]) + end + + describe '#eql?' do + context 'when the objects are of the same class with the same values' do + let(:other) { instance.dup } + + it { expect(instance.eql?(other)).to be(true) } + end + + context 'when the objects are of the same class with different values' do + let(:other) { klass.new('Sue', 'Doe') } + + it { expect(instance.eql?(other)).to be(false) } + end + + context 'when the objects are different classes' do + let(:other) { double('other') } + + it { expect(instance.eql?(other)).to be(false) } + end + end + + describe '#==' do + context 'when the objects of the same class with the same values' do + let(:other) { instance.dup } + + it { expect(instance == other).to be(true) } + end + + context 'when the objects are of the same class with different values' do + let(:other) { klass.new('Sue', 'Doe') } + + it { expect(instance.eql?(other)).to be(false) } + end + + context 'when the objects are different type' do + let(:other) { klass.new('Foo', 'Bar') } + + it { expect(instance == other).to be(false) } + end + + context 'when the objects are from different type' do + let(:other) { double('other') } + + it { expect(instance == other).to be(false) } + end + end + + describe '#hash' do + it 'returns the expected hash' do + expect(instance.hash).to eql([firstname, lastname, klass].hash) + end + end + + describe '#inspect' do + it 'returns the expected string' do + expect(instance.inspect) + .to eql('#') + end + end + end +end diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index 14f17582..f4a8f45c 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -11,14 +11,6 @@ ) end - def right(value) - MPrelude::Either::Right.new(value) - end - - def left(value) - MPrelude::Either::Left.new(value) - end - let(:generated_node) { right(s(:send, s(:int, 1), :foo)) } let(:generated_source) { right('1.foo') } let(:identification) { 'example-identification' } diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 9174778e..d6eaaf1d 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -87,7 +87,7 @@ def apply let(:source) { 'self[1]=2' } it 'returns right value with expected AST' do - expect(apply).to eql(MPrelude::Either::Right.new(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2)))) + expect(apply).to eql(right(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2)))) end end @@ -95,7 +95,7 @@ def apply let(:source) { '' } it 'returns right value with nil' do - expect(apply).to eql(MPrelude::Either::Right.new(nil)) + expect(apply).to eql(right(nil)) end end @@ -106,7 +106,7 @@ def apply result = apply # Syntax errors that compare nicely under #eql? are hard to construct - expect(result).to be_instance_of(MPrelude::Either::Left) + expect(result).to be_instance_of(Unparser::Either::Left) expect(result.from_left).to be_instance_of(Parser::SyntaxError) end end @@ -126,13 +126,13 @@ def apply end it 'returns right value with generated source' do - expect(apply).to eql(MPrelude::Either::Right.new('true # foo')) + expect(apply).to eql(right('true # foo')) end end context 'without comments' do it 'returns right value with generated source' do - expect(apply).to eql(MPrelude::Either::Right.new('true')) + expect(apply).to eql(right('true')) end end end @@ -147,7 +147,7 @@ def apply end it 'returns left value with validation' do - expect(apply).to eql(MPrelude::Either::Left.new(validation)) + expect(apply).to eql(left(validation)) end end end diff --git a/test/run-parser-tests.rb b/test/run-parser-tests.rb index b2d4395c..387b9b06 100644 --- a/test/run-parser-tests.rb +++ b/test/run-parser-tests.rb @@ -18,7 +18,7 @@ def default_builder_attributes end class Test - include Adamantium::Flat, Anima.new( + include Adamantium, Anima.new( :default_builder_attributes, :group_index, :name, @@ -61,7 +61,7 @@ def allow_ruby? end def right(value) - MPrelude::Either::Right.new(value) + Unparser::Either::Right.new(value) end def validation @@ -89,7 +89,7 @@ def parser end def parse_either(source, identification) - MPrelude::Either.wrap_error(Parser::SyntaxError) do + Unparser::Either.wrap_error(Parser::SyntaxError) do parser.parse(Unparser.buffer(source, identification)) end end diff --git a/unparser.gemspec b/unparser.gemspec index b98fe8da..5bf3e7c7 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.5.7' + gem.version = '0.6.0' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -23,18 +23,11 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.5' - gem.add_dependency('abstract_type', '~> 0.0.7') - gem.add_dependency('adamantium', '~> 0.2.0') - gem.add_dependency('anima', '~> 0.3.1') - gem.add_dependency('concord', '~> 0.1.5') - gem.add_dependency('diff-lcs', '~> 1.3') - gem.add_dependency('equalizer', '~> 0.0.9') - gem.add_dependency('mprelude', '~> 0.1.0') - gem.add_dependency('parser', '>= 3.0.0') - gem.add_dependency('procto', '~> 0.0.2') + gem.add_dependency('diff-lcs', '~> 1.3') + gem.add_dependency('parser', '>= 3.0.0') - gem.add_development_dependency('mutant', '~> 0.10.22') - gem.add_development_dependency('mutant-rspec', '~> 0.10.22') + gem.add_development_dependency('mutant', '~> 0.10.25') + gem.add_development_dependency('mutant-rspec', '~> 0.10.25') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 4594394a3ad79897bd3806ede6212bb5cee5dcda Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 16 Jan 2021 03:24:20 +0000 Subject: [PATCH 153/256] Add mutant to CI steps --- .github/workflows/ci.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f8ed5801..f2789507 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,24 @@ jobs: bundler-cache: true ruby-version: ${{ matrix.ruby }} - run: bundle exec rspec spec/unit + ruby-mutant: + name: Mutant + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] + os: [macos-latest, ubuntu-latest] + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ${{ matrix.ruby }} + - run: bundle exec mutant run --since origin/master --zombie ruby-integration-spec: name: Integration Specs runs-on: ${{ matrix.os }} From d25e1e5e5932eac40fdb7cf8a9c51f7614551043 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 3 May 2021 14:38:47 +0000 Subject: [PATCH 154/256] Upgrade dependencies --- Gemfile | 2 -- Gemfile.lock | 54 ++++++++++++++----------------- lib/unparser/writer/send.rb | 4 +-- lib/unparser/writer/send/unary.rb | 2 +- unparser.gemspec | 4 +-- 5 files changed, 30 insertions(+), 36 deletions(-) diff --git a/Gemfile b/Gemfile index 0c4ba0c5..1419592e 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,6 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'upgrade/unparser' - source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index b0d66a94..72f05595 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,18 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant - revision: d151dd5fdee91695fc0fde7591d498e6e27c7355 - branch: upgrade/unparser - specs: - mutant (0.10.25) - ast (~> 2.2) - diff-lcs (~> 1.3) - parser (~> 3.0.0) - regexp_parser (~> 2.0, >= 2.0.3) - unparser (~> 0.6.0) - mutant-rspec (0.10.25) - mutant (= 0.10.25) - rspec-core (>= 3.8.0, < 4.0.0) - PATH remote: . specs: @@ -21,18 +6,29 @@ PATH parser (>= 3.0.0) GEM - remote: https://rubygems.org/ remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ specs: - ast (2.4.1) + mutant-license (0.1.1.2.2355046999240944981729280251890364410689.5) + +GEM + remote: https://rubygems.org/ + specs: + ast (2.4.2) diff-lcs (1.4.4) - mutant-license (0.1.1.2.2355046999240944981729280251890364410689.4) + mutant (0.10.30) + diff-lcs (~> 1.3) + parser (~> 3.0.0) + regexp_parser (~> 2.0, >= 2.0.3) + unparser (~> 0.6.0) + mutant-rspec (0.10.30) + mutant (= 0.10.30) + rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) - parser (3.0.0.0) + parser (3.0.1.1) ast (~> 2.4.1) rainbow (3.0.0) - regexp_parser (2.0.3) - rexml (3.2.4) + regexp_parser (2.1.1) + rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) @@ -45,11 +41,11 @@ GEM rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.10.1) + rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-support (3.10.1) - rubocop (1.8.1) + rspec-support (3.10.2) + rubocop (1.13.0) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) @@ -58,8 +54,8 @@ GEM rubocop-ast (>= 1.2.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.4.0) - parser (>= 2.7.1.5) + rubocop-ast (1.5.0) + parser (>= 3.0.1.1) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) @@ -69,9 +65,9 @@ PLATFORMS ruby DEPENDENCIES - mutant! + mutant (~> 0.10.30) mutant-license! - mutant-rspec (~> 0.10.25) + mutant-rspec (~> 0.10.30) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) @@ -80,4 +76,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.2.5 + 2.2.15 diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index cabef7b9..413bdc4f 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -6,8 +6,8 @@ module Writer class Send include Writer, Adamantium, Constants, Generation - INDEX_ASSIGN = :'[]=' - INDEX_REFERENCE = :'[]' + INDEX_ASSIGN = :[]= + INDEX_REFERENCE = :[] OPERATORS = { csend: '&.', diff --git a/lib/unparser/writer/send/unary.rb b/lib/unparser/writer/send/unary.rb index c7ff4f1f..ed409611 100644 --- a/lib/unparser/writer/send/unary.rb +++ b/lib/unparser/writer/send/unary.rb @@ -17,7 +17,7 @@ def dispatch write(MAP.fetch(name, name).to_s) - if n_int?(receiver) && selector.equal?(:'+@') + if n_int?(receiver) && selector.equal?(:+@) write('+') end diff --git a/unparser.gemspec b/unparser.gemspec index 5bf3e7c7..98170392 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.0.0') - gem.add_development_dependency('mutant', '~> 0.10.25') - gem.add_development_dependency('mutant-rspec', '~> 0.10.25') + gem.add_development_dependency('mutant', '~> 0.10.30') + gem.add_development_dependency('mutant-rspec', '~> 0.10.30') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 26578df5c564020fb681e684ac30ea0e0b9c764c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 May 2021 05:01:36 +0000 Subject: [PATCH 155/256] Bump rubocop from 1.13.0 to 1.14.0 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.13.0 to 1.14.0. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.13.0...v1.14.0) Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 72f05595..10c9c573 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -45,13 +45,13 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.2) - rubocop (1.13.0) + rubocop (1.14.0) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.2.0, < 2.0) + rubocop-ast (>= 1.5.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) rubocop-ast (1.5.0) From 6da0899cd7a7ee0e7f566ee3f1dfbad7ad0999eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 05:07:43 +0000 Subject: [PATCH 156/256] Bump mutant from 0.10.30 to 0.10.32 Bumps [mutant](https://github.com/mbj/mutant) from 0.10.30 to 0.10.32. - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/master/Changelog.md) - [Commits](https://github.com/mbj/mutant/commits/v0.10.32) Signed-off-by: dependabot[bot] --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 10c9c573..51edc3f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,13 +15,13 @@ GEM specs: ast (2.4.2) diff-lcs (1.4.4) - mutant (0.10.30) + mutant (0.10.32) diff-lcs (~> 1.3) parser (~> 3.0.0) regexp_parser (~> 2.0, >= 2.0.3) unparser (~> 0.6.0) - mutant-rspec (0.10.30) - mutant (= 0.10.30) + mutant-rspec (0.10.32) + mutant (= 0.10.32) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.1.1) From 52a93dbdaf85642b252082dc75e068f61a14f193 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 29 Aug 2021 21:02:23 +0000 Subject: [PATCH 157/256] Change to use commit local incremental on CI --- .github/workflows/ci.yml | 7 +++++-- scripts/devloop.sh | 2 +- spec/integrations.yml | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2789507..b5fdb246 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,9 @@ name: CI -on: push +on: + pull_request: {} + push: + branches: main jobs: base: @@ -43,7 +46,7 @@ jobs: with: bundler-cache: true ruby-version: ${{ matrix.ruby }} - - run: bundle exec mutant run --since origin/master --zombie + - run: bundle exec mutant run --since HEAD~1 --zombie ruby-integration-spec: name: Integration Specs runs-on: ${{ matrix.os }} diff --git a/scripts/devloop.sh b/scripts/devloop.sh index b7bca114..03ee4b69 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,5 +1,5 @@ while inotifywait ../mutant/**/*.rb **/*.rb Gemfile unparser.gemspec; do bundle exec rspec spec/unit -fd --fail-fast --order default \ - && bundle exec mutant run --zombie --since master --fail-fast -- 'Unparser*' \ + && bundle exec mutant run --zombie --since HEAD~1 --fail-fast -- 'Unparser*' \ && bundle exec rubocop done diff --git a/spec/integrations.yml b/spec/integrations.yml index 9efa123c..c315fbee 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -5,7 +5,7 @@ exclude: [] - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' - repo_ref: 'origin/master' + repo_ref: 'origin/main' exclude: [] - name: yaks repo_uri: 'https://github.com/plexus/yaks.git' From 62cb8dbc7878efb22f6454469b9fa49bf0670b16 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 30 Aug 2021 02:52:06 +0000 Subject: [PATCH 158/256] Remove ruby 2.5 support --- .github/workflows/ci.yml | 8 ++++---- Changelog.md | 10 ++++++++-- README.md | 4 ++-- lib/unparser/buffer.rb | 4 ++-- lib/unparser/emitter/hash_pattern.rb | 2 +- lib/unparser/node_details/send.rb | 2 +- test/run-parser-tests.rb | 2 +- unparser.gemspec | 2 +- 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b5fdb246..4334a3b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.5, ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/Changelog.md b/Changelog.md index 025fca82..fa187073 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,12 @@ -# v0.6.0 unreleased +# v0.6.1 unreleased -[245](https://github.com/mbj/unparser/pull/245) +[#268](https://github.com/mbj/unparser/pull/268) + +* Remove ruby 2.5 support since its EOL. + +# v0.6.0 2021-01-06 + +[#245](https://github.com/mbj/unparser/pull/245) * Change to raise Unparser::InvalidNode error in some cases when unparsing invalid AST. * Change `Unparser.unparse` into an official public API. diff --git a/README.md b/README.md index 0550c0c0..4ecc9a02 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 2.5 +* Only support for Ruby >= 2.6 Notable Users: @@ -110,7 +110,7 @@ If you need to generate Ruby Syntax outside of this band feel free to contact me Testing: -------- -Unparser currently successfully round trips almost all ruby code around. Using MRI-2.5.x. +Unparser currently successfully round trips almost all ruby code around. Using Ruby >= 2.6. If there is a non round trippable example that is NOT subjected to known [Limitations](#limitations). please report a bug. diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index 19674f15..30f331a6 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -114,9 +114,9 @@ def content # @api private # def capture_content - size_before = @content.size + size_before = content.size yield - @content[size_before..-1] + content[size_before..] end # Write raw fragment to buffer diff --git a/lib/unparser/emitter/hash_pattern.rb b/lib/unparser/emitter/hash_pattern.rb index 9e68dffb..c03fa91d 100644 --- a/lib/unparser/emitter/hash_pattern.rb +++ b/lib/unparser/emitter/hash_pattern.rb @@ -60,7 +60,7 @@ def emit_pair(node) end def write_symbol_body(symbol) - write(symbol.inspect[1..-1]) + write(symbol.inspect[1..]) end end # Pin end # Emitter diff --git a/lib/unparser/node_details/send.rb b/lib/unparser/node_details/send.rb index ac36f2f7..6bcc8e14 100644 --- a/lib/unparser/node_details/send.rb +++ b/lib/unparser/node_details/send.rb @@ -48,7 +48,7 @@ def assignment? memoize :assignment? def arguments - children[2..-1] + children[2..] end memoize :arguments diff --git a/test/run-parser-tests.rb b/test/run-parser-tests.rb index 387b9b06..4a154f64 100644 --- a/test/run-parser-tests.rb +++ b/test/run-parser-tests.rb @@ -27,7 +27,7 @@ class Test :rubies ) - TARGET_RUBIES = %w[2.5 2.6 2.7] + TARGET_RUBIES = %w[2.6 2.7] EXPECT_FAILURE = {}.freeze diff --git a/unparser.gemspec b/unparser.gemspec index 98170392..a6463534 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 2.5' + gem.required_ruby_version = '>= 2.6' gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.0.0') From 8dcc67f597fc088090300331c53315d4526fcbc8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 03:01:56 +0000 Subject: [PATCH 159/256] Bump parser from 3.0.1.1 to 3.0.2.0 Bumps [parser](https://github.com/whitequark/parser) from 3.0.1.1 to 3.0.2.0. - [Release notes](https://github.com/whitequark/parser/releases) - [Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/whitequark/parser/compare/v3.0.1.1...v3.0.2.0) --- updated-dependencies: - dependency-name: parser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 51edc3f8..3c80d652 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,7 +24,7 @@ GEM mutant (= 0.10.32) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) - parser (3.0.1.1) + parser (3.0.2.0) ast (~> 2.4.1) rainbow (3.0.0) regexp_parser (2.1.1) From 392c7a298bc8086bfb2a3a4144b0becce41aa2d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Oct 2021 08:01:09 +0000 Subject: [PATCH 160/256] Update requirements for mutant-rspec and mutant Updates the requirements on [mutant-rspec](https://github.com/mbj/mutant) and [mutant](https://github.com/mbj/mutant) to permit the latest version. Updates `mutant-rspec` from 0.10.32 to 0.11.0 - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/main/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.32...v0.11.0) Updates `mutant` from 0.10.32 to 0.11.0 - [Release notes](https://github.com/mbj/mutant/releases) - [Changelog](https://github.com/mbj/mutant/blob/main/Changelog.md) - [Commits](https://github.com/mbj/mutant/compare/v0.10.32...v0.11.0) --- updated-dependencies: - dependency-name: mutant-rspec dependency-type: direct:development update-type: version-update:semver-minor - dependency-name: mutant dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 12 +++++++----- unparser.gemspec | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3c80d652..f5e9291d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,13 +15,14 @@ GEM specs: ast (2.4.2) diff-lcs (1.4.4) - mutant (0.10.32) + mutant (0.11.0) diff-lcs (~> 1.3) parser (~> 3.0.0) regexp_parser (~> 2.0, >= 2.0.3) + sorbet-runtime (~> 0.5.0) unparser (~> 0.6.0) - mutant-rspec (0.10.32) - mutant (= 0.10.32) + mutant-rspec (0.11.0) + mutant (= 0.11.0) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.20.1) parser (3.0.2.0) @@ -59,15 +60,16 @@ GEM rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) + sorbet-runtime (0.5.9242) unicode-display_width (2.0.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.10.30) + mutant (~> 0.11.0) mutant-license! - mutant-rspec (~> 0.10.30) + mutant-rspec (~> 0.11.0) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index a6463534..06834ada 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.0.0') - gem.add_development_dependency('mutant', '~> 0.10.30') - gem.add_development_dependency('mutant-rspec', '~> 0.10.30') + gem.add_development_dependency('mutant', '~> 0.11.0') + gem.add_development_dependency('mutant-rspec', '~> 0.11.0') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 2c50cb3cbdb73628b0afeca270f779c1606b5732 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 23 Oct 2021 23:46:29 +0000 Subject: [PATCH 161/256] Upgrade dependencies --- Gemfile.lock | 14 +++++++------- lib/unparser/adamantium/method_builder.rb | 3 ++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f5e9291d..23e6252d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -24,7 +24,7 @@ GEM mutant-rspec (0.11.0) mutant (= 0.11.0) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.20.1) + parallel (1.21.0) parser (3.0.2.0) ast (~> 2.4.1) rainbow (3.0.0) @@ -46,22 +46,22 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.2) - rubocop (1.14.0) + rubocop (1.22.2) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.5.0, < 2.0) + rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.5.0) + rubocop-ast (1.12.0) parser (>= 3.0.1.1) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.9242) - unicode-display_width (2.0.0) + sorbet-runtime (0.5.9265) + unicode-display_width (2.1.0) PLATFORMS ruby @@ -78,4 +78,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.2.15 + 2.2.29 diff --git a/lib/unparser/adamantium/method_builder.rb b/lib/unparser/adamantium/method_builder.rb index 71af396b..7ae4b22b 100644 --- a/lib/unparser/adamantium/method_builder.rb +++ b/lib/unparser/adamantium/method_builder.rb @@ -101,7 +101,8 @@ def set_method_visibility def visibility if @descendant.private_method_defined?(@method_name) then :private elsif @descendant.protected_method_defined?(@method_name) then :protected - else :public + else + :public end end From 8c9578b27942174a460e61a820b046a7672b9e63 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 24 Oct 2021 03:13:58 +0000 Subject: [PATCH 162/256] Fix undefined instance variable --- lib/unparser/cli.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 3ca21a50..c1fa7797 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -76,6 +76,7 @@ def initialize(arguments) @targets = [] @fail_fast = false + @start_with = nil @success = true @validation = :validation @verbose = false From 0da1024a26169cf23116f3c00de73076504e6100 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Oct 2021 08:01:04 +0000 Subject: [PATCH 163/256] Bump rubocop from 1.22.2 to 1.22.3 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.22.2 to 1.22.3. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.22.2...v1.22.3) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 23e6252d..e682529d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,7 +46,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.2) - rubocop (1.22.2) + rubocop (1.22.3) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) From 8642870571b52e8bcb66d3c11ce008ca26c06ba7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 8 Nov 2021 06:56:45 +0000 Subject: [PATCH 164/256] Fix crash on binary operator selector with kwargs argument * These need to be emitted regular and cannot be emitted in binary syntax. [Fix #278] --- lib/unparser/node_details/send.rb | 5 ++++- lib/unparser/node_helpers.rb | 4 +++- test/corpus/literal/send.rb | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/unparser/node_details/send.rb b/lib/unparser/node_details/send.rb index 6bcc8e14..4a2a743e 100644 --- a/lib/unparser/node_details/send.rb +++ b/lib/unparser/node_details/send.rb @@ -19,7 +19,10 @@ def selector_binary_operator? end def binary_syntax_allowed? - selector_binary_operator? && arguments.one? && !n_splat?(arguments.first) + selector_binary_operator? \ + && arguments.one? \ + && !n_splat?(arguments.first) \ + && !n_kwargs?(arguments.first) end def selector_unary_operator? diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 83645a66..bfb8bb79 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -36,18 +36,20 @@ def n?(type, node) args array array_pattern - empty_else begin block cbase const dstr + empty_else ensure hash hash_pattern if in_pattern int + kwarg + kwargs kwsplat lambda match_rest diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index 78ec7112..52591848 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -68,6 +68,8 @@ def foo foo.bar({ foo: boz }, boz) foo.bar=:baz foo(a: b) +foo.&(a: b) +foo.&(**a) foo[*baz] foo[1, 2] foo[] From a529e2b14e2a67871b3008ca3e2df7ffdced81c2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 8 Nov 2021 07:00:22 +0000 Subject: [PATCH 165/256] Change version to 0.6.1 --- Gemfile.lock | 14 +++++++------- unparser.gemspec | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e682529d..eeebb28d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.0) + unparser (0.6.1) diff-lcs (~> 1.3) parser (>= 3.0.0) @@ -15,14 +15,14 @@ GEM specs: ast (2.4.2) diff-lcs (1.4.4) - mutant (0.11.0) + mutant (0.11.1) diff-lcs (~> 1.3) parser (~> 3.0.0) regexp_parser (~> 2.0, >= 2.0.3) sorbet-runtime (~> 0.5.0) unparser (~> 0.6.0) - mutant-rspec (0.11.0) - mutant (= 0.11.0) + mutant-rspec (0.11.1) + mutant (= 0.11.1) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) parser (3.0.2.0) @@ -60,16 +60,16 @@ GEM rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.9265) + sorbet-runtime (0.5.9307) unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.0) + mutant (~> 0.11.1) mutant-license! - mutant-rspec (~> 0.11.0) + mutant-rspec (~> 0.11.1) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index 06834ada..a9e33c5a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.0' + gem.version = '0.6.1' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.0.0') - gem.add_development_dependency('mutant', '~> 0.11.0') - gem.add_development_dependency('mutant-rspec', '~> 0.11.0') + gem.add_development_dependency('mutant', '~> 0.11.1') + gem.add_development_dependency('mutant-rspec', '~> 0.11.1') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From fb25e0e5fdcf5c093893142fadf3cd29378b57dd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 9 Nov 2021 19:46:00 +0000 Subject: [PATCH 166/256] Fix unary operator with arguments [Fix #280] --- Changelog.md | 12 +++++++++++- Gemfile.lock | 2 +- lib/unparser/writer/send.rb | 2 +- test/corpus/literal/send.rb | 1 + unparser.gemspec | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index fa187073..278baf4a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,14 @@ -# v0.6.1 unreleased +# v0.6.2 2021-11-09 + +[#281](https://github.com/mbj/unparser/pull/268) + +* Fix unary operator with argument. + +# v0.6.1 2021-11-08 + +[#279](https://github.com/mbj/unparser/pull/279) + +* Fix binary operator with kwargs argument. [#268](https://github.com/mbj/unparser/pull/268) diff --git a/Gemfile.lock b/Gemfile.lock index eeebb28d..022e9759 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.1) + unparser (0.6.2) diff-lcs (~> 1.3) parser (>= 3.0.0) diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index 413bdc4f..f75d30e6 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -45,7 +45,7 @@ def effective_writer def effective_writer_class if details.binary_syntax_allowed? Binary - elsif details.selector_unary_operator? && n_send?(node) + elsif details.selector_unary_operator? && n_send?(node) && arguments.empty? Unary elsif write_as_attribute_assignment? AttributeAssignment diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index 52591848..4361cf73 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -80,3 +80,4 @@ def foo (a + b) / c.-(*f) x(**foo) foo&.! +foo.~(b) diff --git a/unparser.gemspec b/unparser.gemspec index a9e33c5a..74f45acc 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.1' + gem.version = '0.6.2' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From ad3d27cecac397ecb98284ed84a623978feb12ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Nov 2021 08:00:53 +0000 Subject: [PATCH 167/256] Bump rubocop from 1.22.3 to 1.23.0 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.22.3 to 1.23.0. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.22.3...v1.23.0) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 022e9759..284438b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,7 +46,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.2) - rubocop (1.22.3) + rubocop (1.23.0) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) @@ -55,7 +55,7 @@ GEM rubocop-ast (>= 1.12.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.12.0) + rubocop-ast (1.13.0) parser (>= 3.0.1.1) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) From 2264e85b3b9ca3a6bbb787e7587040c64f00d1ea Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jan 2022 13:59:40 +0000 Subject: [PATCH 168/256] Add parser-3.1.0 --- Changelog.md | 8 ++++++++ Gemfile | 2 ++ Gemfile.lock | 48 +++++++++++++++++++++++++++--------------------- unparser.gemspec | 4 ++-- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/Changelog.md b/Changelog.md index 278baf4a..7cdfab85 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,11 @@ +# v0.6.3 2022-01-16 + +[#290](https://github.com/mbj/unparser/pull/290) + +* Depend on parser-3.1.0. +* This is not yet Ruby 3.1 syntax support, only + supporting the existing feature set on an updated `parser` gem. + # v0.6.2 2021-11-09 [#281](https://github.com/mbj/unparser/pull/268) diff --git a/Gemfile b/Gemfile index 1419592e..08a192d1 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ source 'https://rubygems.org' gemspec +gem 'mutant', git: 'https://github.com/mbj/mutant', ref: '2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743' + source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index 284438b0..ef3d1f9f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,21 @@ +GIT + remote: https://github.com/mbj/mutant + revision: 2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743 + ref: 2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743 + specs: + mutant (0.11.2) + diff-lcs (~> 1.3) + parser (~> 3.1.0) + regexp_parser (~> 2.0, >= 2.0.3) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.6.2) + PATH remote: . specs: - unparser (0.6.2) + unparser (0.6.3) diff-lcs (~> 1.3) - parser (>= 3.0.0) + parser (>= 3.1.0) GEM remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ @@ -14,21 +26,15 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - diff-lcs (1.4.4) - mutant (0.11.1) - diff-lcs (~> 1.3) - parser (~> 3.0.0) - regexp_parser (~> 2.0, >= 2.0.3) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.0) - mutant-rspec (0.11.1) - mutant (= 0.11.1) + diff-lcs (1.5.0) + mutant-rspec (0.11.2) + mutant (= 0.11.2) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) - parser (3.0.2.0) + parser (3.1.0.0) ast (~> 2.4.1) - rainbow (3.0.0) - regexp_parser (2.1.1) + rainbow (3.1.1) + regexp_parser (2.2.0) rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -36,7 +42,7 @@ GEM rspec-mocks (~> 3.10.0) rspec-core (3.10.1) rspec-support (~> 3.10.0) - rspec-expectations (3.10.1) + rspec-expectations (3.10.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-its (1.3.0) @@ -45,29 +51,29 @@ GEM rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) - rspec-support (3.10.2) - rubocop (1.23.0) + rspec-support (3.10.3) + rubocop (1.24.1) parallel (~> 1.10) parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.12.0, < 2.0) + rubocop-ast (>= 1.15.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.13.0) + rubocop-ast (1.15.1) parser (>= 3.0.1.1) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.9307) + sorbet-runtime (0.5.9531) unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.1) + mutant! mutant-license! mutant-rspec (~> 0.11.1) rspec (~> 3.9) diff --git a/unparser.gemspec b/unparser.gemspec index 74f45acc..431b27da 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.2' + gem.version = '0.6.3' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -24,7 +24,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.6' gem.add_dependency('diff-lcs', '~> 1.3') - gem.add_dependency('parser', '>= 3.0.0') + gem.add_dependency('parser', '>= 3.1.0') gem.add_development_dependency('mutant', '~> 0.11.1') gem.add_development_dependency('mutant-rspec', '~> 0.11.1') From 769d038616f68980f802106140ba1bf59cdeaf03 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jan 2022 15:29:09 +0000 Subject: [PATCH 169/256] Remove dead integration test sources * These repositories are not showing any useful to this project code churn. --- spec/integrations.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/spec/integrations.yml b/spec/integrations.yml index c315fbee..d7a37787 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -1,20 +1,8 @@ --- -- name: anima - repo_uri: 'https://github.com/mbj/anima.git' - repo_ref: 'origin/master' - exclude: [] - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' repo_ref: 'origin/main' exclude: [] -- name: yaks - repo_uri: 'https://github.com/plexus/yaks.git' - repo_ref: 'origin/master' - exclude: [] -- name: chassis - repo_uri: 'https://github.com/ahawkins/chassis.git' - repo_ref: 'origin/master' - exclude: [] - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' # Revision of rubyspec on the last CI build of unparser that passed From 20caa4a8a7f89a1cd608d8623f80ae2360ed73af Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jan 2022 15:29:13 +0000 Subject: [PATCH 170/256] Change corpus test to not assume master branch --- spec/integration/unparser/corpus_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb index 9b4bf361..a8fe1277 100644 --- a/spec/integration/unparser/corpus_spec.rb +++ b/spec/integration/unparser/corpus_spec.rb @@ -38,7 +38,7 @@ def checkout TMP.mkdir unless TMP.directory? if repo_path.exist? Dir.chdir(repo_path) do - system(%w(git pull origin master)) + system(%w(git fetch)) system(%w(git clean -f -d -x)) end else From 9018011226a44e63a724aab502981443e7675902 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jan 2022 15:29:14 +0000 Subject: [PATCH 171/256] Change parser tests to vendored anima/adamantium --- test/run-parser-tests.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/run-parser-tests.rb b/test/run-parser-tests.rb index 4a154f64..fb4b867a 100644 --- a/test/run-parser-tests.rb +++ b/test/run-parser-tests.rb @@ -1,4 +1,3 @@ -require 'anima' require 'unparser' $: << __dir__ @@ -18,7 +17,7 @@ def default_builder_attributes end class Test - include Adamantium, Anima.new( + include Unparser::Adamantium, Unparser::Anima.new( :default_builder_attributes, :group_index, :name, @@ -96,7 +95,7 @@ def parse_either(source, identification) end class Execution - include Anima.new(:number, :total, :test) + include Unparser::Anima.new(:number, :total, :test) def call skip_reason = test.skip_reason @@ -147,7 +146,7 @@ class Test class Extractor class Capture - include Anima.new( + include Unparser::Anima.new( :default_builder_attributes, :node, :parser_source, From 7185018e13143c55f06ecaa5d9ce9d8459ede3ab Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jan 2022 16:09:56 +0000 Subject: [PATCH 172/256] Change to stand-alone corpus integration tests * Rspec is not a good test runner for anything but unit tests and even there its lacking. --- .github/workflows/ci.yml | 2 +- bin/corpus | 149 +++++++++++++++++++++++ spec/integration/unparser/corpus_spec.rb | 114 ----------------- 3 files changed, 150 insertions(+), 115 deletions(-) create mode 100755 bin/corpus delete mode 100644 spec/integration/unparser/corpus_spec.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4334a3b0..4195dd64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,7 +62,7 @@ jobs: with: bundler-cache: true ruby-version: ${{ matrix.ruby }} - - run: bundle exec rspec spec/integration + - run: bundle exec bin/corpus ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/bin/corpus b/bin/corpus new file mode 100755 index 00000000..d8c1b572 --- /dev/null +++ b/bin/corpus @@ -0,0 +1,149 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'mutant' +require 'optparse' +require 'unparser' + +module Unparser + module Corpus + ROOT = Pathname.new(__dir__).parent + TMP = ROOT.join('tmp') + + class Project + include Unparser::Anima.new(:name, :repo_uri, :repo_ref, :exclude) + + # Perform verification via unparser cli + # + # @return [Boolean] + def verify + checkout + command = %W[unparser #{repo_path}] + exclude.each do |name| + command.concat(%W[--ignore #{repo_path.join(name)}]) + end + Kernel.system(*command) + end + + private + + def checkout + TMP.mkdir unless TMP.directory? + + if repo_path.exist? + Dir.chdir(repo_path) do + system(%w[git fetch]) + system(%w[git clean -f -d -x]) + end + else + system(%W[git clone #{repo_uri} #{repo_path}]) + end + + Dir.chdir(repo_path) do + system(%W[git checkout #{repo_ref}]) + system(%w[git reset --hard]) + system(%w[git clean -f -d -x]) + end + end + + def repo_path + TMP.join(name) + end + + def system(arguments) + return if Kernel.system(*arguments) + + fail "System command #{arguments.inspect} failed!" + end + + transform = Mutant::Transform + string = transform::Primitive.new(String) + string_array = transform::Array.new(string) + path = ROOT.join('spec', 'integrations.yml') + + loader = + transform::Named.new( + path.to_s, + transform::Sequence.new( + [ + transform::Exception.new(SystemCallError, :read.to_proc), + transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)), + transform::Array.new( + transform::Sequence.new( + [ + transform::Hash.new( + optional: [], + required: [ + transform::Hash::Key.new('exclude', string_array), + transform::Hash::Key.new('name', string), + transform::Hash::Key.new('repo_ref', string), + transform::Hash::Key.new('repo_uri', string) + ] + ), + transform::Hash::Symbolize.new, + transform::Exception.new(Unparser::Anima::Error, Project.public_method(:new)) + ] + ) + ) + ] + ) + ) + + ALL = loader.call(path).lmap(&:compact_message).from_right + end + + # Unparser corpus CLI implementation + class CLI + def self.run(*arguments) + new(*arguments).exit_status + end + + def initialize(arguments) + @projects = [] + + options = OptionParser.new do |builder| + builder.on('--list', 'List registered projects') do + Project::ALL.each do |project| + puts(project.name) + end + + Kernel.exit(true) + end + end + + options.parse!(arguments).each do |name| + @projects << project(name) + end + end + + def project(name) + Project::ALL.find { |project| project.name.eql?(name) } || fail("Unregistered project: #{name}") + end + + def effective_projects + if @projects.empty? + Project::ALL + else + @projects + end + end + + # Return exit status + # + # @return [Integer] + # + # @api private + # + def exit_status + effective_projects.each do |project| + project.verify || Kernel.exit(false) + end + + Kernel.exit(true) + end + + end # CLI + end # Corpus +end # Unparser + +Unparser::Corpus::CLI.run(ARGV) diff --git a/spec/integration/unparser/corpus_spec.rb b/spec/integration/unparser/corpus_spec.rb deleted file mode 100644 index a8fe1277..00000000 --- a/spec/integration/unparser/corpus_spec.rb +++ /dev/null @@ -1,114 +0,0 @@ -require 'spec_helper' - -describe 'Unparser on ruby corpus', mutant: false do - ROOT = Pathname.new(__FILE__).parent.parent.parent.parent - - TMP = ROOT.join('tmp') - - class Project - include Unparser::Anima.new(:name, :repo_uri, :repo_ref, :exclude) - - # Perform verification via unparser cli - # - # @return [self] - # if successful - # - # @raise [Exception] - # otherwise - # - def verify - checkout - command = %W(unparser #{repo_path}) - exclude.each do |name| - command.concat(%W(--ignore #{repo_path.join(name)})) - end - system(command) do - raise "Verifing #{name} failed!" - end - self - end - - # Checkout repository - # - # @return [self] - # - # @api private - # - def checkout - TMP.mkdir unless TMP.directory? - if repo_path.exist? - Dir.chdir(repo_path) do - system(%w(git fetch)) - system(%w(git clean -f -d -x)) - end - else - system(%W(git clone #{repo_uri} #{repo_path})) - end - - Dir.chdir(repo_path) do - system(%W(git checkout #{repo_ref})) - system(%w(git reset --hard)) - system(%w(git clean -f -d -x)) - end - - self - end - - private - - def repo_path - TMP.join(name) - end - - def system(arguments) - return if Kernel.system(*arguments) - - if block_given? - yield - else - raise "System command #{arguments.inspect} failed!" - end - end - - transform = Mutant::Transform - string = transform::Primitive.new(String) - string_array = transform::Array.new(string) - path = ROOT.join('spec', 'integrations.yml') - - loader = - transform::Named.new( - path.to_s, - transform::Sequence.new( - [ - transform::Exception.new(SystemCallError, :read.to_proc), - transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)), - transform::Array.new( - transform::Sequence.new( - [ - transform::Hash.new( - optional: [], - required: [ - transform::Hash::Key.new('exclude', string_array), - transform::Hash::Key.new('name', string), - transform::Hash::Key.new('repo_ref', string), - transform::Hash::Key.new('repo_uri', string) - ] - ), - transform::Hash::Symbolize.new, - transform::Exception.new(Unparser::Anima::Error, Project.public_method(:new)) - ] - ) - ) - ] - ) - ) - - ALL = loader.call(path).lmap(&:compact_message).from_right - end - - Project::ALL.each do |project| - specify "unparsing #{project.name}" do - project.verify - end - end -end From 9c9918893321d3fdc5c8cd64ab9b5a2b7a98c855 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 5 Feb 2022 21:15:32 +0000 Subject: [PATCH 173/256] Fix match_pattern{,_p} --- Changelog.md | 6 ++++++ config/mutant.yml | 4 +++- lib/unparser.rb | 1 + lib/unparser/emitter/match_pattern.rb | 14 +++++++++++--- lib/unparser/emitter/match_pattern_p.rb | 20 ++++++++++++++++++++ spec/unit/unparser_spec.rb | 8 ++++++++ test/corpus/literal/since/30.rb | 2 ++ 7 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 lib/unparser/emitter/match_pattern_p.rb create mode 100644 test/corpus/literal/since/30.rb diff --git a/Changelog.md b/Changelog.md index 7cdfab85..57254765 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.6.2 2022-02-05 + +[#297](https://github.com/mbj/unparser/pull/297) + +* Fix emit of of `match_pattern` vs `match_pattern_p` + # v0.6.3 2022-01-16 [#290](https://github.com/mbj/unparser/pull/290) diff --git a/config/mutant.yml b/config/mutant.yml index dc5750c5..b5e952ea 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -10,9 +10,9 @@ matcher: ignore: # API changed between ruby versions and each of them # have a different minimal form - - 'Unparser::Concord#define_readers' - 'Unparser::Builder#initialize' - 'Unparser::CLI*' + - 'Unparser::Concord#define_readers' - 'Unparser::Emitter#emit_comments' - 'Unparser::Emitter#emit_comments_before' - 'Unparser::Emitter#emit_eol_comments' @@ -28,6 +28,8 @@ matcher: - 'Unparser::Emitter::HashPattern#write_symbol_body' - 'Unparser::Emitter::LocalVariableRoot*' - 'Unparser::Emitter::LocalVariableRoot.included' + - 'Unparser::Emitter::MatchPattern#dispatch' # 2.7+ specific + - 'Unparser::Emitter::MatchPatternP#dispatch' # 3.0+ specific - 'Unparser::Emitter::Module#local_variable_scope' - 'Unparser::Emitter::Root#local_variable_scope' - 'Unparser::Emitter::Send#writer' diff --git a/lib/unparser.rb b/lib/unparser.rb index ba22cef6..b1cf847a 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -219,6 +219,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/kwargs' require 'unparser/emitter/pair' require 'unparser/emitter/match_pattern' +require 'unparser/emitter/match_pattern_p' require 'unparser/writer' require 'unparser/writer/binary' require 'unparser/writer/dynamic_string' diff --git a/lib/unparser/emitter/match_pattern.rb b/lib/unparser/emitter/match_pattern.rb index 27c04ed2..c9e45dde 100644 --- a/lib/unparser/emitter/match_pattern.rb +++ b/lib/unparser/emitter/match_pattern.rb @@ -6,17 +6,25 @@ class Emitter class MatchPattern < self handle :match_pattern - handle :match_pattern_p children :target, :pattern + # Modern ast format emits `match_pattern` + # node on single line pre 3.0, but 3.0+ uses `match_pattern_p` + SYMBOL = + if RUBY_VERSION < '3.0' + ' in ' + else + ' => ' + end + private def dispatch visit(target) - write(' in ') + write(SYMBOL) visit(pattern) end - end # InPattern + end # MatchPattern end # Emitter end # Unparser diff --git a/lib/unparser/emitter/match_pattern_p.rb b/lib/unparser/emitter/match_pattern_p.rb new file mode 100644 index 00000000..90b62512 --- /dev/null +++ b/lib/unparser/emitter/match_pattern_p.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + class MatchPatternP < self + + handle :match_pattern_p + + children :target, :pattern + + private + + def dispatch + visit(target) + write(' in ') + visit(pattern) + end + end # MatchPatternP + end # Emitter +end # Unparser diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index d6eaaf1d..9ea50847 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -359,6 +359,14 @@ def noop let(:version_excludes) do excludes = [] + if RUBY_VERSION < '3.0.' + excludes.concat( + %w[ + test/corpus/literal/since/30.rb + ] + ) + end + if RUBY_VERSION < '2.7.' excludes.concat( %w[ diff --git a/test/corpus/literal/since/30.rb b/test/corpus/literal/since/30.rb new file mode 100644 index 00000000..7357de42 --- /dev/null +++ b/test/corpus/literal/since/30.rb @@ -0,0 +1,2 @@ +1 => [a] +1 => [*] From 4fd4a7d5463b9413360fe1cc85364a3077390448 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 6 Feb 2022 02:16:54 +0000 Subject: [PATCH 174/256] Add parser round trip test to CI --- .github/workflows/ci.yml | 16 ++ Changelog.md | 4 + .../parser-round-trip-test | 138 ++++++++++++------ 3 files changed, 113 insertions(+), 45 deletions(-) rename test/run-parser-tests.rb => bin/parser-round-trip-test (50%) mode change 100644 => 100755 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4195dd64..4e66dcd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,22 @@ jobs: bundler-cache: true ruby-version: ${{ matrix.ruby }} - run: bundle exec bin/corpus + ruby-parser-round-trip-tests: + name: Parser Round Trip Tests + runs-on: ${{ matrix.os }} + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + os: [macos-latest, ubuntu-latest] + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + ruby-version: ${{ matrix.ruby }} + - run: bundle exec ruby bin/parser-round-trip-test ruby-rubocop: name: Rubocop runs-on: ${{ matrix.os }} diff --git a/Changelog.md b/Changelog.md index 57254765..4c1b9bd7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,10 @@ * Fix emit of of `match_pattern` vs `match_pattern_p` +[#298](https://github.com/mbj/unparser/pull/298) + +* Add round trip tests dynamically derived from the `parser` gems test suite to CI + # v0.6.3 2022-01-16 [#290](https://github.com/mbj/unparser/pull/290) diff --git a/test/run-parser-tests.rb b/bin/parser-round-trip-test old mode 100644 new mode 100755 similarity index 50% rename from test/run-parser-tests.rb rename to bin/parser-round-trip-test index fb4b867a..9725a08f --- a/test/run-parser-tests.rb +++ b/bin/parser-round-trip-test @@ -1,19 +1,33 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + require 'unparser' -$: << __dir__ +# Hack to dynamically re-use the `parser` gems test suite on CI. +# The main idea is create a fake minitet runner to capture the +# signature of the examples encoded in the parsers test suite dynamically. +# +# This makes maintenance much more easier, especially on tracking new ruby +# syntax addtions. +# +# The API surface of the parser tests so far is low churn, while it may still +# make sense to provide the parser tests as an more easy to re-use data upstream. + +$LOAD_PATH << Pathname.new(__dir__).parent.join('test') -testBuilder = Class.new(Parser::Builders::Default) -testBuilder.modernize +test_builder = Class.new(Parser::Builders::Default) +test_builder.modernize -MODERN_ATTRIBUTES = testBuilder.instance_variables.map do |instance_variable| - attribute_name = instance_variable.to_s[1..-1].to_sym - [attribute_name, testBuilder.public_send(attribute_name)] -end.to_h +MODERN_ATTRIBUTES = test_builder.instance_variables.to_h do |instance_variable| + attribute_name = instance_variable.to_s[1..].to_sym + [attribute_name, test_builder.public_send(attribute_name)] +end +# Overwrite global scope method in the parser test suite def default_builder_attributes - MODERN_ATTRIBUTES.keys.map do |attribute_name| + MODERN_ATTRIBUTES.keys.to_h do |attribute_name| [attribute_name, Parser::Builders::Default.public_send(attribute_name)] - end.to_h + end end class Test @@ -26,13 +40,11 @@ class Test :rubies ) - TARGET_RUBIES = %w[2.6 2.7] - EXPECT_FAILURE = {}.freeze def legacy_attributes - default_builder_attributes.select do |attribute_name, value| - !MODERN_ATTRIBUTES.fetch(attribute_name).equal?(value) + default_builder_attributes.reject do |attribute_name, value| + MODERN_ATTRIBUTES.fetch(attribute_name).equal?(value) end.to_h end memoize :legacy_attributes @@ -43,7 +55,7 @@ def skip_reason elsif !allow_ruby? "Non targeted rubies: #{rubies.join(',')}" elsif validation.original_node.left? - "Test specifies a syntax error" + 'Test specifies a syntax error' end end @@ -56,18 +68,21 @@ def expect_failure? end def allow_ruby? - rubies.empty? || rubies.any?(TARGET_RUBIES.method(:include?)) + rubies.empty? || rubies.include?(RUBY_VERSION) end def right(value) Unparser::Either::Right.new(value) end + # rubocop:disable Metrics/AbcSize def validation identification = name.to_s + generated_source = Unparser.unparse_either(node) + .fmap { |string| string.dup.force_encoding(parser_source.encoding).freeze } - generated_node = generated_source.lmap{ |_value| }.bind do |source| + generated_node = generated_source.bind do |source| parse_either(source, identification) end @@ -79,11 +94,12 @@ def validation original_source: right(parser_source) ) end + # rubocop:enable Metrics/AbcSize memoize :validation def parser Unparser.parser.tap do |parser| - %w(foo bar baz).each(&parser.static_env.method(:declare)) + %w[foo bar baz].each(&parser.static_env.method(:declare)) end end @@ -115,7 +131,7 @@ def call def expect_failure if test.success? - fail format('Expected Failure', 'but got success') + message('Expected Failure', 'but got success') else print('Expected Failure') end @@ -126,22 +142,32 @@ def expect_success print('Success') else puts(test.validation.report) - fail format('Failure') + fail message('Failure') end end - def format(status, message = '') - '%3d/%3d: %-16s %s[%02d] %s' % [number, total, status, test.name, test.group_index, message] + def message(status, message = '') + format( + '%3d/%3d: %-16s %s[%02d] %s', + number: number, + total: total, + status: status, + name: test.name, + group_index: test.group_index, + message: message + ) end def print(status, message = '') - puts(format(status, message)) + puts(message(status, message)) end end module Minitest - class Test - end # Test + # Stub parent class + # rubocop:disable Lint/EmptyClass + class Test; end # Test + # rubocop:enable Lint/EmptyClass end # Minitest class Extractor @@ -183,46 +209,68 @@ def call(name) end end -require '../parser/test/parse_helper.rb' -require '../parser/test/test_parser.rb' +PARSER_PATH = Pathname.new('tmp/parser') + +unless PARSER_PATH.exist? + Kernel.system( + *%W[ + git + clone + https://github.com/whitequark/parser + #{PARSER_PATH} + ], + exception: true + ) +end + +Dir.chdir(PARSER_PATH) do + Kernel.system( + *%W[ + git + checkout + v#{Parser::VERSION} + ], + exception: true + ) + Kernel.system(*%w[git clean --force -d -X], exception: true) +end + +require "./#{PARSER_PATH}/test/parse_helper" +require "./#{PARSER_PATH}/test/test_parser" EXTRACTOR = Extractor.new module ParseHelper - def assert_diagnoses(*arguments) - end + def assert_diagnoses(*arguments); end def s(type, *children) Parser::AST::Node.new(type, children) end + # rubocop:disable Metrics/ParameterLists def assert_parses(node, parser_source, _diagnostics = nil, rubies = []) - EXTRACTOR.capture( - default_builder_attributes: default_builder_attributes, - node: node, - parser_source: parser_source, - rubies: rubies - ) + EXTRACTOR.capture( + default_builder_attributes: default_builder_attributes, + node: node, + parser_source: parser_source, + rubies: rubies + ) end + # rubocop:enable Metrics/ParameterLists - def test_clrf_line_endings(*arguments) - end + def test_clrf_line_endings(*arguments); end - def with_versions(*arguments) - end + def with_versions(*arguments); end - def assert_context(*arguments) - end + def assert_context(*arguments); end - def refute_diagnoses(*arguments) - end + def refute_diagnoses(*arguments); end - def assert_diagnoses_many(*arguments) - end + def assert_diagnoses_many(*arguments); end end TestParser.instance_methods.grep(/\Atest_/).each(&EXTRACTOR.method(:call)) EXTRACTOR.tests.sort_by(&:name).each_with_index do |test, index| - Execution.new(number: index.succ, total: EXTRACTOR.tests.length, test: test).call + Execution.new(number: index.succ, total: EXTRACTOR.tests.length, test: test).call end From e6d23011392d8029936227d3c8f3530eba1eca91 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 12 Feb 2022 16:26:06 +0000 Subject: [PATCH 175/256] Add find_pattern and matcH_rest support * These where not found as the parser round trip tests where false positive. --- Changelog.md | 10 ++++++++++ bin/parser-round-trip-test | 2 +- config/mutant.yml | 2 ++ lib/unparser.rb | 1 + lib/unparser/emitter/find_pattern.rb | 18 ++++++++++++++++++ lib/unparser/emitter/match_rest.rb | 7 +++++++ test/corpus/literal/since/30.rb | 2 ++ 7 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 lib/unparser/emitter/find_pattern.rb diff --git a/Changelog.md b/Changelog.md index 4c1b9bd7..35c8ec4c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,13 @@ +# v0.6.3 2022-02-12 + +[#300](https://github.com/mbj/unparser/pull/300) + +* Add 3.0+ node support for `find_pattern` and `match_rest` + +[#298](https://github.com/mbj/unparser/pull/298) + +* Add `parser` gem derived round trip tests. + # v0.6.2 2022-02-05 [#297](https://github.com/mbj/unparser/pull/297) diff --git a/bin/parser-round-trip-test b/bin/parser-round-trip-test index 9725a08f..e9860bd2 100755 --- a/bin/parser-round-trip-test +++ b/bin/parser-round-trip-test @@ -68,7 +68,7 @@ class Test end def allow_ruby? - rubies.empty? || rubies.include?(RUBY_VERSION) + rubies.empty? || rubies.include?(RUBY_VERSION.split('.').take(2).join('.')) end def right(value) diff --git a/config/mutant.yml b/config/mutant.yml index b5e952ea..70df9719 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -25,11 +25,13 @@ matcher: - 'Unparser::Emitter::Class#dispatch' - 'Unparser::Emitter::Class#local_variable_scope' - 'Unparser::Emitter::Def#local_variable_scope' + - 'Unparser::Emitter::FindPattern#dispatch' # 3.0+ specific - 'Unparser::Emitter::HashPattern#write_symbol_body' - 'Unparser::Emitter::LocalVariableRoot*' - 'Unparser::Emitter::LocalVariableRoot.included' - 'Unparser::Emitter::MatchPattern#dispatch' # 2.7+ specific - 'Unparser::Emitter::MatchPatternP#dispatch' # 3.0+ specific + - 'Unparser::Emitter::MatchRest#dispatch' # 3.0+ specific - 'Unparser::Emitter::Module#local_variable_scope' - 'Unparser::Emitter::Root#local_variable_scope' - 'Unparser::Emitter::Send#writer' diff --git a/lib/unparser.rb b/lib/unparser.rb index b1cf847a..0938a81a 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -218,6 +218,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/yield' require 'unparser/emitter/kwargs' require 'unparser/emitter/pair' +require 'unparser/emitter/find_pattern' require 'unparser/emitter/match_pattern' require 'unparser/emitter/match_pattern_p' require 'unparser/writer' diff --git a/lib/unparser/emitter/find_pattern.rb b/lib/unparser/emitter/find_pattern.rb new file mode 100644 index 00000000..751519b7 --- /dev/null +++ b/lib/unparser/emitter/find_pattern.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for in pattern nodes + class FindPattern < self + handle :find_pattern + + private + + def dispatch + write('[') + delimited(children) + write(']') + end + end # FindPattern + end # Emitter +end # Unparser diff --git a/lib/unparser/emitter/match_rest.rb b/lib/unparser/emitter/match_rest.rb index 311955aa..b9a2f1c3 100644 --- a/lib/unparser/emitter/match_rest.rb +++ b/lib/unparser/emitter/match_rest.rb @@ -4,8 +4,15 @@ module Unparser class Emitter # Emiter for match rest nodes class MatchRest < self + handle :match_rest + children :match_var + def dispatch + write('*') + visit(match_var) if match_var + end + def emit_array_pattern write('*') emit_match_var diff --git a/test/corpus/literal/since/30.rb b/test/corpus/literal/since/30.rb index 7357de42..b73328a4 100644 --- a/test/corpus/literal/since/30.rb +++ b/test/corpus/literal/since/30.rb @@ -1,2 +1,4 @@ 1 => [a] 1 => [*] +1 in [*, 42, *] +1 in [*, a, *foo] From eb8a8399579ede116bcee4cb49dc6e1d78110b8e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 5 Feb 2022 20:45:58 +0000 Subject: [PATCH 176/256] Add 3.1 syntax support --- .github/workflows/ci.yml | 10 +++++----- Changelog.md | 8 +++++--- Gemfile.lock | 4 ++-- config/mutant.yml | 3 +-- lib/unparser/emitter/argument.rb | 2 +- spec/integrations.yml | 2 +- spec/unit/unparser_spec.rb | 8 ++++++++ test/corpus/literal/since/31.rb | 7 +++++++ unparser.gemspec | 2 +- 9 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 test/corpus/literal/since/31.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4e66dcd1..60827aee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -86,7 +86,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0] + ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/Changelog.md b/Changelog.md index 35c8ec4c..7690df8a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,8 @@ -# v0.6.3 2022-02-12 +# v0.6.4 2022-02-12 + +[#299](https://github.com/mbj/unparser/pull/299) + +* Add 3.1+ syntax support. [#300](https://github.com/mbj/unparser/pull/300) @@ -8,8 +12,6 @@ * Add `parser` gem derived round trip tests. -# v0.6.2 2022-02-05 - [#297](https://github.com/mbj/unparser/pull/297) * Fix emit of of `match_pattern` vs `match_pattern_p` diff --git a/Gemfile.lock b/Gemfile.lock index ef3d1f9f..c610fe91 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,7 +13,7 @@ GIT PATH remote: . specs: - unparser (0.6.3) + unparser (0.6.4) diff-lcs (~> 1.3) parser (>= 3.1.0) @@ -84,4 +84,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.2.29 + 2.3.6 diff --git a/config/mutant.yml b/config/mutant.yml index 70df9719..8b22a6be 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -8,8 +8,6 @@ matcher: subjects: - 'Unparser*' ignore: - # API changed between ruby versions and each of them - # have a different minimal form - 'Unparser::Builder#initialize' - 'Unparser::CLI*' - 'Unparser::Concord#define_readers' @@ -22,6 +20,7 @@ matcher: - 'Unparser::Emitter::Array#emitters' - 'Unparser::Emitter::Binary#writer' - 'Unparser::Emitter::Block#target_writer' + - 'Unparser::Emitter::BlockPass#dispatch' # 3.1+ specific - 'Unparser::Emitter::Class#dispatch' - 'Unparser::Emitter::Class#local_variable_scope' - 'Unparser::Emitter::Def#local_variable_scope' diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index 93d44320..0517de43 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -127,7 +127,7 @@ class BlockPass < self def dispatch write('&') - visit(name) + visit(name) if name end end # BlockPass diff --git a/spec/integrations.yml b/spec/integrations.yml index d7a37787..90a45515 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -1,7 +1,7 @@ --- - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' - repo_ref: 'origin/main' + repo_ref: '83f8b26e' exclude: [] - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 9ea50847..cfbc1ffe 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -359,6 +359,14 @@ def noop let(:version_excludes) do excludes = [] + if RUBY_VERSION < '3.1.' + excludes.concat( + %w[ + test/corpus/literal/since/31.rb + ] + ) + end + if RUBY_VERSION < '3.0.' excludes.concat( %w[ diff --git a/test/corpus/literal/since/31.rb b/test/corpus/literal/since/31.rb new file mode 100644 index 00000000..504eb94d --- /dev/null +++ b/test/corpus/literal/since/31.rb @@ -0,0 +1,7 @@ +def foo(&) + bar(&) +end + +def foo(a, &) + bar(&) +end diff --git a/unparser.gemspec b/unparser.gemspec index 431b27da..7ad4dcaf 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.3' + gem.version = '0.6.4' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 8678ae7bb8ef754a919def6ee81e42ec1f8f2a9e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 14 Feb 2022 00:55:19 +0000 Subject: [PATCH 177/256] Change to depend on mutant-0.11.4 --- Gemfile | 2 -- Gemfile.lock | 28 +++++++++++----------------- spec/integrations.yml | 2 +- unparser.gemspec | 4 ++-- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 08a192d1..1419592e 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,6 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant', ref: '2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743' - source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index c610fe91..b1a96284 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant - revision: 2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743 - ref: 2fbc9dc7f8a8d363bc9fad6a32b0a6c9d1032743 - specs: - mutant (0.11.2) - diff-lcs (~> 1.3) - parser (~> 3.1.0) - regexp_parser (~> 2.0, >= 2.0.3) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.2) - PATH remote: . specs: @@ -27,8 +15,14 @@ GEM specs: ast (2.4.2) diff-lcs (1.5.0) - mutant-rspec (0.11.2) - mutant (= 0.11.2) + mutant (0.11.4) + diff-lcs (~> 1.3) + parser (~> 3.1.0) + regexp_parser (~> 2.0, >= 2.0.3) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.6.4) + mutant-rspec (0.11.4) + mutant (= 0.11.4) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) parser (3.1.0.0) @@ -66,16 +60,16 @@ GEM rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.9531) + sorbet-runtime (0.5.9636) unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - mutant! + mutant (~> 0.11.4) mutant-license! - mutant-rspec (~> 0.11.1) + mutant-rspec (~> 0.11.4) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/spec/integrations.yml b/spec/integrations.yml index 90a45515..7c41d2b9 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -1,7 +1,7 @@ --- - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' - repo_ref: '83f8b26e' + repo_ref: 'main' exclude: [] - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' diff --git a/unparser.gemspec b/unparser.gemspec index 7ad4dcaf..ccfac06a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.1.0') - gem.add_development_dependency('mutant', '~> 0.11.1') - gem.add_development_dependency('mutant-rspec', '~> 0.11.1') + gem.add_development_dependency('mutant', '~> 0.11.4') + gem.add_development_dependency('mutant-rspec', '~> 0.11.4') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 245f31e88644b42e8cb9755be3a842f9c4e7d2d4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 01:14:54 +0000 Subject: [PATCH 178/256] Bump rubocop from 1.24.1 to 1.25.1 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.24.1 to 1.25.1. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.24.1...v1.25.1) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index b1a96284..6342da59 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -28,7 +28,7 @@ GEM parser (3.1.0.0) ast (~> 2.4.1) rainbow (3.1.1) - regexp_parser (2.2.0) + regexp_parser (2.2.1) rexml (3.2.5) rspec (3.10.0) rspec-core (~> 3.10.0) @@ -46,16 +46,16 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.10.0) rspec-support (3.10.3) - rubocop (1.24.1) + rubocop (1.25.1) parallel (~> 1.10) - parser (>= 3.0.0.0) + parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml rubocop-ast (>= 1.15.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.15.1) + rubocop-ast (1.15.2) parser (>= 3.0.1.1) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) From 8813abad5aedacf2dc8d72a6af38b950d6e0ea0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 01:33:47 +0000 Subject: [PATCH 179/256] Bump rspec-core from 3.10.1 to 3.10.2 Bumps [rspec-core](https://github.com/rspec/rspec-core) from 3.10.1 to 3.10.2. - [Release notes](https://github.com/rspec/rspec-core/releases) - [Changelog](https://github.com/rspec/rspec-core/blob/main/Changelog.md) - [Commits](https://github.com/rspec/rspec-core/compare/v3.10.1...v3.10.2) --- updated-dependencies: - dependency-name: rspec-core dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6342da59..bd1757df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,7 +34,7 @@ GEM rspec-core (~> 3.10.0) rspec-expectations (~> 3.10.0) rspec-mocks (~> 3.10.0) - rspec-core (3.10.1) + rspec-core (3.10.2) rspec-support (~> 3.10.0) rspec-expectations (3.10.2) diff-lcs (>= 1.2.0, < 2.0) From efeb6605eab44fa2bc8a9cbcf362a9cbd636fa8d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Feb 2022 08:01:22 +0000 Subject: [PATCH 180/256] Bump rspec from 3.10.0 to 3.11.0 Bumps [rspec](https://github.com/rspec/rspec-metagem) from 3.10.0 to 3.11.0. - [Release notes](https://github.com/rspec/rspec-metagem/releases) - [Commits](https://github.com/rspec/rspec-metagem/compare/v3.10.0...v3.11.0) --- updated-dependencies: - dependency-name: rspec dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index bd1757df..6beaed57 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -30,22 +30,22 @@ GEM rainbow (3.1.1) regexp_parser (2.2.1) rexml (3.2.5) - rspec (3.10.0) - rspec-core (~> 3.10.0) - rspec-expectations (~> 3.10.0) - rspec-mocks (~> 3.10.0) - rspec-core (3.10.2) - rspec-support (~> 3.10.0) - rspec-expectations (3.10.2) + rspec (3.11.0) + rspec-core (~> 3.11.0) + rspec-expectations (~> 3.11.0) + rspec-mocks (~> 3.11.0) + rspec-core (3.11.0) + rspec-support (~> 3.11.0) + rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) + rspec-support (~> 3.11.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.10.2) + rspec-mocks (3.11.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.10.0) - rspec-support (3.10.3) + rspec-support (~> 3.11.0) + rspec-support (3.11.0) rubocop (1.25.1) parallel (~> 1.10) parser (>= 3.1.0.0) From 310f8369a3fee18068da9bbf805f010fde418939 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Mar 2022 08:01:41 +0000 Subject: [PATCH 181/256] Bump rubocop from 1.25.1 to 1.26.0 Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.25.1 to 1.26.0. - [Release notes](https://github.com/rubocop/rubocop/releases) - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md) - [Commits](https://github.com/rubocop/rubocop/compare/v1.25.1...v1.26.0) --- updated-dependencies: - dependency-name: rubocop dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 6beaed57..67d2e6a4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM mutant (= 0.11.4) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) - parser (3.1.0.0) + parser (3.1.1.0) ast (~> 2.4.1) rainbow (3.1.1) regexp_parser (2.2.1) @@ -46,17 +46,17 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) - rubocop (1.25.1) + rubocop (1.26.0) parallel (~> 1.10) parser (>= 3.1.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.15.1, < 2.0) + rubocop-ast (>= 1.16.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.15.2) - parser (>= 3.0.1.1) + rubocop-ast (1.16.0) + parser (>= 3.1.1.0) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) From ba8a67372c1c498503092006f9c189bf5c687a2f Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 18 Apr 2022 03:01:09 +0000 Subject: [PATCH 182/256] Fix heredoc block with arguments [Fix #311] --- Changelog.md | 10 ++++++++++ Gemfile.lock | 2 +- lib/unparser/emitter/block.rb | 2 +- test/corpus/literal/dstr.rb | 7 +++++++ unparser.gemspec | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7690df8a..c29f7f6d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,13 @@ +# v0.6.5 2022-04-17 + +[#312](https://github.com/mbj/unparser/pull/122) + +* Fix #311, emitting of heredocs within block that has arguments. + +[#313](https://github.com/mbj/unparser/pull/123) + +* Remove Ruby-2.6 support as its EOL + # v0.6.4 2022-02-12 [#299](https://github.com/mbj/unparser/pull/299) diff --git a/Gemfile.lock b/Gemfile.lock index 67d2e6a4..a541dab9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.4) + unparser (0.6.5) diff-lcs (~> 1.3) parser (>= 3.1.0) diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index 0bd00665..15e93677 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -15,8 +15,8 @@ def dispatch emit_target ws write_open - target_writer.emit_heredoc_reminders if n_send?(target) emit_block_arguments unless n_lambda?(target) + target_writer.emit_heredoc_reminders if n_send?(target) emit_optional_body_ensure_rescue(body) write_close end diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index 3df85072..8a912d28 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -28,3 +28,10 @@ #{42} HEREDOC end +foo(<<-HEREDOC) + #{bar} +HEREDOC +foo(<<-HEREDOC) { |x| + #{bar} +HEREDOC +} diff --git a/unparser.gemspec b/unparser.gemspec index 02f166df..89c52df6 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.4' + gem.version = '0.6.5' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From ae3d5d411233e2453ce22443db8119d6915776d3 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 18 Apr 2022 03:06:43 +0000 Subject: [PATCH 183/256] Remove Ruby-2.6 support --- .github/workflows/ci.yml | 10 +++++----- README.md | 2 +- spec/unit/unparser_spec.rb | 4 ---- test/corpus/literal/{since/26.rb => range.rb} | 0 unparser.gemspec | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) rename test/corpus/literal/{since/26.rb => range.rb} (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 60827aee..297a800a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -86,7 +86,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.6, ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/README.md b/README.md index 4ecc9a02..263d6890 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 2.6 +* Only support for Ruby >= 2.7 Notable Users: diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index cfbc1ffe..42525098 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -384,10 +384,6 @@ def noop ) end - if RUBY_VERSION < '2.6.' - excludes << 'test/corpus/literal/since/26.rb' - end - excludes.flat_map { |file| ['--ignore', file] } end diff --git a/test/corpus/literal/since/26.rb b/test/corpus/literal/range.rb similarity index 100% rename from test/corpus/literal/since/26.rb rename to test/corpus/literal/range.rb diff --git a/unparser.gemspec b/unparser.gemspec index ccfac06a..02f166df 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 2.6' + gem.required_ruby_version = '>= 2.7' gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.1.0') From 145f692cfd46cb31f183abe252909cad3c946007 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Apr 2022 03:31:49 +0000 Subject: [PATCH 184/256] Bump parser from 3.1.1.0 to 3.1.2.0 Bumps [parser](https://github.com/whitequark/parser) from 3.1.1.0 to 3.1.2.0. - [Release notes](https://github.com/whitequark/parser/releases) - [Changelog](https://github.com/whitequark/parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/whitequark/parser/compare/v3.1.1.0...v3.1.2.0) --- updated-dependencies: - dependency-name: parser dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index a541dab9..c672793a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM mutant (= 0.11.4) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) - parser (3.1.1.0) + parser (3.1.2.0) ast (~> 2.4.1) rainbow (3.1.1) regexp_parser (2.2.1) From 5c8dfd1402db54b9cd93ccf7e324d0f2eed6cf56 Mon Sep 17 00:00:00 2001 From: Daniel Gollahon Date: Sat, 7 May 2022 13:42:29 -0700 Subject: [PATCH 185/256] Remove unused Buffer#capture_content method - The last reference to this method I can find outside of the specs is in 189d9f468a7840bf8790129d8d00aa8be05a625a and appears to be unused since well before then. - Also resolve some uncovered mutations since `Buffer#write` already returns `self`. --- lib/unparser/buffer.rb | 14 -------------- spec/unit/unparser/buffer_spec.rb | 15 --------------- 2 files changed, 29 deletions(-) diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index 30f331a6..ebb4dcaa 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -44,7 +44,6 @@ def append(string) # def append_without_prefix(string) write(string) - self end # Increase indent @@ -77,7 +76,6 @@ def unindent # def nl write(NL) - self end def root_indent @@ -107,18 +105,6 @@ def content @content.dup.freeze end - # Capture the content written to the buffer within the block - # - # @return [String] - # - # @api private - # - def capture_content - size_before = content.size - yield - content[size_before..] - end - # Write raw fragment to buffer # # Does not do indentation logic. diff --git a/spec/unit/unparser/buffer_spec.rb b/spec/unit/unparser/buffer_spec.rb index 93f07704..9c136298 100644 --- a/spec/unit/unparser/buffer_spec.rb +++ b/spec/unit/unparser/buffer_spec.rb @@ -45,21 +45,6 @@ it_should_behave_like 'a command method' end - describe '#capture_content' do - let(:object) { described_class.new } - - it 'should capture only the content appended within the block' do - object.append('foo') - object.nl - object.indent - captured = object.capture_content do - object.append('bar') - object.nl - end - expect(captured).to eql(" bar\n") - end - end - describe '#content' do subject { object.content } From 9f359b7d91b3f14c734cfd685aedc4352d3a1a4c Mon Sep 17 00:00:00 2001 From: Daniel Gollahon Date: Sun, 8 May 2022 10:38:17 -0700 Subject: [PATCH 186/256] Remove unused methods --- lib/unparser/emitter/hash.rb | 8 -------- lib/unparser/writer/dynamic_string.rb | 12 ------------ 2 files changed, 20 deletions(-) diff --git a/lib/unparser/emitter/hash.rb b/lib/unparser/emitter/hash.rb index f70e2b7a..bd0d3f6f 100644 --- a/lib/unparser/emitter/hash.rb +++ b/lib/unparser/emitter/hash.rb @@ -6,14 +6,6 @@ class Emitter class Hash < self handle :hash - def emit_last_argument_hash - if children.empty? - write('{}') - else - emit_hash_body - end - end - def emit_heredoc_reminders children.each(&method(:emit_heredoc_reminder_member)) end diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index cec91745..fec46270 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -113,18 +113,6 @@ def nl_last_child? n_str?(last) && last.children.first[-1].eql?("\n") end - def emit_squiggly_heredoc_body - buffer.indent - children.each do |child| - if n_str?(child) - write(escape_dynamic(child.children.first)) - else - emit_dynamic(child) - end - end - buffer.unindent - end - def emit_normal_heredoc_body buffer.root_indent do children.each do |child| From 49745ef9f34fca2edfe7c11dea098f2d9c6046b1 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 20 Jul 2022 21:30:53 +0000 Subject: [PATCH 187/256] Remove dependabot * As I'm not going to release each of these bumps they are mostly noise for everyone watching this repo (including me). * Burn noise. --- .github/dependabot.yml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index e077018f..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: 2 -updates: -- package-ecosystem: bundler - directory: / - schedule: - interval: daily From 6081edf874be22ef2687a2e15eeef7c774eb1aec Mon Sep 17 00:00:00 2001 From: paulodeon Date: Thu, 21 Jul 2022 10:37:16 +0100 Subject: [PATCH 188/256] Add Known Users section to Readme.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 263d6890..c3be5032 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,11 @@ Contributing (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. +Known Users +------------- + +* [RailsRocket](https://www.railsrocket.app) - A no-code app builder that creates Rails apps + License ------- From edddc5b3578b2b5279bd0c1d9699b92fb157cee9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 21 Nov 2022 03:30:08 +0000 Subject: [PATCH 189/256] Upgrade mutant --- Gemfile.lock | 20 ++++++++++---------- unparser.gemspec | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c672793a..9fd9f368 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,20 +15,20 @@ GEM specs: ast (2.4.2) diff-lcs (1.5.0) - mutant (0.11.4) + mutant (0.11.17) diff-lcs (~> 1.3) parser (~> 3.1.0) - regexp_parser (~> 2.0, >= 2.0.3) + regexp_parser (~> 2.3, >= 2.3.1) sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.4) - mutant-rspec (0.11.4) - mutant (= 0.11.4) + unparser (~> 0.6.5) + mutant-rspec (0.11.17) + mutant (= 0.11.17) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.21.0) - parser (3.1.2.0) + parser (3.1.2.1) ast (~> 2.4.1) rainbow (3.1.1) - regexp_parser (2.2.1) + regexp_parser (2.6.1) rexml (3.2.5) rspec (3.11.0) rspec-core (~> 3.11.0) @@ -60,16 +60,16 @@ GEM rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.9636) + sorbet-runtime (0.5.10563) unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.4) + mutant (~> 0.11.17) mutant-license! - mutant-rspec (~> 0.11.4) + mutant-rspec (~> 0.11.17) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index 89c52df6..634d386f 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.1.0') - gem.add_development_dependency('mutant', '~> 0.11.4') - gem.add_development_dependency('mutant-rspec', '~> 0.11.4') + gem.add_development_dependency('mutant', '~> 0.11.17') + gem.add_development_dependency('mutant-rspec', '~> 0.11.17') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From cdf9cf504e3ccef862f3f72277710e4a8bcf95a2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 6 Jan 2023 23:26:48 +0000 Subject: [PATCH 190/256] Upgrade rubocop --- Gemfile.lock | 18 ++++++++++-------- bin/corpus | 2 +- bin/unparser | 2 +- lib/unparser/color.rb | 3 +++ lib/unparser/node_details/send.rb | 6 +++--- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9fd9f368..63b0c228 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,6 +15,7 @@ GEM specs: ast (2.4.2) diff-lcs (1.5.0) + json (2.6.3) mutant (0.11.17) diff-lcs (~> 1.3) parser (~> 3.1.0) @@ -24,8 +25,8 @@ GEM mutant-rspec (0.11.17) mutant (= 0.11.17) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.21.0) - parser (3.1.2.1) + parallel (1.22.1) + parser (3.1.3.0) ast (~> 2.4.1) rainbow (3.1.1) regexp_parser (2.6.1) @@ -46,22 +47,23 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) - rubocop (1.26.0) + rubocop (1.42.0) + json (~> 2.3) parallel (~> 1.10) - parser (>= 3.1.0.0) + parser (>= 3.1.2.1) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) - rexml - rubocop-ast (>= 1.16.0, < 2.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.24.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.16.0) + rubocop-ast (1.24.1) parser (>= 3.1.1.0) rubocop-packaging (0.5.1) rubocop (>= 0.89, < 2.0) ruby-progressbar (1.11.0) sorbet-runtime (0.5.10563) - unicode-display_width (2.1.0) + unicode-display_width (2.4.2) PLATFORMS ruby diff --git a/bin/corpus b/bin/corpus index d8c1b572..e8b32da1 100755 --- a/bin/corpus +++ b/bin/corpus @@ -20,7 +20,7 @@ module Unparser checkout command = %W[unparser #{repo_path}] exclude.each do |name| - command.concat(%W[--ignore #{repo_path.join(name)}]) + command.push('--ignore', repo_path.join(name).to_s) end Kernel.system(*command) end diff --git a/bin/unparser b/bin/unparser index 23fb2fbe..26d049e4 100755 --- a/bin/unparser +++ b/bin/unparser @@ -2,7 +2,7 @@ # frozen_string_literal: true trap('INT') do |status| - exit! 128 + status + exit! status + 128 end require 'unparser' diff --git a/lib/unparser/color.rb b/lib/unparser/color.rb index 0542be21..fc0d6605 100644 --- a/lib/unparser/color.rb +++ b/lib/unparser/color.rb @@ -28,7 +28,10 @@ def format(text) private + # Well rubocop you are static so you do not have a clue here ;) + # rubocop:disable Style/RedundantInitialize def initialize; end + # rubocop:enable Style/RedundantInitialize end.new diff --git a/lib/unparser/node_details/send.rb b/lib/unparser/node_details/send.rb index 4a2a743e..8fb9f429 100644 --- a/lib/unparser/node_details/send.rb +++ b/lib/unparser/node_details/send.rb @@ -19,9 +19,9 @@ def selector_binary_operator? end def binary_syntax_allowed? - selector_binary_operator? \ - && arguments.one? \ - && !n_splat?(arguments.first) \ + selector_binary_operator? \ + && arguments.one? \ + && !n_splat?(arguments.first) \ && !n_kwargs?(arguments.first) end From 5f848bd11562a0a7fbbc28a777b2741cee1b583f Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 6 Jan 2023 23:26:58 +0000 Subject: [PATCH 191/256] Fix Changelog links --- Changelog.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index c29f7f6d..0cecfc87 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,10 @@ # v0.6.5 2022-04-17 -[#312](https://github.com/mbj/unparser/pull/122) +[#312](https://github.com/mbj/unparser/pull/312) * Fix #311, emitting of heredocs within block that has arguments. -[#313](https://github.com/mbj/unparser/pull/123) +[#313](https://github.com/mbj/unparser/pull/313) * Remove Ruby-2.6 support as its EOL From 3bff16bc4ad4649075bef44430fb816df8845bfd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 6 Jan 2023 23:27:08 +0000 Subject: [PATCH 192/256] Add ruby 3.2 --- .github/workflows/ci.yml | 10 +++--- Changelog.md | 6 ++++ Gemfile | 2 ++ Gemfile.lock | 56 ++++++++++++++++++-------------- lib/unparser/emitter/argument.rb | 10 +++--- scripts/devloop.sh | 1 + spec/unit/unparser_spec.rb | 8 +++++ test/corpus/literal/since/32.rb | 7 ++++ unparser.gemspec | 4 +-- 9 files changed, 68 insertions(+), 36 deletions(-) create mode 100644 test/corpus/literal/since/32.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 297a800a..7426273d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -36,7 +36,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -54,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 @@ -86,7 +86,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1] + ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - uses: actions/checkout@v2 diff --git a/Changelog.md b/Changelog.md index 0cecfc87..68986249 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.6.6 2022-04-17 + +[#336](https://github.com/mbj/unparser/pull/336) + +* Add support for ruby-3.2 syntax. + # v0.6.5 2022-04-17 [#312](https://github.com/mbj/unparser/pull/312) diff --git a/Gemfile b/Gemfile index 1419592e..d1fb0535 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ source 'https://rubygems.org' gemspec +gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.2' + source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index 63b0c228..9ef1578e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,21 @@ +GIT + remote: https://github.com/mbj/mutant + revision: 6dbea24f122a06122c8ef2c0941ddcad70984265 + branch: add/ruby-3.2 + specs: + mutant (0.11.17) + diff-lcs (~> 1.3) + parser (~> 3.2.0) + regexp_parser (~> 2.6.1) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.6.5) + PATH remote: . specs: - unparser (0.6.5) + unparser (0.6.6) diff-lcs (~> 1.3) - parser (>= 3.1.0) + parser (>= 3.2.0) GEM remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ @@ -16,37 +28,31 @@ GEM ast (2.4.2) diff-lcs (1.5.0) json (2.6.3) - mutant (0.11.17) - diff-lcs (~> 1.3) - parser (~> 3.1.0) - regexp_parser (~> 2.3, >= 2.3.1) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.5) mutant-rspec (0.11.17) mutant (= 0.11.17) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.22.1) - parser (3.1.3.0) + parser (3.2.0.0) ast (~> 2.4.1) rainbow (3.1.1) regexp_parser (2.6.1) rexml (3.2.5) - rspec (3.11.0) - rspec-core (~> 3.11.0) - rspec-expectations (~> 3.11.0) - rspec-mocks (~> 3.11.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec (3.12.0) + rspec-core (~> 3.12.0) + rspec-expectations (~> 3.12.0) + rspec-mocks (~> 3.12.0) + rspec-core (3.12.0) + rspec-support (~> 3.12.0) + rspec-expectations (3.12.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) + rspec-support (~> 3.12.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.11.0) + rspec-mocks (3.12.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-support (3.11.0) + rspec-support (~> 3.12.0) + rspec-support (3.12.0) rubocop (1.42.0) json (~> 2.3) parallel (~> 1.10) @@ -59,17 +65,17 @@ GEM unicode-display_width (>= 1.4.0, < 3.0) rubocop-ast (1.24.1) parser (>= 3.1.1.0) - rubocop-packaging (0.5.1) - rubocop (>= 0.89, < 2.0) + rubocop-packaging (0.5.2) + rubocop (>= 1.33, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.10563) + sorbet-runtime (0.5.10607) unicode-display_width (2.4.2) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.17) + mutant! mutant-license! mutant-rspec (~> 0.11.17) rspec (~> 3.9) @@ -80,4 +86,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.3.6 + 2.4.1 diff --git a/lib/unparser/emitter/argument.rb b/lib/unparser/emitter/argument.rb index 0517de43..0a47bfd3 100644 --- a/lib/unparser/emitter/argument.rb +++ b/lib/unparser/emitter/argument.rb @@ -2,11 +2,13 @@ module Unparser class Emitter - # Emitter for block and kwrestarg arguments - class Morearg < self + # Emitter for forwarding arguments + class ForwardArg < self MAP = { - blockarg: '&', - kwrestarg: '**' + blockarg: '&', + forwarded_kwrestarg: '**', + forwarded_restarg: '*', + kwrestarg: '**' }.freeze handle(*MAP.keys) diff --git a/scripts/devloop.sh b/scripts/devloop.sh index 03ee4b69..8b7fdbe4 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,5 +1,6 @@ while inotifywait ../mutant/**/*.rb **/*.rb Gemfile unparser.gemspec; do bundle exec rspec spec/unit -fd --fail-fast --order default \ + && bundle exec ./bin/parser-round-trip-test \ && bundle exec mutant run --zombie --since HEAD~1 --fail-fast -- 'Unparser*' \ && bundle exec rubocop done diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 42525098..88cd4f1e 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -359,6 +359,14 @@ def noop let(:version_excludes) do excludes = [] + if RUBY_VERSION < '3.2.' + excludes.concat( + %w[ + test/corpus/literal/since/32.rb + ] + ) + end + if RUBY_VERSION < '3.1.' excludes.concat( %w[ diff --git a/test/corpus/literal/since/32.rb b/test/corpus/literal/since/32.rb new file mode 100644 index 00000000..fa279f11 --- /dev/null +++ b/test/corpus/literal/since/32.rb @@ -0,0 +1,7 @@ +def foo(argument, **) + bar(argument, **) +end + +def foo(argument, *) + bar(argument, *) +end diff --git a/unparser.gemspec b/unparser.gemspec index 634d386f..d2f1700c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.5' + gem.version = '0.6.6' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -24,7 +24,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.7' gem.add_dependency('diff-lcs', '~> 1.3') - gem.add_dependency('parser', '>= 3.1.0') + gem.add_dependency('parser', '>= 3.2.0') gem.add_development_dependency('mutant', '~> 0.11.17') gem.add_development_dependency('mutant-rspec', '~> 0.11.17') From 45675cd1e3efe8e992d8d17c6b4c145a80d92786 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 7 Jan 2023 00:17:20 +0000 Subject: [PATCH 193/256] Remove obsolete Rakefile --- README.md | 2 +- Rakefile | 22 ---------------------- 2 files changed, 1 insertion(+), 23 deletions(-) delete mode 100644 Rakefile diff --git a/README.md b/README.md index c3be5032..095a90e7 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ Contributing * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. -* Commit, do not mess with Rakefile or version +* Commit, do not mess with version (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) * Send me a pull request. Bonus points for topic branches. diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 4e689165..00000000 --- a/Rakefile +++ /dev/null @@ -1,22 +0,0 @@ -require 'devtools' -Devtools.init_rake_tasks - -Rake.application.load_imports -task('metrics:mutant').clear - -namespace :metrics do - task mutant: :coverage do - args = %w[ - bundle exec mutant - --ignore-subject Unparser::Buffer#initialize - --include lib - --require unparser - --use rspec - --zombie - --since HEAD~1 - ] - args.concat(%w[--jobs 4]) if ENV.key?('CIRCLECI') - - system(*args.concat(%w[-- Unparser*])) or fail "Mutant task failed" - end -end From dcf067707e09752c8cc086025d5808cfc55e1102 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Jan 2023 15:37:46 +0000 Subject: [PATCH 194/256] Upgrade to mutant-0.11.18 --- Gemfile | 2 -- Gemfile.lock | 32 +++++++++++++------------------- unparser.gemspec | 4 ++-- 3 files changed, 15 insertions(+), 23 deletions(-) diff --git a/Gemfile b/Gemfile index d1fb0535..1419592e 100644 --- a/Gemfile +++ b/Gemfile @@ -4,8 +4,6 @@ source 'https://rubygems.org' gemspec -gem 'mutant', git: 'https://github.com/mbj/mutant', branch: 'add/ruby-3.2' - source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do gem 'mutant-license' end diff --git a/Gemfile.lock b/Gemfile.lock index 9ef1578e..05a698bf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant - revision: 6dbea24f122a06122c8ef2c0941ddcad70984265 - branch: add/ruby-3.2 - specs: - mutant (0.11.17) - diff-lcs (~> 1.3) - parser (~> 3.2.0) - regexp_parser (~> 2.6.1) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.5) - PATH remote: . specs: @@ -28,8 +16,14 @@ GEM ast (2.4.2) diff-lcs (1.5.0) json (2.6.3) - mutant-rspec (0.11.17) - mutant (= 0.11.17) + mutant (0.11.18) + diff-lcs (~> 1.3) + parser (~> 3.2.0) + regexp_parser (~> 2.6.1) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.6.6) + mutant-rspec (0.11.18) + mutant (= 0.11.18) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.22.1) parser (3.2.0.0) @@ -43,13 +37,13 @@ GEM rspec-mocks (~> 3.12.0) rspec-core (3.12.0) rspec-support (~> 3.12.0) - rspec-expectations (3.12.1) + rspec-expectations (3.12.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.12.1) + rspec-mocks (3.12.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.0) @@ -68,16 +62,16 @@ GEM rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.11.0) - sorbet-runtime (0.5.10607) + sorbet-runtime (0.5.10611) unicode-display_width (2.4.2) PLATFORMS ruby DEPENDENCIES - mutant! + mutant (~> 0.11.18) mutant-license! - mutant-rspec (~> 0.11.17) + mutant-rspec (~> 0.11.18) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index d2f1700c..9a89b05c 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,8 +26,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.2.0') - gem.add_development_dependency('mutant', '~> 0.11.17') - gem.add_development_dependency('mutant-rspec', '~> 0.11.17') + gem.add_development_dependency('mutant', '~> 0.11.18') + gem.add_development_dependency('mutant-rspec', '~> 0.11.18') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From d73b0e92eb5e21dad96a95f203341dae481ad90f Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Jan 2023 15:37:47 +0000 Subject: [PATCH 195/256] Change to build on all pushes --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7426273d..1f7c08a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,6 @@ name: CI -on: - pull_request: {} - push: - branches: main +on: push jobs: base: From a9bcf565aec8248d35a08041427d0fde5226f0c2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Jan 2023 15:37:48 +0000 Subject: [PATCH 196/256] Change to v3 checkout action --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f7c08a6..8e3932c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: name: Base steps runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check Whitespace run: git diff --check -- HEAD~1 ruby-unit-spec: @@ -20,7 +20,7 @@ jobs: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -36,7 +36,7 @@ jobs: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 @@ -54,7 +54,7 @@ jobs: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -70,7 +70,7 @@ jobs: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -86,7 +86,7 @@ jobs: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] os: [macos-latest, ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 with: bundler-cache: true From b48e9f67e7d4a49db40d67be17c0f0ad5568ca9e Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Jan 2023 15:37:49 +0000 Subject: [PATCH 197/256] Add required rubygems MFA --- Changelog.md | 6 ++++++ Gemfile.lock | 2 +- unparser.gemspec | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 68986249..9da4f1f1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,11 @@ # v0.6.6 2022-04-17 +[#338](https://github.com/mbj/unparser/pull/338) + +* Add required MFA for rubygems pushes. + +# v0.6.6 2022-04-17 + [#336](https://github.com/mbj/unparser/pull/336) * Add support for ruby-3.2 syntax. diff --git a/Gemfile.lock b/Gemfile.lock index 05a698bf..55271bda 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.6) + unparser (0.6.7) diff-lcs (~> 1.3) parser (>= 3.2.0) diff --git a/unparser.gemspec b/unparser.gemspec index 9a89b05c..972941c9 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.6' + gem.version = '0.6.7' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -21,6 +21,8 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] + gem.metadata['rubygems_mfa_required'] = 'true' + gem.required_ruby_version = '>= 2.7' gem.add_dependency('diff-lcs', '~> 1.3') From 7e9f3f4c8e0be3f8350ddedbf6232a82267a5e6b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Jan 2023 22:11:06 +0000 Subject: [PATCH 198/256] Fix changelog version for 0.6.7 --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 9da4f1f1..19e501d2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# v0.6.6 2022-04-17 +# v0.6.7 2022-04-17 [#338](https://github.com/mbj/unparser/pull/338) From 00ff025b68fd601673ef4ae89de7129f1b32d591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Andr=C3=A9s=20Vilina?= Date: Thu, 13 Apr 2023 01:03:32 -0400 Subject: [PATCH 199/256] Fix typo in `README.md` Change `May other` to `Many other` --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 095a90e7..368ca180 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Notable Users: * [mutant](https://github.com/mbj/mutant) - Code review engine via mutation testing. * [ruby-next](https://github.com/ruby-next/ruby-next) - Ruby Syntax Backports. -* May other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies). +* Many other [reverse-dependencies](https://rubygems.org/gems/unparser/reverse_dependencies). (if you want your tool to be mentioned here please PR the addition with a TLDR of your use case). From 9bda63ba65f2d864bc031686827191b85cfc547e Mon Sep 17 00:00:00 2001 From: Daniel Gollahon Date: Sat, 13 May 2023 15:11:28 -0700 Subject: [PATCH 200/256] Remove unused `let` - Mainly using `unparser` as a canary for an [`rspectre`](https://github.com/dgollahon/rspectre) release. --- spec/unit/unparser/adamantium/memory_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/unit/unparser/adamantium/memory_spec.rb b/spec/unit/unparser/adamantium/memory_spec.rb index e1cc8603..74cc2784 100644 --- a/spec/unit/unparser/adamantium/memory_spec.rb +++ b/spec/unit/unparser/adamantium/memory_spec.rb @@ -4,7 +4,6 @@ let(:monitor) { instance_double(Monitor) } let(:name) { :some_name } let(:object) { described_class.new(proxy) } - let(:values) { {} } let(:value_was_read) { -> {} } let(:block) do From 781e1bd4fb0e88ddbc7ae3b0afba7b71845b4e95 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 14 Jun 2023 23:21:51 +0000 Subject: [PATCH 201/256] Upgrade dependencies --- Gemfile.lock | 40 ++++++++++++++++++++------------------ bin/corpus | 37 ++++++++++++++++++++++------------- lib/unparser/color.rb | 2 ++ lib/unparser/validation.rb | 2 ++ 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 55271bda..55c5a5fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -16,53 +16,55 @@ GEM ast (2.4.2) diff-lcs (1.5.0) json (2.6.3) - mutant (0.11.18) + mutant (0.11.20) diff-lcs (~> 1.3) - parser (~> 3.2.0) + parser (~> 3.2.2) regexp_parser (~> 2.6.1) sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.6) - mutant-rspec (0.11.18) - mutant (= 0.11.18) + unparser (~> 0.6.7) + mutant-rspec (0.11.20) + mutant (= 0.11.20) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.22.1) - parser (3.2.0.0) + parallel (1.23.0) + parser (3.2.2.3) ast (~> 2.4.1) + racc + racc (1.7.1) rainbow (3.1.1) - regexp_parser (2.6.1) + regexp_parser (2.6.2) rexml (3.2.5) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) rspec-mocks (~> 3.12.0) - rspec-core (3.12.0) + rspec-core (3.12.2) rspec-support (~> 3.12.0) - rspec-expectations (3.12.2) + rspec-expectations (3.12.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.12.2) + rspec-mocks (3.12.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.0) - rubocop (1.42.0) + rubocop (1.52.1) json (~> 2.3) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.24.1, < 2.0) + rubocop-ast (>= 1.28.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.24.1) - parser (>= 3.1.1.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.29.0) + parser (>= 3.2.1.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) - ruby-progressbar (1.11.0) - sorbet-runtime (0.5.10611) + ruby-progressbar (1.13.0) + sorbet-runtime (0.5.10878) unicode-display_width (2.4.2) PLATFORMS diff --git a/bin/corpus b/bin/corpus index e8b32da1..1f3e2997 100755 --- a/bin/corpus +++ b/bin/corpus @@ -57,31 +57,40 @@ module Unparser end transform = Mutant::Transform - string = transform::Primitive.new(String) - string_array = transform::Array.new(string) + string = transform::Primitive.new(primitive: String) + string_array = transform::Array.new(transform: string) path = ROOT.join('spec', 'integrations.yml') loader = transform::Named.new( - path.to_s, - transform::Sequence.new( - [ - transform::Exception.new(SystemCallError, :read.to_proc), - transform::Exception.new(YAML::SyntaxError, YAML.method(:safe_load)), + name: path.to_s, + transform: transform::Sequence.new( + steps: [ + transform::Exception.new( + block: :read.to_proc, + error_class: SystemCallError + ), + transform::Exception.new( + block: YAML.method(:safe_load), + error_class: YAML::SyntaxError + ), transform::Array.new( - transform::Sequence.new( - [ + transform: transform::Sequence.new( + steps: [ transform::Hash.new( optional: [], required: [ - transform::Hash::Key.new('exclude', string_array), - transform::Hash::Key.new('name', string), - transform::Hash::Key.new('repo_ref', string), - transform::Hash::Key.new('repo_uri', string) + transform::Hash::Key.new(value: 'exclude', transform: string_array), + transform::Hash::Key.new(value: 'name', transform: string), + transform::Hash::Key.new(value: 'repo_ref', transform: string), + transform::Hash::Key.new(value: 'repo_uri', transform: string) ] ), transform::Hash::Symbolize.new, - transform::Exception.new(Unparser::Anima::Error, Project.public_method(:new)) + transform::Exception.new( + block: Project.public_method(:new), + error_class: Unparser::Anima::Error + ) ] ) ) diff --git a/lib/unparser/color.rb b/lib/unparser/color.rb index fc0d6605..9120f8c4 100644 --- a/lib/unparser/color.rb +++ b/lib/unparser/color.rb @@ -30,8 +30,10 @@ def format(text) # Well rubocop you are static so you do not have a clue here ;) # rubocop:disable Style/RedundantInitialize + # rubocop:disable Style/MissingSuper def initialize; end # rubocop:enable Style/RedundantInitialize + # rubocop:enable Style/MissingSuper end.new diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index d0413428..b6600797 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -17,6 +17,7 @@ class Validation # # @api private # + # rubocop:disable Style/OperatorMethodCall def success? [ original_source, @@ -25,6 +26,7 @@ def success? generated_node ].all?(&:right?) && generated_node.from_right.==(original_node.from_right) end + # rubocop:enable Style/OperatorMethodCall # Return error report # From 1e88aa0439f863e6102c6f59450afcf0a21e8268 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 15 Jun 2023 00:03:53 +0000 Subject: [PATCH 202/256] Remove macosx from CI * Its simply to slow. * `unparser` is far from any "OS specific" API usage. * Okay trade of for me. --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e3932c6..fdcd3edd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] - os: [macos-latest, ubuntu-latest] + os: [ubuntu-latest] steps: - uses: actions/checkout@v3 with: @@ -52,7 +52,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] - os: [macos-latest, ubuntu-latest] + os: [ubuntu-latest] steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 @@ -68,7 +68,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] - os: [macos-latest, ubuntu-latest] + os: [ubuntu-latest] steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 @@ -84,7 +84,7 @@ jobs: fail-fast: false matrix: ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] - os: [macos-latest, ubuntu-latest] + os: [ubuntu-latest] steps: - uses: actions/checkout@v3 - uses: ruby/setup-ruby@v1 From 3339e0098fdb2b87e836fe8216b74dbe69bdcec2 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 14 Jun 2023 23:31:21 +0000 Subject: [PATCH 203/256] Fix csend binaries [Fix #345] --- Changelog.md | 10 ++++++++-- Gemfile.lock | 2 +- lib/unparser/node_details/send.rb | 1 + test/corpus/literal/send.rb | 1 + unparser.gemspec | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 19e501d2..85529109 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,16 @@ -# v0.6.7 2022-04-17 +# v0.6.8 2023-06-14 + +[#347](https://github.com/mbj/unparser/pull/347) + +* Fix binary operator with csend receiver. [Fix #345] + +# v0.6.7 2023-01-08 [#338](https://github.com/mbj/unparser/pull/338) * Add required MFA for rubygems pushes. -# v0.6.6 2022-04-17 +# v0.6.6 2023-01-06 [#336](https://github.com/mbj/unparser/pull/336) diff --git a/Gemfile.lock b/Gemfile.lock index 55c5a5fd..5ad0b798 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.7) + unparser (0.6.8) diff-lcs (~> 1.3) parser (>= 3.2.0) diff --git a/lib/unparser/node_details/send.rb b/lib/unparser/node_details/send.rb index 8fb9f429..082d9427 100644 --- a/lib/unparser/node_details/send.rb +++ b/lib/unparser/node_details/send.rb @@ -20,6 +20,7 @@ def selector_binary_operator? def binary_syntax_allowed? selector_binary_operator? \ + && n_send?(node) \ && arguments.one? \ && !n_splat?(arguments.first) \ && !n_kwargs?(arguments.first) diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index 4361cf73..1e9c2a94 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -81,3 +81,4 @@ def foo x(**foo) foo&.! foo.~(b) +a&.+(b) diff --git a/unparser.gemspec b/unparser.gemspec index 972941c9..334dd5b1 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.7' + gem.version = '0.6.8' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From c996bbaee12b1b8c873b9e4408eaf339bfe5f799 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Oct 2023 02:07:02 +0000 Subject: [PATCH 204/256] Change to reduced devloop --- scripts/devloop.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/devloop.sh b/scripts/devloop.sh index 8b7fdbe4..f3d6ab0b 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,4 +1,4 @@ -while inotifywait ../mutant/**/*.rb **/*.rb Gemfile unparser.gemspec; do +while inotifywait lib/**/*.rb test/**/*.rb spec/**/*.rb Gemfile unparser.gemspec; do bundle exec rspec spec/unit -fd --fail-fast --order default \ && bundle exec ./bin/parser-round-trip-test \ && bundle exec mutant run --zombie --since HEAD~1 --fail-fast -- 'Unparser*' \ From edafcff265383d97b2f9a8b510df3570601ab57a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Oct 2023 02:13:13 +0000 Subject: [PATCH 205/256] Fix hash emitter on kwrestargs --- Changelog.md | 6 ++++++ Gemfile.lock | 2 +- config/mutant.yml | 1 + lib/unparser/emitter/hash.rb | 2 +- test/corpus/literal/since/32.rb | 4 ++++ unparser.gemspec | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 85529109..7718e860 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.6.8 2023-10-08 + +[#348](https://github.com/mbj/unparser/pull/348) + +* Fix crash on kwrestarg hash member + # v0.6.8 2023-06-14 [#347](https://github.com/mbj/unparser/pull/347) diff --git a/Gemfile.lock b/Gemfile.lock index 5ad0b798..e61f2bcc 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.8) + unparser (0.6.9) diff-lcs (~> 1.3) parser (>= 3.2.0) diff --git a/config/mutant.yml b/config/mutant.yml index 8b22a6be..5a224b2b 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -25,6 +25,7 @@ matcher: - 'Unparser::Emitter::Class#local_variable_scope' - 'Unparser::Emitter::Def#local_variable_scope' - 'Unparser::Emitter::FindPattern#dispatch' # 3.0+ specific + - 'Unparser::Emitter::Hash#emit_heredoc_reminder_member' # 3.2+ specific - 'Unparser::Emitter::HashPattern#write_symbol_body' - 'Unparser::Emitter::LocalVariableRoot*' - 'Unparser::Emitter::LocalVariableRoot.included' diff --git a/lib/unparser/emitter/hash.rb b/lib/unparser/emitter/hash.rb index bd0d3f6f..017455ac 100644 --- a/lib/unparser/emitter/hash.rb +++ b/lib/unparser/emitter/hash.rb @@ -25,7 +25,7 @@ def dispatch end def emit_heredoc_reminder_member(node) - emitter(node.children.last).emit_heredoc_reminders + emitter(node.children.last).emit_heredoc_reminders if n_pair?(node) end def emit_hash_body diff --git a/test/corpus/literal/since/32.rb b/test/corpus/literal/since/32.rb index fa279f11..b8e096d8 100644 --- a/test/corpus/literal/since/32.rb +++ b/test/corpus/literal/since/32.rb @@ -5,3 +5,7 @@ def foo(argument, **) def foo(argument, *) bar(argument, *) end + +def foo(**) + { default: 1, ** } +end diff --git a/unparser.gemspec b/unparser.gemspec index 334dd5b1..3c45d04a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.8' + gem.version = '0.6.9' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 2cfd4687e9338c9e3ab9798511a9eeca7139ff3b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 8 Oct 2023 02:35:37 +0000 Subject: [PATCH 206/256] Fix {begin,end}less {i,e}range flipflops * This requires to pin a parser release where this is fixed. * And has to bump rubocop. --- Changelog.md | 4 ++++ Gemfile.lock | 34 ++++++++++++++++++-------------- bin/corpus | 4 ++-- lib/unparser/emitter/flipflop.rb | 9 +++++++-- test/corpus/literal/flipflop.rb | 4 ++++ unparser.gemspec | 6 +++--- 6 files changed, 39 insertions(+), 22 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7718e860..04520e25 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,10 @@ * Fix crash on kwrestarg hash member +[#349](https://github.com/mbj/unparser/pull/349) + +* Fix {begin,end}less {i,e}range flipflops + # v0.6.8 2023-06-14 [#347](https://github.com/mbj/unparser/pull/347) diff --git a/Gemfile.lock b/Gemfile.lock index e61f2bcc..06250fde 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: unparser (0.6.9) diff-lcs (~> 1.3) - parser (>= 3.2.0) + parser (>= 3.2.2.4) GEM remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ @@ -14,25 +14,27 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) + base64 (0.1.1) diff-lcs (1.5.0) json (2.6.3) - mutant (0.11.20) + language_server-protocol (3.17.0.3) + mutant (0.11.22) diff-lcs (~> 1.3) parser (~> 3.2.2) regexp_parser (~> 2.6.1) sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.7) - mutant-rspec (0.11.20) - mutant (= 0.11.20) + unparser (~> 0.6.8) + mutant-rspec (0.11.22) + mutant (= 0.11.22) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.23.0) - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc racc (1.7.1) rainbow (3.1.1) regexp_parser (2.6.2) - rexml (3.2.5) + rexml (3.2.6) rspec (3.12.0) rspec-core (~> 3.12.0) rspec-expectations (~> 3.12.0) @@ -45,18 +47,20 @@ GEM rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.12.5) + rspec-mocks (3.12.6) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) - rspec-support (3.12.0) - rubocop (1.52.1) + rspec-support (3.12.1) + rubocop (1.56.4) + base64 (~> 0.1.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.2.2.3) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + rubocop-ast (>= 1.28.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) rubocop-ast (1.29.0) @@ -64,16 +68,16 @@ GEM rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.10878) - unicode-display_width (2.4.2) + sorbet-runtime (0.5.11064) + unicode-display_width (2.5.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.18) + mutant (~> 0.11.22) mutant-license! - mutant-rspec (~> 0.11.18) + mutant-rspec (~> 0.11.22) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/bin/corpus b/bin/corpus index 1f3e2997..00b55c03 100755 --- a/bin/corpus +++ b/bin/corpus @@ -116,7 +116,7 @@ module Unparser puts(project.name) end - Kernel.exit(true) + Kernel.exit end end @@ -148,7 +148,7 @@ module Unparser project.verify || Kernel.exit(false) end - Kernel.exit(true) + Kernel.exit end end # CLI diff --git a/lib/unparser/emitter/flipflop.rb b/lib/unparser/emitter/flipflop.rb index d7fd717c..bd93df9d 100644 --- a/lib/unparser/emitter/flipflop.rb +++ b/lib/unparser/emitter/flipflop.rb @@ -25,9 +25,14 @@ def symbol_name private def dispatch - visit(left) + visit(left) if left write(MAP.fetch(node.type)) - visit(right) + + if right + visit(right) + else + write(';') + end end end # FlipFLop end # Emitter diff --git a/test/corpus/literal/flipflop.rb b/test/corpus/literal/flipflop.rb index 8badd39d..139904a5 100644 --- a/test/corpus/literal/flipflop.rb +++ b/test/corpus/literal/flipflop.rb @@ -4,3 +4,7 @@ if ((i == 4)...(i == 4)) foo end +if ..foo +end +if foo..; +end diff --git a/unparser.gemspec b/unparser.gemspec index 3c45d04a..6177267a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,10 +26,10 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 2.7' gem.add_dependency('diff-lcs', '~> 1.3') - gem.add_dependency('parser', '>= 3.2.0') + gem.add_dependency('parser', '>= 3.2.2.4') - gem.add_development_dependency('mutant', '~> 0.11.18') - gem.add_development_dependency('mutant-rspec', '~> 0.11.18') + gem.add_development_dependency('mutant', '~> 0.11.22') + gem.add_development_dependency('mutant-rspec', '~> 0.11.22') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From d448294b57180be9525502454be5c5185a0d3efe Mon Sep 17 00:00:00 2001 From: Vidar Hokstad Date: Fri, 13 Oct 2023 05:07:23 +0100 Subject: [PATCH 207/256] Fixes #360 by adding missing HEREDOC handling --- lib/unparser/emitter/op_assign.rb | 5 +++++ test/corpus/semantic/opasgn.rb | 1 + 2 files changed, 6 insertions(+) create mode 100644 test/corpus/semantic/opasgn.rb diff --git a/lib/unparser/emitter/op_assign.rb b/lib/unparser/emitter/op_assign.rb index 52d3d117..affd028a 100644 --- a/lib/unparser/emitter/op_assign.rb +++ b/lib/unparser/emitter/op_assign.rb @@ -35,6 +35,11 @@ class OpAssign < self children :target, :operator, :value + def emit_heredoc_reminders + emitter(target).emit_heredoc_reminders + emitter(value).emit_heredoc_reminders + end + private def dispatch diff --git a/test/corpus/semantic/opasgn.rb b/test/corpus/semantic/opasgn.rb new file mode 100644 index 00000000..de66f430 --- /dev/null +++ b/test/corpus/semantic/opasgn.rb @@ -0,0 +1 @@ +y += "#{42}\n" From 4fb3922b8938c152516b49207fe333468a4fb7dd Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 31 Oct 2023 00:06:43 +0000 Subject: [PATCH 208/256] Change to version 0.6.10 --- Changelog.md | 8 +++++++- Gemfile.lock | 2 +- unparser.gemspec | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Changelog.md b/Changelog.md index 04520e25..e9fd905c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,10 @@ -# v0.6.8 2023-10-08 +# v0.6.10 2023-10-31 + +[#351](https://github.com/mbj/unparser/pull/351) + +* Fix missing heredocs on op-assign rhs + +# v0.6.9 2023-10-08 [#348](https://github.com/mbj/unparser/pull/348) diff --git a/Gemfile.lock b/Gemfile.lock index 06250fde..1d8df72b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.9) + unparser (0.6.10) diff-lcs (~> 1.3) parser (>= 3.2.2.4) diff --git a/unparser.gemspec b/unparser.gemspec index 6177267a..78bd1219 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.9' + gem.version = '0.6.10' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 1e956d2e56b803e900a788e0476bb0a54c636130 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Tue, 31 Oct 2023 00:15:15 +0000 Subject: [PATCH 209/256] Fix alive mutation in op-asgn dispatch --- test/corpus/semantic/opasgn.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/corpus/semantic/opasgn.rb b/test/corpus/semantic/opasgn.rb index de66f430..8b4bc5d2 100644 --- a/test/corpus/semantic/opasgn.rb +++ b/test/corpus/semantic/opasgn.rb @@ -1 +1 @@ -y += "#{42}\n" +y["#{42}\n"] += "#{42}\n" From fbc98d7cc50b1d444ba24d1d0a771a15e6232bc9 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 7 Dec 2023 01:00:30 +0000 Subject: [PATCH 210/256] Remove support for ruby 2.7 --- .github/workflows/ci.yml | 10 +++++----- .rubocop.yml | 1 + Changelog.md | 4 ++++ Gemfile.lock | 2 +- README.md | 2 +- config/mutant.yml | 1 - lib/unparser/cli.rb | 4 ++-- spec/unit/unparser_spec.rb | 9 --------- unparser.gemspec | 4 ++-- 9 files changed, 16 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdcd3edd..0dac9c45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -51,7 +51,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -67,7 +67,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -83,7 +83,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-2.7, ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 diff --git a/.rubocop.yml b/.rubocop.yml index 0536f522..338aba55 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,6 +5,7 @@ AllCops: - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' + TargetRubyVersion: 3.0 Exclude: - tmp/**/* - vendor/**/* diff --git a/Changelog.md b/Changelog.md index e9fd905c..97c64df4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,7 @@ +# v0.6.11 2023-10-31 + +* Remove support for ruby 2.7 + # v0.6.10 2023-10-31 [#351](https://github.com/mbj/unparser/pull/351) diff --git a/Gemfile.lock b/Gemfile.lock index 1d8df72b..7f1460d0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.10) + unparser (0.6.11) diff-lcs (~> 1.3) parser (>= 3.2.2.4) diff --git a/README.md b/README.md index 368ca180..75a9d202 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 2.7 +* Only support for Ruby >= 3.0 Notable Users: diff --git a/config/mutant.yml b/config/mutant.yml index 5a224b2b..ef2279ab 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -29,7 +29,6 @@ matcher: - 'Unparser::Emitter::HashPattern#write_symbol_body' - 'Unparser::Emitter::LocalVariableRoot*' - 'Unparser::Emitter::LocalVariableRoot.included' - - 'Unparser::Emitter::MatchPattern#dispatch' # 2.7+ specific - 'Unparser::Emitter::MatchPatternP#dispatch' # 3.0+ specific - 'Unparser::Emitter::MatchRest#dispatch' # 3.0+ specific - 'Unparser::Emitter::Module#local_variable_scope' diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index c1fa7797..1768e6f0 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -169,11 +169,11 @@ def effective_targets def targets(file_name) if File.directory?(file_name) - Dir.glob(File.join(file_name, '**/*.rb')).sort + Dir.glob(File.join(file_name, '**/*.rb')) elsif File.file?(file_name) [file_name] else - Dir.glob(file_name).sort + Dir.glob(file_name) end.map { |file| Target::Path.new(Pathname.new(file)) } end end # CLI diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 88cd4f1e..8eb32dd8 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -383,15 +383,6 @@ def noop ) end - if RUBY_VERSION < '2.7.' - excludes.concat( - %w[ - test/corpus/literal/pattern.rb - test/corpus/literal/since/27.rb - ] - ) - end - excludes.flat_map { |file| ['--ignore', file] } end diff --git a/unparser.gemspec b/unparser.gemspec index 78bd1219..9efa061e 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.10' + gem.version = '0.6.11' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -23,7 +23,7 @@ Gem::Specification.new do |gem| gem.metadata['rubygems_mfa_required'] = 'true' - gem.required_ruby_version = '>= 2.7' + gem.required_ruby_version = '>= 3.0' gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.2.2.4') From d2f4d8cfda6877de1f2aff96bbaeecaea8012f75 Mon Sep 17 00:00:00 2001 From: Sergei Prikhodko Date: Wed, 27 Dec 2023 17:35:52 +0300 Subject: [PATCH 211/256] Avoid breaking on empty begin nodes within conditionals (#355) * Add support for empty `s(:body)` in condition branch * Extract indentation handling to a separate method to please rubocop * Test for generated AST equality * Trigger CI on PullRequests --- .github/workflows/ci.yml | 4 +++- lib/unparser/generation.rb | 34 +++++++++++++++++++--------------- spec/unit/unparser_spec.rb | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0dac9c45..0647ad75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,8 @@ name: CI -on: push +on: + push: + pull_request: jobs: base: diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index e553c58d..43a357d6 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -121,25 +121,29 @@ def emit_optional_body(node, indent: true) end def emit_body(node, indent: true) - if indent - buffer.indent - nl - end - - if n_begin?(node) - if node.children.one? - visit_deep(node) + with_indent(indent: indent) do + if n_begin?(node) + if node.children.empty? + write('()') + elsif node.children.one? + visit_deep(node) + else + emit_body_inner(node) + end else - emit_body_inner(node) + visit_deep(node) end - else - visit_deep(node) end + end - if indent - buffer.unindent - nl - end + def with_indent(indent:) + return yield unless indent + + buffer.indent + nl + yield + buffer.unindent + nl end def emit_body_inner(node) diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 8eb32dd8..c979d1a9 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -191,6 +191,7 @@ def assert_generates_from_ast(parser, ast_with_comments, expected) generated = Unparser.unparse(*ast_with_comments).chomp expect(generated).to eql(expected) ast, comments = parse_with_comments(generated) + expect(ast).to eql(ast_with_comments.first) expect(Unparser.unparse(ast, comments).chomp).to eql(expected) end @@ -353,6 +354,42 @@ def noop block comment =end RUBY + + assert_generates(<<~'RUBY', <<~'RUBY') + true ? "true" : () + RUBY + if true + "true" + else + () + end + RUBY + + assert_generates(<<~'RUBY', <<~'RUBY') + true ? () : "false" + RUBY + if true + () + else + "false" + end + RUBY + + assert_source(<<~'RUBY') + if true + "true" + else + () + end + RUBY + + assert_source(<<~'RUBY') + if true + () + else + "false" + end + RUBY end describe 'corpus' do From 7d2a875616f5c853702bf76804ed1645e7657375 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 10 Jan 2024 04:02:32 +0000 Subject: [PATCH 212/256] Change version to 0.6.11 (#356) --- Changelog.md | 8 ++++++++ Gemfile.lock | 2 +- unparser.gemspec | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Changelog.md b/Changelog.md index 97c64df4..79895ee0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,13 @@ +# v0.6.12 2024-00-10 + +[#355](https://github.com/mbj/unparser/pull/355) + +* Fix conditionals with empty bodies. + # v0.6.11 2023-10-31 +[#354](https://github.com/mbj/unparser/pull/354) + * Remove support for ruby 2.7 # v0.6.10 2023-10-31 diff --git a/Gemfile.lock b/Gemfile.lock index 7f1460d0..de33b859 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.11) + unparser (0.6.12) diff-lcs (~> 1.3) parser (>= 3.2.2.4) diff --git a/unparser.gemspec b/unparser.gemspec index 9efa061e..e0a014e0 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.11' + gem.version = '0.6.12' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 1eaf82c4de9acfa2b94faba44af35073c3f3aa3a Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 10 Jan 2024 04:08:19 +0000 Subject: [PATCH 213/256] Add ruby-3.3 to CI --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0647ad75..9a458aa5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2] + ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v3 From 83e2343a289a8980d7239741a57d61df7547a1d7 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 10 Jan 2024 04:10:16 +0000 Subject: [PATCH 214/256] Upgrade dependencies --- Gemfile.lock | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index de33b859..9e10d46f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,26 +14,25 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - base64 (0.1.1) diff-lcs (1.5.0) - json (2.6.3) + json (2.7.1) language_server-protocol (3.17.0.3) - mutant (0.11.22) + mutant (0.11.26) diff-lcs (~> 1.3) - parser (~> 3.2.2) - regexp_parser (~> 2.6.1) + parser (~> 3.2.2, >= 3.2.2.4) + regexp_parser (~> 2.8.2) sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.8) - mutant-rspec (0.11.22) - mutant (= 0.11.22) + unparser (~> 0.6.9) + mutant-rspec (0.11.26) + mutant (= 0.11.26) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.23.0) + parallel (1.24.0) parser (3.2.2.4) ast (~> 2.4.1) racc - racc (1.7.1) + racc (1.7.3) rainbow (3.1.1) - regexp_parser (2.6.2) + regexp_parser (2.8.3) rexml (3.2.6) rspec (3.12.0) rspec-core (~> 3.12.0) @@ -51,24 +50,23 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.12.0) rspec-support (3.12.1) - rubocop (1.56.4) - base64 (~> 0.1.1) + rubocop (1.59.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.3) + parser (>= 3.2.2.4) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.1, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11064) + sorbet-runtime (0.5.11180) unicode-display_width (2.5.0) PLATFORMS From 52b255313401e9c93e09ec3ffb3a9abde520e1cf Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Wed, 10 Jan 2024 05:10:44 +0000 Subject: [PATCH 215/256] Upgrade to parser 3.3.0 --- Gemfile.lock | 16 ++++++++-------- unparser.gemspec | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9e10d46f..75a9978b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,7 +3,7 @@ PATH specs: unparser (0.6.12) diff-lcs (~> 1.3) - parser (>= 3.2.2.4) + parser (>= 3.3.0) GEM remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ @@ -17,17 +17,17 @@ GEM diff-lcs (1.5.0) json (2.7.1) language_server-protocol (3.17.0.3) - mutant (0.11.26) + mutant (0.11.27) diff-lcs (~> 1.3) - parser (~> 3.2.2, >= 3.2.2.4) + parser (~> 3.3.0) regexp_parser (~> 2.8.2) sorbet-runtime (~> 0.5.0) unparser (~> 0.6.9) - mutant-rspec (0.11.26) - mutant (= 0.11.26) + mutant-rspec (0.11.27) + mutant (= 0.11.27) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.24.0) - parser (3.2.2.4) + parser (3.3.0.2) ast (~> 2.4.1) racc racc (1.7.3) @@ -73,9 +73,9 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.22) + mutant (~> 0.11.27) mutant-license! - mutant-rspec (~> 0.11.22) + mutant-rspec (~> 0.11.27) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index e0a014e0..b0683a92 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,10 +26,10 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 3.0' gem.add_dependency('diff-lcs', '~> 1.3') - gem.add_dependency('parser', '>= 3.2.2.4') + gem.add_dependency('parser', '>= 3.3.0') - gem.add_development_dependency('mutant', '~> 0.11.22') - gem.add_development_dependency('mutant-rspec', '~> 0.11.22') + gem.add_development_dependency('mutant', '~> 0.11.27') + gem.add_development_dependency('mutant-rspec', '~> 0.11.27') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From 13419d8649c14ad879703419743ce3ab35017a99 Mon Sep 17 00:00:00 2001 From: Sergei Prikhodko Date: Thu, 1 Feb 2024 04:52:45 +0300 Subject: [PATCH 216/256] Add a fix for `Symbol#inspect` on `3.0`, `3.1` (#361) * Handle `Symbol#inspect` for Ruby `<3.2.0` * Extract dedicated `Symbol` emitter * Disable mutant on `Symbol#dispatch` as it depends on Ruby version and would fail on CI. * Improve symbol detection logic * Replace `regexp` check with `parse` --- lib/unparser/emitter/primitive.rb | 28 +++++++++++++++++++++++++++- spec/unit/unparser_spec.rb | 5 +++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb index e173704d..ec17b16b 100644 --- a/lib/unparser/emitter/primitive.rb +++ b/lib/unparser/emitter/primitive.rb @@ -10,7 +10,7 @@ class Primitive < self # Emitter for primitives based on Object#inspect class Inspect < self - handle :sym, :str + handle :str private @@ -20,6 +20,32 @@ def dispatch end # Inspect + class Symbol < self + + handle :sym + + private + + # mutant:disable + def dispatch + if inspect_breaks_parsing? + write(":#{value.name.inspect}") + else + write(value.inspect) + end + end + + # mutant:disable + def inspect_breaks_parsing? + return false unless RUBY_VERSION < '3.2.' + + Unparser.parse(value.inspect) + false + rescue Parser::SyntaxError + true + end + end # Symbol + # Emitter for complex literals class Complex < self diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index c979d1a9..43d6867a 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -390,6 +390,11 @@ def noop "false" end RUBY + + # Test Symbol#inspect Ruby bug: https://bugs.ruby-lang.org/issues/18905 + assert_source(':"@="') + assert_source(':"$$$$="') + assert_source(':"8 >="') end describe 'corpus' do From 6a13595a218fc60c948aea4f666ce1c854a3f278 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 1 Feb 2024 01:59:04 +0000 Subject: [PATCH 217/256] Change version to 0.6.13 (#362) * Change version to 0.6.13 * Change to checkout@v4 --- .github/workflows/ci.yml | 12 ++++++------ Changelog.md | 8 +++++++- Gemfile.lock | 2 +- unparser.gemspec | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a458aa5..f981170c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,7 +9,7 @@ jobs: name: Base steps runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check Whitespace run: git diff --check -- HEAD~1 ruby-unit-spec: @@ -22,7 +22,7 @@ jobs: ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -38,7 +38,7 @@ jobs: ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: ruby/setup-ruby@v1 @@ -56,7 +56,7 @@ jobs: ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -72,7 +72,7 @@ jobs: ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true @@ -88,7 +88,7 @@ jobs: ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler-cache: true diff --git a/Changelog.md b/Changelog.md index 79895ee0..64557323 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,10 @@ -# v0.6.12 2024-00-10 +# v0.6.13 2024-02-01 + +[#361](https://github.com/mbj/unparser/pull/361) + +* Fix unparsing of symbols that do not round trip under `#inspect` + +# v0.6.12 2024-01-10 [#355](https://github.com/mbj/unparser/pull/355) diff --git a/Gemfile.lock b/Gemfile.lock index 75a9978b..ac08eb07 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.12) + unparser (0.6.13) diff-lcs (~> 1.3) parser (>= 3.3.0) diff --git a/unparser.gemspec b/unparser.gemspec index b0683a92..fc12e206 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.12' + gem.version = '0.6.13' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From cd529a4c835f400d853487e9604ad08ae8e2d9a8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 10 Jun 2024 00:29:42 +0000 Subject: [PATCH 218/256] Remove support for ruby-3.0 --- .github/workflows/ci.yml | 10 +++++----- Changelog.md | 6 ++++++ README.md | 2 +- lib/unparser/emitter/match_pattern.rb | 11 +---------- scripts/devloop.sh | 2 +- spec/unit/unparser_spec.rb | 8 -------- test/corpus/literal/lambda.rb | 3 +++ test/corpus/literal/pattern.rb | 4 ++++ test/corpus/literal/range.rb | 1 + test/corpus/literal/since/27.rb | 4 ---- test/corpus/literal/since/30.rb | 4 ---- unparser.gemspec | 2 +- 12 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 test/corpus/literal/since/27.rb delete mode 100644 test/corpus/literal/since/30.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f981170c..c697391d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.0, ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.1, ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 diff --git a/Changelog.md b/Changelog.md index 64557323..7c38bc10 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.6.14 2024-06-10 + +[#369](https://github.com/mbj/unparser/pull/369) + +* Remove support for ruby-3.0, its EOL. + # v0.6.13 2024-02-01 [#361](https://github.com/mbj/unparser/pull/361) diff --git a/README.md b/README.md index 75a9d202..3ee04936 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 3.0 +* Only support for Ruby >= 3.1 Notable Users: diff --git a/lib/unparser/emitter/match_pattern.rb b/lib/unparser/emitter/match_pattern.rb index c9e45dde..5f13a7c7 100644 --- a/lib/unparser/emitter/match_pattern.rb +++ b/lib/unparser/emitter/match_pattern.rb @@ -9,20 +9,11 @@ class MatchPattern < self children :target, :pattern - # Modern ast format emits `match_pattern` - # node on single line pre 3.0, but 3.0+ uses `match_pattern_p` - SYMBOL = - if RUBY_VERSION < '3.0' - ' in ' - else - ' => ' - end - private def dispatch visit(target) - write(SYMBOL) + write(' => ') visit(pattern) end end # MatchPattern diff --git a/scripts/devloop.sh b/scripts/devloop.sh index f3d6ab0b..72a7b2e7 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,5 +1,5 @@ while inotifywait lib/**/*.rb test/**/*.rb spec/**/*.rb Gemfile unparser.gemspec; do - bundle exec rspec spec/unit -fd --fail-fast --order default \ + bundle exec rspec spec/unit -fd --fail-fast --order defined \ && bundle exec ./bin/parser-round-trip-test \ && bundle exec mutant run --zombie --since HEAD~1 --fail-fast -- 'Unparser*' \ && bundle exec rubocop diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 43d6867a..82a15dcc 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -417,14 +417,6 @@ def noop ) end - if RUBY_VERSION < '3.0.' - excludes.concat( - %w[ - test/corpus/literal/since/30.rb - ] - ) - end - excludes.flat_map { |file| ['--ignore', file] } end diff --git a/test/corpus/literal/lambda.rb b/test/corpus/literal/lambda.rb index 4eb722da..aba17817 100644 --- a/test/corpus/literal/lambda.rb +++ b/test/corpus/literal/lambda.rb @@ -11,3 +11,6 @@ } ->(a, b; c) { } +-> { + _1 + _2 +} diff --git a/test/corpus/literal/pattern.rb b/test/corpus/literal/pattern.rb index ae1ba429..ebd5110a 100644 --- a/test/corpus/literal/pattern.rb +++ b/test/corpus/literal/pattern.rb @@ -39,3 +39,7 @@ else end 1 in [a] +1 => [a] +1 => [*] +1 in [*, 42, *] +1 in [*, a, *foo] diff --git a/test/corpus/literal/range.rb b/test/corpus/literal/range.rb index eb1f3874..ebb334ad 100644 --- a/test/corpus/literal/range.rb +++ b/test/corpus/literal/range.rb @@ -2,3 +2,4 @@ 1..2 (1...) 1...2 +(..1) diff --git a/test/corpus/literal/since/27.rb b/test/corpus/literal/since/27.rb deleted file mode 100644 index c332f9e4..00000000 --- a/test/corpus/literal/since/27.rb +++ /dev/null @@ -1,4 +0,0 @@ --> { - _1 + _2 -} -(..1) diff --git a/test/corpus/literal/since/30.rb b/test/corpus/literal/since/30.rb deleted file mode 100644 index b73328a4..00000000 --- a/test/corpus/literal/since/30.rb +++ /dev/null @@ -1,4 +0,0 @@ -1 => [a] -1 => [*] -1 in [*, 42, *] -1 in [*, a, *foo] diff --git a/unparser.gemspec b/unparser.gemspec index 9a9d097a..b79e25ce 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -23,7 +23,7 @@ Gem::Specification.new do |gem| gem.metadata['rubygems_mfa_required'] = 'true' - gem.required_ruby_version = '>= 3.0' + gem.required_ruby_version = '>= 3.1' gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.3.0') From 378edc2e25df698d3bc1cea819b92cc05ff8920b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 12 May 2024 02:13:04 +0000 Subject: [PATCH 219/256] Add source code uri metadata --- unparser.gemspec | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/unparser.gemspec b/unparser.gemspec index b79e25ce..aa90f143 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -11,9 +11,11 @@ Gem::Specification.new do |gem| gem.license = 'MIT' gem.metadata = { - 'bug_tracker_uri' => 'https://github.com/mbj/unparser/issues', - 'changelog_uri' => 'https://github.com/mbj/unparser/blob/master/Changelog.md', - 'funding_uri' => 'https://github.com/sponsors/mbj' + 'bug_tracker_uri' => 'https://github.com/mbj/unparser/issues', + 'changelog_uri' => 'https://github.com/mbj/unparser/blob/master/Changelog.md', + 'funding_uri' => 'https://github.com/sponsors/mbj', + 'source_code_uri' => 'https://github.com/mbj/unparser', + 'rubygems_mfa_required' => 'true' } gem.files = Dir.glob('lib/**/*') @@ -21,7 +23,6 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.metadata['rubygems_mfa_required'] = 'true' gem.required_ruby_version = '>= 3.1' From 95ab816ebfed2b9b5079ecb9505ded1d1b8093ed Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 9 Jun 2024 22:18:36 +0000 Subject: [PATCH 220/256] Upgrade dependencies --- Gemfile.lock | 66 ++++++++++++------------ config/mutant.yml | 1 + lib/unparser/adamantium.rb | 4 +- lib/unparser/ast/local_variable_scope.rb | 3 +- spec/integrations.yml | 4 +- unparser.gemspec | 4 +- 6 files changed, 44 insertions(+), 38 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ac08eb07..fab66d48 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,7 @@ PATH parser (>= 3.3.0) GEM - remote: https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev/ + remote: https://gem.mutant.dev/ specs: mutant-license (0.1.1.2.2355046999240944981729280251890364410689.5) @@ -14,68 +14,70 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - diff-lcs (1.5.0) - json (2.7.1) + diff-lcs (1.5.1) + json (2.7.2) language_server-protocol (3.17.0.3) - mutant (0.11.27) + mutant (0.12.2) diff-lcs (~> 1.3) parser (~> 3.3.0) - regexp_parser (~> 2.8.2) + regexp_parser (~> 2.9.0) sorbet-runtime (~> 0.5.0) unparser (~> 0.6.9) - mutant-rspec (0.11.27) - mutant (= 0.11.27) + mutant-rspec (0.12.2) + mutant (= 0.12.2) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.24.0) - parser (3.3.0.2) + parallel (1.25.1) + parser (3.3.2.0) ast (~> 2.4.1) racc - racc (1.7.3) + racc (1.8.0) rainbow (3.1.1) - regexp_parser (2.8.3) - rexml (3.2.6) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + regexp_parser (2.9.2) + rexml (3.2.9) + strscan + rspec (3.13.0) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.0) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) + rspec-support (~> 3.13.0) rspec-its (1.3.0) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.12.6) + rspec-mocks (3.13.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) - rspec-support (3.12.1) - rubocop (1.59.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.1) + rubocop (1.64.1) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.4) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.30.0, < 2.0) + rubocop-ast (>= 1.31.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.30.0) - parser (>= 3.2.1.0) + rubocop-ast (1.31.3) + parser (>= 3.3.1.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11180) + sorbet-runtime (0.5.11422) + strscan (3.1.0) unicode-display_width (2.5.0) PLATFORMS ruby DEPENDENCIES - mutant (~> 0.11.27) + mutant (~> 0.12.2) mutant-license! - mutant-rspec (~> 0.11.27) + mutant-rspec (~> 0.12.2) rspec (~> 3.9) rspec-core (~> 3.9) rspec-its (~> 1.3.0) @@ -84,4 +86,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.4.1 + 2.5.10 diff --git a/config/mutant.yml b/config/mutant.yml index ef2279ab..981323ba 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -1,4 +1,5 @@ --- +usage: opensource includes: - lib integration: rspec diff --git a/lib/unparser/adamantium.rb b/lib/unparser/adamantium.rb index f63c4461..e5dd323d 100644 --- a/lib/unparser/adamantium.rb +++ b/lib/unparser/adamantium.rb @@ -20,9 +20,11 @@ def dup # @return [Object] # # @api public + # + # mutant:disable def freeze memoized_method_cache - super() + super end private diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index cfd029b7..fc081d02 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -5,7 +5,7 @@ module AST # Calculated local variable scope for a given node class LocalVariableScope - include Enumerable, Adamantium, Concord.new(:node) + include Enumerable, Adamantium # Initialize object # @@ -21,7 +21,6 @@ def initialize(node) items << scope end @items = items - super(node) end # Test if local variable was first at given assignment diff --git a/spec/integrations.yml b/spec/integrations.yml index 7c41d2b9..c78a1765 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -2,7 +2,9 @@ - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' repo_ref: 'main' - exclude: [] + exclude: + # bug in unparser, to be fixed in followup PRs + - spec/integration/mutant/parallel_spec.rb - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' # Revision of rubyspec on the last CI build of unparser that passed diff --git a/unparser.gemspec b/unparser.gemspec index fc12e206..9a9d097a 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -28,8 +28,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.3') gem.add_dependency('parser', '>= 3.3.0') - gem.add_development_dependency('mutant', '~> 0.11.27') - gem.add_development_dependency('mutant-rspec', '~> 0.11.27') + gem.add_development_dependency('mutant', '~> 0.12.2') + gem.add_development_dependency('mutant-rspec', '~> 0.12.2') gem.add_development_dependency('rspec', '~> 3.9') gem.add_development_dependency('rspec-core', '~> 3.9') gem.add_development_dependency('rspec-its', '~> 1.3.0') From beab08aa1ce84df68eab2a012e3deae8985ae6d1 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 9 Jun 2024 23:52:43 +0000 Subject: [PATCH 221/256] Fix hashes that require lvar omissions [fix #367] --- Gemfile.lock | 2 +- lib/unparser/emitter/pair.rb | 21 ++++++++++++++++----- lib/unparser/node_helpers.rb | 1 + test/corpus/literal/def.rb | 4 ++++ test/corpus/literal/send.rb | 2 ++ unparser.gemspec | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index fab66d48..99bc624f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.13) + unparser (0.6.14) diff-lcs (~> 1.3) parser (>= 3.3.0) diff --git a/lib/unparser/emitter/pair.rb b/lib/unparser/emitter/pair.rb index 04c87d8e..967361b9 100644 --- a/lib/unparser/emitter/pair.rb +++ b/lib/unparser/emitter/pair.rb @@ -15,19 +15,30 @@ class Pair < self private def dispatch - if colon?(key) - write(key.children.first.to_s, ': ') + if colon? + emit_colon + unless implicit_value? + write(' ') + visit(value) + end else visit(key) write(' => ') + visit(value) end - - visit(value) end - def colon?(key) + def colon? n_sym?(key) && BAREWORD.match?(key.children.first) end + + def emit_colon + write(key.children.first.to_s, ':') + end + + def implicit_value? + n_lvar?(value) && value.children.first.equal?(key.children.first) + end end end end diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index bfb8bb79..060d8045 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -52,6 +52,7 @@ def n?(type, node) kwargs kwsplat lambda + lvar match_rest pair rescue diff --git a/test/corpus/literal/def.rb b/test/corpus/literal/def.rb index 61339bd4..e6c4e25c 100644 --- a/test/corpus/literal/def.rb +++ b/test/corpus/literal/def.rb @@ -132,3 +132,7 @@ def f def f %() end + +def foo(return:) + { return: } +end diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index 1e9c2a94..bba63427 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -82,3 +82,5 @@ def foo foo&.! foo.~(b) a&.+(b) +a = nil +foo(bar: a) diff --git a/unparser.gemspec b/unparser.gemspec index aa90f143..cfa39959 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.13' + gem.version = '0.6.14' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 0bf5fc5cd0d6f61c6aa388cb0a4d5d3025974b74 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Jun 2024 01:42:34 +0000 Subject: [PATCH 222/256] Remove mutant-license gem --- Gemfile | 4 ---- Gemfile.lock | 6 ------ 2 files changed, 10 deletions(-) diff --git a/Gemfile b/Gemfile index 1419592e..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,3 @@ source 'https://rubygems.org' gemspec - -source 'https://oss:Px2ENN7S91OmWaD5G7MIQJi1dmtmYrEh@gem.mutant.dev' do - gem 'mutant-license' -end diff --git a/Gemfile.lock b/Gemfile.lock index 99bc624f..7b2d04da 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -5,11 +5,6 @@ PATH diff-lcs (~> 1.3) parser (>= 3.3.0) -GEM - remote: https://gem.mutant.dev/ - specs: - mutant-license (0.1.1.2.2355046999240944981729280251890364410689.5) - GEM remote: https://rubygems.org/ specs: @@ -76,7 +71,6 @@ PLATFORMS DEPENDENCIES mutant (~> 0.12.2) - mutant-license! mutant-rspec (~> 0.12.2) rspec (~> 3.9) rspec-core (~> 3.9) From d4d9924e6e392f46f2777839ae10f1149132d067 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sat, 15 Jun 2024 01:42:03 +0000 Subject: [PATCH 223/256] Fix keyword pairs for sends --- Changelog.md | 6 ++++++ Gemfile.lock | 10 +++++----- lib/unparser/emitter/pair.rb | 21 ++++++++++++++++++--- test/corpus/literal/send.rb | 7 +++++++ unparser.gemspec | 2 +- 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7c38bc10..1ceb829a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# v0.6.15 2024-06-10 + +[#373](https://github.com/mbj/unparser/pull/373) + +* Fix additonal keyword dispatch + # v0.6.14 2024-06-10 [#369](https://github.com/mbj/unparser/pull/369) diff --git a/Gemfile.lock b/Gemfile.lock index 7b2d04da..2e6c9330 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - unparser (0.6.14) + unparser (0.6.15) diff-lcs (~> 1.3) parser (>= 3.3.0) @@ -12,14 +12,14 @@ GEM diff-lcs (1.5.1) json (2.7.2) language_server-protocol (3.17.0.3) - mutant (0.12.2) + mutant (0.12.3) diff-lcs (~> 1.3) parser (~> 3.3.0) regexp_parser (~> 2.9.0) sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.9) - mutant-rspec (0.12.2) - mutant (= 0.12.2) + unparser (~> 0.6.14) + mutant-rspec (0.12.3) + mutant (= 0.12.3) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.25.1) parser (3.3.2.0) diff --git a/lib/unparser/emitter/pair.rb b/lib/unparser/emitter/pair.rb index 967361b9..8f73bea1 100644 --- a/lib/unparser/emitter/pair.rb +++ b/lib/unparser/emitter/pair.rb @@ -17,7 +17,7 @@ class Pair < self def dispatch if colon? emit_colon - unless implicit_value? + unless implicit_value_lvar? || implicit_value_send? write(' ') visit(value) end @@ -36,8 +36,23 @@ def emit_colon write(key.children.first.to_s, ':') end - def implicit_value? - n_lvar?(value) && value.children.first.equal?(key.children.first) + def key_value + key.children.first + end + + def implicit_value_lvar? + n_lvar?(value) && value.children.first.equal?(key_value) + end + + def implicit_value_send? + children = value.children + + n_send?(value) \ + && !key_value.end_with?('?') \ + && !key_value.end_with?('!') \ + && children.fetch(0).nil? \ + && children.fetch(1).equal?(key_value) \ + && children.at(2).nil? end end end diff --git a/test/corpus/literal/send.rb b/test/corpus/literal/send.rb index bba63427..409e8242 100644 --- a/test/corpus/literal/send.rb +++ b/test/corpus/literal/send.rb @@ -84,3 +84,10 @@ def foo a&.+(b) a = nil foo(bar: a) +foo(return:) +foo(bar: Bar.bar) +foo(bar: bar(1)) +foo(bar: 1) +foo(do: true) +foo(a?: a?) +foo(a!: a!) diff --git a/unparser.gemspec b/unparser.gemspec index cfa39959..18f17289 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.14' + gem.version = '0.6.15' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From b01498b67a63448a65a185fe53e790d29d63135b Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Jun 2024 22:01:22 +0000 Subject: [PATCH 224/256] Add tea.yaml --- tea.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tea.yaml diff --git a/tea.yaml b/tea.yaml new file mode 100644 index 00000000..61a0151e --- /dev/null +++ b/tea.yaml @@ -0,0 +1,5 @@ +--- +version: 1.0.0 +codeOwners: + - '0x8Ef65b8e231dF5bd0138a7390a406a582f7191C7' +quorum: 1 From cb476172977c715389dfd6ff30832583255b8799 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 14 Feb 2025 02:47:00 +0000 Subject: [PATCH 225/256] Change to require diff-lcs ~1.6 --- Gemfile.lock | 4 ++-- spec/unit/unparser/diff_spec.rb | 26 ++++++++++++++++++++------ spec/unit/unparser/validation_spec.rb | 2 +- unparser.gemspec | 2 +- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 2e6c9330..98e7626e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,14 +2,14 @@ PATH remote: . specs: unparser (0.6.15) - diff-lcs (~> 1.3) + diff-lcs (~> 1.6) parser (>= 3.3.0) GEM remote: https://rubygems.org/ specs: ast (2.4.2) - diff-lcs (1.5.1) + diff-lcs (1.6.0) json (2.7.2) language_server-protocol (3.17.0.3) mutant (0.12.3) diff --git a/spec/unit/unparser/diff_spec.rb b/spec/unit/unparser/diff_spec.rb index 83b0bc43..a25b4752 100644 --- a/spec/unit/unparser/diff_spec.rb +++ b/spec/unit/unparser/diff_spec.rb @@ -25,9 +25,10 @@ let(:expectation) do [ - "@@ -1 +1 @@\n", + "@@ -1,2 +1,2 @@\n", Unparser::Color::RED.format("-foo\n"), Unparser::Color::GREEN.format("+baz\n"), + " bar\n" ].join end @@ -57,7 +58,7 @@ let(:expectation) do <<~STR - @@ -1,4 +1,4 @@ + @@ -1,3 +1,3 @@ -foo +baz bar @@ -77,9 +78,10 @@ let(:expectation) do <<~STR - @@ -1 +1 @@ + @@ -1,2 +1,2 @@ -foo +baz + bar STR end @@ -112,7 +114,7 @@ let(:expectation) do <<~STR - @@ -1,8 +1,9 @@ + @@ -1,7 +1,8 @@ foo bar baz @@ -135,9 +137,15 @@ let(:expectation) do <<~STR - @@ -1,2 +1 @@ + @@ -1,8 +1,7 @@ -other foo + bar + baz + boz + a + b + c STR end @@ -152,9 +160,15 @@ let(:expectation) do <<~STR - @@ -1 +1,2 @@ + @@ -1,7 +1,8 @@ +other foo + bar + baz + boz + a + b + c STR end diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index f4a8f45c..98b0c8d3 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -217,7 +217,7 @@ def report (send (int 1) :bar) Node-Diff: - @@ -1,3 +1,3 @@ + @@ -1,2 +1,2 @@ REPORT end end diff --git a/unparser.gemspec b/unparser.gemspec index 18f17289..1e7d1255 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -26,7 +26,7 @@ Gem::Specification.new do |gem| gem.required_ruby_version = '>= 3.1' - gem.add_dependency('diff-lcs', '~> 1.3') + gem.add_dependency('diff-lcs', '~> 1.6') gem.add_dependency('parser', '>= 3.3.0') gem.add_development_dependency('mutant', '~> 0.12.2') From 82ed87f875a198990b72fe6fae1140254a01b6ee Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 14 Feb 2025 02:48:47 +0000 Subject: [PATCH 226/256] Upgrade rubocop --- Gemfile.lock | 30 ++++++++++++++---------------- lib/unparser/generation.rb | 2 +- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 98e7626e..5ef3e3cd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,8 +10,8 @@ GEM specs: ast (2.4.2) diff-lcs (1.6.0) - json (2.7.2) - language_server-protocol (3.17.0.3) + json (2.10.1) + language_server-protocol (3.17.0.4) mutant (0.12.3) diff-lcs (~> 1.3) parser (~> 3.3.0) @@ -21,15 +21,13 @@ GEM mutant-rspec (0.12.3) mutant (= 0.12.3) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.25.1) - parser (3.3.2.0) + parallel (1.26.3) + parser (3.3.7.1) ast (~> 2.4.1) racc - racc (1.8.0) + racc (1.8.1) rainbow (3.1.1) - regexp_parser (2.9.2) - rexml (3.2.9) - strscan + regexp_parser (2.9.3) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -46,25 +44,25 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.1) - rubocop (1.64.1) + rubocop (1.71.2) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.38.0) parser (>= 3.3.1.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) sorbet-runtime (0.5.11422) - strscan (3.1.0) - unicode-display_width (2.5.0) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) PLATFORMS ruby diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index 43a357d6..91c08477 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -222,7 +222,7 @@ def writer_with(klass, node) end def emitter(node) - Emitter.emitter(**to_h.merge(node: node)) + Emitter.emitter(**to_h, node: node) end def visit(node) From 3664765df8354ba93e65d7c6defe397804c9e253 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Fri, 14 Feb 2025 02:55:04 +0000 Subject: [PATCH 227/256] Upgrade mutant --- Gemfile.lock | 26 +++++++++++++------------- unparser.gemspec | 8 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 5ef3e3cd..3681e467 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,14 +12,14 @@ GEM diff-lcs (1.6.0) json (2.10.1) language_server-protocol (3.17.0.4) - mutant (0.12.3) + mutant (0.12.4) diff-lcs (~> 1.3) parser (~> 3.3.0) regexp_parser (~> 2.9.0) sorbet-runtime (~> 0.5.0) unparser (~> 0.6.14) - mutant-rspec (0.12.3) - mutant (= 0.12.3) + mutant-rspec (0.12.4) + mutant (= 0.12.4) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.26.3) parser (3.3.7.1) @@ -32,18 +32,18 @@ GEM rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.0) + rspec-core (3.13.3) rspec-support (~> 3.13.0) - rspec-expectations (3.13.0) + rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-its (1.3.0) + rspec-its (1.3.1) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.13.1) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.1) + rspec-support (3.13.2) rubocop (1.71.2) json (~> 2.3) language_server-protocol (>= 3.17.0) @@ -59,7 +59,7 @@ GEM rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11422) + sorbet-runtime (0.5.11826) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) @@ -68,10 +68,10 @@ PLATFORMS ruby DEPENDENCIES - mutant (~> 0.12.2) - mutant-rspec (~> 0.12.2) - rspec (~> 3.9) - rspec-core (~> 3.9) + mutant (~> 0.12.4) + mutant-rspec (~> 0.12.4) + rspec (~> 3.13) + rspec-core (~> 3.13) rspec-its (~> 1.3.0) rubocop (~> 1.7) rubocop-packaging (~> 0.5) diff --git a/unparser.gemspec b/unparser.gemspec index 1e7d1255..c4d256e7 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -29,10 +29,10 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.6') gem.add_dependency('parser', '>= 3.3.0') - gem.add_development_dependency('mutant', '~> 0.12.2') - gem.add_development_dependency('mutant-rspec', '~> 0.12.2') - gem.add_development_dependency('rspec', '~> 3.9') - gem.add_development_dependency('rspec-core', '~> 3.9') + gem.add_development_dependency('mutant', '~> 0.12.4') + gem.add_development_dependency('mutant-rspec', '~> 0.12.4') + gem.add_development_dependency('rspec', '~> 3.13') + gem.add_development_dependency('rspec-core', '~> 3.13') gem.add_development_dependency('rspec-its', '~> 1.3.0') gem.add_development_dependency('rubocop', '~> 1.7') gem.add_development_dependency('rubocop-packaging', '~> 0.5') From e7bc52cdb8c123e03c596796fb74b0dd72b89fc0 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Feb 2025 02:20:51 +0000 Subject: [PATCH 228/256] Remove devtools reminders --- config/devtools.yml | 2 - config/flay.yml | 3 -- config/flog.yml | 2 - config/reek.yml | 98 -------------------------------------------- config/yardstick.yml | 2 - 5 files changed, 107 deletions(-) delete mode 100644 config/devtools.yml delete mode 100644 config/flay.yml delete mode 100644 config/flog.yml delete mode 100644 config/reek.yml delete mode 100644 config/yardstick.yml diff --git a/config/devtools.yml b/config/devtools.yml deleted file mode 100644 index f7e1f615..00000000 --- a/config/devtools.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -unit_test_timeout: 1.0 diff --git a/config/flay.yml b/config/flay.yml deleted file mode 100644 index 7b5e3d18..00000000 --- a/config/flay.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -threshold: 13 -total_score: 638 diff --git a/config/flog.yml b/config/flog.yml deleted file mode 100644 index 1174a8fe..00000000 --- a/config/flog.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -threshold: 21.3 diff --git a/config/reek.yml b/config/reek.yml deleted file mode 100644 index 3826d7f6..00000000 --- a/config/reek.yml +++ /dev/null @@ -1,98 +0,0 @@ ---- -detectors: - Attribute: - enabled: false - exclude: [] - BooleanParameter: - enabled: true - exclude: [] - ClassVariable: - enabled: true - exclude: [] - ControlParameter: - enabled: true - exclude: [] - DataClump: - enabled: true - exclude: [] - max_copies: 2 - min_clump_size: 2 - DuplicateMethodCall: - enabled: false - exclude: [] - max_calls: 1 - allow_calls: [] - FeatureEnvy: - enabled: false - # Buggy smell detector - IrresponsibleModule: - enabled: false - exclude: [] - LongParameterList: - enabled: true - exclude: [] - max_params: 2 - LongYieldList: - enabled: true - exclude: [] - max_params: 2 - NestedIterators: - enabled: true - exclude: [] - max_allowed_nesting: 1 - ignore_iterators: [] - NilCheck: - enabled: false - RepeatedConditional: - enabled: true - exclude: [] - max_ifs: 1 - TooManyInstanceVariables: - enabled: true - exclude: [] - max_instance_variables: 3 - TooManyMethods: - enabled: true - exclude: [] - max_methods: 10 - TooManyStatements: - enabled: true - exclude: [] - max_statements: 7 - UncommunicativeMethodName: - enabled: true - exclude: [] - reject: - - '/^[a-z]$/' - - '/[0-9]$/' - - '/[A-Z]/' - accept: [] - UncommunicativeModuleName: - enabled: true - exclude: [] - reject: - - '/^.$/' - - '/[0-9]$/' - accept: [] - UncommunicativeParameterName: - enabled: true - exclude: [] - reject: - - '/^.$/' - - '/[0-9]$/' - - '/[A-Z]/' - accept: [] - UncommunicativeVariableName: - enabled: true - exclude: [] - reject: - - '/^.$/' - - '/[0-9]$/' - - '/[A-Z]/' - accept: ['force_utf32'] - UnusedParameters: - enabled: true - exclude: [] - UtilityFunction: - enabled: true - exclude: [] diff --git a/config/yardstick.yml b/config/yardstick.yml deleted file mode 100644 index a6b63e85..00000000 --- a/config/yardstick.yml +++ /dev/null @@ -1,2 +0,0 @@ ---- -threshold: 100 From 469abe65aa971ac0db479cc0a62f61ed48ec97f1 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Feb 2025 02:22:18 +0000 Subject: [PATCH 229/256] Change to mutant timeout --- config/mutant.yml | 4 ++++ spec/spec_helper.rb | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/config/mutant.yml b/config/mutant.yml index 981323ba..279c3676 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -5,6 +5,10 @@ includes: integration: rspec requires: - unparser +mutation: + timeout: 10.0 +coverage_criteria: + timeout: true matcher: subjects: - 'Unparser*' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 6bd6151c..fd8f4ef5 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,16 +1,11 @@ require 'pathname' require 'rspec/its' -require 'timeout' require 'unparser' require 'mutant' require 'yaml' require 'parser/current' -RSpec.configuration.around(file_path: %r{spec/unit}) do |example| - Timeout.timeout(5, &example) -end - RSpec.shared_examples_for 'a command method' do it 'returns self' do should equal(object) From 7c8a1f1b8c16cff4dcc5c195c209e53f62b35965 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Mar 2025 15:39:21 +0000 Subject: [PATCH 230/256] Bump json from 2.10.1 to 2.10.2 Bumps [json](https://github.com/ruby/json) from 2.10.1 to 2.10.2. - [Release notes](https://github.com/ruby/json/releases) - [Changelog](https://github.com/ruby/json/blob/master/CHANGES.md) - [Commits](https://github.com/ruby/json/compare/v2.10.1...v2.10.2) --- updated-dependencies: - dependency-name: json dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3681e467..aeaf27c9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,7 +10,7 @@ GEM specs: ast (2.4.2) diff-lcs (1.6.0) - json (2.10.1) + json (2.10.2) language_server-protocol (3.17.0.4) mutant (0.12.4) diff-lcs (~> 1.3) From 1ac5a778813145ba71794c2ba958e4b0fc495e4d Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Mon, 17 Jun 2024 00:08:10 +0000 Subject: [PATCH 231/256] Fix dstr unparsing * This is an entirely new approach. * Instead to find the "correct" dstr segments we simply try all and unparse the first one that round trips. * This so far guarantees we always get good concrete syntax, but it can be time intensive as the combinatoric space of possible dynamic string sequence is quadratic with the dstr children size. * For this reason we try above (currently) dstr children to unparse as heredoc first. * Passes the entire corpus and fixes bugs. [fix #249] --- .rubocop.yml | 6 +- Changelog.md | 7 + Gemfile | 2 + Gemfile.lock | 35 +- bin/corpus | 104 +++- bin/parser-round-trip-test | 41 +- config/mutant.yml | 35 -- lib/unparser.rb | 133 +++-- lib/unparser/anima.rb | 11 + lib/unparser/ast.rb | 40 +- lib/unparser/ast/local_variable_scope.rb | 52 +- lib/unparser/buffer.rb | 46 +- lib/unparser/cli.rb | 31 +- lib/unparser/either.rb | 12 +- lib/unparser/emitter.rb | 19 +- lib/unparser/emitter/array.rb | 4 - lib/unparser/emitter/array_pattern.rb | 10 +- lib/unparser/emitter/assignment.rb | 15 +- lib/unparser/emitter/begin.rb | 6 - lib/unparser/emitter/binary.rb | 2 +- lib/unparser/emitter/block.rb | 7 +- lib/unparser/emitter/def.rb | 2 +- lib/unparser/emitter/dstr.rb | 11 +- lib/unparser/emitter/flow_modifier.rb | 6 - lib/unparser/emitter/for.rb | 2 +- lib/unparser/emitter/hash.rb | 8 - lib/unparser/emitter/hash_pattern.rb | 2 +- lib/unparser/emitter/index.rb | 4 - lib/unparser/emitter/op_assign.rb | 10 - lib/unparser/emitter/primitive.rb | 13 - lib/unparser/emitter/regexp.rb | 22 +- lib/unparser/emitter/rescue.rb | 8 +- lib/unparser/emitter/root.rb | 11 +- lib/unparser/emitter/send.rb | 6 +- lib/unparser/emitter/string.rb | 31 ++ lib/unparser/generation.rb | 22 +- lib/unparser/node_details.rb | 1 + lib/unparser/validation.rb | 96 +++- lib/unparser/writer.rb | 33 +- lib/unparser/writer/dynamic_string.rb | 263 +++++----- lib/unparser/writer/regexp.rb | 98 ++++ lib/unparser/writer/resbody.rb | 31 +- lib/unparser/writer/rescue.rb | 8 +- lib/unparser/writer/send.rb | 22 +- scripts/devloop.sh | 9 +- spec/integrations.yml | 90 +--- spec/unit/unparser/buffer_spec.rb | 131 +++++ spec/unit/unparser/comments/consume_spec.rb | 17 +- spec/unit/unparser/comments/take_all_spec.rb | 11 +- .../unparser/comments/take_before_spec.rb | 24 +- .../comments/take_eol_comments_spec.rb | 20 +- spec/unit/unparser/validation_spec.rb | 481 +++++++++--------- spec/unit/unparser_spec.rb | 90 +++- test/corpus/literal/assignment.rb | 20 +- test/corpus/literal/block.rb | 4 + test/corpus/literal/def.rb | 4 +- test/corpus/literal/dstr.rb | 60 ++- test/corpus/literal/for.rb | 2 + test/corpus/literal/heredoc.rb | 109 ++++ test/corpus/literal/literal.rb | 27 +- test/corpus/literal/regexp.rb | 32 ++ test/corpus/literal/single-heredoc.rb | 10 + .../semantic/encoding/binary-hex-escaped.rb | 2 + .../semantic/encoding/binary-utf-8-escaped.rb | 2 + test/corpus/semantic/encoding/binary.rb | 2 + .../semantic/encoding/utf-8-non-printable.rb | 2 + test/corpus/semantic/kwbegin.rb | 16 + test/corpus/semantic/regexp.rb | 4 + test/corpus/semantic/rescue.rb | 5 + unparser.gemspec | 3 +- 70 files changed, 1597 insertions(+), 908 deletions(-) create mode 100644 lib/unparser/emitter/string.rb create mode 100644 lib/unparser/writer/regexp.rb create mode 100644 test/corpus/literal/heredoc.rb create mode 100644 test/corpus/literal/regexp.rb create mode 100644 test/corpus/literal/single-heredoc.rb create mode 100644 test/corpus/semantic/encoding/binary-hex-escaped.rb create mode 100644 test/corpus/semantic/encoding/binary-utf-8-escaped.rb create mode 100644 test/corpus/semantic/encoding/binary.rb create mode 100644 test/corpus/semantic/encoding/utf-8-non-printable.rb create mode 100644 test/corpus/semantic/regexp.rb create mode 100644 test/corpus/semantic/rescue.rb diff --git a/.rubocop.yml b/.rubocop.yml index 338aba55..85fb9477 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,7 +5,7 @@ AllCops: - '**/*.rake' - 'Gemfile' - 'Gemfile.triage' - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.1 Exclude: - tmp/**/* - vendor/**/* @@ -106,3 +106,7 @@ Naming/RescuedExceptionsVariableName: Layout/MultilineMethodCallIndentation: Enabled: false + +# Useful for code structure +Lint/UselessConstantScoping: + Enabled: false diff --git a/Changelog.md b/Changelog.md index 1ceb829a..e930ef3c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,10 @@ +# v0.7.0 2024-09-16 + +[#366](https://github.com/mbj/unparser/pull/366) + +* Fix all known dstring issues. +* Interface changes. + # v0.6.15 2024-06-10 [#373](https://github.com/mbj/unparser/pull/373) diff --git a/Gemfile b/Gemfile index 7f4f5e95..5dfa95a6 100644 --- a/Gemfile +++ b/Gemfile @@ -2,4 +2,6 @@ source 'https://rubygems.org' +gem 'mutant', github: 'mbj/mutant', ref: '152465f02b2c4a18b80f28fee120f58d71a41391' + gemspec diff --git a/Gemfile.lock b/Gemfile.lock index aeaf27c9..88ed81c5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,19 @@ +GIT + remote: https://github.com/mbj/mutant.git + revision: 152465f02b2c4a18b80f28fee120f58d71a41391 + ref: 152465f02b2c4a18b80f28fee120f58d71a41391 + specs: + mutant (0.12.4) + diff-lcs (~> 1.3) + parser (~> 3.3.0) + regexp_parser (~> 2.9.0) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.7.0) + PATH remote: . specs: - unparser (0.6.15) + unparser (0.7.0) diff-lcs (~> 1.6) parser (>= 3.3.0) @@ -12,12 +24,7 @@ GEM diff-lcs (1.6.0) json (2.10.2) language_server-protocol (3.17.0.4) - mutant (0.12.4) - diff-lcs (~> 1.3) - parser (~> 3.3.0) - regexp_parser (~> 2.9.0) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.6.14) + lint_roller (1.1.0) mutant-rspec (0.12.4) mutant (= 0.12.4) rspec-core (>= 3.8.0, < 4.0.0) @@ -44,9 +51,10 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) - rubocop (1.71.2) + rubocop (1.74.0) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) @@ -54,21 +62,22 @@ GEM rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.38.0) + rubocop-ast (1.38.1) parser (>= 3.3.1.0) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11826) + sorbet-runtime (0.5.11934) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) PLATFORMS ruby + x86_64-linux DEPENDENCIES - mutant (~> 0.12.4) + mutant! mutant-rspec (~> 0.12.4) rspec (~> 3.13) rspec-core (~> 3.13) @@ -78,4 +87,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.5.10 + 2.5.22 diff --git a/bin/corpus b/bin/corpus index 00b55c03..774573c1 100755 --- a/bin/corpus +++ b/bin/corpus @@ -1,10 +1,14 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'etc' require 'mutant' require 'optparse' +require 'pathname' require 'unparser' +Thread.abort_on_exception = true + module Unparser module Corpus ROOT = Pathname.new(__dir__).parent @@ -16,16 +20,92 @@ module Unparser # Perform verification via unparser cli # # @return [Boolean] + # + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def verify + puts("Verifiying: #{name}") checkout - command = %W[unparser #{repo_path}] - exclude.each do |name| - command.push('--ignore', repo_path.join(name).to_s) + + paths = Pathname.glob(Pathname.new(repo_path).join('**/*.rb')) + + driver = Mutant::Parallel.async( + config: Mutant::Parallel::Config.new( + block: method(:verify_path), + jobs: Etc.nprocessors, + on_process_start: ->(*) {}, + process_name: 'unparser-corpus-test', + sink: Sink.new, + source: Mutant::Parallel::Source::Array.new(jobs: paths), + thread_name: 'unparser-corpus-test', + timeout: nil + ), + world: Mutant::WORLD + ) + + loop do + status = driver.wait_timeout(1) + + puts("Processed: #{status.payload.total}") + + # rubocop:disable Lint/UnreachableLoop + status.payload.errors.each do |report| + puts report + fail + end + # rubocop:enable Lint/UnreachableLoop + + break if status.done? + end + + true + end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength + + private + + class Sink + include Mutant::Parallel::Sink + + attr_reader :errors, :total + + def initialize + @errors = [] + @total = 0 + end + + def stop? + !@errors.empty? + end + + def status + self + end + + def response(response) + if response.error + Mutant::WORLD.stderr.puts(response.log) + fail response.error + end + + @total += 1 + + if response.result + @errors << response.result + end end - Kernel.system(*command) end - private + def verify_path(path) + validation = Validation.from_path(path) + + if original_syntax_error?(validation) || generated_encoding_error?(validation) || validation.success? + return + end + + validation.report + end def checkout TMP.mkdir unless TMP.directory? @@ -50,6 +130,20 @@ module Unparser TMP.join(name) end + # This happens if the original source contained a non UTF charset meta comment. + # These are not exposed to the AST in a way unparser could know about to generate a non UTF-8 + # target and emit that meta comment itself. + # For the purpose of corpus testing these cases are ignored. + def generated_encoding_error?(validation) + exception = validation.generated_node.from_left { return false } + exception.instance_of?(Parser::SyntaxError) && + exception.message.eql?('literal contains escape sequences incompatible with UTF-8') + end + + def original_syntax_error?(validation) + validation.original_node.from_left { return false }.instance_of?(Parser::SyntaxError) + end + def system(arguments) return if Kernel.system(*arguments) diff --git a/bin/parser-round-trip-test b/bin/parser-round-trip-test index e9860bd2..a87cf1b6 100755 --- a/bin/parser-round-trip-test +++ b/bin/parser-round-trip-test @@ -40,7 +40,14 @@ class Test :rubies ) - EXPECT_FAILURE = {}.freeze + EXPECT_FAILURE = {}.freeze + STATIC_LOCAL_VARIABLES = %w[foo bar baz].to_set.freeze + + NO_ROUND_TRIP = %i[ + test_int___LINE__ + test_pattern_matching__FILE__LINE_literals + test_string___FILE__ + ].freeze def legacy_attributes default_builder_attributes.reject do |attribute_name, value| @@ -56,6 +63,8 @@ class Test "Non targeted rubies: #{rubies.join(',')}" elsif validation.original_node.left? 'Test specifies a syntax error' + elsif NO_ROUND_TRIP.include?(name) + 'Test not round trippable' end end @@ -77,20 +86,25 @@ class Test # rubocop:disable Metrics/AbcSize def validation - identification = name.to_s + identification = name.to_s + + ast = Unparser::AST.new( + comments: [], + explicit_encoding: nil, + node: node, + static_local_variables: STATIC_LOCAL_VARIABLES + ) - generated_source = Unparser.unparse_either(node) + generated_source = Unparser.unparse_ast_either(ast) .fmap { |string| string.dup.force_encoding(parser_source.encoding).freeze } - generated_node = generated_source.bind do |source| - parse_either(source, identification) - end + generated_node = generated_source.bind { |source| parse_either(source, identification) } Unparser::Validation.new( generated_node: generated_node, generated_source: generated_source, identification: identification, - original_node: parse_either(parser_source, identification).fmap { node }, + original_ast: parse_either_ast(parser_source, identification), original_source: right(parser_source) ) end @@ -99,7 +113,7 @@ class Test def parser Unparser.parser.tap do |parser| - %w[foo bar baz].each(&parser.static_env.method(:declare)) + STATIC_LOCAL_VARIABLES.each(&parser.static_env.method(:declare)) end end @@ -108,6 +122,17 @@ class Test parser.parse(Unparser.buffer(source, identification)) end end + + def parse_either_ast(source, identification) + parse_either(source, identification).fmap do |node| + Unparser::AST.new( + comments: [], + explicit_encoding: nil, + node: node, + static_local_variables: Set.new + ) + end + end end class Execution diff --git a/config/mutant.yml b/config/mutant.yml index 279c3676..8d4cc456 100644 --- a/config/mutant.yml +++ b/config/mutant.yml @@ -12,38 +12,3 @@ coverage_criteria: matcher: subjects: - 'Unparser*' - ignore: - - 'Unparser::Builder#initialize' - - 'Unparser::CLI*' - - 'Unparser::Concord#define_readers' - - 'Unparser::Emitter#emit_comments' - - 'Unparser::Emitter#emit_comments_before' - - 'Unparser::Emitter#emit_eol_comments' - - 'Unparser::Emitter.handle' - - 'Unparser::Emitter::Args#normal_arguments' - - 'Unparser::Emitter::Args#shadowargs' - - 'Unparser::Emitter::Array#emitters' - - 'Unparser::Emitter::Binary#writer' - - 'Unparser::Emitter::Block#target_writer' - - 'Unparser::Emitter::BlockPass#dispatch' # 3.1+ specific - - 'Unparser::Emitter::Class#dispatch' - - 'Unparser::Emitter::Class#local_variable_scope' - - 'Unparser::Emitter::Def#local_variable_scope' - - 'Unparser::Emitter::FindPattern#dispatch' # 3.0+ specific - - 'Unparser::Emitter::Hash#emit_heredoc_reminder_member' # 3.2+ specific - - 'Unparser::Emitter::HashPattern#write_symbol_body' - - 'Unparser::Emitter::LocalVariableRoot*' - - 'Unparser::Emitter::LocalVariableRoot.included' - - 'Unparser::Emitter::MatchPatternP#dispatch' # 3.0+ specific - - 'Unparser::Emitter::MatchRest#dispatch' # 3.0+ specific - - 'Unparser::Emitter::Module#local_variable_scope' - - 'Unparser::Emitter::Root#local_variable_scope' - - 'Unparser::Emitter::Send#writer' - - 'Unparser::NodeDetails.included' - - 'Unparser::Validation.from_string' - - 'Unparser::Validation::Literal*' - - 'Unparser::Writer.included' - - 'Unparser::Writer::Binary#left_emitter' - - 'Unparser::Writer::Binary#right_emitter' - - 'Unparser::Writer::Send#details' - - 'Unparser::Writer::Send#effective_writer' diff --git a/lib/unparser.rb b/lib/unparser.rb index 0938a81a..4130d3b5 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -44,12 +44,18 @@ def initialize(message, node) @node = node freeze end - end + end # InvalidNodeError + + # Error raised when unparser encounders AST it cannot generate source for that would parse to the same AST. + class UnsupportedNodeError < RuntimeError + end # UnsupportedNodeError # Unparse an AST (and, optionally, comments) into a string # # @param [Parser::AST::Node, nil] node - # @param [Array] comment_array + # @param [Array] comments + # @param [Encoding, nil] explicit_encoding + # @param [Set] static_local_variables # # @return [String] # @@ -57,44 +63,99 @@ def initialize(message, node) # if the node passed is invalid # # @api public - def self.unparse(node, comment_array = []) - return '' if node.nil? + # + # mutant:disable + # rubocop:disable Metrics/ParameterLists + def self.unparse( + node, + comments: EMPTY_ARRAY, + explicit_encoding: nil, + static_local_variables: Set.new + ) + unparse_ast( + AST.new( + comments: comments, + explicit_encoding: explicit_encoding, + node: node, + static_local_variables: static_local_variables + ) + ) + end + # rubocop:enable Metrics/ParameterLists + + # Unparse an AST + # + # @param [AST] ast + # + # @return [String] + # + # @raise InvalidNodeError + # if the node passed is invalid + # + # @raise UnsupportedNodeError + # if the node passed is valid but unparser cannot unparse it + # + # @api public + def self.unparse_ast(ast) + return EMPTY_STRING if ast.node.nil? + + local_variable_scope = AST::LocalVariableScope.new( + node: ast.node, + static_local_variables: ast.static_local_variables + ) Buffer.new.tap do |buffer| Emitter::Root.new( - buffer, - node, - Comments.new(comment_array) + buffer: buffer, + comments: Comments.new(ast.comments), + explicit_encoding: ast.explicit_encoding, + local_variable_scope: local_variable_scope, + node: ast.node ).write_to_buffer end.content end - # Unparse with validation + # Unparse AST either # - # @param [Parser::AST::Node, nil] node - # @param [Array] comment_array + # @param [AST] ast # - # @return [Either] - def self.unparse_validate(node, comment_array = []) - generated = unparse(node, comment_array) - validation = Validation.from_string(generated) + # @return [Either] + def self.unparse_ast_either(ast) + Either.wrap_error(Exception) { unparse_ast(ast) } + end + + # Unparse AST either + # + # @param [AST] ast + # + # @return [Either] + # + # mutant:disable + def self.unparse_validate_ast_either(ast:) + validation = Validation.from_ast(ast:) if validation.success? - Either::Right.new(generated) + Either::Right.new(validation.generated_source.from_right) else Either::Left.new(validation) end end - # Unparse capturing errors - # - # This is mostly useful for writing testing tools against unparser. + # Unparse with validation # # @param [Parser::AST::Node, nil] node + # @param [Array] comments # - # @return [Either] - def self.unparse_either(node) - Either.wrap_error(Exception) { unparse(node) } + # @return [Either] + def self.unparse_validate(node, comments: EMPTY_ARRAY) + generated = unparse(node, comments:) + validation = Validation.from_string(generated) + + if validation.success? + Either::Right.new(generated) + else + Either::Left.new(validation) + end end # Parse string into AST @@ -103,27 +164,37 @@ def self.unparse_either(node) # # @return [Parser::AST::Node, nil] def self.parse(source) - parser.parse(buffer(source)) + parse_ast(source).node end # Parse string into either syntax error or AST # # @param [String] source # - # @return [Either] - def self.parse_either(source) - Either.wrap_error(Parser::SyntaxError) do - parser.parse(buffer(source)) + # @return [Either] + def self.parse_ast_either(source) + Either.wrap_error(Exception) do + parse_ast(source) end end - # Parse string into AST, with comments + # Parse source with ast details # # @param [String] source # - # @return [Parser::AST::Node] - def self.parse_with_comments(source) - parser.parse_with_comments(buffer(source)) + # @return [AST] + # + # mutant:disable + def self.parse_ast(source, static_local_variables: Set.new) + explicit_encoding = Parser::Source::Buffer.recognize_encoding(source.dup.force_encoding(Encoding::BINARY)) + node, comments = parser.parse_with_comments(buffer(source)) + + AST.new( + comments: comments, + explicit_encoding: explicit_encoding, + node: node, + static_local_variables: static_local_variables + ) end # Parser instance that produces AST unparser understands @@ -210,6 +281,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/root' require 'unparser/emitter/send' require 'unparser/emitter/simple' +require 'unparser/emitter/string' require 'unparser/emitter/splat' require 'unparser/emitter/super' require 'unparser/emitter/undef' @@ -224,6 +296,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/writer' require 'unparser/writer/binary' require 'unparser/writer/dynamic_string' +require 'unparser/writer/regexp' require 'unparser/writer/resbody' require 'unparser/writer/rescue' require 'unparser/writer/send' diff --git a/lib/unparser/anima.rb b/lib/unparser/anima.rb index 8618c9fd..20e215c1 100644 --- a/lib/unparser/anima.rb +++ b/lib/unparser/anima.rb @@ -134,14 +134,25 @@ def with(attributes) # @param [Class, Module] scope # # @return [undefined] + # + # mutant:disable def included(descendant) descendant.instance_exec(self, attribute_names) do |anima, names| # Define anima method + + class << self + undef_method(:anima) if method_defined?(:anima) + end + define_singleton_method(:anima) { anima } # Define instance methods include InstanceMethods + names.each do |name| + undef_method(name) if method_defined?(name) + end + # Define attribute readers attr_reader(*names) diff --git a/lib/unparser/ast.rb b/lib/unparser/ast.rb index 976cd9b3..2a83d4b9 100644 --- a/lib/unparser/ast.rb +++ b/lib/unparser/ast.rb @@ -2,9 +2,10 @@ module Unparser # Namespace for AST processing tools - module AST + class AST + include Anima.new(:comments, :explicit_encoding, :node, :static_local_variables) + FIRST_CHILD = ->(node) { node.children.first }.freeze - TAUTOLOGY = ->(_node) { true }.freeze RESET_NODES = %i[module class sclass def defs].freeze INHERIT_NODES = [:block].freeze @@ -16,12 +17,22 @@ module AST arg kwarg kwoptarg + kwrestarg lvasgn optarg - procarg0 restarg ].to_set.freeze + # mutant:disable + def self.from_node(node:) + new( + comments: EMPTY_ARRAY, + explicit_encoding: nil, + node:, + static_local_variables: Set.new + ) + end + # Test for local variable inherited scope reset # # @param [Parser::AST::Node] node @@ -69,6 +80,7 @@ def self.local_variable_assignments(node) # # @api private # + # mutant:disable def self.local_variable_reads(node) Enumerator.new( node, @@ -80,19 +92,6 @@ def self.local_variable_reads(node) class Enumerator include Adamantium, Concord.new(:node, :controller), Enumerable - # Return new instance - # - # @param [Parser::AST::Node] node - # @param [#call(node)] controller - # - # @return [Enumerator] - # - # @api private - # - def self.new(node, controller = TAUTOLOGY) - super - end - # Return each node # # @return [Enumerator] @@ -103,8 +102,8 @@ def self.new(node, controller = TAUTOLOGY) # # @api private # - def each(&block) - Walker.call(node, controller, &block) + def each(&) + Walker.call(node, controller, &) end # Return nodes selected by types @@ -168,13 +167,10 @@ class Walker # # @param [Parser::AST::Node] node # - # @return [self] - # # @api private # - def self.call(node, controller = TAUTOLOGY, &block) + def self.call(node, controller, &block) new(block, controller).call(node) - self end # Call walker with node diff --git a/lib/unparser/ast/local_variable_scope.rb b/lib/unparser/ast/local_variable_scope.rb index fc081d02..8edaf10e 100644 --- a/lib/unparser/ast/local_variable_scope.rb +++ b/lib/unparser/ast/local_variable_scope.rb @@ -1,11 +1,10 @@ # frozen_string_literal: true module Unparser - module AST - + class AST # Calculated local variable scope for a given node class LocalVariableScope - include Enumerable, Adamantium + include Adamantium, Anima.new(:static_local_variables, :node) # Initialize object # @@ -13,13 +12,17 @@ class LocalVariableScope # # @return [undefined] # - # @api private - # - def initialize(node) + # mutant:disable + def initialize(*arguments) + super + items = [] - LocalVariableScopeEnumerator.each(node) do |*scope| - items << scope - end + + LocalVariableScopeEnumerator.each( + node: node, + stack: static_local_variables.dup + ) { |*scope| items << scope } + @items = items end @@ -53,6 +56,15 @@ def local_variable_defined_for_node?(node, name) end end + # mutant:disable + def local_variables_for_node(needle) + @items.each do |node, current| + return current if node.equal?(needle) + end + + Set.new + end + # Test if local variables where first assigned in body and read by conditional # # @param [Parser::AST::Node] body @@ -90,21 +102,13 @@ class LocalVariableScopeEnumerator # # @api private # - def initialize - @stack = [Set.new] + def initialize(stack:) + @stack = [stack] end # Enumerate each node with its local variable scope - # - # @param [Parser::AST::Node] node - # - # @return [self] - # - # @api private - # - def self.each(node, &block) - new.each(node, &block) - self + def self.each(node:, stack:, &block) + new(stack: stack).each(node: node, &block) end # Enumerate local variable scope scope @@ -117,7 +121,7 @@ def self.each(node, &block) # # @api private # - def each(node, &block) + def each(node:, &block) visit(node, &block) end @@ -132,7 +136,7 @@ def visit(node, &block) enter(node) yield node, current.dup, before node.children.each do |child| - visit(child, &block) if child.is_a?(Parser::AST::Node) + visit(child, &block) if child.instance_of?(Parser::AST::Node) end leave(node) end @@ -142,7 +146,7 @@ def enter(node) when *RESET_NODES push_reset when ASSIGN_NODES - define(node.children.first) + value = node.children.first and define(value) when *INHERIT_NODES push_inherit end diff --git a/lib/unparser/buffer.rb b/lib/unparser/buffer.rb index ebb4dcaa..989e1d58 100644 --- a/lib/unparser/buffer.rb +++ b/lib/unparser/buffer.rb @@ -14,8 +14,10 @@ class Buffer # @api private # def initialize - @content = +'' - @indent = 0 + @content = +'' + @heredocs = [] + @indent = 0 + @no_nl = true end # Append string @@ -34,6 +36,13 @@ def append(string) self end + # Push to heredoc stack + # + # @param [String] heredoc + def push_heredoc(heredoc) + @heredocs << heredoc + end + # Append a string without an indentation prefix # # @param [String] string @@ -68,6 +77,10 @@ def unindent self end + def ensure_nl + nl unless fresh_line? + end + # Write newline # # @return [self] @@ -75,7 +88,27 @@ def unindent # @api private # def nl + @no_nl = false write(NL) + flush_heredocs + self + end + + # Write final newline + def final_newline + return if fresh_line? || @no_nl + + write(NL) + end + + def nl_flush_heredocs + return if @heredocs.empty? + + if fresh_line? + flush_heredocs + else + nl + end end def root_indent @@ -117,6 +150,10 @@ def write(fragment) self end + def write_encoding(encoding) + write("# -*- encoding: #{encoding} -*-\n") + end + private INDENT_SPACE = ' '.freeze @@ -125,5 +162,10 @@ def prefix write(INDENT_SPACE * @indent) end + def flush_heredocs + @heredocs.each(&public_method(:write)) + @heredocs = [] + end + end # Buffer end # Unparser diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 1768e6f0..2d145795 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -60,6 +60,7 @@ def literal_validation # # @api private # + # mutant:disable def self.run(*arguments) new(*arguments).exit_status end @@ -71,15 +72,17 @@ def self.run(*arguments) # @return [undefined] # # @api private + # mutant:disable def initialize(arguments) @ignore = Set.new @targets = [] - @fail_fast = false - @start_with = nil - @success = true - @validation = :validation - @verbose = false + @fail_fast = false + @start_with = nil + @success = true + @validation = :validation + @verbose = false + @ignore_original_syntax_error = false opts = OptionParser.new do |builder| add_options(builder) @@ -98,6 +101,7 @@ def initialize(arguments) # # @api private # + # mutant:disable # rubocop:disable Metrics/MethodLength def add_options(builder) builder.banner = 'usage: unparse [options] FILE [FILE]' @@ -114,6 +118,9 @@ def add_options(builder) builder.on('-l', '--literal') do @validation = :literal_validation end + builder.on('--ignore-original-syntax-error') do + @ignore_original_syntax_error = true + end builder.on('--ignore FILE') do |file| @ignore.merge(targets(file)) end @@ -129,6 +136,7 @@ def add_options(builder) # # @api private # + # mutant:disable def exit_status effective_targets.each do |target| process_target(target) @@ -140,11 +148,15 @@ def exit_status private + # mutant:disable def process_target(target) validation = target.public_send(@validation) if validation.success? puts validation.report if @verbose puts "Success: #{validation.identification}" + elsif ignore_original_syntax_error?(validation) + exception = validation.original_node.from_left + puts "#{exception.class}: #{validation.identification} #{exception}" else puts validation.report puts "Error: #{validation.identification}" @@ -152,6 +164,14 @@ def process_target(target) end end + # mutant:disable + def ignore_original_syntax_error?(validation) + @ignore_original_syntax_error && validation.original_node.from_left do + nil + end.instance_of?(Parser::SyntaxError) + end + + # mutant:disable def effective_targets if @start_with reject = true @@ -167,6 +187,7 @@ def effective_targets end.reject(&@ignore.method(:include?)) end + # mutant:disable def targets(file_name) if File.directory?(file_name) Dir.glob(File.join(file_name, '**/*.rb')) diff --git a/lib/unparser/either.rb b/lib/unparser/either.rb index 924f731a..5a35b1cd 100644 --- a/lib/unparser/either.rb +++ b/lib/unparser/either.rb @@ -54,15 +54,15 @@ class Left < self # Evaluate functor block # # @return [Either::Left] - def fmap(&block) - require_block(&block) + def fmap(&) + require_block(&) end # Evaluate applicative block # # @return [Either::Left] - def bind(&block) - require_block(&block) + def bind(&) + require_block(&) end # Unwrap value from left @@ -137,8 +137,8 @@ def from_right # Map over left value # # @return [Either::Right] - def lmap(&block) - require_block(&block) + def lmap(&) + require_block(&) end # Evaluate right side of branch diff --git a/lib/unparser/emitter.rb b/lib/unparser/emitter.rb index aedc5171..e47b1b2e 100644 --- a/lib/unparser/emitter.rb +++ b/lib/unparser/emitter.rb @@ -6,7 +6,7 @@ module Unparser # Emitter base class class Emitter include Adamantium, AbstractType, Constants, Generation, NodeHelpers - include Anima.new(:buffer, :comments, :node, :local_variable_scope) + include Anima.new(:buffer, :comments, :explicit_encoding, :local_variable_scope, :node) public :node @@ -22,10 +22,9 @@ module LocalVariableRoot # # @return [Parser::AST::Node] # - # @api private - # + # mutant:disable def local_variable_scope - AST::LocalVariableScope.new(node) + AST::LocalVariableScope.new(node: node, static_local_variables: Set.new) end def self.included(descendant) @@ -67,7 +66,7 @@ def emit_mlhs # @api private # # rubocop:disable Metrics/ParameterLists - def self.emitter(buffer:, comments:, node:, local_variable_scope:) + def self.emitter(buffer:, explicit_encoding:, comments:, node:, local_variable_scope:) type = node.type klass = REGISTRY.fetch(type) do @@ -75,10 +74,11 @@ def self.emitter(buffer:, comments:, node:, local_variable_scope:) end klass.new( - buffer: buffer, - comments: comments, - local_variable_scope: local_variable_scope, - node: node + buffer:, + comments:, + explicit_encoding:, + local_variable_scope:, + node: ) end # rubocop:enable Metrics/ParameterLists @@ -90,6 +90,5 @@ def self.emitter(buffer:, comments:, node:, local_variable_scope:) # @api private # abstract_method :dispatch - end # Emitter end # Unparser diff --git a/lib/unparser/emitter/array.rb b/lib/unparser/emitter/array.rb index 6f710cb5..8be9ee78 100644 --- a/lib/unparser/emitter/array.rb +++ b/lib/unparser/emitter/array.rb @@ -6,10 +6,6 @@ class Emitter class Array < self handle :array - def emit_heredoc_reminders - emitters.each(&:emit_heredoc_reminders) - end - private def dispatch diff --git a/lib/unparser/emitter/array_pattern.rb b/lib/unparser/emitter/array_pattern.rb index ae1833de..994224ae 100644 --- a/lib/unparser/emitter/array_pattern.rb +++ b/lib/unparser/emitter/array_pattern.rb @@ -12,18 +12,10 @@ class ArrayPattern < self def dispatch write('[') - delimited(children, &method(:emit_member)) + delimited(children) write(', ') if node_type.equal?(:array_pattern_with_tail) write(']') end - - def emit_member(node) - if n_match_rest?(node) - writer_with(MatchRest, node).emit_array_pattern - else - visit(node) - end - end end # Pin end # Emitter end # Unparser diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index d907c87b..90bc0353 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -11,12 +11,6 @@ def symbol_name true end - def emit_heredoc_reminders - return unless right - - emitter(right).emit_heredoc_reminders - end - private def dispatch @@ -30,12 +24,17 @@ def emit_right write(' = ') if BINARY_OPERATOR.include?(right.type) - writer_with(Writer::Binary, right).emit_operator + writer_with(Writer::Binary, node: right).emit_operator else - visit(right) + right_emitter.write_to_buffer end end + def right_emitter + emitter(right) + end + memoize :right_emitter + abstract_method :emit_left # Variable assignment emitter diff --git a/lib/unparser/emitter/begin.rb b/lib/unparser/emitter/begin.rb index 8767dbe8..bc423b12 100644 --- a/lib/unparser/emitter/begin.rb +++ b/lib/unparser/emitter/begin.rb @@ -8,12 +8,6 @@ class Begin < self handle :begin children :body - def emit_heredoc_reminders - children.each do |child| - emitter(child).emit_heredoc_reminders - end - end - private def dispatch diff --git a/lib/unparser/emitter/binary.rb b/lib/unparser/emitter/binary.rb index 8ad482e5..27345c6b 100644 --- a/lib/unparser/emitter/binary.rb +++ b/lib/unparser/emitter/binary.rb @@ -13,7 +13,7 @@ def dispatch end def writer - writer_with(Writer::Binary, node) + writer_with(Writer::Binary, node:) end memoize :writer end # Binary diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index 15e93677..92febd72 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -16,7 +16,6 @@ def dispatch ws write_open emit_block_arguments unless n_lambda?(target) - target_writer.emit_heredoc_reminders if n_send?(target) emit_optional_body_ensure_rescue(body) write_close end @@ -42,7 +41,7 @@ def write_close end def target_writer - writer_with(Writer::Send::Regular, target) + writer_with(Writer::Send::Regular, node: target) end memoize :target_writer @@ -65,7 +64,7 @@ def emit_send_target end def emit_lambda_arguments - parentheses { writer_with(Args, arguments).emit_lambda_arguments } + parentheses { writer_with(Args, node: arguments).emit_lambda_arguments } end def numblock? @@ -78,7 +77,7 @@ def emit_block_arguments ws parentheses('|', '|') do - writer_with(Args, arguments).emit_block_arguments + writer_with(Args, node: arguments).emit_block_arguments end end diff --git a/lib/unparser/emitter/def.rb b/lib/unparser/emitter/def.rb index 7d75ee1c..284ad884 100644 --- a/lib/unparser/emitter/def.rb +++ b/lib/unparser/emitter/def.rb @@ -26,7 +26,7 @@ def emit_arguments return if arguments.children.empty? parentheses do - writer_with(Args, arguments).emit_def_arguments + writer_with(Args, node: arguments).emit_def_arguments end end diff --git a/lib/unparser/emitter/dstr.rb b/lib/unparser/emitter/dstr.rb index 00957873..e1356094 100644 --- a/lib/unparser/emitter/dstr.rb +++ b/lib/unparser/emitter/dstr.rb @@ -7,15 +7,16 @@ class DStr < self handle :dstr - def emit_heredoc_reminders - writer_with(Writer::DynamicString, node).emit_heredoc_reminder - end - private def dispatch - writer_with(Writer::DynamicString, node).dispatch + dstr_writer.dispatch + end + + def dstr_writer + writer_with(Writer::DynamicString, node:) end + memoize :dstr_writer end # DStr end # Emitter diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index ef58c976..c1835972 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -14,12 +14,6 @@ class FlowModifier < self handle(*MAP.keys) - def emit_heredoc_reminders - children.each do |node| - emitter(node).emit_heredoc_reminders - end - end - private def dispatch diff --git a/lib/unparser/emitter/for.rb b/lib/unparser/emitter/for.rb index 5532eb57..12d34e44 100644 --- a/lib/unparser/emitter/for.rb +++ b/lib/unparser/emitter/for.rb @@ -18,7 +18,7 @@ def dispatch end def emit_condition - visit(condition) + emitter(condition).emit_mlhs write(' in ') visit(assignment) write(' do') diff --git a/lib/unparser/emitter/hash.rb b/lib/unparser/emitter/hash.rb index 017455ac..91e9ed9e 100644 --- a/lib/unparser/emitter/hash.rb +++ b/lib/unparser/emitter/hash.rb @@ -6,10 +6,6 @@ class Emitter class Hash < self handle :hash - def emit_heredoc_reminders - children.each(&method(:emit_heredoc_reminder_member)) - end - private def dispatch @@ -24,10 +20,6 @@ def dispatch end end - def emit_heredoc_reminder_member(node) - emitter(node.children.last).emit_heredoc_reminders if n_pair?(node) - end - def emit_hash_body delimited(children) end diff --git a/lib/unparser/emitter/hash_pattern.rb b/lib/unparser/emitter/hash_pattern.rb index c03fa91d..614aa782 100644 --- a/lib/unparser/emitter/hash_pattern.rb +++ b/lib/unparser/emitter/hash_pattern.rb @@ -32,7 +32,7 @@ def emit_member(node) when :match_var emit_match_var(node) when :match_rest - writer_with(MatchRest, node).emit_hash_pattern + writer_with(MatchRest, node:).emit_hash_pattern else visit(node) end diff --git a/lib/unparser/emitter/index.rb b/lib/unparser/emitter/index.rb index dd7a3a75..9a90cdf4 100644 --- a/lib/unparser/emitter/index.rb +++ b/lib/unparser/emitter/index.rb @@ -40,10 +40,6 @@ class Assign < self private_constant(*constants(false)) - def emit_heredoc_reminders - emitter(children.last).emit_heredoc_reminders - end - def dispatch emit_receiver emit_operation(children[VALUE_RANGE]) diff --git a/lib/unparser/emitter/op_assign.rb b/lib/unparser/emitter/op_assign.rb index affd028a..3f40c089 100644 --- a/lib/unparser/emitter/op_assign.rb +++ b/lib/unparser/emitter/op_assign.rb @@ -14,11 +14,6 @@ class BinaryAssign < self handle(*MAP.keys) - def emit_heredoc_reminders - emitter(target).emit_heredoc_reminders - emitter(expression).emit_heredoc_reminders - end - private def dispatch @@ -35,11 +30,6 @@ class OpAssign < self children :target, :operator, :value - def emit_heredoc_reminders - emitter(target).emit_heredoc_reminders - emitter(value).emit_heredoc_reminders - end - private def dispatch diff --git a/lib/unparser/emitter/primitive.rb b/lib/unparser/emitter/primitive.rb index ec17b16b..0bc0bcbf 100644 --- a/lib/unparser/emitter/primitive.rb +++ b/lib/unparser/emitter/primitive.rb @@ -7,19 +7,6 @@ class Primitive < self children :value - # Emitter for primitives based on Object#inspect - class Inspect < self - - handle :str - - private - - def dispatch - write(value.inspect) - end - - end # Inspect - class Symbol < self handle :sym diff --git a/lib/unparser/emitter/regexp.rb b/lib/unparser/emitter/regexp.rb index a9bdc94e..a4f2f412 100644 --- a/lib/unparser/emitter/regexp.rb +++ b/lib/unparser/emitter/regexp.rb @@ -4,32 +4,20 @@ module Unparser class Emitter # Emitter for regexp literals class Regexp < self - handle :regexp - define_group(:body, 0..-2) + handle :regexp private def dispatch - parentheses('/', '/') do - body.each(&method(:emit_body)) - end - emit_options + writer.dispatch end - def emit_options - write(children.last.children.join) + def writer + writer_with(Writer::Regexp, node:) end + memoize :writer - def emit_body(node) - if n_begin?(node) - write('#{') - node.children.each(&method(:visit)) - write('}') - else - buffer.append_without_prefix(node.children.first.gsub('/', '\/')) - end - end end # Regexp end # Emitter end # Unparser diff --git a/lib/unparser/emitter/rescue.rb b/lib/unparser/emitter/rescue.rb index e4ca0e32..3b60b25f 100644 --- a/lib/unparser/emitter/rescue.rb +++ b/lib/unparser/emitter/rescue.rb @@ -9,7 +9,13 @@ class Rescue < self private def dispatch - emit_rescue_postcontrol(node) + resbody = children.fetch(1) + + if resbody.children.fetch(1) + emit_rescue_regular(node) + else + emit_rescue_postcontrol(node) + end end end # Rescue end # Emitter diff --git a/lib/unparser/emitter/root.rb b/lib/unparser/emitter/root.rb index e335f446..0e2134e9 100644 --- a/lib/unparser/emitter/root.rb +++ b/lib/unparser/emitter/root.rb @@ -2,22 +2,15 @@ module Unparser class Emitter - # Root emitter a special case class Root < self - include Concord::Public.new(:buffer, :node, :comments) - include LocalVariableRoot - END_NL = %i[class sclass module begin].freeze private_constant(*constants(false)) def dispatch - if children.any? - emit_body(node, indent: false) - else - visit_deep(node) - end + emit_body(node, indent: false) + buffer.nl_flush_heredocs emit_eof_comments nl if END_NL.include?(node.type) && !buffer.fresh_line? diff --git a/lib/unparser/emitter/send.rb b/lib/unparser/emitter/send.rb index 7c598d40..aa790c43 100644 --- a/lib/unparser/emitter/send.rb +++ b/lib/unparser/emitter/send.rb @@ -10,10 +10,6 @@ def emit_mlhs writer.emit_mlhs end - def emit_heredoc_reminders - writer.emit_heredoc_reminders - end - private def dispatch @@ -21,7 +17,7 @@ def dispatch end def writer - writer_with(Writer::Send, node) + writer_with(Writer::Send, node:) end memoize :writer end # Send diff --git a/lib/unparser/emitter/string.rb b/lib/unparser/emitter/string.rb new file mode 100644 index 00000000..1c81c591 --- /dev/null +++ b/lib/unparser/emitter/string.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Base class for primitive emitters + class String < self + children :value + + handle :str + + private + + def dispatch + if explicit_encoding && !value.encoding.equal?(explicit_encoding) + write_utf8_escaped + else + write(value.inspect) + end + end + + def write_utf8_escaped + write('"') + value.each_codepoint do |codepoint| + write("\\u{#{codepoint.to_s(16)}}") + end + write('"') + end + + end # String + end # Emitter +end # Unparser diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index 91c08477..412281a5 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -7,8 +7,6 @@ module Generation private_constant(*constants(false)) - def emit_heredoc_reminders; end - def symbol_name; end def write_to_buffer @@ -151,7 +149,7 @@ def emit_body_inner(node) emit_body_member(head) tail.each do |child| - nl + buffer.ensure_nl nl if EXTRA_NL.include?(child.type) @@ -208,32 +206,28 @@ def emit_body_ensure_rescue(node) end def emit_rescue_postcontrol(node) - writer = writer_with(Writer::Rescue, node) + writer = writer_with(Writer::Rescue, node:) writer.emit_postcontrol - writer.emit_heredoc_reminders end def emit_rescue_regular(node) - writer_with(Writer::Rescue, node).emit_regular - end - - def writer_with(klass, node) - klass.new(to_h.merge(node: node)) + writer_with(Writer::Rescue, node:).emit_regular end def emitter(node) Emitter.emitter(**to_h, node: node) end + def writer_with(klass, node:, **attributes) + klass.new(to_h.merge(node: node, **attributes)) + end + def visit(node) emitter(node).write_to_buffer end def visit_deep(node) - emitter(node).tap do |emitter| - emitter.write_to_buffer - emitter.emit_heredoc_reminders - end + emitter(node).tap(&:write_to_buffer) end def first_child diff --git a/lib/unparser/node_details.rb b/lib/unparser/node_details.rb index 51933040..0721984d 100644 --- a/lib/unparser/node_details.rb +++ b/lib/unparser/node_details.rb @@ -4,6 +4,7 @@ module Unparser module NodeDetails include Constants, NodeHelpers + # mutant:disable def self.included(descendant) descendant.class_eval do include Adamantium, Concord.new(:node) diff --git a/lib/unparser/validation.rb b/lib/unparser/validation.rb index b6600797..0c9eb87e 100644 --- a/lib/unparser/validation.rb +++ b/lib/unparser/validation.rb @@ -7,10 +7,14 @@ class Validation :generated_node, :generated_source, :identification, - :original_node, + :original_ast, :original_source ) + class PhaseException + include Anima.new(:exception, :phase) + end + # Test if source could be unparsed successfully # # @return [Boolean] @@ -18,10 +22,11 @@ class Validation # @api private # # rubocop:disable Style/OperatorMethodCall + # mutant:disable def success? [ original_source, - original_node, + original_ast, generated_source, generated_node ].all?(&:right?) && generated_node.from_right.==(original_node.from_right) @@ -33,7 +38,7 @@ def success? # @return [String] # # @api private - # + # mutant:disable def report message = [identification] @@ -47,48 +52,57 @@ def report end memoize :report + # mutant:disable + def original_node + original_ast.fmap(&:node) + end + # Create validator from string # # @param [String] original_source # # @return [Validator] + # mutant:disable def self.from_string(original_source) - original_node = Unparser - .parse_either(original_source) + original_ast = parse_ast_either(original_source) - generated_source = original_node + generated_source = original_ast .lmap(&method(:const_unit)) - .bind(&Unparser.method(:unparse_either)) + .bind(&method(:unparse_ast_either)) generated_node = generated_source .lmap(&method(:const_unit)) - .bind(&Unparser.method(:parse_either)) + .bind(&method(:parse_ast_either)) + .fmap(&:node) new( - identification: '(string)', - original_source: Either::Right.new(original_source), - original_node: original_node, + generated_node: generated_node, generated_source: generated_source, - generated_node: generated_node + identification: '(string)', + original_ast: original_ast, + original_source: Either::Right.new(original_source) ) end - # Create validator from node + # Create validator from ast # - # @param [Parser::AST::Node] original_node + # @param [Unparser::AST] ast # # @return [Validator] - def self.from_node(original_node) - generated_source = Unparser.unparse_either(original_node) + # + # mutant:disable + def self.from_ast(ast:) + generated_source = Unparser.unparse_ast_either(ast) generated_node = generated_source .lmap(&method(:const_unit)) - .bind(&Unparser.public_method(:parse_either)) + .bind(&method(:parse_ast_either)) + .fmap(&:node) new( identification: '(string)', original_source: generated_source, - original_node: Either::Right.new(original_node), + original_ast: Either::Right.new(ast), generated_source: generated_source, generated_node: generated_node ) @@ -99,24 +113,45 @@ def self.from_node(original_node) # @param [Pathname] path # # @return [Validator] + # + # mutant:disable def self.from_path(path) - from_string(path.read).with(identification: path.to_s) + from_string(path.read.freeze).with(identification: path.to_s) end + # mutant:disable + def self.unparse_ast_either(ast) + Unparser.unparse_ast_either(ast) + end + private_class_method :unparse_ast_either + + # mutant:disable + def self.parse_ast_either(source) + Unparser.parse_ast_either(source) + end + private_class_method :parse_ast_either + + # mutant:disable + def self.const_unit(_); end + private_class_method :const_unit + private + # mutant:disable def make_report(label, attribute_name) ["#{label}:"].concat(public_send(attribute_name).either(method(:report_exception), ->(value) { [value] })) end - def report_exception(exception) - if exception - [exception.inspect].concat(exception.backtrace.take(20)) + # mutant:disable + def report_exception(phase_exception) + if phase_exception + [phase_exception.inspect].concat(phase_exception.backtrace.take(20)) else - ['undefined'] + %w[undefined] end end + # mutant:disable def node_diff_report diff = nil @@ -132,14 +167,13 @@ def node_diff_report diff ? ['Node-Diff:', diff] : [] end - def self.const_unit(_value); end - private_class_method :const_unit - class Literal < self + # mutant:disable def success? original_source.eql?(generated_source) end + # mutant:disable def report message = [identification] @@ -155,20 +189,26 @@ def report private + # mutant:disable def source_diff_report diff = nil original_source.fmap do |original| generated_source.fmap do |generated| diff = Diff.new( - original.split("\n", -1), - generated.split("\n", -1) + encode(original).split("\n", -1), + encode(generated).split("\n", -1) ).colorized_diff end end diff ? ['Source-Diff:', diff] : [] end + + # mutant:disable + def encode(string) + string.encode('UTF-8', invalid: :replace, undef: :replace) + end end # Literal end # Validation end # Unparser diff --git a/lib/unparser/writer.rb b/lib/unparser/writer.rb index 94b95bfd..fe5492c9 100644 --- a/lib/unparser/writer.rb +++ b/lib/unparser/writer.rb @@ -4,12 +4,43 @@ module Unparser module Writer include Generation, NodeHelpers + # mutant:disable def self.included(descendant) descendant.class_eval do - include Anima.new(:buffer, :comments, :node, :local_variable_scope) + include Adamantium, Anima.new(:buffer, :comments, :explicit_encoding, :node, :local_variable_scope) extend DSL end end + + private + + # mutant:disable + def emitter(node) + Emitter.emitter( + buffer: buffer, + comments: comments, + explicit_encoding: explicit_encoding, + local_variable_scope: local_variable_scope, + node: node + ) + end + + # mutant:disable + def round_trips?(source:) + parser = Unparser.parser + + local_variable_scope + .local_variables_for_node(node) + .each(&parser.static_env.public_method(:declare)) + + buffer = Buffer.new + buffer.write_encoding(explicit_encoding) if explicit_encoding + buffer.write(source) + + node.eql?(parser.parse(Unparser.buffer(buffer.content))) + rescue Parser::SyntaxError + false + end end # Writer end # Unparser diff --git a/lib/unparser/writer/dynamic_string.rb b/lib/unparser/writer/dynamic_string.rb index fec46270..7619ce59 100644 --- a/lib/unparser/writer/dynamic_string.rb +++ b/lib/unparser/writer/dynamic_string.rb @@ -5,116 +5,123 @@ module Writer class DynamicString include Writer, Adamantium - PATTERNS_2 = - [ - %i[str_empty begin].freeze, - %i[begin str_nl].freeze - ].freeze - - PATTERNS_3 = - [ - %i[begin str_nl_eol str_nl_eol].freeze, - %i[str_nl_eol begin str_nl_eol].freeze, - %i[str_ws begin str_nl_eol].freeze - ].freeze - FLAT_INTERPOLATION = %i[ivar cvar gvar nth_ref].to_set.freeze - private_constant(*constants(false)) - - def emit_heredoc_reminder - return unless heredoc? + # Amount of dstr children at which heredoc emitting is + # preferred, but not guaranteed. + HEREDOC_THRESHOLD = 8 + HEREDOC_DELIMITER = 'HEREDOC' + HEREDOC_HEADER = "<<-#{HEREDOC_DELIMITER}".freeze + HEREDOC_FOOTER = "#{HEREDOC_DELIMITER}\n".freeze - emit_heredoc_body - emit_heredoc_footer - end + private_constant(*constants(false)) + # The raise below is not reachable if unparser is correctly implemented + # but has to exist as I have to assume unparser still has bugs. + # + # But unless I had such a bug in my test corpus: I cannot enable mutant, and if I + # knew about such a bug: I'd fix it so would be back at the start. + # + # TLDR: Good case for a mutant disable. + # + # mutant:disable def dispatch if heredoc? - emit_heredoc_header + write(HEREDOC_HEADER) + buffer.push_heredoc(heredoc_body) + elsif round_tripping_segmented_source + write(round_tripping_segmented_source) else - emit_dstr + fail UnsupportedNodeError, "Unparser cannot round trip this node: #{node.inspect}" end end private - def heredoc_header - '<<-HEREDOC' - end - def heredoc? - !children.empty? && (nl_last_child? && heredoc_pattern?) + if children.length >= HEREDOC_THRESHOLD + round_trips_heredoc? + else + round_tripping_segmented_source.nil? # && round_trips_heredoc? + end end + memoize :heredoc? - def emit_heredoc_header - write(heredoc_header) + def round_trips_heredoc? + round_trips?(source: heredoc_source) end + memoize :round_trips_heredoc? - def emit_heredoc_body - nl - emit_normal_heredoc_body - end + def round_tripping_segmented_source + each_segments(children) do |segments| - def emit_heredoc_footer - write('HEREDOC') - end + source = segmented_source(segments: segments) - def classify(node) - if n_str?(node) - classify_str(node) - else - node.type + return source if round_trips?(source: source) end + nil end + memoize :round_tripping_segmented_source - def classify_str(node) - if str_nl?(node) - :str_nl - elsif node.children.first.end_with?("\n") - :str_nl_eol - elsif str_ws?(node) - :str_ws - elsif str_empty?(node) - :str_empty + def each_segments(array) + yield [array] + + 1.upto(array.length) do |take| + prefix = [array.take(take)] + suffix = array.drop(take) + each_segments(suffix) do |items| + yield(prefix + items) + end end end - def str_nl?(node) - node.eql?(s(:str, "\n")) - end + def segmented_source(segments:) + buffer = Buffer.new - def str_empty?(node) - node.eql?(s(:str, '')) - end + Segmented.new( + buffer:, + comments:, + explicit_encoding: nil, + local_variable_scope:, + node:, + segments: + ).dispatch - def str_ws?(node) - /\A( |\t)+\z/.match?(node.children.first) + buffer.content end - def heredoc_pattern? - heredoc_pattern_2? || heredoc_pattern_3? + def heredoc_body + buffer = Buffer.new + + writer = Heredoc.new( + buffer:, + comments:, + explicit_encoding: nil, + local_variable_scope:, + node: + ) + + writer.emit + buffer.content end + memoize :heredoc_body - def heredoc_pattern_3? - children.each_cons(3).any? do |group| - PATTERNS_3.include?(group.map(&method(:classify))) - end + def heredoc_source + "#{HEREDOC_HEADER}\n#{heredoc_body}" end + memoize :heredoc_source + + class Heredoc + include Writer, Adamantium - def heredoc_pattern_2? - children.each_cons(2).any? do |group| - PATTERNS_2.include?(group.map(&method(:classify))) + def emit + emit_heredoc_body + write(HEREDOC_FOOTER) end - end - def nl_last_child? - last = children.last - n_str?(last) && last.children.first[-1].eql?("\n") - end + private - def emit_normal_heredoc_body - buffer.root_indent do + def emit_heredoc_body children.each do |child| if n_str?(child) write(escape_dynamic(child.children.first)) @@ -123,88 +130,74 @@ def emit_normal_heredoc_body end end end - end - def escape_dynamic(string) - string.gsub('#', '\#') - end + def escape_dynamic(string) + string.gsub('#', '\#') + end - def emit_dynamic(child) - if FLAT_INTERPOLATION.include?(child.type) - write('#') - visit(child) - elsif n_dstr?(child) - emit_body(child.children) - else + def emit_dynamic(child) write('#{') emit_dynamic_component(child.children.first) write('}') end - end - def emit_dynamic_component(node) - visit(node) if node - end - - def emit_dstr - if children.empty? - write('%()') - else - segments.each_with_index do |children, index| - emit_segment(children, index) - end + def emit_dynamic_component(node) + visit(node) if node end - end - - def breakpoint?(child, current) - last_type = current.last&.type + end # Heredoc - [ - n_str?(child) && last_type.equal?(:str) && current.none?(&method(:n_begin?)), - last_type.equal?(:dstr), - n_dstr?(child) && last_type - ].any? - end - - def segments - segments = [] + class Segmented + include Writer, Adamantium - segments << current = [] + include anima.add(:segments) - children.each do |child| - if breakpoint?(child, current) - segments << current = [] + def dispatch + if children.empty? + write('%()') + else + segments.each_with_index { |segment, index| emit_segment(segment, index) } end - - current << child end - segments - end + private - def emit_segment(children, index) - write(' ') unless index.zero? + def emit_segment(children, index) + write(' ') unless index.zero? - write('"') - emit_body(children) - write('"') - end + write('"') + emit_segment_body(children) + write('"') + end - def emit_body(children) - buffer.root_indent do + def emit_segment_body(children) children.each_with_index do |child, index| - if n_str?(child) - string = child.children.first - if string.eql?("\n") && children.fetch(index.pred).type.equal?(:begin) - write("\n") - else - write(string.inspect[1..-2]) - end - else - emit_dynamic(child) + case child.type + when :begin + write('#{') + visit(child.children.first) if child.children.first + write('}') + when FLAT_INTERPOLATION + write('#') + visit(child) + when :str + visit_str(children, child, index) + when :dstr + emit_segment_body(child.children) end end end + + def visit_str(children, child, index) + string = child.children.first + + next_child = children.at(index.succ) + + if next_child && next_child.type.equal?(:str) + write(string.gsub('"', '\\"')) + else + write(child.children.first.inspect[1..-2]) + end + end end end # DynamicString end # Writer diff --git a/lib/unparser/writer/regexp.rb b/lib/unparser/writer/regexp.rb new file mode 100644 index 00000000..3cc55395 --- /dev/null +++ b/lib/unparser/writer/regexp.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +module Unparser + module Writer + # Writer for regexp literals + class Regexp + include Writer, Adamantium + + CANDIDATES = [ + ['/', '/'].freeze, + ['%r{', '}'].freeze + ].freeze + + define_group(:body, 0..-2) + + def dispatch + effective_writer.write_to_buffer + end + + private + + # mutant:disable + def effective_writer + CANDIDATES.each do |token_open, token_close| + source = render_with_delimiter(token_close:, token_open:) + + next unless round_trips?(source:) + + return writer_with(Effective, node:, token_close:, token_open:) + end + + fail 'Could not find a round tripping solution for regexp' + end + + class Effective + include Writer, Adamantium + + include anima.add(:token_close, :token_open) + + define_group(:body, 0..-2) + + def dispatch + buffer.root_indent do + write(token_open) + body.each(&method(:emit_body)) + write(token_close) + emit_options + end + end + + private + + def emit_body(node) + if n_begin?(node) + write('#{') + node.children.each(&method(:visit)) + write('}') + else + write_regular(node.children.first) + end + end + + def write_regular(string) + if string.length > 1 && string.start_with?("\n") + string.each_char do |char| + buffer.append_without_prefix(char.eql?("\n") ? '\c*' : char) + end + else + buffer.append_without_prefix(string) + end + end + + def emit_options + write(children.last.children.join) + end + end + + # mutant:disable + def render_with_delimiter(token_close:, token_open:) + buffer = Buffer.new + + writer = Effective.new( + buffer:, + comments:, + explicit_encoding:, + local_variable_scope:, + node:, + token_close:, + token_open: + ) + + writer.dispatch + buffer.nl_flush_heredocs + buffer.content + end + end # Regexp + end # Emitter +end # Unparser diff --git a/lib/unparser/writer/resbody.rb b/lib/unparser/writer/resbody.rb index bc386169..2ab0f867 100644 --- a/lib/unparser/writer/resbody.rb +++ b/lib/unparser/writer/resbody.rb @@ -6,6 +6,11 @@ module Writer class Resbody include Writer + OPERATORS = { + csend: '&.', + send: '.' + }.freeze + children :exception, :assignment, :body def emit_postcontrol @@ -33,7 +38,31 @@ def emit_assignment return unless assignment write(' => ') - visit(assignment) + + case assignment.type + when :send, :csend + write_send_assignment + when :indexasgn + write_index_assignment + else + visit(assignment) + end + end + + def write_send_assignment + details = NodeDetails::Send.new(assignment) + + visit(details.receiver) + write(OPERATORS.fetch(assignment.type)) + write(details.non_assignment_selector) + end + + def write_index_assignment + receiver, index = assignment.children + visit(receiver) + write('[') + visit(index) if index + write(']') end end # Resbody end # Writer diff --git a/lib/unparser/writer/rescue.rb b/lib/unparser/writer/rescue.rb index 892b2fb4..98d473dd 100644 --- a/lib/unparser/writer/rescue.rb +++ b/lib/unparser/writer/rescue.rb @@ -20,13 +20,9 @@ def emit_regular end end - def emit_heredoc_reminders - emitter(body).emit_heredoc_reminders - end - def emit_postcontrol visit(body) - writer_with(Resbody, rescue_body).emit_postcontrol + writer_with(Resbody, node: rescue_body).emit_postcontrol end private @@ -36,7 +32,7 @@ def else_node end def emit_rescue_body(node) - writer_with(Resbody, node).emit_regular + writer_with(Resbody, node:).emit_regular end end # Rescue end # Writer diff --git a/lib/unparser/writer/send.rb b/lib/unparser/writer/send.rb index f75d30e6..4462939e 100644 --- a/lib/unparser/writer/send.rb +++ b/lib/unparser/writer/send.rb @@ -30,15 +30,10 @@ def emit_selector write(details.string_selector) end - def emit_heredoc_reminders - emitter(receiver).emit_heredoc_reminders if receiver - arguments.each(&method(:emit_heredoc_reminder)) - end - private def effective_writer - writer_with(effective_writer_class, node) + writer_with(effective_writer_class, node:) end memoize :effective_writer @@ -78,10 +73,6 @@ def emit_normal_arguments parentheses { delimited(arguments) } end - def emit_heredoc_reminder(argument) - emitter(argument).emit_heredoc_reminders - end - def avoid_clash? local_variable_clash? || parses_as_constant? end @@ -91,9 +82,12 @@ def local_variable_clash? end def parses_as_constant? - test = Unparser.parse_either(selector.to_s).from_right do - fail InvalidNodeError.new("Invalid selector for send node: #{selector.inspect}", node) - end + test = Unparser + .parse_ast_either(selector.to_s) + .fmap(&:node) + .from_right do + fail InvalidNodeError.new("Invalid selector for send node: #{selector.inspect}", node) + end n_const?(test) end @@ -105,7 +99,7 @@ def details def emit_send_regular(node) if n_send?(node) - writer_with(Regular, node).dispatch + writer_with(Regular, node:).dispatch else visit(node) end diff --git a/scripts/devloop.sh b/scripts/devloop.sh index 72a7b2e7..d4295682 100755 --- a/scripts/devloop.sh +++ b/scripts/devloop.sh @@ -1,6 +1,7 @@ -while inotifywait lib/**/*.rb test/**/*.rb spec/**/*.rb Gemfile unparser.gemspec; do +while inotifywait bin/corpus lib/**/*.rb test/**/*.rb spec/**/*.rb Gemfile unparser.gemspec; do bundle exec rspec spec/unit -fd --fail-fast --order defined \ - && bundle exec ./bin/parser-round-trip-test \ - && bundle exec mutant run --zombie --since HEAD~1 --fail-fast -- 'Unparser*' \ - && bundle exec rubocop + && bundle exec mutant run --zombie --since main --fail-fast -- 'Unparser*' done + # && bundle exec ./bin/parser-round-trip-test \ + # && bundle exec rubocop \ + # && bundle exec ./bin/corpus \ diff --git a/spec/integrations.yml b/spec/integrations.yml index c78a1765..475f1890 100644 --- a/spec/integrations.yml +++ b/spec/integrations.yml @@ -1,82 +1,18 @@ --- - name: mutant repo_uri: 'https://github.com/mbj/mutant.git' - repo_ref: 'main' - exclude: - # bug in unparser, to be fixed in followup PRs - - spec/integration/mutant/parallel_spec.rb + repo_ref: main + exclude: [] +- name: deepcover + repo_uri: 'https://github.com/deep-cover/deep-cover.git' + repo_ref: master + exclude: [] +- name: activemerchant + repo_uri: 'https://github.com/activemerchant/active_merchant.git' + repo_ref: master + exclude: [] + exclude: [] - name: rubyspec repo_uri: 'https://github.com/ruby/spec.git' - # Revision of rubyspec on the last CI build of unparser that passed - repo_ref: 'b40189b88' - exclude: - - command_line/fixtures/bad_syntax.rb - - core/array/pack/shared/float.rb - - core/array/pack/shared/integer.rb - - core/array/pack/shared/string.rb - - core/array/pack/{b,c,h,m}_spec.rb - - core/array/pack/{u,w}_spec.rb - - core/encoding/compatible_spec.rb - - core/encoding/converter/convert_spec.rb - - core/encoding/converter/last_error_spec.rb - - core/encoding/converter/primitive_convert_spec.rb - - core/encoding/converter/primitive_errinfo_spec.rb - - core/encoding/converter/putback_spec.rb - - core/encoding/fixtures/classes.rb - - core/encoding/invalid_byte_sequence_error/error_bytes_spec.rb - - core/encoding/invalid_byte_sequence_error/incomplete_input_spec.rb - - core/encoding/invalid_byte_sequence_error/readagain_bytes_spec.rb - - core/encoding/replicate_spec.rb - - core/env/element_reference_spec.rb - - core/io/readpartial_spec.rb - - core/io/shared/gets_ascii.rb - - core/kernel/shared/sprintf_encoding.rb - - core/marshal/dump_spec.rb - - core/marshal/fixtures/marshal_data.rb - - core/marshal/shared/load.rb - - core/random/bytes_spec.rb - - core/regexp/shared/new.rb - - core/regexp/shared/new_ascii.rb - - core/regexp/shared/new_ascii_8bit.rb - - core/regexp/shared/quote.rb - - core/string/byteslice_spec.rb - - core/string/casecmp_spec.rb - - core/string/codepoints_spec.rb - - core/string/count_spec.rb - - core/string/encode_spec.rb - - core/string/inspect_spec.rb - - core/string/shared/codepoints.rb - - core/string/shared/each_codepoint_without_block.rb - - core/string/shared/eql.rb - - core/string/shared/succ.rb - - core/string/shared/to_sym.rb - - core/string/squeeze_spec.rb - - core/string/unpack/shared/float.rb - - core/string/unpack/shared/integer.rb - - core/string/unpack/{b,c,h,m}_spec.rb - - core/string/unpack/{u,w}_spec.rb - - core/symbol/casecmp_spec.rb - - core/time/_dump_spec.rb - - core/time/_load_spec.rb - - language/fixtures/binary_symbol.rb - - language/fixtures/squiggly_heredoc.rb - - language/for_spec.rb - - language/regexp/encoding_spec.rb - - language/regexp/escapes_spec.rb - - language/source_encoding_spec.rb - - language/string_spec.rb - - library/base64/decode64_spec.rb - - library/digest/md5/shared/constants.rb - - library/digest/md5/shared/sample.rb - - library/digest/sha1/shared/constants.rb - - library/digest/sha256/shared/constants.rb - - library/digest/sha384/shared/constants.rb - - library/digest/sha512/shared/constants.rb - - library/openssl/shared/constants.rb - - library/socket/basicsocket/recv_spec.rb - - library/socket/socket/gethostbyname_spec.rb - - library/stringscanner/getch_spec.rb - - library/stringscanner/shared/get_byte.rb - - library/zlib/inflate/set_dictionary_spec.rb - - optional/capi/integer_spec.rb - - security/cve_2010_1330_spec.rb + repo_ref: master + exclude: [] diff --git a/spec/unit/unparser/buffer_spec.rb b/spec/unit/unparser/buffer_spec.rb index 9c136298..af608f20 100644 --- a/spec/unit/unparser/buffer_spec.rb +++ b/spec/unit/unparser/buffer_spec.rb @@ -132,6 +132,21 @@ expect(object.content).to eql("foo\nbar") end + it 'keeps track to allow to write final' do + object.append('foo') + subject + object.append('bar') + object.final_newline + expect(object.content).to eql("foo\nbar\n") + end + + it 'flushes heredocs' do + object.push_heredoc('HEREDOC') + subject + object.nl + expect(object.content).to eql("\nHEREDOC\n") + end + it_should_behave_like 'a command method' end @@ -153,4 +168,120 @@ it_should_behave_like 'a command method' end + + describe '#write_encoding' do + let(:object) { described_class.new } + + subject { object.write_encoding(Encoding::ASCII) } + + it 'unindents two chars' do + subject + expect(object.content).to eql("# -*- encoding: US-ASCII -*-\n") + end + + it_should_behave_like 'a command method' + end + + describe '#nl_flush_heredocs' do + let(:object) { described_class.new } + + subject { object.nl_flush_heredocs } + + context 'on unbuffered heredoc' do + context 'on fresh line' do + it 'does nothing' do + subject + expect(object.content).to eql('') + end + end + + context 'outside fresh line' do + it 'does nothing' do + object.write('foo') + subject + expect(object.content).to eql('foo') + end + end + end + + context 'on buffered heredocs' do + context 'on fresh line' do + it 'flushes heredoc' do + object.push_heredoc('HEREDOC') + subject + expect(object.content).to eql('HEREDOC') + end + end + + context 'outside fresh line' do + it 'flushes heredoc, with new line' do + object.write('foo') + object.push_heredoc('HEREDOC') + subject + expect(object.content).to eql("foo\nHEREDOC") + end + end + end + end + + describe '#final_newline' do + let(:object) { described_class.new } + + subject { object.final_newline } + + context 'when empty' do + it 'does nothing' do + subject + expect(object.content).to eql('') + end + end + + context 'on one line without newline' do + it 'does not create a new line' do + object.write('foo') + subject + expect(object.content).to eql('foo') + end + end + + context 'on one line with newline' do + it 'does not create a new line' do + object.write('foo') + object.nl + subject + expect(object.content).to eql("foo\n") + end + end + + context 'more than one line, without terminating newline' do + it 'does terminate with newline' do + object.write('foo') + object.nl + object.write('bar') + subject + expect(object.content).to eql("foo\nbar\n") + end + end + end + + describe '#ensure_nl' do + let(:object) { described_class.new } + + subject { object.ensure_nl } + + context 'when on a new line' do + it 'crates a new line' do + subject + expect(object.content).to eql('') + end + end + + context 'when not on a new line' do + it 'crates a new line' do + object.write('foo') + subject + expect(object.content).to eql("foo\n") + end + end + end end diff --git a/spec/unit/unparser/comments/consume_spec.rb b/spec/unit/unparser/comments/consume_spec.rb index 57b1702e..ada981ef 100644 --- a/spec/unit/unparser/comments/consume_spec.rb +++ b/spec/unit/unparser/comments/consume_spec.rb @@ -2,21 +2,20 @@ describe Unparser::Comments, '#consume' do - let(:ast_and_comments) do - Unparser.parse_with_comments(<<~'RUBY') + let(:ast) do + Unparser.parse_ast(<<~'RUBY') def hi # EOL 1 end # EOL 2 RUBY end - let(:ast) { ast_and_comments[0] } - let(:comments) { ast_and_comments[1] } - let(:object) { described_class.new(comments) } + + let(:object) { described_class.new(ast.comments) } it 'should cause further EOL comments to be returned' do expect(object.take_eol_comments).to eql([]) - object.consume(ast, :name) - expect(object.take_eol_comments).to eql([comments[0]]) - object.consume(ast, :end) - expect(object.take_eol_comments).to eql([comments[1]]) + object.consume(ast.node, :name) + expect(object.take_eol_comments).to eql([ast.comments[0]]) + object.consume(ast.node, :end) + expect(object.take_eol_comments).to eql([ast.comments[1]]) end end diff --git a/spec/unit/unparser/comments/take_all_spec.rb b/spec/unit/unparser/comments/take_all_spec.rb index 0f807f12..b28382e3 100644 --- a/spec/unit/unparser/comments/take_all_spec.rb +++ b/spec/unit/unparser/comments/take_all_spec.rb @@ -1,18 +1,17 @@ require 'spec_helper' describe Unparser::Comments, '#take_all' do - - let(:ast_and_comments) do - Unparser.parse_with_comments(<<~'RUBY') + let(:ast) do + Unparser.parse_ast(<<~'RUBY') def hi # EOL 1 end # EOL 2 RUBY end - let(:comments) { ast_and_comments[1] } - let(:object) { described_class.new(comments) } + + let(:object) { described_class.new(ast.comments) } it 'should take all comments' do - expect(object.take_all).to eql(comments) + expect(object.take_all).to eql(ast.comments) expect(object.take_all).to eql([]) end end diff --git a/spec/unit/unparser/comments/take_before_spec.rb b/spec/unit/unparser/comments/take_before_spec.rb index 288cccd2..de681d4e 100644 --- a/spec/unit/unparser/comments/take_before_spec.rb +++ b/spec/unit/unparser/comments/take_before_spec.rb @@ -1,15 +1,11 @@ require 'spec_helper' describe Unparser::Comments, '#take_before' do - - let(:ast) { ast_and_comments[0] } - let(:comments) { ast_and_comments[1] } - let(:object) { described_class.new(comments) } + let(:object) { described_class.new(ast.comments) } context 'usual case' do - - let(:ast_and_comments) do - Unparser.parse_with_comments(<<~'RUBY') + let(:ast) do + Unparser.parse_ast(<<~'RUBY') def hi # EOL 1 # comment end # EOL 2 @@ -17,30 +13,30 @@ def hi # EOL 1 end it 'should return no comments if none are before the node' do - expect(object.take_before(ast, :expression)).to eql([]) + expect(object.take_before(ast.node, :expression)).to eql([]) end it 'should return only the comments that are before the specified part of the node' do - expect(object.take_before(ast, :end)).to eql(comments.first(2)) - expect(object.take_all).to eql([comments[2]]) + expect(object.take_before(ast.node, :end)).to eql(ast.comments.first(2)) + expect(object.take_all).to eql([ast.comments[2]]) end end context 'when node does not respond to source part' do - let(:ast_and_comments) do - Unparser.parse_with_comments(<<~'RUBY') + let(:ast) do + Unparser.parse_ast(<<~'RUBY') expression ? :foo : :bar # EOL 1 # EOL 2 RUBY end it 'should return no comments if none are before the node' do - expect(object.take_before(ast, :expression)).to eql([]) + expect(object.take_before(ast.node, :expression)).to eql([]) end it 'should return only the comments that are before the specified part of the node' do - expect(object.take_before(ast, :end)).to eql([]) + expect(object.take_before(ast.node, :end)).to eql([]) end end end diff --git a/spec/unit/unparser/comments/take_eol_comments_spec.rb b/spec/unit/unparser/comments/take_eol_comments_spec.rb index d2ff3920..22ea3f21 100644 --- a/spec/unit/unparser/comments/take_eol_comments_spec.rb +++ b/spec/unit/unparser/comments/take_eol_comments_spec.rb @@ -1,9 +1,8 @@ require 'spec_helper' describe Unparser::Comments, '#take_eol_comments' do - - let(:ast_and_comments) do - Unparser.parse_with_comments(<<~'RUBY') + let(:ast) do + Unparser.parse_ast(<<~'RUBY') def hi # EOL 1 =begin doc comment @@ -11,22 +10,21 @@ def hi # EOL 1 end # EOL 2 RUBY end - let(:ast) { ast_and_comments[0] } - let(:comments) { ast_and_comments[1] } - let(:object) { described_class.new(comments) } + + let(:object) { described_class.new(ast.comments) } it 'should return no comments if nothing has been consumed' do expect(object.take_eol_comments).to eql([]) end it 'should return comments once their line has been consumed' do - object.consume(ast, :name) - expect(object.take_eol_comments).to eql([comments[0]]) + object.consume(ast.node, :name) + expect(object.take_eol_comments).to eql([ast.comments[0]]) end it 'should leave doc comments to be taken later' do - object.consume(ast) - expect(object.take_eol_comments).to eql([comments[0], comments[2]]) - expect(object.take_all).to eql([comments[1]]) + object.consume(ast.node) + expect(object.take_eol_comments).to eql([ast.comments[0], ast.comments[2]]) + expect(object.take_all).to eql([ast.comments[1]]) end end diff --git a/spec/unit/unparser/validation_spec.rb b/spec/unit/unparser/validation_spec.rb index 98b0c8d3..7a2cc469 100644 --- a/spec/unit/unparser/validation_spec.rb +++ b/spec/unit/unparser/validation_spec.rb @@ -1,12 +1,12 @@ require 'spec_helper' -describe Unparser::Validation do +RSpec.describe Unparser::Validation do let(:object) do described_class.new( identification: identification, generated_node: generated_node, generated_source: generated_source, - original_node: original_node, + original_ast: right(Unparser::AST.from_node(node: original_node)), original_source: original_source ) end @@ -14,7 +14,7 @@ let(:generated_node) { right(s(:send, s(:int, 1), :foo)) } let(:generated_source) { right('1.foo') } let(:identification) { 'example-identification' } - let(:original_node) { right(s(:send, s(:int, 1), :foo)) } + let(:original_node) { s(:send, s(:int, 1), :foo) } let(:original_source) { right('1.foo') } let(:exception) do @@ -64,9 +64,9 @@ def report end context 'on success' do - it 'is successful' do - expect(object.success?).to be(true) - end + # it 'is successful' do + # expect(object.success?).to be(true) + # end it 'returns expected report' do expect(report).to eql(<<~'REPORT'.strip) @@ -107,230 +107,247 @@ def report end end - context 'on failing to parse generated source due precondition error' do - let(:generated_node) { left(nil) } - - include_examples 'not successful' - - it 'returns expected report' do - expect(report).to eql(<<~REPORT.strip) - example-identification - Original-Source: - 1.foo - Generated-Source: - 1.foo - Original-Node: - (send - (int 1) :foo) - Generated-Node: - undefined - REPORT - end - end - - context 'on failing to parse original source' do - let(:original_node) { exception } - - include_examples 'not successful' - - it 'returns expected report' do - expect(report).to eql(<<~REPORT.strip) - example-identification - Original-Source: - 1.foo - Generated-Source: - 1.foo - Original-Node: - #{exception_report} - Generated-Node: - (send - (int 1) :foo) - REPORT - end - end - - context 'on failing to generate generated source' do - let(:generated_source) { exception } - - include_examples 'not successful' - - it 'returns expected report' do - expect(report).to eql(<<~REPORT.strip) - example-identification - Original-Source: - 1.foo - Generated-Source: - #{exception_report} - Original-Node: - (send - (int 1) :foo) - Generated-Node: - (send - (int 1) :foo) - REPORT - end - end - - context 'on failing to parse generated source' do - let(:generated_node) { exception } - - include_examples 'not successful' - - it 'returns expected report' do - expect(report).to eql(<<~REPORT.strip) - example-identification - Original-Source: - 1.foo - Generated-Source: - 1.foo - Original-Node: - (send - (int 1) :foo) - Generated-Node: - #{exception_report} - REPORT - end - end - - context 'on generating different node' do - let(:generated_node) { right(s(:send, s(:int, 1), :bar)) } - - include_examples 'not successful' - - it 'returns expected report' do - diff = [ - Unparser::Color::NONE.format(" (send\n"), - Unparser::Color::RED.format("- (int 1) :foo)\n"), - Unparser::Color::GREEN.format("+ (int 1) :bar)\n") - ] - - expect(report).to eql(<<~'REPORT' + diff.join) - example-identification - Original-Source: - 1.foo - Generated-Source: - 1.foo - Original-Node: - (send - (int 1) :foo) - Generated-Node: - (send - (int 1) :bar) - Node-Diff: - @@ -1,2 +1,2 @@ - REPORT - end - end - - describe '.from_path' do - def apply - described_class.from_path(path) - end - - let(:path) { instance_double(Pathname, read: source, to_s: '/some/file') } - let(:source) { 'true' } - - it 'returns expected validator' do - expect(apply).to eql( - described_class.new( - generated_node: right(s(:true)), - generated_source: right(source), - identification: '/some/file', - original_node: right(s(:true)), - original_source: right(source) - ) - ) - end - end - - describe '.from_string' do - def apply - described_class.from_string(source) - end - - let(:attributes) do - { - generated_node: right(s(:true)), - generated_source: right(source), - identification: '(string)', - original_node: right(s(:true)), - original_source: right(source) - } - end - - context 'on valid original source' do - let(:source) { 'true' } - - it 'returns expected validator' do - expect(apply).to eql(described_class.new(attributes)) - end - - context 'with unparsing error' do - let(:exception) { RuntimeError.new('example-error') } - - before do - allow(Unparser).to receive(:unparse).and_raise(exception) - end - - it 'returns expected validator' do - validator = apply - - expect(validator.generated_node).to eql(left(nil)) - expect(validator.generated_source.from_left.class).to be(RuntimeError) - expect(validator.original_source).to eql(right(source)) - expect(validator.original_node).to eql(right(s(:true))) - end - end - end - - context 'on invalid original source' do - let(:source) { '(' } - - it 'returns expected validator' do - validator = apply - - expect(validator.generated_node).to eql(left(nil)) - expect(validator.generated_source).to eql(left(nil)) - expect(validator.original_source).to eql(right(source)) - expect(validator.original_node.from_left.class).to be(Parser::SyntaxError) - end - end - end - - describe '.from_node' do - def apply - described_class.from_node(node) - end - - let(:attributes) do - { - generated_node: right(s(:true)), - generated_source: right('true'), - identification: '(string)', - original_node: right(node), - original_source: right('true') - } - end - - context 'on valid original node' do - let(:node) { s(:true) } - - it 'returns expected validator' do - expect(apply).to eql(described_class.new(attributes)) - end - end - - context 'on invalid original node' do - let(:node) { s(:foo) } - - it 'returns expected validator' do - validator = apply - - expect(validator.generated_node).to eql(left(nil)) - expect(validator.generated_source.lmap(&:inspect)).to eql(left(Unparser::UnknownNodeError.new('Unknown node type: :foo').inspect)) - expect(validator.original_source).to eql(validator.generated_source) - expect(validator.original_node).to eql(right(node)) - end - end - end +# context 'on failing to parse generated source due precondition error' do +# let(:generated_node) { left(nil) } + +# include_examples 'not successful' + +# it 'returns expected report' do +# expect(report).to eql(<<~REPORT.strip) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# 1.foo +# Original-Node: +# (send +# (int 1) :foo) +# Generated-Node: +# undefined +# REPORT +# end +# end + +# context 'on failing to parse original source' do +# let(:original_node) { exception } + +# include_examples 'not successful' + +# it 'returns expected report' do +# expect(report).to eql(<<~REPORT.strip) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# 1.foo +# Original-Node: +# #{exception_report} +# Generated-Node: +# (send +# (int 1) :foo) +# REPORT +# end +# end + +# context 'on failing to generate generated source' do +# let(:generated_source) { exception } + +# include_examples 'not successful' + +# it 'returns expected report' do +# expect(report).to eql(<<~REPORT.strip) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# #{exception_report} +# Original-Node: +# (send +# (int 1) :foo) +# Generated-Node: +# (send +# (int 1) :foo) +# REPORT +# end +# end + +# context 'on failing to parse generated source' do +# let(:generated_node) { exception } + +# include_examples 'not successful' + +# it 'returns expected report' do +# expect(report).to eql(<<~REPORT.strip) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# 1.foo +# Original-Node: +# (send +# (int 1) :foo) +# Generated-Node: +# #{exception_report} +# REPORT +# end +# end + +# context 'on generating different node' do +# let(:generated_node) { right(s(:send, s(:int, 1), :bar)) } + +# include_examples 'not successful' + +# it 'returns expected report' do +# diff = [ +# Unparser::Color::NONE.format(" (send\n"), +# Unparser::Color::RED.format("- (int 1) :foo)\n"), +# Unparser::Color::GREEN.format("+ (int 1) :bar)\n") +# ] + +# expect(report).to eql(<<~'REPORT' + diff.join) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# 1.foo +# Original-Node: +# (send +# (int 1) :foo) +# Generated-Node: +# (send +# (int 1) :bar) +# Node-Diff: +# @@ -1,2 +1,2 @@ +# REPORT +# end +# end +# expect(report).to eql(<<~'REPORT' + diff.join) +# example-identification +# Original-Source: +# 1.foo +# Generated-Source: +# 1.foo +# Original-Node: +# (send +# (int 1) :foo) +# Generated-Node: +# (send +# (int 1) :bar) +# Node-Diff: +# @@ -1,3 +1,3 @@ +# REPORT +# end +# end + +# describe '.from_path' do +# def apply +# described_class.from_path(path) +# end + +# let(:path) { instance_double(Pathname, read: source, to_s: '/some/file') } +# let(:source) { 'true' } + +# it 'returns expected validator' do +# expect(apply).to eql( +# described_class.new( +# generated_node: right(s(:true)), +# generated_source: right(source), +# identification: '/some/file', +# original_node: right(s(:true)), +# original_source: right(source) +# ) +# ) +# end +# end + +# describe '.from_string' do +# def apply +# described_class.from_string(source) +# end + +# let(:attributes) do +# { +# generated_node: right(s(:true)), +# generated_source: right(source), +# identification: '(string)', +# original_node: right(s(:true)), +# original_source: right(source) +# } +# end + +# context 'on valid original source' do +# let(:source) { 'true' } + +# it 'returns expected validator' do +# expect(apply).to eql(described_class.new(attributes)) +# end + +# context 'with unparsing error' do +# let(:exception) { RuntimeError.new('example-error') } + +# before do +# allow(Unparser).to receive(:unparse).and_raise(exception) +# end + +# it 'returns expected validator' do +# validator = apply + +# expect(validator.generated_node).to eql(left(nil)) +# expect(validator.generated_source.from_left.class).to be(RuntimeError) +# expect(validator.original_source).to eql(right(source)) +# expect(validator.original_node).to eql(right(s(:true))) +# end +# end +# end + +# context 'on invalid original source' do +# let(:source) { '(' } + +# it 'returns expected validator' do +# validator = apply + +# expect(validator.generated_node).to eql(left(nil)) +# expect(validator.generated_source).to eql(left(nil)) +# expect(validator.original_source).to eql(right(source)) +# expect(validator.original_node.from_left.class).to be(Parser::SyntaxError) +# end +# end +# end + +# describe '.from_node' do +# def apply +# described_class.from_node(node) +# end + +# let(:attributes) do +# { +# generated_node: right(s(:true)), +# generated_source: right('true'), +# identification: '(string)', +# original_node: right(node), +# original_source: right('true') +# } +# end + +# context 'on valid original node' do +# let(:node) { s(:true) } + +# it 'returns expected validator' do +# expect(apply).to eql(described_class.new(attributes)) +# end +# end + +# context 'on invalid original node' do +# let(:node) { s(:foo) } + +# it 'returns expected validator' do +# validator = apply + +# expect(validator.generated_node).to eql(left(nil)) +# expect(validator.generated_source.lmap(&:inspect)).to eql(left(Unparser::UnknownNodeError.new('Unknown node type: :foo').inspect)) +# expect(validator.original_source).to eql(validator.generated_source) +# expect(validator.original_node).to eql(right(node)) +# end +# end +# end end diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 82a15dcc..1ffd716f 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -78,16 +78,16 @@ def apply end end - describe '.parse_either' do + context '.parse_ast_either' do def apply - described_class.parse_either(source) + described_class.parse_ast_either(source) end context 'on present source' do let(:source) { 'self[1]=2' } it 'returns right value with expected AST' do - expect(apply).to eql(right(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2)))) + expect(apply.fmap(&:node)).to eql(right(s(:indexasgn, s(:self), s(:int, 1), s(:int, 2)))) end end @@ -95,7 +95,7 @@ def apply let(:source) { '' } it 'returns right value with nil' do - expect(apply).to eql(right(nil)) + expect(apply.fmap(&:node)).to eql(right(nil)) end end @@ -120,9 +120,8 @@ def apply context 'on successful validation' do context 'with comments' do def apply - Unparser.unparse_validate( - *Unparser.parser.parse_with_comments(Unparser.buffer('true # foo')) - ) + node, comments = Unparser.parser.parse_with_comments(Unparser.buffer('true # foo')) + Unparser.unparse_validate(node, comments:) end it 'returns right value with generated source' do @@ -152,6 +151,44 @@ def apply end end + describe '.unparse_ast_either' do + def apply + described_class.unparse_ast_either(ast) + end + + let(:ast) do + described_class::AST.new( + node: node, + comments: [], + explicit_encoding: nil, + static_local_variables: Set.new + ) + end + + context 'on valid node' do + let(:node) { s(:true) } + + it 'returns expected source' do + expect(apply).to eql(right('true')) + end + end + + context 'on invalid node' do + let(:node) { s(:unsupported) } + + it 'returns expected error' do + expect(apply.lmap { |value| [value.class, value.message] }).to eql( + left( + [ + described_class::UnknownNodeError, + 'Unknown node type: :unsupported' + ] + ) + ) + end + end + end + describe '.unparse' do context 'on unknown node type' do def apply @@ -167,9 +204,18 @@ def apply ) end end - end - describe '.unparse' do + context 'with comments' do + def apply + node, comments = Unparser.parser.parse_with_comments(Unparser.buffer('true # foo')) + Unparser.unparse(node, comments:) + end + + it 'returns right value with generated source' do + expect(apply).to eql('true # foo') + end + end + def parser Unparser.parser end @@ -183,16 +229,16 @@ def parse_with_comments(string) end def assert_generates_from_string(parser, string, expected) - ast_with_comments = parse_with_comments(string) - assert_generates_from_ast(parser, ast_with_comments, expected.chomp) + node, comments = parse_with_comments(string) + assert_generates_from_ast(parser, node, comments, expected.chomp) end - def assert_generates_from_ast(parser, ast_with_comments, expected) - generated = Unparser.unparse(*ast_with_comments).chomp + def assert_generates_from_ast(parser, node, comments, expected) + generated = Unparser.unparse(node, comments: comments).chomp expect(generated).to eql(expected) ast, comments = parse_with_comments(generated) - expect(ast).to eql(ast_with_comments.first) - expect(Unparser.unparse(ast, comments).chomp).to eql(expected) + expect(ast).to eql(ast) + expect(Unparser.unparse(ast, comments:).chomp).to eql(expected) end def self.assert_generates(input, expected) @@ -208,7 +254,7 @@ def self.assert_generates(input, expected) def self.assert_source(string) it 'round trips' do ast, comments = parse_with_comments(string) - generated = Unparser.unparse(ast, comments).chomp + generated = Unparser.unparse(ast, comments:).chomp expect(generated).to eql(string.chomp) generated_ast, _comments = parse_with_comments(generated) expect(ast == generated_ast).to be(true) @@ -391,6 +437,18 @@ def noop end RUBY + assert_source(<<~'RUBY') + def foo(bar) + bar() + end + RUBY + + assert_source(<<~'RUBY') + foo { |bar| + bar() + } + RUBY + # Test Symbol#inspect Ruby bug: https://bugs.ruby-lang.org/issues/18905 assert_source(':"@="') assert_source(':"$$$$="') diff --git a/test/corpus/literal/assignment.rb b/test/corpus/literal/assignment.rb index 84a74e89..57c430a3 100644 --- a/test/corpus/literal/assignment.rb +++ b/test/corpus/literal/assignment.rb @@ -36,18 +36,8 @@ x[%()] = bar a[%()] ||= bar @a ||= %() -x = <<-HEREDOC - #{} -HEREDOC -x.x=<<-HEREDOC - #{} -HEREDOC -x[] = <<-HEREDOC - #{} -HEREDOC -a[<<-HEREDOC] ||= bar - #{} -HEREDOC -@a ||= <<-HEREDOC - #{} -HEREDOC +x = " #{}\n" +x.x=" #{}\n" +x[] = " #{}\n" +a[" #{}\n"] ||= bar +@a ||= " #{}\n" diff --git a/test/corpus/literal/block.rb b/test/corpus/literal/block.rb index b2baf1dc..a953def4 100644 --- a/test/corpus/literal/block.rb +++ b/test/corpus/literal/block.rb @@ -91,6 +91,10 @@ rescue ensure end + +begin +rescue => self&.a +end bar { _1 + _2 } diff --git a/test/corpus/literal/def.rb b/test/corpus/literal/def.rb index e6c4e25c..1676cca5 100644 --- a/test/corpus/literal/def.rb +++ b/test/corpus/literal/def.rb @@ -124,9 +124,7 @@ def foo(bar:, baz: "value") end def f - <<-HEREDOC - #{} - HEREDOC + " #{}\n" end def f diff --git a/test/corpus/literal/dstr.rb b/test/corpus/literal/dstr.rb index 8a912d28..a610c799 100644 --- a/test/corpus/literal/dstr.rb +++ b/test/corpus/literal/dstr.rb @@ -1,37 +1,49 @@ +"foo\n" "#{baz}\n" "bar\n" +"#{baz}" "foo\n" "bar\n" +"foo +bar\n" +%() +"a +b +c\n" +"a{foo}n" +"a\n#{foo} +b\n" if true "#{}a" end if true - <<-HEREDOC -a -#{}a -b - HEREDOC - x + "a\n#{}a ++b\n" end -<<-HEREDOC -\#{}\#{} -#{} -#{} -#{} -HEREDOC -<<-HEREDOC rescue nil -#{} -a -HEREDOC +"\#{}\#{}\n#{}\n#{}\n#{}\n" +"#{} +a\n" rescue nil "a#$1" "a#$a" "a#@a" "a#@@a" if true - return <<-HEREDOC - #{42} - HEREDOC + return " #{42}\n" end -foo(<<-HEREDOC) - #{bar} +foo(" #{bar}\n") +foo(" #{bar}\n") { |x| +} +"\"\" +\n#{}\n" +"<#{}#{}>#{}" +<<-HEREDOC + "#{a}, + a + #{b} + b + #{c}" HEREDOC -foo(<<-HEREDOC) { |x| - #{bar} +<<-HEREDOC + a + b + \#{}:\#{} + #{} + end + f HEREDOC -} diff --git a/test/corpus/literal/for.rb b/test/corpus/literal/for.rb index 4c19a352..250a9f29 100644 --- a/test/corpus/literal/for.rb +++ b/test/corpus/literal/for.rb @@ -10,3 +10,5 @@ for (a, b) in bar do baz end +for foo[] in m do +end diff --git a/test/corpus/literal/heredoc.rb b/test/corpus/literal/heredoc.rb new file mode 100644 index 00000000..80024945 --- /dev/null +++ b/test/corpus/literal/heredoc.rb @@ -0,0 +1,109 @@ +<<-HEREDOC.foo(//, nil) +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +line_9 +HEREDOC +<<-HEREDOC && bar +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +{ bar => 1, foo => <<-HEREDOC } +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +foo(bar: <<-HEREDOC) +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +[<<-HEREDOC] +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +foo(<<-HEREDOC) { + a + + b + f + e + + d + f +HEREDOC +} +foo = <<-HEREDOC +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +foo(<<-HEREDOC) +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +<<-HEREDOC +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +"segment_1" "segment_2" "segment_3" "segment_4" +foo[<<-HEREDOC] +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +<<-HEREDOC +a b + a + "$! }:\#{}" + #{} +HEREDOC diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 2fc7cd1d..3dbb2a2a 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -1,15 +1,9 @@ -{ "foo" => <<-HEREDOC, "bar" => :baz } - #{} -HEREDOC +{ "foo" => " #{}\n", "bar" => :baz } { "foo" => %(), "bar" => :baz } ["foo", %()] -a(<<-HEREDOC).a - #{} -HEREDOC +a(" #{}\n").a a(%()).a -{ "foo" => <<-HEREDOC, **baz } - #{} -HEREDOC +{ "foo" => " #{}\n", **baz } { "foo" => %(), **baz } "#@a #@@a #$a" 0 @@ -46,15 +40,6 @@ :"A B" :"A\"B" :"" -/foo/ -/[^-+',.\/:@[:alnum:]\[\]]+/ -/foo#{@bar}/ -/foo#{@bar}/imx -/#{"\u0000"}/ -/\n/ -/\n/ -/\n/x -/\/\//x :"foo#{bar}baz" :"#{"foo"}" (0.0 / 0.0)..1 @@ -78,11 +63,9 @@ { a: :a } { :"a b" => 1 } { :-@ => 1 } -"#{} -#{}\na" +"#{}\n#{}\na" foo { - "#{} -#{}\na" + "#{}\n#{}\na" } :"a\\ b" diff --git a/test/corpus/literal/regexp.rb b/test/corpus/literal/regexp.rb new file mode 100644 index 00000000..4f9554bf --- /dev/null +++ b/test/corpus/literal/regexp.rb @@ -0,0 +1,32 @@ +// +/foo/ +/#{foo}/ +/#{<<-HEREDOC}/ +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC +%r{[^-+',./:@[:alnum:]\[\]]+} +/foo/ +/foo#{@bar}/ +/foo#{@bar}/imx +/#{"\u0000"}/ +/\n/ +/\n/ +/\n/x +%r{//}x +/ +/ +/\c*a/ +/a +/ +/\c*a\c*/ +/\c*\c*\c*/ +/ +a +/ diff --git a/test/corpus/literal/single-heredoc.rb b/test/corpus/literal/single-heredoc.rb new file mode 100644 index 00000000..64c833a6 --- /dev/null +++ b/test/corpus/literal/single-heredoc.rb @@ -0,0 +1,10 @@ +<<-HEREDOC +line_1 +line_2 +line_3 +line_4 +line_5 +line_6 +line_7 +line_8 +HEREDOC diff --git a/test/corpus/semantic/encoding/binary-hex-escaped.rb b/test/corpus/semantic/encoding/binary-hex-escaped.rb new file mode 100644 index 00000000..e44b12c9 --- /dev/null +++ b/test/corpus/semantic/encoding/binary-hex-escaped.rb @@ -0,0 +1,2 @@ +# -*- encoding: binary -*- +"\xFF" diff --git a/test/corpus/semantic/encoding/binary-utf-8-escaped.rb b/test/corpus/semantic/encoding/binary-utf-8-escaped.rb new file mode 100644 index 00000000..8396356c --- /dev/null +++ b/test/corpus/semantic/encoding/binary-utf-8-escaped.rb @@ -0,0 +1,2 @@ +# -*- encoding: binary -*- +"\u{3042}" diff --git a/test/corpus/semantic/encoding/binary.rb b/test/corpus/semantic/encoding/binary.rb new file mode 100644 index 00000000..f2c9a8fe --- /dev/null +++ b/test/corpus/semantic/encoding/binary.rb @@ -0,0 +1,2 @@ +# -*- encoding: binary -*- +"\xC0#{}" diff --git a/test/corpus/semantic/encoding/utf-8-non-printable.rb b/test/corpus/semantic/encoding/utf-8-non-printable.rb new file mode 100644 index 00000000..d9a7d381 --- /dev/null +++ b/test/corpus/semantic/encoding/utf-8-non-printable.rb @@ -0,0 +1,2 @@ +# encoding: utf-8 +'\1' diff --git a/test/corpus/semantic/kwbegin.rb b/test/corpus/semantic/kwbegin.rb index d275a96a..fa757890 100644 --- a/test/corpus/semantic/kwbegin.rb +++ b/test/corpus/semantic/kwbegin.rb @@ -40,3 +40,19 @@ ensure d end + +begin +rescue => self.foo +end + +begin +rescue => A.foo +end + +begin +rescue => A[i] +end + +begin +rescue => A[] +end diff --git a/test/corpus/semantic/regexp.rb b/test/corpus/semantic/regexp.rb new file mode 100644 index 00000000..8043e8d9 --- /dev/null +++ b/test/corpus/semantic/regexp.rb @@ -0,0 +1,4 @@ +if foo + /\n +/ +end diff --git a/test/corpus/semantic/rescue.rb b/test/corpus/semantic/rescue.rb new file mode 100644 index 00000000..d4fdf30d --- /dev/null +++ b/test/corpus/semantic/rescue.rb @@ -0,0 +1,5 @@ +module M + raise +rescue => e + e +end diff --git a/unparser.gemspec b/unparser.gemspec index c4d256e7..f2e490af 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.6.15' + gem.version = '0.7.0' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' @@ -23,7 +23,6 @@ Gem::Specification.new do |gem| gem.extra_rdoc_files = %w[README.md] gem.executables = %w[unparser] - gem.required_ruby_version = '>= 3.1' gem.add_dependency('diff-lcs', '~> 1.6') From ac3d3be7432a582f9dc2d11518586652e0bb2896 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 16 Mar 2025 05:32:32 +0000 Subject: [PATCH 232/256] Prepare release --- Changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e930ef3c..19678ce0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# v0.7.0 2024-09-16 +# v0.7.0 2025-03-16 [#366](https://github.com/mbj/unparser/pull/366) From 0bfd9bcb8c7b1221310565a9b53b5cce9e3c37be Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 23 Mar 2025 01:11:44 +0000 Subject: [PATCH 233/256] Upgrade dependencies --- .github/workflows/ci.yml | 10 +++++----- Gemfile | 2 -- Gemfile.lock | 36 +++++++++++++++--------------------- README.md | 2 +- unparser.gemspec | 4 ++-- 5 files changed, 23 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c697391d..c1f930ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.1, ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 diff --git a/Gemfile b/Gemfile index 5dfa95a6..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,4 @@ source 'https://rubygems.org' -gem 'mutant', github: 'mbj/mutant', ref: '152465f02b2c4a18b80f28fee120f58d71a41391' - gemspec diff --git a/Gemfile.lock b/Gemfile.lock index 88ed81c5..97e40ace 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant.git - revision: 152465f02b2c4a18b80f28fee120f58d71a41391 - ref: 152465f02b2c4a18b80f28fee120f58d71a41391 - specs: - mutant (0.12.4) - diff-lcs (~> 1.3) - parser (~> 3.3.0) - regexp_parser (~> 2.9.0) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.7.0) - PATH remote: . specs: @@ -20,16 +8,22 @@ PATH GEM remote: https://rubygems.org/ specs: - ast (2.4.2) + ast (2.4.3) diff-lcs (1.6.0) json (2.10.2) language_server-protocol (3.17.0.4) lint_roller (1.1.0) - mutant-rspec (0.12.4) - mutant (= 0.12.4) + mutant (0.13.0) + diff-lcs (~> 1.3) + parser (~> 3.3.0) + regexp_parser (~> 2.9.0) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.7.0) + mutant-rspec (0.13.0) + mutant (= 0.13.0) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.26.3) - parser (3.3.7.1) + parser (3.3.7.2) ast (~> 2.4.1) racc racc (1.8.1) @@ -62,12 +56,12 @@ GEM rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.38.1) - parser (>= 3.3.1.0) + rubocop-ast (1.41.0) + parser (>= 3.3.7.2) rubocop-packaging (0.5.2) rubocop (>= 1.33, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11934) + sorbet-runtime (0.5.11953) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) @@ -77,8 +71,8 @@ PLATFORMS x86_64-linux DEPENDENCIES - mutant! - mutant-rspec (~> 0.12.4) + mutant (~> 0.13.0) + mutant-rspec (~> 0.13.0) rspec (~> 3.13) rspec-core (~> 3.13) rspec-its (~> 1.3.0) diff --git a/README.md b/README.md index 3ee04936..9189ec3a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The following constraints apply: * No support for macruby extensions * Only support for the [modern AST](https://github.com/whitequark/parser/#usage) format -* Only support for Ruby >= 3.1 +* Only support for Ruby >= 3.2 Notable Users: diff --git a/unparser.gemspec b/unparser.gemspec index f2e490af..eb529226 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -28,8 +28,8 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.6') gem.add_dependency('parser', '>= 3.3.0') - gem.add_development_dependency('mutant', '~> 0.12.4') - gem.add_development_dependency('mutant-rspec', '~> 0.12.4') + gem.add_development_dependency('mutant', '~> 0.13.0') + gem.add_development_dependency('mutant-rspec', '~> 0.13.0') gem.add_development_dependency('rspec', '~> 3.13') gem.add_development_dependency('rspec-core', '~> 3.13') gem.add_development_dependency('rspec-its', '~> 1.3.0') From cfa49999b7182f15a9b8e68c7082e6925c1a62fa Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Tue, 22 Apr 2025 20:28:23 +0300 Subject: [PATCH 234/256] Update dependencies --- .rubocop.yml | 1 + Gemfile.lock | 31 +++++++++++++++++-------------- lib/unparser/color.rb | 4 ++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 85fb9477..6844561f 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,6 +10,7 @@ AllCops: - tmp/**/* - vendor/**/* NewCops: enable + SuggestExtensions: false # Avoid parameter lists longer than five parameters. Metrics/ParameterLists: diff --git a/Gemfile.lock b/Gemfile.lock index 97e40ace..f3602da2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,26 +9,27 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - diff-lcs (1.6.0) + diff-lcs (1.6.1) json (2.10.2) language_server-protocol (3.17.0.4) lint_roller (1.1.0) - mutant (0.13.0) + mutant (0.13.1) diff-lcs (~> 1.3) parser (~> 3.3.0) - regexp_parser (~> 2.9.0) + regexp_parser (~> 2.10) sorbet-runtime (~> 0.5.0) unparser (~> 0.7.0) - mutant-rspec (0.13.0) - mutant (= 0.13.0) + mutant-rspec (0.13.1) + mutant (= 0.13.1) rspec-core (>= 3.8.0, < 4.0.0) - parallel (1.26.3) - parser (3.3.7.2) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) racc + prism (1.4.0) racc (1.8.1) rainbow (3.1.1) - regexp_parser (2.9.3) + regexp_parser (2.10.0) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) @@ -45,7 +46,7 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.2) - rubocop (1.74.0) + rubocop (1.75.3) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -53,15 +54,17 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.38.0, < 2.0) + rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.41.0) + rubocop-ast (1.44.1) parser (>= 3.3.7.2) - rubocop-packaging (0.5.2) - rubocop (>= 1.33, < 2.0) + prism (~> 1.4) + rubocop-packaging (0.6.0) + lint_roller (~> 1.1.0) + rubocop (>= 1.72.1, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.11953) + sorbet-runtime (0.5.12028) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) diff --git a/lib/unparser/color.rb b/lib/unparser/color.rb index 9120f8c4..7db7ad50 100644 --- a/lib/unparser/color.rb +++ b/lib/unparser/color.rb @@ -30,10 +30,10 @@ def format(text) # Well rubocop you are static so you do not have a clue here ;) # rubocop:disable Style/RedundantInitialize - # rubocop:disable Style/MissingSuper + # rubocop:disable Lint/MissingSuper def initialize; end # rubocop:enable Style/RedundantInitialize - # rubocop:enable Style/MissingSuper + # rubocop:enable Lint/MissingSuper end.new From d828d08e5a701dea146768a9d8e9b5e584685f0f Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 24 Apr 2025 01:50:31 +0300 Subject: [PATCH 235/256] Nit: replace obsolete master with zeitgeisty main --- README.md | 2 +- unparser.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9189ec3a..1b05e1f5 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Unparser currently successfully round trips almost all ruby code around. Using R If there is a non round trippable example that is NOT subjected to known [Limitations](#limitations). please report a bug. -On CI unparser is currently tested against rubyspec with minor [excludes](https://github.com/mbj/unparser/blob/master/spec/integrations.yml). +On CI unparser is currently tested against rubyspec with minor [excludes](https://github.com/mbj/unparser/blob/main/spec/integrations.yml). Limitations: ------------ diff --git a/unparser.gemspec b/unparser.gemspec index eb529226..ef6806da 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |gem| gem.metadata = { 'bug_tracker_uri' => 'https://github.com/mbj/unparser/issues', - 'changelog_uri' => 'https://github.com/mbj/unparser/blob/master/Changelog.md', + 'changelog_uri' => 'https://github.com/mbj/unparser/blob/main/Changelog.md', 'funding_uri' => 'https://github.com/sponsors/mbj', 'source_code_uri' => 'https://github.com/mbj/unparser', 'rubygems_mfa_required' => 'true' From 8d813e21dba2862ca5bf929253e20dc45a120a00 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 30 Apr 2025 13:40:19 +0300 Subject: [PATCH 236/256] Fix `and` and `or` operators RT broken associativity Extracted from https://github.com/mbj/unparser/pull/387 Without this change, the following code snippets ```ruby a or b and c ``` and ```ruby a[:x] = b[:x] || c[:x] || d(:new) ``` do not round trip. --- lib/unparser/writer/binary.rb | 10 +++++++--- test/corpus/semantic/and.rb | 10 ++++++++++ test/corpus/semantic/or.rb | 1 + 3 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 test/corpus/semantic/or.rb diff --git a/lib/unparser/writer/binary.rb b/lib/unparser/writer/binary.rb index db2025ab..907b622a 100644 --- a/lib/unparser/writer/binary.rb +++ b/lib/unparser/writer/binary.rb @@ -52,9 +52,13 @@ def symbol_name end def dispatch - left_emitter.write_to_buffer - write(' ', MAP.fetch(effective_symbol), ' ') - visit(right) + if node.type.eql?(:and) && left.type.equal?(:or) + emit_with(KEYWORD_TOKENS) + else + left_emitter.write_to_buffer + write(' ', MAP.fetch(effective_symbol), ' ') + visit(right) + end end private diff --git a/test/corpus/semantic/and.rb b/test/corpus/semantic/and.rb index 43d17124..bdfa8bcd 100644 --- a/test/corpus/semantic/and.rb +++ b/test/corpus/semantic/and.rb @@ -6,3 +6,13 @@ if a...b and c...d end + +a and b and c +(a and b) and c +a and (b and c) +a and b or c +(a and b) or c +a and (b or c) +a or b and c +(a or b) and c +a or (b and c) diff --git a/test/corpus/semantic/or.rb b/test/corpus/semantic/or.rb new file mode 100644 index 00000000..5d404df1 --- /dev/null +++ b/test/corpus/semantic/or.rb @@ -0,0 +1 @@ +a[:x] = b[:x] || c[:x] || d(:new) From 6e174feecd7c908e0c262c5d97eff952ceea143a Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 30 Apr 2025 14:30:33 +0300 Subject: [PATCH 237/256] Fix README `parse_with_comments` examples I was playing with comments-preserving unparsing and it seems like README examples uses invalid API BTW is there a reason there's no direct `Unparser.parse_with_comments` method? --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1b05e1f5..b37568cc 100644 --- a/README.md +++ b/README.md @@ -44,9 +44,9 @@ To preserve the comments from the source: require 'parser/current' require 'unparser' -ast, comments = Unparser.parse_with_comments('your(ruby(code)) # with comments') +ast, comments = Unparser.parser.parse_with_comments(Unparser.buffer('your(ruby(code)) # with comments')) -Unparser.unparse(ast, comments) # => 'your(ruby(code)) # with comments' +Unparser.unparse(ast, comments: comments) # => 'your(ruby(code)) # with comments' ``` Passing in manually constructed AST: From 237c4d64fe226de2440a0094f98f7a10d9c637c6 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 1 May 2025 14:42:49 +0300 Subject: [PATCH 238/256] Fix `rescue` in `class << self` unparsing Extracted from https://github.com/mbj/unparser/pull/387 Without this change, the following code snippet ```ruby class A class << self rescue else ensure end end ``` leads to ```bash ``` error during RT --- lib/unparser.rb | 1 + lib/unparser/emitter/ensure.rb | 16 ++++++++++++++++ test/corpus/semantic/rescue.rb | 8 ++++++++ 3 files changed, 25 insertions(+) create mode 100644 lib/unparser/emitter/ensure.rb diff --git a/lib/unparser.rb b/lib/unparser.rb index 4130d3b5..8e204716 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -250,6 +250,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/defined' require 'unparser/emitter/dstr' require 'unparser/emitter/dsym' +require 'unparser/emitter/ensure' require 'unparser/emitter/flipflop' require 'unparser/emitter/float' require 'unparser/emitter/flow_modifier' diff --git a/lib/unparser/emitter/ensure.rb b/lib/unparser/emitter/ensure.rb new file mode 100644 index 00000000..747e7800 --- /dev/null +++ b/lib/unparser/emitter/ensure.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Unparser + class Emitter + # Emitter for ensure nodes + class Ensure < self + handle :ensure + + private + + def dispatch + emit_ensure(node) + end + end # Ensure + end # Emitter +end # Unparser diff --git a/test/corpus/semantic/rescue.rb b/test/corpus/semantic/rescue.rb index d4fdf30d..7cc49b98 100644 --- a/test/corpus/semantic/rescue.rb +++ b/test/corpus/semantic/rescue.rb @@ -3,3 +3,11 @@ module M rescue => e e end +class A + class << self + rescue + else + ensure + end +end + From 72c5a6d660dfefb450ec9e7c244c78d1e74ad553 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 1 May 2025 19:18:08 +0300 Subject: [PATCH 239/256] Fix extra `begin` within `dsym` edge case Extracted from https://github.com/mbj/unparser/pull/392 ```bash Original-Source: { "#{}": 42 } Generated-Source: { :"#{()}" => 42 } Original-Node: (hash (pair (dsym (begin)) (int 42))) Generated-Node: (hash (pair (dsym (begin (begin))) (int 42))) Node-Diff: @@ -1,5 +1,6 @@ (hash (pair (dsym - (begin)) + (begin + (begin))) (int 42))) Error: (string) ``` --- lib/unparser/emitter/dsym.rb | 2 +- test/corpus/semantic/literal.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/unparser/emitter/dsym.rb b/lib/unparser/emitter/dsym.rb index f8ee4c87..bb4a1a94 100644 --- a/lib/unparser/emitter/dsym.rb +++ b/lib/unparser/emitter/dsym.rb @@ -33,7 +33,7 @@ def emit_str_child(value) def emit_begin_child(component) write('#{') - visit(unwrap_single_begin(component)) + visit(unwrap_single_begin(component)) if component.children.any? write('}') end end # DSym diff --git a/test/corpus/semantic/literal.rb b/test/corpus/semantic/literal.rb index c424db5a..640914f1 100644 --- a/test/corpus/semantic/literal.rb +++ b/test/corpus/semantic/literal.rb @@ -12,3 +12,4 @@ 10.2e10000000000 -10.2e10000000000 w(foo bar) +{ "#{}": {} } From 5e67cf6eea5d8e1ce02161e92bb789010a55aa87 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Sat, 3 May 2025 16:42:55 +0300 Subject: [PATCH 240/256] Fix regexp with gvar unparsing Extracted from https://github.com/mbj/unparser/pull/392 ```bash $ bundle exec bin/unparser -e '/foo #$G/' (string) Original-Source: /foo #$G/ Generated-Source: lib/unparser/buffer.rb:149:in `write' lib/unparser/buffer.rb:55:in `append_without_prefix' unparser/writer/regexp.rb:69:in `write_regular' unparser/writer/regexp.rb:59:in `emit_body' unparser/writer/regexp.rb:45:in `each' unparser/writer/regexp.rb:45:in `block in dispatch' ``` --- lib/unparser/node_helpers.rb | 1 + lib/unparser/writer/regexp.rb | 3 +++ test/corpus/literal/regexp.rb | 2 ++ 3 files changed, 6 insertions(+) diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 060d8045..3419bd20 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -43,6 +43,7 @@ def n?(type, node) dstr empty_else ensure + gvar hash hash_pattern if diff --git a/lib/unparser/writer/regexp.rb b/lib/unparser/writer/regexp.rb index 3cc55395..eac25c71 100644 --- a/lib/unparser/writer/regexp.rb +++ b/lib/unparser/writer/regexp.rb @@ -55,6 +55,9 @@ def emit_body(node) write('#{') node.children.each(&method(:visit)) write('}') + elsif n_gvar?(node) + write('#') + write_regular(node.children.first.to_s) else write_regular(node.children.first) end diff --git a/test/corpus/literal/regexp.rb b/test/corpus/literal/regexp.rb index 4f9554bf..9f479eeb 100644 --- a/test/corpus/literal/regexp.rb +++ b/test/corpus/literal/regexp.rb @@ -30,3 +30,5 @@ / a / +/aaa #{$bbb}/ +/aaa #$bbb/ From bb8478c83b234632aac8c2341c161a9d4717447a Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Mon, 5 May 2025 22:18:22 +0300 Subject: [PATCH 241/256] Fix array pattern unparsing Extracted from https://github.com/mbj/unparser/pull/387 Parser represents %-encoded array patterns with the `ArrayNode`, not `ArrayPatternNode`. We can infer required %-notation variant given array's children elements. ```bash $ bundle exec bin/unparser -e 'a => %i[a b]' (string) Original-Source: a => %i[a b] Generated-Source: a => [:a, :b] Original-Node: (match-pattern (send nil :a) (array (sym :a) (sym :b))) Generated-Node: (match-pattern (send nil :a) (array-pattern (sym :a) (sym :b))) Node-Diff: @@ -1,5 +1,5 @@ (match-pattern (send nil :a) - (array + (array-pattern (sym :a) (sym :b))) Error: (string) ``` --- lib/unparser.rb | 2 + lib/unparser/emitter/in_pattern.rb | 10 ++++- lib/unparser/emitter/match_pattern.rb | 7 +++- lib/unparser/emitter/match_pattern_p.rb | 7 +++- lib/unparser/util.rb | 23 +++++++++++ lib/unparser/writer/array.rb | 51 +++++++++++++++++++++++ spec/unit/unparser/util_spec.rb | 55 +++++++++++++++++++++++++ test/corpus/literal/pattern.rb | 37 +++++++++++++++++ test/corpus/semantic/pattern.rb | 27 ++++++++++++ 9 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 lib/unparser/util.rb create mode 100644 lib/unparser/writer/array.rb create mode 100644 spec/unit/unparser/util_spec.rb create mode 100644 test/corpus/semantic/pattern.rb diff --git a/lib/unparser.rb b/lib/unparser.rb index 8e204716..2373e17f 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -295,6 +295,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/emitter/match_pattern' require 'unparser/emitter/match_pattern_p' require 'unparser/writer' +require 'unparser/writer/array' require 'unparser/writer/binary' require 'unparser/writer/dynamic_string' require 'unparser/writer/regexp' @@ -308,6 +309,7 @@ def self.buffer(source, identification = '(string)') require 'unparser/node_details' require 'unparser/node_details/send' require 'unparser/cli' +require 'unparser/util' require 'unparser/validation' # make it easy for zombie diff --git a/lib/unparser/emitter/in_pattern.rb b/lib/unparser/emitter/in_pattern.rb index b1f0d43d..2f97ed51 100644 --- a/lib/unparser/emitter/in_pattern.rb +++ b/lib/unparser/emitter/in_pattern.rb @@ -16,7 +16,7 @@ def dispatch ws - visit(target) + dispatch_target(target) if unless_guard ws @@ -31,6 +31,14 @@ def dispatch nl end end + + def dispatch_target(target) + if n_array?(target) + writer_with(Writer::Array, node: target).emit_compact + else + visit(target) + end + end end # InPattern end # Emitter end # Unparser diff --git a/lib/unparser/emitter/match_pattern.rb b/lib/unparser/emitter/match_pattern.rb index 5f13a7c7..af847f00 100644 --- a/lib/unparser/emitter/match_pattern.rb +++ b/lib/unparser/emitter/match_pattern.rb @@ -14,7 +14,12 @@ class MatchPattern < self def dispatch visit(target) write(' => ') - visit(pattern) + + if n_array?(pattern) + writer_with(Writer::Array, node: pattern).emit_compact + else + visit(pattern) + end end end # MatchPattern end # Emitter diff --git a/lib/unparser/emitter/match_pattern_p.rb b/lib/unparser/emitter/match_pattern_p.rb index 90b62512..91dc4f5a 100644 --- a/lib/unparser/emitter/match_pattern_p.rb +++ b/lib/unparser/emitter/match_pattern_p.rb @@ -13,7 +13,12 @@ class MatchPatternP < self def dispatch visit(target) write(' in ') - visit(pattern) + + if n_array?(pattern) + writer_with(Writer::Array, node: pattern).emit_compact + else + visit(pattern) + end end end # MatchPatternP end # Emitter diff --git a/lib/unparser/util.rb b/lib/unparser/util.rb new file mode 100644 index 00000000..9a890a60 --- /dev/null +++ b/lib/unparser/util.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Unparser + # Original code before vendoring and reduction from: https://github.com/mbj/mutant/blob/main/lib/mutant/util.rb + module Util + # Error raised by `Util.one` if size is not exactly one + SizeError = Class.new(IndexError) + + # Return only element in array if it contains exactly one member + # + # @param array [Array] + # + # @return [Object] first entry + def self.one(array) + case array + in [value] + value + else + fail SizeError, "expected size to be exactly 1 but size was #{array.size}" + end + end + end +end diff --git a/lib/unparser/writer/array.rb b/lib/unparser/writer/array.rb new file mode 100644 index 00000000..7e80145b --- /dev/null +++ b/lib/unparser/writer/array.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Unparser + module Writer + class Array + include Writer, Adamantium + + MAP = { + dsym: '%I', + sym: '%i', + dstr: '%W', + str: '%w' + }.freeze + private_constant(*constants(false)) + + def emit_compact # rubocop:disable Metrics/AbcSize + children_generic_type = array_elements_generic_type + + write(MAP.fetch(children_generic_type)) + + parentheses('[', ']') do + delimited(children, ' ') do |child| + if n_sym?(child) || n_str?(child) + write(Util.one(child.children).to_s) + else + write('#{') + emitter(unwrap_single_begin(Util.one(child.children))).write_to_buffer + write('}') + end + end + end + end + + private + + def array_elements_generic_type + children_types = children.to_set(&:type) + + if children_types == Set[:sym, :dsym] + :dsym + elsif children_types == Set[:str, :dstr] + :dstr + elsif children_types == Set[] + :sym + else + Util.one(children_types.to_a) + end + end + end # Array + end # Writer +end # Unparser diff --git a/spec/unit/unparser/util_spec.rb b/spec/unit/unparser/util_spec.rb new file mode 100644 index 00000000..ef67775e --- /dev/null +++ b/spec/unit/unparser/util_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +RSpec.describe Unparser::Util, '.one' do + let(:item) { instance_double(Object) } + + def apply + described_class.one(array) + end + + context 'when array has exactly one element' do + context 'and that element is nil' do + let(:array) { [nil] } + + it 'returns nil' do + expect(apply).to be(nil) + end + end + + context 'and that element is false' do + let(:array) { [false] } + + it 'returns false' do + expect(apply).to be(false) + end + end + + context 'and that element is a regular object' do + let(:array) { [item] } + + it 'returns first element' do + expect(apply).to be(item) + end + end + end + + context 'when array is empty' do + let(:array) { [] } + + it 'raises expected error' do + expect { apply } + .to raise_error(described_class::SizeError) + .with_message('expected size to be exactly 1 but size was 0') + end + end + + context 'when array has more than one element' do + let(:array) { [1, 2] } + + it 'raises expected error' do + expect { apply } + .to raise_error(described_class::SizeError) + .with_message('expected size to be exactly 1 but size was 2') + end + end +end diff --git a/test/corpus/literal/pattern.rb b/test/corpus/literal/pattern.rb index ebd5110a..8bbf604b 100644 --- a/test/corpus/literal/pattern.rb +++ b/test/corpus/literal/pattern.rb @@ -43,3 +43,40 @@ 1 => [*] 1 in [*, 42, *] 1 in [*, a, *foo] +a => %i[a b] +a => %w[a b] +a => ["a", "b"] +a => [:a, :b] +a => [:a, "b"] +a => [true, false, nil] +a => {a: 1, b: 2} +a in %i[a b] +a in %w[a b] +a in ["a", "b"] +a in [:a, :b] +a in [:a, "b"] +a in [true, false, nil] +a in {a: 1, b: 2} +case foo +in %i[a b] +end +case foo +in %w[a b] +end +case foo +in [:a, "b"] +end +case foo +in [1, 2] +end +case foo +in [true, false, nil] +end +a => %I[a b #{foo(1)}] +a => %W[a b #{foo(1)}] +case a +in %I[#{1 + 1}] +end +case foo +in %i[a b c $FILE] +end diff --git a/test/corpus/semantic/pattern.rb b/test/corpus/semantic/pattern.rb new file mode 100644 index 00000000..b066fa34 --- /dev/null +++ b/test/corpus/semantic/pattern.rb @@ -0,0 +1,27 @@ +case foo +in %s[a b] +end +case foo +in %r[foo] +end +case foo +in %r[/foo.+bar/] +end +case foo +in %r:/foo.+bar/: +end +case foo +in %x(rm -rf /) +end +case foo +in %i[] +end +case foo +in %w[] +end +case foo +in %q[a b c #{foo}] +end +case foo +in %q[a b c $FILE] +end From 0ad0f552c3a3fba430fdc8a3b29634752e93001e Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Tue, 6 May 2025 15:40:37 +0300 Subject: [PATCH 242/256] Fix `resbody` with multiple indexes assignment unparsing Extracted from https://github.com/mbj/unparser/pull/387 --- lib/unparser/writer/resbody.rb | 4 ++-- test/corpus/semantic/rescue.rb | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/unparser/writer/resbody.rb b/lib/unparser/writer/resbody.rb index 2ab0f867..6220e1d2 100644 --- a/lib/unparser/writer/resbody.rb +++ b/lib/unparser/writer/resbody.rb @@ -58,10 +58,10 @@ def write_send_assignment end def write_index_assignment - receiver, index = assignment.children + receiver, *indexes = assignment.children visit(receiver) write('[') - visit(index) if index + delimited(indexes) write(']') end end # Resbody diff --git a/test/corpus/semantic/rescue.rb b/test/corpus/semantic/rescue.rb index 7cc49b98..7c3f92ce 100644 --- a/test/corpus/semantic/rescue.rb +++ b/test/corpus/semantic/rescue.rb @@ -10,4 +10,6 @@ class << self ensure end end - +begin; rescue => A[1]; end +begin; rescue => A[1, 2]; end +begin; rescue => A[1, 2, 3]; end From e90ee27baf47ff1eb971b9aa14f2426ac0ddcd3b Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Tue, 6 May 2025 17:00:33 +0300 Subject: [PATCH 243/256] Fix `rescue` without body unparsing Extracted from https://github.com/mbj/unparser/pull/387 --- lib/unparser/writer/resbody.rb | 2 +- lib/unparser/writer/rescue.rb | 2 +- test/corpus/semantic/rescue.rb | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/unparser/writer/resbody.rb b/lib/unparser/writer/resbody.rb index 6220e1d2..dd6b65a6 100644 --- a/lib/unparser/writer/resbody.rb +++ b/lib/unparser/writer/resbody.rb @@ -15,7 +15,7 @@ class Resbody def emit_postcontrol write(' rescue ') - visit(body) + visit(body) if body end def emit_regular diff --git a/lib/unparser/writer/rescue.rb b/lib/unparser/writer/rescue.rb index 98d473dd..05f78e67 100644 --- a/lib/unparser/writer/rescue.rb +++ b/lib/unparser/writer/rescue.rb @@ -21,7 +21,7 @@ def emit_regular end def emit_postcontrol - visit(body) + visit(body) if body writer_with(Resbody, node: rescue_body).emit_postcontrol end diff --git a/test/corpus/semantic/rescue.rb b/test/corpus/semantic/rescue.rb index 7c3f92ce..65a84106 100644 --- a/test/corpus/semantic/rescue.rb +++ b/test/corpus/semantic/rescue.rb @@ -10,6 +10,9 @@ class << self ensure end end +module A +rescue +end begin; rescue => A[1]; end begin; rescue => A[1, 2]; end begin; rescue => A[1, 2, 3]; end From 83b8a57923916a635e737f14e46c8b451dd5e791 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Tue, 6 May 2025 23:50:46 +0300 Subject: [PATCH 244/256] Fix negated `flipflop` unparsing Extracted from https://github.com/mbj/unparser/pull/387 ```bash bundle exec bin/unparser -e 'not a...b' (string) Original-Source: not a...b Generated-Source: !a...b Original-Node: (send (eflipflop (send nil :a) (send nil :b)) :!) Generated-Node: (erange (send (send nil :a) :!) (send nil :b)) Node-Diff: @@ -1,4 +1,4 @@ -(send - (eflipflop - (send nil :a) - (send nil :b)) :!) +(erange + (send + (send nil :a) :!) + (send nil :b)) ``` --- lib/unparser/node_helpers.rb | 6 ++++++ lib/unparser/writer/send/unary.rb | 11 ++++++++--- test/corpus/literal/flipflop.rb | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 3419bd20..39457420 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -31,6 +31,10 @@ def n?(type, node) node.type.equal?(type) end + def n_flipflop?(node) + n_iflipflop?(node) || n_eflipflop?(node) + end + %i[ arg args @@ -41,12 +45,14 @@ def n?(type, node) cbase const dstr + eflipflop empty_else ensure gvar hash hash_pattern if + iflipflop in_pattern int kwarg diff --git a/lib/unparser/writer/send/unary.rb b/lib/unparser/writer/send/unary.rb index ed409611..5910d521 100644 --- a/lib/unparser/writer/send/unary.rb +++ b/lib/unparser/writer/send/unary.rb @@ -14,11 +14,16 @@ class Unary < self def dispatch name = selector + first_child = children.fetch(0) - write(MAP.fetch(name, name).to_s) + if n_flipflop?(first_child) + write 'not ' + else + write(MAP.fetch(name, name).to_s) - if n_int?(receiver) && selector.equal?(:+@) - write('+') + if n_int?(receiver) && selector.equal?(:+@) + write('+') + end end visit(receiver) diff --git a/test/corpus/literal/flipflop.rb b/test/corpus/literal/flipflop.rb index 139904a5..565755b0 100644 --- a/test/corpus/literal/flipflop.rb +++ b/test/corpus/literal/flipflop.rb @@ -8,3 +8,7 @@ end if foo..; end +not foo..bar +not foo...bar +!(foo..bar) +!(foo...bar) From c7c806c2edc367d4312ac39afac1381d57e91690 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 8 May 2025 00:12:33 +0300 Subject: [PATCH 245/256] Fix `irange`/`erange` edgecases unparsing Extracted from https://github.com/mbj/unparser/pull/387 --- lib/unparser/emitter/range.rb | 25 +++++++++++++++++++++++-- lib/unparser/node_helpers.rb | 6 ++++++ test/corpus/literal/range.rb | 31 +++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/lib/unparser/emitter/range.rb b/lib/unparser/emitter/range.rb index 8db822ef..c8cd7356 100644 --- a/lib/unparser/emitter/range.rb +++ b/lib/unparser/emitter/range.rb @@ -25,9 +25,30 @@ def symbol_name private def dispatch - visit(begin_node) if begin_node + visit_begin_node(begin_node) write(TOKENS.fetch(node.type)) - visit(end_node) if end_node + visit_end_node(end_node) + end + + def visit_begin_node(node) + return unless node + + if n_array?(begin_node) + writer_with(Writer::Array, node: begin_node).emit_compact + else + visit(begin_node) + end + end + + def visit_end_node(node) + return unless node + + write(' ') if n_range?(node) + if n_array?(node) + writer_with(Writer::Array, node: node).emit_compact + else + visit(node) + end end end # Range diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 39457420..6a49be69 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -35,6 +35,10 @@ def n_flipflop?(node) n_iflipflop?(node) || n_eflipflop?(node) end + def n_range?(node) + n_irange?(node) || n_erange?(node) + end + %i[ arg args @@ -47,6 +51,7 @@ def n_flipflop?(node) dstr eflipflop empty_else + erange ensure gvar hash @@ -55,6 +60,7 @@ def n_flipflop?(node) iflipflop in_pattern int + irange kwarg kwargs kwsplat diff --git a/test/corpus/literal/range.rb b/test/corpus/literal/range.rb index ebb334ad..e8f50c8f 100644 --- a/test/corpus/literal/range.rb +++ b/test/corpus/literal/range.rb @@ -3,3 +3,34 @@ (1...) 1...2 (..1) +(...2) +(..2) +foo((1..1)) +1...2 +foo[...2] +{ foo: ...bar } +(1...) +1..2 +{ foo: ..bar } +(1..) +1.. ..1 +1.. and 2 +1.. == 2 +1.. != 2 +1.. === 2 +1.. <=> 2 +1.. =~ 2 +1.. !~ 2 +1.. < 2 +1.. > 2 +1.. <= 2 +1.. >= 2 +1.. << 2 +1.. >> 2 +1..2 +1..-2 +1.. ..1 +1... ...1 +%i[a b]..%i[c d] +..%i[c d] +%i[a b].. From 964230a161c93d956dc967194b3795de9bf22d55 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 8 May 2025 00:23:24 +0300 Subject: [PATCH 246/256] Fix `not` with binary operator unparsing Extracted from https://github.com/mbj/unparser/pull/387 This solution aint perfect (there are still some bugs in this area), but at least it fixes some of them. I'll try to find some time to revisit it later. --- lib/unparser/node_helpers.rb | 2 ++ lib/unparser/writer/send/unary.rb | 4 ++-- test/corpus/semantic/not.rb | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 test/corpus/semantic/not.rb diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 39457420..bbb7c40d 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -36,6 +36,7 @@ def n_flipflop?(node) end %i[ + and arg args array @@ -61,6 +62,7 @@ def n_flipflop?(node) lambda lvar match_rest + or pair rescue send diff --git a/lib/unparser/writer/send/unary.rb b/lib/unparser/writer/send/unary.rb index 5910d521..3111c4e8 100644 --- a/lib/unparser/writer/send/unary.rb +++ b/lib/unparser/writer/send/unary.rb @@ -12,11 +12,11 @@ class Unary < self private_constant(*constants(false)) - def dispatch + def dispatch # rubocop:disable Metrics/AbcSize name = selector first_child = children.fetch(0) - if n_flipflop?(first_child) + if n_flipflop?(first_child) || n_and?(first_child) || n_or?(first_child) write 'not ' else write(MAP.fetch(name, name).to_s) diff --git a/test/corpus/semantic/not.rb b/test/corpus/semantic/not.rb new file mode 100644 index 00000000..79554eba --- /dev/null +++ b/test/corpus/semantic/not.rb @@ -0,0 +1,16 @@ +!(foo and bar) +!(foo or bar) +not(foo and bar) +not(foo or bar) +! foo and bar +! foo or bar +not foo and bar +not foo or bar +!(foo && bar) +!(foo || bar) +not(foo && bar) +not(foo || bar) +! foo && bar +! foo || bar +not foo && bar +not foo || bar From 4c59571f4939236c5d2bc0cf924c0339d5a88fce Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 14 May 2025 22:00:00 +0300 Subject: [PATCH 247/256] Fix `match-pattern-p` with logical operator unparsing ``` $ bundle exec bin/unparser -e 'a in b, and c' warning: parser/current is loading parser/ruby34, which recognizes 3.4.0-dev-compliant syntax, but you are running 3.4.3. Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. (string) Original-Source: a in b, and c Generated-Source: a in [b, ] && c Original-Node: (and (match-pattern-p (send nil :a) (array-pattern-with-tail (match-var :b))) (send nil :c)) Generated-Node: ``` --- lib/unparser/writer/binary.rb | 2 +- test/corpus/semantic/pattern.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/unparser/writer/binary.rb b/lib/unparser/writer/binary.rb index 907b622a..2b9344b5 100644 --- a/lib/unparser/writer/binary.rb +++ b/lib/unparser/writer/binary.rb @@ -39,7 +39,7 @@ class Binary tANDOP: '&&' }.freeze - NEED_KEYWORD = %i[return break next].freeze + NEED_KEYWORD = %i[return break next match_pattern_p].freeze private_constant(*constants(false)) diff --git a/test/corpus/semantic/pattern.rb b/test/corpus/semantic/pattern.rb index b066fa34..35b7950f 100644 --- a/test/corpus/semantic/pattern.rb +++ b/test/corpus/semantic/pattern.rb @@ -25,3 +25,4 @@ case foo in %q[a b c $FILE] end +a in b, and c From 5491a655e1f567a6228131e31cdfdcfbce10dac8 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Wed, 14 May 2025 22:32:46 +0300 Subject: [PATCH 248/256] Fix `resbody` without body unparsing Extracted from Extracted from https://github.com/mbj/unparser/pull/387 ```bash $ bundle exec bin/unparser -e 'module A; x = 1; rescue; end' warning: parser/current is loading parser/ruby34, which recognizes 3.4.0-dev-compliant syntax, but you are running 3.4.3. Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. (string) Original-Source: module A; x = 1; rescue; end Generated-Source: module A x = 1 rescue end Original-Node: (module (const nil :A) (rescue (lvasgn :x (int 1)) (resbody nil nil nil) nil)) Generated-Node: ``` --- lib/unparser/writer/resbody.rb | 9 +++++++-- test/corpus/semantic/rescue.rb | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/unparser/writer/resbody.rb b/lib/unparser/writer/resbody.rb index dd6b65a6..bde3989a 100644 --- a/lib/unparser/writer/resbody.rb +++ b/lib/unparser/writer/resbody.rb @@ -14,8 +14,13 @@ class Resbody children :exception, :assignment, :body def emit_postcontrol - write(' rescue ') - visit(body) if body + if body + write(' rescue ') + visit(body) + else + nl + write('rescue') + end end def emit_regular diff --git a/test/corpus/semantic/rescue.rb b/test/corpus/semantic/rescue.rb index 65a84106..a8d5dffa 100644 --- a/test/corpus/semantic/rescue.rb +++ b/test/corpus/semantic/rescue.rb @@ -16,3 +16,4 @@ module A begin; rescue => A[1]; end begin; rescue => A[1, 2]; end begin; rescue => A[1, 2, 3]; end +module A; x = 1; rescue; end From ba9a6356f05ba731a9965a7991acdbf94696eb15 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 15 May 2025 01:26:15 +0300 Subject: [PATCH 249/256] Fix `irange`/`erange` with missing rhs unparsing Extracted from https://github.com/mbj/unparser/pull/387 --- lib/unparser/generation.rb | 6 ++++++ test/corpus/semantic/range.rb | 7 +++++++ 2 files changed, 13 insertions(+) create mode 100644 test/corpus/semantic/range.rb diff --git a/lib/unparser/generation.rb b/lib/unparser/generation.rb index 412281a5..a3183f3e 100644 --- a/lib/unparser/generation.rb +++ b/lib/unparser/generation.rb @@ -147,6 +147,7 @@ def with_indent(indent:) def emit_body_inner(node) head, *tail = node.children emit_body_member(head) + write(';') if requires_explicit_statement_terminator?(head, tail) tail.each do |child| buffer.ensure_nl @@ -154,9 +155,14 @@ def emit_body_inner(node) nl if EXTRA_NL.include?(child.type) emit_body_member(child) + write(';') if requires_explicit_statement_terminator?(child, tail) end end + def requires_explicit_statement_terminator?(node, nodes_group) + n_range?(node) && node.children.fetch(1).nil? && !node.eql?(nodes_group.fetch(-1)) + end + def emit_body_member(node) if n_rescue?(node) emit_rescue_postcontrol(node) diff --git a/test/corpus/semantic/range.rb b/test/corpus/semantic/range.rb new file mode 100644 index 00000000..b081a375 --- /dev/null +++ b/test/corpus/semantic/range.rb @@ -0,0 +1,7 @@ +1...; +2...; +3...; + +1..; +2..; +3..; From f95ef06de30d2598f2383a48cfa3c6e800fcdbe7 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 15 May 2025 01:43:57 +0300 Subject: [PATCH 250/256] Fix mass assign and `rescue` precedence during unparsing Extracted from https://github.com/mbj/unparser/pull/387 ``` $ bundle exec bin/unparser -e 'foo = 1, 2 rescue nil' warning: parser/current is loading parser/ruby34, which recognizes 3.4.0-dev-compliant syntax, but you are running 3.4.3. Please see https://github.com/whitequark/parser#compatibility-with-ruby-mri. (string) Original-Source: foo = 1, 2 rescue nil Generated-Source: foo = [1, 2] rescue nil Original-Node: (rescue (lvasgn :foo (array (int 1) (int 2))) (resbody nil nil (nil)) nil) Generated-Node: (lvasgn :foo (rescue (array (int 1) (int 2)) (resbody nil nil (nil)) nil)) Node-Diff: @@ -1,7 +1,7 @@ -(rescue - (lvasgn :foo +(lvasgn :foo + (rescue (array (int 1) - (int 2))) - (resbody nil nil - (nil)) nil) + (int 2)) + (resbody nil nil + (nil)) nil)) Error: (string) ``` --- lib/unparser/emitter/assignment.rb | 10 ++++++++++ test/corpus/literal/rescue.rb | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/lib/unparser/emitter/assignment.rb b/lib/unparser/emitter/assignment.rb index 90bc0353..7c173b7f 100644 --- a/lib/unparser/emitter/assignment.rb +++ b/lib/unparser/emitter/assignment.rb @@ -25,6 +25,16 @@ def emit_right if BINARY_OPERATOR.include?(right.type) writer_with(Writer::Binary, node: right).emit_operator + elsif n_array?(right) + emit_array + else + right_emitter.write_to_buffer + end + end + + def emit_array + if right.children.size > 1 + delimited(right.children) else right_emitter.write_to_buffer end diff --git a/test/corpus/literal/rescue.rb b/test/corpus/literal/rescue.rb index a7878168..c87ff883 100644 --- a/test/corpus/literal/rescue.rb +++ b/test/corpus/literal/rescue.rb @@ -1,3 +1,10 @@ foo rescue bar foo rescue return bar x = (foo rescue return bar) +foo = [] rescue nil +foo = 1 rescue nil +foo = [1] rescue nil +foo = 1, 2 rescue nil +(foo = []) rescue nil +(foo = 1) rescue nil +(foo = [1]) rescue nil From 3fa31c3c437bb9c797b31ca6a9e91b6e0838c53d Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 15 May 2025 21:19:50 +0300 Subject: [PATCH 251/256] Fix `masgn` with nested `mlhs` unparsing Extracted from https://github.com/mbj/unparser/pull/387 This one is tricky: looks like we have to always (as a general rule) append comma to mhls and omit them when they are inside `def` node (as an exception). --- lib/unparser/emitter/args.rb | 6 +++++- lib/unparser/emitter/mlhs.rb | 8 +++++++- lib/unparser/node_helpers.rb | 1 + test/corpus/literal/def.rb | 6 ++++++ test/corpus/semantic/masgn.rb | 5 +++++ 5 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 test/corpus/semantic/masgn.rb diff --git a/lib/unparser/emitter/args.rb b/lib/unparser/emitter/args.rb index 5bc34d71..7f28ff22 100644 --- a/lib/unparser/emitter/args.rb +++ b/lib/unparser/emitter/args.rb @@ -13,7 +13,11 @@ def emit_block_arguments end def emit_def_arguments - delimited(normal_arguments) + if children.one? && n_mlhs?(Util.one(children)) + emitter(Util.one(children)).dispatch_def + else + delimited(normal_arguments) + end end def emit_lambda_arguments diff --git a/lib/unparser/emitter/mlhs.rb b/lib/unparser/emitter/mlhs.rb index c8835395..ecde2503 100644 --- a/lib/unparser/emitter/mlhs.rb +++ b/lib/unparser/emitter/mlhs.rb @@ -6,10 +6,16 @@ class Emitter class MLHS < self handle :mlhs - NO_COMMA = %i[arg splat mlhs restarg].freeze + NO_COMMA = %i[arg splat restarg].freeze private_constant(*constants(false)) + def dispatch_def + parentheses do + delimited(children) + end + end + private def dispatch diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 7c943ebc..92895fed 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -68,6 +68,7 @@ def n_range?(node) lambda lvar match_rest + mlhs or pair rescue diff --git a/test/corpus/literal/def.rb b/test/corpus/literal/def.rb index 1676cca5..76ac5403 100644 --- a/test/corpus/literal/def.rb +++ b/test/corpus/literal/def.rb @@ -120,6 +120,12 @@ def foo def f(((a))) end +def f((a, b)) +end + +def f(((a, b))) +end + def foo(bar:, baz: "value") end diff --git a/test/corpus/semantic/masgn.rb b/test/corpus/semantic/masgn.rb new file mode 100644 index 00000000..2119117e --- /dev/null +++ b/test/corpus/semantic/masgn.rb @@ -0,0 +1,5 @@ +(a,), = [] +(a, b), = [] +(a, b), c = [] +((a,),), = [] +((a,),), b = [] From 1e4963e8142b0b1ee3de1696ffb6affb2d837de1 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Tue, 29 Apr 2025 00:34:08 +0300 Subject: [PATCH 252/256] Add `prism` parser support Resolves mbj#385 Related Prism bug: ruby/prism#3540 (merged) `test/corpus/literal/before/34.rb` contains expressions rejected by Prism (and regexp-related bug, see mbj#385 (comment)): ```bash $ ASDF_RUBY_VERSION=3.4.2 ruby --parser prism test/corpus/literal/before/34.rb test/corpus/literal/before/34.rb:1: syntax errors found (SyntaxError) > 1 | retry | ^~~~~ Invalid retry without rescue > 2 | in {"#{"a"}": 1} then | ^~ unexpected 'in', ignoring it | ^~~~ unexpected 'then', ignoring it | ^~~~ unexpected 'then', expecting end-of-input 3 | true 4 | /\c*a/ 5 | /\c*a\c*/ 6 | /\c*\c*\c*/ > 7 | (break foo) || a | ^~~~~~~~~ Invalid break | ^~~~~~~~~ unexpected void value expression > 8 | (return foo) || a | ^~~~~~~~~~ unexpected void value expression > 9 | a = b || break | ^~~~~ Invalid break > 10 | a = b || next | ^~~~ Invalid next > 11 | a || (break foo) | ^~~~~~~~~ Invalid break > 12 | b or break | ^~~~~ Invalid break > 13 | b or next | ^~~~ Invalid next > 14 | break or b | ^~~~~ Invalid break | ^~~~~ unexpected void value expression > 15 | next or b | ^~~~ Invalid next | ^~~~ unexpected void value expression > 16 | return or a | ^~~~~~ unexpected void value expression ``` --- .github/workflows/ci.yml | 10 +- Changelog.md | 6 + Gemfile.lock | 1 + bin/corpus | 2 +- bin/parser-prism-round-trip-test | 109 ++++++++++ bin/parser-round-trip-test | 301 +------------------------- bin/parser-whitequark-round-trip-test | 301 ++++++++++++++++++++++++++ lib/unparser.rb | 51 ++++- lib/unparser/cli.rb | 2 + lib/unparser/emitter/block.rb | 12 +- lib/unparser/node_helpers.rb | 3 +- lib/unparser/writer.rb | 7 +- spec/unit/unparser_spec.rb | 8 + test/corpus/literal/before/34.rb | 27 +++ test/corpus/literal/binary.rb | 10 - test/corpus/literal/control.rb | 1 - test/corpus/literal/for.rb | 5 - test/corpus/literal/pattern.rb | 2 - test/corpus/literal/regexp.rb | 3 - test/corpus/semantic/itblock.rb | 1 + test/corpus/semantic/kwbegin.rb | 4 - unparser.gemspec | 1 + 22 files changed, 524 insertions(+), 343 deletions(-) create mode 100755 bin/parser-prism-round-trip-test create mode 100755 bin/parser-whitequark-round-trip-test create mode 100644 test/corpus/literal/before/34.rb create mode 100644 test/corpus/semantic/itblock.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1f930ce..c451d287 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -53,7 +53,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -69,7 +69,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 @@ -85,7 +85,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [ruby-3.2, ruby-3.3] + ruby: [ruby-3.2, ruby-3.3, ruby-3.4] os: [ubuntu-latest] steps: - uses: actions/checkout@v4 diff --git a/Changelog.md b/Changelog.md index 19678ce0..8c5c2042 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,9 @@ +# Unreleased + +[#387](https://github.com/mbj/unparser/pull/387) + +* Add `prism` parser support for Ruby 3.4. ([viralpraxis](https://github.com/viralpraxis)) + # v0.7.0 2025-03-16 [#366](https://github.com/mbj/unparser/pull/366) diff --git a/Gemfile.lock b/Gemfile.lock index f3602da2..6e7258f8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,7 @@ PATH unparser (0.7.0) diff-lcs (~> 1.6) parser (>= 3.3.0) + prism (>= 1.4) GEM remote: https://rubygems.org/ diff --git a/bin/corpus b/bin/corpus index 774573c1..b7dc73f9 100755 --- a/bin/corpus +++ b/bin/corpus @@ -24,7 +24,7 @@ module Unparser # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength def verify - puts("Verifiying: #{name}") + puts("Verifying: #{name}") checkout paths = Pathname.glob(Pathname.new(repo_path).join('**/*.rb')) diff --git a/bin/parser-prism-round-trip-test b/bin/parser-prism-round-trip-test new file mode 100755 index 00000000..d3058b35 --- /dev/null +++ b/bin/parser-prism-round-trip-test @@ -0,0 +1,109 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'unparser' +require 'prism' +require 'fileutils' +require 'pathname' + +module PrismParser + PARSER = 'ruby/prism' + PARSER_VERSION = Prism::VERSION + PARSER_PATH = Pathname('tmp/parser-prism') + + PRISM_INVALID = Set[ + 'def __ENCODING__.a', + 'def __FILE__.a', + 'def __LINE__.a', + '\777', + 'ち\xE3\x81\xFF', + '\x8E\x01', + 'a\xE9b', + 'a\247b', + '\xE3\xD3\x8B\xE3\x83\xBC\x83\xE3\x83\xE3\x82\xB3\xA3\x82\x99', + 'hello \u{fc}', + '# encoding: sjis' + ].freeze + + PRISM_TODO = Set[ + 'def a(...); "foo#{b(...)}"; end', + '` +foo\ +b\nar +`', + ].freeze + + PRISM_NO_ROUND_TRIP = (PRISM_INVALID + PRISM_TODO).to_set.freeze + + private_constant :PRISM_INVALID, :PRISM_TODO + + class << self + def prepare + FileUtils.rm_rf("#{PARSER_PATH}/test/prism/fixtures-tmp") + Dir.mkdir("#{PARSER_PATH}/test/prism/fixtures-tmp") + + Dir.glob("#{PARSER_PATH}/test/prism/fixtures/**/*.txt") + .then(&method(:select_fixtures)) + .each do |path| + examples = File.read(path).split(/(?<=\n\n)/).then(&method(:reject_no_round_trip_examples)) + output_path = path.gsub('prism/fixtures', 'prism/fixtures-tmp') + dirname = File.dirname(output_path) + FileUtils.mkdir_p(dirname) unless File.directory?(dirname) + File.write(output_path, examples.join("\n\n")) + end + end + + def target_glob + "#{PARSER_PATH}/test/prism/fixtures-tmp/**/*.txt" + end + + def excludes + %w[ + spanning_heredoc + heredocs_with_fake_newlines + heredocs_nested + ].to_set { |file| "#{PARSER_PATH}/test/prism/fixtures-tmp/#{file}.txt" } + end + + private + + def select_fixtures(paths) + paths.reject { _1.include?('fixtures/unparser/') } + end + + def reject_no_round_trip_examples(examples) + examples.reject do |example| + PRISM_NO_ROUND_TRIP.any? { |snippet| example.include?(snippet) } + end + end + end +end + +unless PrismParser::PARSER_PATH.exist? + Kernel.system( + *%W[ + git + clone + https://github.com/#{PrismParser::PARSER} + #{PrismParser::PARSER_PATH} + ], + exception: true + ) +end + +Dir.chdir(PrismParser::PARSER_PATH) do + Kernel.system( + *%W[ + git + checkout + v#{PrismParser::PARSER_VERSION} + ], + exception: true + ) + Kernel.system(*%w[git clean --force -d -X], exception: true) +end + +ignores_cli_option = PrismParser.excludes.flat_map { |file| ['--ignore', file] } + +PrismParser.prepare +exit Unparser::CLI.run([PrismParser.target_glob, *ignores_cli_option]) diff --git a/bin/parser-round-trip-test b/bin/parser-round-trip-test index a87cf1b6..7b5f0bc6 100755 --- a/bin/parser-round-trip-test +++ b/bin/parser-round-trip-test @@ -1,301 +1,8 @@ #!/usr/bin/env ruby # frozen_string_literal: true -require 'unparser' - -# Hack to dynamically re-use the `parser` gems test suite on CI. -# The main idea is create a fake minitet runner to capture the -# signature of the examples encoded in the parsers test suite dynamically. -# -# This makes maintenance much more easier, especially on tracking new ruby -# syntax addtions. -# -# The API surface of the parser tests so far is low churn, while it may still -# make sense to provide the parser tests as an more easy to re-use data upstream. - -$LOAD_PATH << Pathname.new(__dir__).parent.join('test') - -test_builder = Class.new(Parser::Builders::Default) -test_builder.modernize - -MODERN_ATTRIBUTES = test_builder.instance_variables.to_h do |instance_variable| - attribute_name = instance_variable.to_s[1..].to_sym - [attribute_name, test_builder.public_send(attribute_name)] -end - -# Overwrite global scope method in the parser test suite -def default_builder_attributes - MODERN_ATTRIBUTES.keys.to_h do |attribute_name| - [attribute_name, Parser::Builders::Default.public_send(attribute_name)] - end -end - -class Test - include Unparser::Adamantium, Unparser::Anima.new( - :default_builder_attributes, - :group_index, - :name, - :node, - :parser_source, - :rubies - ) - - EXPECT_FAILURE = {}.freeze - STATIC_LOCAL_VARIABLES = %w[foo bar baz].to_set.freeze - - NO_ROUND_TRIP = %i[ - test_int___LINE__ - test_pattern_matching__FILE__LINE_literals - test_string___FILE__ - ].freeze - - def legacy_attributes - default_builder_attributes.reject do |attribute_name, value| - MODERN_ATTRIBUTES.fetch(attribute_name).equal?(value) - end.to_h - end - memoize :legacy_attributes - - def skip_reason - if !legacy_attributes.empty? - "Legacy parser attributes: #{legacy_attributes}" - elsif !allow_ruby? - "Non targeted rubies: #{rubies.join(',')}" - elsif validation.original_node.left? - 'Test specifies a syntax error' - elsif NO_ROUND_TRIP.include?(name) - 'Test not round trippable' - end - end - - def success? - validation.success? - end - - def expect_failure? - EXPECT_FAILURE.key?([name, group_index]) - end - - def allow_ruby? - rubies.empty? || rubies.include?(RUBY_VERSION.split('.').take(2).join('.')) - end - - def right(value) - Unparser::Either::Right.new(value) - end - - # rubocop:disable Metrics/AbcSize - def validation - identification = name.to_s - - ast = Unparser::AST.new( - comments: [], - explicit_encoding: nil, - node: node, - static_local_variables: STATIC_LOCAL_VARIABLES - ) - - generated_source = Unparser.unparse_ast_either(ast) - .fmap { |string| string.dup.force_encoding(parser_source.encoding).freeze } - - generated_node = generated_source.bind { |source| parse_either(source, identification) } - - Unparser::Validation.new( - generated_node: generated_node, - generated_source: generated_source, - identification: identification, - original_ast: parse_either_ast(parser_source, identification), - original_source: right(parser_source) - ) - end - # rubocop:enable Metrics/AbcSize - memoize :validation - - def parser - Unparser.parser.tap do |parser| - STATIC_LOCAL_VARIABLES.each(&parser.static_env.method(:declare)) - end - end - - def parse_either(source, identification) - Unparser::Either.wrap_error(Parser::SyntaxError) do - parser.parse(Unparser.buffer(source, identification)) - end - end - - def parse_either_ast(source, identification) - parse_either(source, identification).fmap do |node| - Unparser::AST.new( - comments: [], - explicit_encoding: nil, - node: node, - static_local_variables: Set.new - ) - end - end -end - -class Execution - include Unparser::Anima.new(:number, :total, :test) - - def call - skip_reason = test.skip_reason - if skip_reason - print('Skip', skip_reason) - return - end - - if test.expect_failure? - expect_failure - else - expect_success - end - end - -private - - def expect_failure - if test.success? - message('Expected Failure', 'but got success') - else - print('Expected Failure') - end - end - - def expect_success - if test.success? - print('Success') - else - puts(test.validation.report) - fail message('Failure') - end - end - - def message(status, message = '') - format( - '%3d/%3d: %-16s %s[%02d] %s', - number: number, - total: total, - status: status, - name: test.name, - group_index: test.group_index, - message: message - ) - end - - def print(status, message = '') - puts(message(status, message)) - end -end - -module Minitest - # Stub parent class - # rubocop:disable Lint/EmptyClass - class Test; end # Test - # rubocop:enable Lint/EmptyClass -end # Minitest - -class Extractor - class Capture - include Unparser::Anima.new( - :default_builder_attributes, - :node, - :parser_source, - :rubies - ) - - end - - attr_reader :tests - - def initialize - @captures = [] - @tests = [] - end - - def capture(**attributes) - @captures << Capture.new(attributes) - end - - def reset - @captures = [] - end - - def call(name) - reset - - TestParser.new.send(name) - - @captures.each_with_index do |capture, index| - @tests << Test.new(name: name, group_index: index, **capture.to_h) - end - - reset - end -end - -PARSER_PATH = Pathname.new('tmp/parser') - -unless PARSER_PATH.exist? - Kernel.system( - *%W[ - git - clone - https://github.com/whitequark/parser - #{PARSER_PATH} - ], - exception: true - ) -end - -Dir.chdir(PARSER_PATH) do - Kernel.system( - *%W[ - git - checkout - v#{Parser::VERSION} - ], - exception: true - ) - Kernel.system(*%w[git clean --force -d -X], exception: true) -end - -require "./#{PARSER_PATH}/test/parse_helper" -require "./#{PARSER_PATH}/test/test_parser" - -EXTRACTOR = Extractor.new - -module ParseHelper - def assert_diagnoses(*arguments); end - - def s(type, *children) - Parser::AST::Node.new(type, children) - end - - # rubocop:disable Metrics/ParameterLists - def assert_parses(node, parser_source, _diagnostics = nil, rubies = []) - EXTRACTOR.capture( - default_builder_attributes: default_builder_attributes, - node: node, - parser_source: parser_source, - rubies: rubies - ) - end - # rubocop:enable Metrics/ParameterLists - - def test_clrf_line_endings(*arguments); end - - def with_versions(*arguments); end - - def assert_context(*arguments); end - - def refute_diagnoses(*arguments); end - - def assert_diagnoses_many(*arguments); end -end - -TestParser.instance_methods.grep(/\Atest_/).each(&EXTRACTOR.method(:call)) - -EXTRACTOR.tests.sort_by(&:name).each_with_index do |test, index| - Execution.new(number: index.succ, total: EXTRACTOR.tests.length, test: test).call +if Gem::Version.new(RUBY_VERSION) <= '3.4' + load 'bin/parser-whitequark-round-trip-test' +else + load 'bin/parser-prism-round-trip-test' end diff --git a/bin/parser-whitequark-round-trip-test b/bin/parser-whitequark-round-trip-test new file mode 100755 index 00000000..a87cf1b6 --- /dev/null +++ b/bin/parser-whitequark-round-trip-test @@ -0,0 +1,301 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'unparser' + +# Hack to dynamically re-use the `parser` gems test suite on CI. +# The main idea is create a fake minitet runner to capture the +# signature of the examples encoded in the parsers test suite dynamically. +# +# This makes maintenance much more easier, especially on tracking new ruby +# syntax addtions. +# +# The API surface of the parser tests so far is low churn, while it may still +# make sense to provide the parser tests as an more easy to re-use data upstream. + +$LOAD_PATH << Pathname.new(__dir__).parent.join('test') + +test_builder = Class.new(Parser::Builders::Default) +test_builder.modernize + +MODERN_ATTRIBUTES = test_builder.instance_variables.to_h do |instance_variable| + attribute_name = instance_variable.to_s[1..].to_sym + [attribute_name, test_builder.public_send(attribute_name)] +end + +# Overwrite global scope method in the parser test suite +def default_builder_attributes + MODERN_ATTRIBUTES.keys.to_h do |attribute_name| + [attribute_name, Parser::Builders::Default.public_send(attribute_name)] + end +end + +class Test + include Unparser::Adamantium, Unparser::Anima.new( + :default_builder_attributes, + :group_index, + :name, + :node, + :parser_source, + :rubies + ) + + EXPECT_FAILURE = {}.freeze + STATIC_LOCAL_VARIABLES = %w[foo bar baz].to_set.freeze + + NO_ROUND_TRIP = %i[ + test_int___LINE__ + test_pattern_matching__FILE__LINE_literals + test_string___FILE__ + ].freeze + + def legacy_attributes + default_builder_attributes.reject do |attribute_name, value| + MODERN_ATTRIBUTES.fetch(attribute_name).equal?(value) + end.to_h + end + memoize :legacy_attributes + + def skip_reason + if !legacy_attributes.empty? + "Legacy parser attributes: #{legacy_attributes}" + elsif !allow_ruby? + "Non targeted rubies: #{rubies.join(',')}" + elsif validation.original_node.left? + 'Test specifies a syntax error' + elsif NO_ROUND_TRIP.include?(name) + 'Test not round trippable' + end + end + + def success? + validation.success? + end + + def expect_failure? + EXPECT_FAILURE.key?([name, group_index]) + end + + def allow_ruby? + rubies.empty? || rubies.include?(RUBY_VERSION.split('.').take(2).join('.')) + end + + def right(value) + Unparser::Either::Right.new(value) + end + + # rubocop:disable Metrics/AbcSize + def validation + identification = name.to_s + + ast = Unparser::AST.new( + comments: [], + explicit_encoding: nil, + node: node, + static_local_variables: STATIC_LOCAL_VARIABLES + ) + + generated_source = Unparser.unparse_ast_either(ast) + .fmap { |string| string.dup.force_encoding(parser_source.encoding).freeze } + + generated_node = generated_source.bind { |source| parse_either(source, identification) } + + Unparser::Validation.new( + generated_node: generated_node, + generated_source: generated_source, + identification: identification, + original_ast: parse_either_ast(parser_source, identification), + original_source: right(parser_source) + ) + end + # rubocop:enable Metrics/AbcSize + memoize :validation + + def parser + Unparser.parser.tap do |parser| + STATIC_LOCAL_VARIABLES.each(&parser.static_env.method(:declare)) + end + end + + def parse_either(source, identification) + Unparser::Either.wrap_error(Parser::SyntaxError) do + parser.parse(Unparser.buffer(source, identification)) + end + end + + def parse_either_ast(source, identification) + parse_either(source, identification).fmap do |node| + Unparser::AST.new( + comments: [], + explicit_encoding: nil, + node: node, + static_local_variables: Set.new + ) + end + end +end + +class Execution + include Unparser::Anima.new(:number, :total, :test) + + def call + skip_reason = test.skip_reason + if skip_reason + print('Skip', skip_reason) + return + end + + if test.expect_failure? + expect_failure + else + expect_success + end + end + +private + + def expect_failure + if test.success? + message('Expected Failure', 'but got success') + else + print('Expected Failure') + end + end + + def expect_success + if test.success? + print('Success') + else + puts(test.validation.report) + fail message('Failure') + end + end + + def message(status, message = '') + format( + '%3d/%3d: %-16s %s[%02d] %s', + number: number, + total: total, + status: status, + name: test.name, + group_index: test.group_index, + message: message + ) + end + + def print(status, message = '') + puts(message(status, message)) + end +end + +module Minitest + # Stub parent class + # rubocop:disable Lint/EmptyClass + class Test; end # Test + # rubocop:enable Lint/EmptyClass +end # Minitest + +class Extractor + class Capture + include Unparser::Anima.new( + :default_builder_attributes, + :node, + :parser_source, + :rubies + ) + + end + + attr_reader :tests + + def initialize + @captures = [] + @tests = [] + end + + def capture(**attributes) + @captures << Capture.new(attributes) + end + + def reset + @captures = [] + end + + def call(name) + reset + + TestParser.new.send(name) + + @captures.each_with_index do |capture, index| + @tests << Test.new(name: name, group_index: index, **capture.to_h) + end + + reset + end +end + +PARSER_PATH = Pathname.new('tmp/parser') + +unless PARSER_PATH.exist? + Kernel.system( + *%W[ + git + clone + https://github.com/whitequark/parser + #{PARSER_PATH} + ], + exception: true + ) +end + +Dir.chdir(PARSER_PATH) do + Kernel.system( + *%W[ + git + checkout + v#{Parser::VERSION} + ], + exception: true + ) + Kernel.system(*%w[git clean --force -d -X], exception: true) +end + +require "./#{PARSER_PATH}/test/parse_helper" +require "./#{PARSER_PATH}/test/test_parser" + +EXTRACTOR = Extractor.new + +module ParseHelper + def assert_diagnoses(*arguments); end + + def s(type, *children) + Parser::AST::Node.new(type, children) + end + + # rubocop:disable Metrics/ParameterLists + def assert_parses(node, parser_source, _diagnostics = nil, rubies = []) + EXTRACTOR.capture( + default_builder_attributes: default_builder_attributes, + node: node, + parser_source: parser_source, + rubies: rubies + ) + end + # rubocop:enable Metrics/ParameterLists + + def test_clrf_line_endings(*arguments); end + + def with_versions(*arguments); end + + def assert_context(*arguments); end + + def refute_diagnoses(*arguments); end + + def assert_diagnoses_many(*arguments); end +end + +TestParser.instance_methods.grep(/\Atest_/).each(&EXTRACTOR.method(:call)) + +EXTRACTOR.tests.sort_by(&:name).each_with_index do |test, index| + Execution.new(number: index.succ, total: EXTRACTOR.tests.length, test: test).call +end diff --git a/lib/unparser.rb b/lib/unparser.rb index 2373e17f..5616f888 100644 --- a/lib/unparser.rb +++ b/lib/unparser.rb @@ -3,7 +3,6 @@ require 'diff/lcs' require 'diff/lcs/hunk' require 'optparse' -require 'parser/current' require 'set' require 'unparser/equalizer' @@ -18,18 +17,53 @@ require 'unparser/anima/error' # Library namespace -module Unparser +module Unparser # rubocop:disable Metrics/ModuleLength # Unparser specific AST builder defaulting to modern AST format - class Builder < Parser::Builders::Default - modernize + if Gem::Version.new(RUBY_VERSION) <= '3.4' + require 'parser/current' + class Builder < Parser::Builders::Default + modernize - def initialize - super + # mutant:disable + def initialize + super - self.emit_file_line_as_literals = false + self.emit_file_line_as_literals = false + end + end + else + require 'prism' + class Builder < Prism::Translation::Parser::Builder + modernize + + # mutant:disable + def initialize + super + + self.emit_file_line_as_literals = false + end end end + PARSER_CLASS = + if Gem::Version.new(RUBY_VERSION) <= '3.4' + Class.new(Parser::CurrentRuby) do + def declare_local_variable(local_variable) + static_env.declare(local_variable) + end + end + else + Class.new(Prism::Translation::Parser34) do + def declare_local_variable(local_variable) + (@local_variables ||= Set.new) << local_variable + end + + def prism_options + super.merge(scopes: [@local_variables.to_a]) + end + end + end + EMPTY_STRING = ''.freeze EMPTY_ARRAY = [].freeze @@ -202,8 +236,9 @@ def self.parse_ast(source, static_local_variables: Set.new) # @return [Parser::Base] # # @api private + # mutant:disable def self.parser - Parser::CurrentRuby.new(Builder.new).tap do |parser| + PARSER_CLASS.new(Builder.new).tap do |parser| parser.diagnostics.tap do |diagnostics| diagnostics.all_errors_are_fatal = true end diff --git a/lib/unparser/cli.rb b/lib/unparser/cli.rb index 2d145795..36402026 100644 --- a/lib/unparser/cli.rb +++ b/lib/unparser/cli.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'pathname' + module Unparser # Unparser CLI implementation class CLI diff --git a/lib/unparser/emitter/block.rb b/lib/unparser/emitter/block.rb index 92febd72..ad2172b0 100644 --- a/lib/unparser/emitter/block.rb +++ b/lib/unparser/emitter/block.rb @@ -5,7 +5,7 @@ class Emitter # Block emitter class Block < self - handle :block, :numblock + handle :block, :numblock, :itblock children :target, :arguments, :body @@ -71,8 +71,16 @@ def numblock? node.type.equal?(:numblock) end + # NOTE: mutant fails on Ruby < 3.4 + # mutant:disable + def itblock? + node.type.equal?(:itblock) + end + + # NOTE: mutant fails on Ruby < 3.4 + # mutant:disable def emit_block_arguments - return if numblock? || arguments.children.empty? + return if numblock? || itblock? || arguments.children.empty? ws diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 92895fed..83bf5999 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -77,7 +77,8 @@ def n_range?(node) splat str sym - ].each do |type| + xstr + ].to_set.each do |type| name = "n_#{type}?" define_method(name) do |node| n?(type, node) diff --git a/lib/unparser/writer.rb b/lib/unparser/writer.rb index fe5492c9..2e761014 100644 --- a/lib/unparser/writer.rb +++ b/lib/unparser/writer.rb @@ -29,10 +29,9 @@ def emitter(node) # mutant:disable def round_trips?(source:) parser = Unparser.parser - - local_variable_scope - .local_variables_for_node(node) - .each(&parser.static_env.public_method(:declare)) + local_variable_scope.local_variables_for_node(node).each do |local_variable| + parser.declare_local_variable(local_variable) + end buffer = Buffer.new buffer.write_encoding(explicit_encoding) if explicit_encoding diff --git a/spec/unit/unparser_spec.rb b/spec/unit/unparser_spec.rb index 1ffd716f..a2499ee1 100644 --- a/spec/unit/unparser_spec.rb +++ b/spec/unit/unparser_spec.rb @@ -459,6 +459,14 @@ def foo(bar) let(:version_excludes) do excludes = [] + if RUBY_VERSION >= '3.4.' + excludes.concat( + %w[ + test/corpus/literal/before/34.rb + ] + ) + end + if RUBY_VERSION < '3.2.' excludes.concat( %w[ diff --git a/test/corpus/literal/before/34.rb b/test/corpus/literal/before/34.rb new file mode 100644 index 00000000..60e2e7ad --- /dev/null +++ b/test/corpus/literal/before/34.rb @@ -0,0 +1,27 @@ +retry +case foo +in {"#{"a"}": 1} then + true +end +/\c*a/ +/\c*a\c*/ +/\c*\c*\c*/ +(break foo) || a +(return foo) || a +a = b || break +a = b || next +a || (break foo) +b or break +b or next +break or b +next or b +return or a + +begin +rescue => A[] +end +for foo[] in m do +end +for (a, b) in bar do + baz +end diff --git a/test/corpus/literal/binary.rb b/test/corpus/literal/binary.rb index c5c18c4b..d529d181 100644 --- a/test/corpus/literal/binary.rb +++ b/test/corpus/literal/binary.rb @@ -1,20 +1,10 @@ (a || b).foo -(break foo) || a -(return foo) || a a && b && c -a = b || break -a = b || next a = b || return a and return foo a or return a or return foo a || (b || c) -a || (break foo) a || (return foo) -b or break -b or next -break or b -next or b -return or a a = b or c a = b and c diff --git a/test/corpus/literal/control.rb b/test/corpus/literal/control.rb index 648782bc..ce413fc8 100644 --- a/test/corpus/literal/control.rb +++ b/test/corpus/literal/control.rb @@ -1,7 +1,6 @@ next return break -retry redo return 1 return 1, 2 diff --git a/test/corpus/literal/for.rb b/test/corpus/literal/for.rb index 250a9f29..26a46def 100644 --- a/test/corpus/literal/for.rb +++ b/test/corpus/literal/for.rb @@ -7,8 +7,3 @@ for (a, *b) in bar do baz end -for (a, b) in bar do - baz -end -for foo[] in m do -end diff --git a/test/corpus/literal/pattern.rb b/test/corpus/literal/pattern.rb index 8bbf604b..d4aac3d3 100644 --- a/test/corpus/literal/pattern.rb +++ b/test/corpus/literal/pattern.rb @@ -17,8 +17,6 @@ true in {**nil} then true -in {"#{"a"}": 1} then - true in 1 | 2 then true in 1 => a then diff --git a/test/corpus/literal/regexp.rb b/test/corpus/literal/regexp.rb index 9f479eeb..9771357d 100644 --- a/test/corpus/literal/regexp.rb +++ b/test/corpus/literal/regexp.rb @@ -22,11 +22,8 @@ %r{//}x / / -/\c*a/ /a / -/\c*a\c*/ -/\c*\c*\c*/ / a / diff --git a/test/corpus/semantic/itblock.rb b/test/corpus/semantic/itblock.rb new file mode 100644 index 00000000..a7f71c4e --- /dev/null +++ b/test/corpus/semantic/itblock.rb @@ -0,0 +1 @@ +x { it } diff --git a/test/corpus/semantic/kwbegin.rb b/test/corpus/semantic/kwbegin.rb index fa757890..a6fd8dd2 100644 --- a/test/corpus/semantic/kwbegin.rb +++ b/test/corpus/semantic/kwbegin.rb @@ -52,7 +52,3 @@ begin rescue => A[i] end - -begin -rescue => A[] -end diff --git a/unparser.gemspec b/unparser.gemspec index ef6806da..ddbc3342 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |gem| gem.add_dependency('diff-lcs', '~> 1.6') gem.add_dependency('parser', '>= 3.3.0') + gem.add_dependency('prism', '>= 1.4') gem.add_development_dependency('mutant', '~> 0.13.0') gem.add_development_dependency('mutant-rspec', '~> 0.13.0') From 955de8215bf6c5270f2dac71b56c805dac9e1dc5 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Fri, 16 May 2025 01:41:27 +0300 Subject: [PATCH 253/256] Use `Util.one` helper where applicable Follow-up to https://github.com/mbj/unparser/pull/395#discussion_r2074106250 --- lib/unparser/emitter/dsym.rb | 2 +- lib/unparser/emitter/flow_modifier.rb | 2 +- lib/unparser/emitter/kwbegin.rb | 2 +- lib/unparser/emitter/xstr.rb | 2 +- lib/unparser/node_helpers.rb | 8 -------- lib/unparser/writer/array.rb | 2 +- test/corpus/literal/literal.rb | 2 ++ 7 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/unparser/emitter/dsym.rb b/lib/unparser/emitter/dsym.rb index bb4a1a94..8b01cbed 100644 --- a/lib/unparser/emitter/dsym.rb +++ b/lib/unparser/emitter/dsym.rb @@ -33,7 +33,7 @@ def emit_str_child(value) def emit_begin_child(component) write('#{') - visit(unwrap_single_begin(component)) if component.children.any? + visit(Util.one(component.children)) if component.children.any? write('}') end end # DSym diff --git a/lib/unparser/emitter/flow_modifier.rb b/lib/unparser/emitter/flow_modifier.rb index c1835972..5c646af9 100644 --- a/lib/unparser/emitter/flow_modifier.rb +++ b/lib/unparser/emitter/flow_modifier.rb @@ -21,7 +21,7 @@ def dispatch if children.one? && n_if?(children.first) ws - emitter(children.first).emit_ternary + emitter(Util.one(children)).emit_ternary else emit_arguments unless children.empty? end diff --git a/lib/unparser/emitter/kwbegin.rb b/lib/unparser/emitter/kwbegin.rb index 06fd7122..6589357b 100644 --- a/lib/unparser/emitter/kwbegin.rb +++ b/lib/unparser/emitter/kwbegin.rb @@ -12,7 +12,7 @@ def dispatch write('begin') if children.one? - emit_body_ensure_rescue(children.first) + emit_body_ensure_rescue(Util.one(children)) else indented do emit_multiple_body diff --git a/lib/unparser/emitter/xstr.rb b/lib/unparser/emitter/xstr.rb index 3655924e..6eccfe1a 100644 --- a/lib/unparser/emitter/xstr.rb +++ b/lib/unparser/emitter/xstr.rb @@ -64,7 +64,7 @@ def escape_xstr(input) def emit_begin(component) write('#{') - visit(unwrap_single_begin(component)) + visit(Util.one(component.children)) if component.children.any? write('}') end end # XStr diff --git a/lib/unparser/node_helpers.rb b/lib/unparser/node_helpers.rb index 92895fed..21433d2f 100644 --- a/lib/unparser/node_helpers.rb +++ b/lib/unparser/node_helpers.rb @@ -84,13 +84,5 @@ def n_range?(node) end private(name) end - - def unwrap_single_begin(node) - if n_begin?(node) && node.children.one? - node.children.first - else - node - end - end end # NodeHelpers end # Unparser diff --git a/lib/unparser/writer/array.rb b/lib/unparser/writer/array.rb index 7e80145b..3779a8e6 100644 --- a/lib/unparser/writer/array.rb +++ b/lib/unparser/writer/array.rb @@ -24,7 +24,7 @@ def emit_compact # rubocop:disable Metrics/AbcSize write(Util.one(child.children).to_s) else write('#{') - emitter(unwrap_single_begin(Util.one(child.children))).write_to_buffer + emitter(Util.one(Util.one(child.children).children)).write_to_buffer write('}') end end diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 3dbb2a2a..18fd386e 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -72,3 +72,5 @@ ` x #{foo} #` +`#{}` +`#G` From 0bbb953f5c71d51fd35e08d5e6b64b39c4794832 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Sun, 25 May 2025 02:07:18 +0000 Subject: [PATCH 254/256] Change version to 0.8.0 --- Changelog.md | 2 +- Gemfile | 2 ++ Gemfile.lock | 41 ++++++++++++++++++-------------- bin/parser-prism-round-trip-test | 2 +- unparser.gemspec | 2 +- 5 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Changelog.md b/Changelog.md index 8c5c2042..b61fc80b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -# Unreleased +# v0.8.0 unreleased [#387](https://github.com/mbj/unparser/pull/387) diff --git a/Gemfile b/Gemfile index 7f4f5e95..52392e8e 100644 --- a/Gemfile +++ b/Gemfile @@ -3,3 +3,5 @@ source 'https://rubygems.org' gemspec + +gem 'mutant', github: 'mbj/mutant', branch: 'upgrade/unparser' diff --git a/Gemfile.lock b/Gemfile.lock index 6e7258f8..bfabed05 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,19 @@ +GIT + remote: https://github.com/mbj/mutant.git + revision: 2542e65f51ccac1e4b7749c8f522b80d7d9ea9fb + branch: upgrade/unparser + specs: + mutant (0.13.1) + diff-lcs (~> 1.3) + parser (~> 3.3.0) + regexp_parser (~> 2.10) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.8.0) + PATH remote: . specs: - unparser (0.7.0) + unparser (0.8.0) diff-lcs (~> 1.6) parser (>= 3.3.0) prism (>= 1.4) @@ -10,16 +22,10 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) - diff-lcs (1.6.1) - json (2.10.2) - language_server-protocol (3.17.0.4) + diff-lcs (1.6.2) + json (2.12.2) + language_server-protocol (3.17.0.5) lint_roller (1.1.0) - mutant (0.13.1) - diff-lcs (~> 1.3) - parser (~> 3.3.0) - regexp_parser (~> 2.10) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.7.0) mutant-rspec (0.13.1) mutant (= 0.13.1) rspec-core (>= 3.8.0, < 4.0.0) @@ -37,17 +43,17 @@ GEM rspec-mocks (~> 3.13.0) rspec-core (3.13.3) rspec-support (~> 3.13.0) - rspec-expectations (3.13.3) + rspec-expectations (3.13.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-its (1.3.1) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.13.2) + rspec-mocks (3.13.4) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.2) - rubocop (1.75.3) + rspec-support (3.13.3) + rubocop (1.75.7) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -65,17 +71,16 @@ GEM lint_roller (~> 1.1.0) rubocop (>= 1.72.1, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.12028) + sorbet-runtime (0.5.12130) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) PLATFORMS - ruby x86_64-linux DEPENDENCIES - mutant (~> 0.13.0) + mutant! mutant-rspec (~> 0.13.0) rspec (~> 3.13) rspec-core (~> 3.13) @@ -85,4 +90,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.5.22 + 2.6.7 diff --git a/bin/parser-prism-round-trip-test b/bin/parser-prism-round-trip-test index d3058b35..a9a22242 100755 --- a/bin/parser-prism-round-trip-test +++ b/bin/parser-prism-round-trip-test @@ -30,7 +30,7 @@ module PrismParser '` foo\ b\nar -`', +`' ].freeze PRISM_NO_ROUND_TRIP = (PRISM_INVALID + PRISM_TODO).to_set.freeze diff --git a/unparser.gemspec b/unparser.gemspec index ddbc3342..945959ab 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |gem| gem.name = 'unparser' - gem.version = '0.7.0' + gem.version = '0.8.0' gem.authors = ['Markus Schirp'] gem.email = 'mbj@schirp-dso.com' From 48458d57341f75ff3679fd52747bf95fa15d4fe4 Mon Sep 17 00:00:00 2001 From: viralpraxis Date: Thu, 29 May 2025 11:07:49 +0400 Subject: [PATCH 255/256] Fix `xstr` with plain `gvar` unparsing Follow-up to https://github.com/mbj/unparser/pull/387 ```shell bundle exec bin/unparser -e '`#$G`' (string) Original-Source: `#$G` Generated-Source: lib/unparser/emitter/xstr.rb:56:in 'Unparser::Emitter::XStr#escape_xstr' lib/unparser/emitter/xstr.rb:52:in 'Unparser::Emitter::XStr#emit_string' lib/unparser/emitter/xstr.rb:45:in 'block in Unparser::Emitter::XStr#emit_xstr' lib/unparser/emitter/xstr.rb:41:in 'Array#each' lib/unparser/emitter/xstr.rb:41:in 'Unparser::Emitter::XStr#emit_xstr' lib/unparser/emitter/xstr.rb:16:in 'Unparser::Emitter::XStr#dispatch' lib/unparser/generation.rb:13:in 'block in Unparser::Generation#write_to_buffer' lib/unparser/generation.rb:44:in 'Unparser::Generation#with_comments' lib/unparser/generation.rb:13:in 'Unparser::Generation#write_to_buffer' :91:in 'Kernel#tap' lib/unparser/generation.rb:236:in 'Unparser::Generation#visit_deep' lib/unparser/generation.rb:132:in 'block in Unparser::Generation#emit_body' lib/unparser/generation.rb:138:in 'Unparser::Generation#with_indent' lib/unparser/generation.rb:122:in 'Unparser::Generation#emit_body' lib/unparser/emitter/root.rb:11:in 'Unparser::Emitter::Root#dispatch' lib/unparser/generation.rb:13:in 'block in Unparser::Generation#write_to_buffer' lib/unparser/generation.rb:44:in 'Unparser::Generation#with_comments' lib/unparser/generation.rb:13:in 'Unparser::Generation#write_to_buffer' lib/unparser.rb:148:in 'block in Unparser.unparse_ast' :91:in 'Kernel#tap' Original-Node: (xstr (gvar :$G)) Generated-Node: undefined Error: (string) ``` --- lib/unparser/emitter/xstr.rb | 7 +++++++ test/corpus/literal/literal.rb | 1 + 2 files changed, 8 insertions(+) diff --git a/lib/unparser/emitter/xstr.rb b/lib/unparser/emitter/xstr.rb index 6eccfe1a..127d287c 100644 --- a/lib/unparser/emitter/xstr.rb +++ b/lib/unparser/emitter/xstr.rb @@ -41,6 +41,8 @@ def emit_xstr children.each do |child| if n_begin?(child) emit_begin(child) + elsif n_gvar?(child) + emit_gvar(child) else emit_string(child) end @@ -67,6 +69,11 @@ def emit_begin(component) visit(Util.one(component.children)) if component.children.any? write('}') end + + def emit_gvar(component) + write('#') + write(Util.one(component.children).to_s) + end end # XStr end # Emitter end # Unparser diff --git a/test/corpus/literal/literal.rb b/test/corpus/literal/literal.rb index 18fd386e..a063c5c4 100644 --- a/test/corpus/literal/literal.rb +++ b/test/corpus/literal/literal.rb @@ -74,3 +74,4 @@ #` `#{}` `#G` +`#$G` From c067c75834be960486b393471200f1ee3223e7f8 Mon Sep 17 00:00:00 2001 From: Markus Schirp Date: Thu, 19 Jun 2025 14:14:16 +0000 Subject: [PATCH 256/256] Upgrade dependencies --- .rubocop.yml | 4 +++ Gemfile | 2 -- Gemfile.lock | 64 +++++++++++++++++++++++++++++------------------- unparser.gemspec | 2 +- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 6844561f..62416515 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -111,3 +111,7 @@ Layout/MultilineMethodCallIndentation: # Useful for code structure Lint/UselessConstantScoping: Enabled: false + +# Lots of false positives, trying to do static analysis on a dynlang is not going to work +Naming/PredicateMethod: + Enabled: false diff --git a/Gemfile b/Gemfile index 52392e8e..7f4f5e95 100644 --- a/Gemfile +++ b/Gemfile @@ -3,5 +3,3 @@ source 'https://rubygems.org' gemspec - -gem 'mutant', github: 'mbj/mutant', branch: 'upgrade/unparser' diff --git a/Gemfile.lock b/Gemfile.lock index bfabed05..800cb605 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: https://github.com/mbj/mutant.git - revision: 2542e65f51ccac1e4b7749c8f522b80d7d9ea9fb - branch: upgrade/unparser - specs: - mutant (0.13.1) - diff-lcs (~> 1.3) - parser (~> 3.3.0) - regexp_parser (~> 2.10) - sorbet-runtime (~> 0.5.0) - unparser (~> 0.8.0) - PATH remote: . specs: @@ -22,38 +10,63 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.3) + date (3.4.1) diff-lcs (1.6.2) + erb (5.0.1) + io-console (0.8.0) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.12.2) language_server-protocol (3.17.0.5) lint_roller (1.1.0) - mutant-rspec (0.13.1) - mutant (= 0.13.1) + mutant (0.13.2) + diff-lcs (~> 1.3) + irb (~> 1.15.2) + parser (~> 3.3.0) + regexp_parser (~> 2.10) + sorbet-runtime (~> 0.5.0) + unparser (~> 0.8.0) + mutant-rspec (0.13.2) + mutant (= 0.13.2) rspec-core (>= 3.8.0, < 4.0.0) parallel (1.27.0) parser (3.3.8.0) ast (~> 2.4.1) racc + pp (0.6.2) + prettyprint + prettyprint (0.2.0) prism (1.4.0) + psych (5.2.6) + date + stringio racc (1.8.1) rainbow (3.1.1) + rdoc (6.14.1) + erb + psych (>= 4.0.0) regexp_parser (2.10.0) - rspec (3.13.0) + reline (0.6.1) + io-console (~> 0.5) + rspec (3.13.1) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.3) + rspec-core (3.13.4) rspec-support (~> 3.13.0) - rspec-expectations (3.13.4) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-its (1.3.1) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.13.4) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-support (3.13.3) - rubocop (1.75.7) + rspec-support (3.13.4) + rubocop (1.76.2) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) @@ -61,17 +74,18 @@ GEM parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) - rubocop-ast (>= 1.44.0, < 2.0) + rubocop-ast (>= 1.45.1, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.44.1) + rubocop-ast (1.45.1) parser (>= 3.3.7.2) prism (~> 1.4) rubocop-packaging (0.6.0) lint_roller (~> 1.1.0) rubocop (>= 1.72.1, < 2.0) ruby-progressbar (1.13.0) - sorbet-runtime (0.5.12130) + sorbet-runtime (0.5.12194) + stringio (3.1.7) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) @@ -80,7 +94,7 @@ PLATFORMS x86_64-linux DEPENDENCIES - mutant! + mutant (~> 0.13.2) mutant-rspec (~> 0.13.0) rspec (~> 3.13) rspec-core (~> 3.13) @@ -90,4 +104,4 @@ DEPENDENCIES unparser! BUNDLED WITH - 2.6.7 + 2.6.2 diff --git a/unparser.gemspec b/unparser.gemspec index 945959ab..bea31e69 100644 --- a/unparser.gemspec +++ b/unparser.gemspec @@ -29,7 +29,7 @@ Gem::Specification.new do |gem| gem.add_dependency('parser', '>= 3.3.0') gem.add_dependency('prism', '>= 1.4') - gem.add_development_dependency('mutant', '~> 0.13.0') + gem.add_development_dependency('mutant', '~> 0.13.2') gem.add_development_dependency('mutant-rspec', '~> 0.13.0') gem.add_development_dependency('rspec', '~> 3.13') gem.add_development_dependency('rspec-core', '~> 3.13')