parser 2.7.1.2 → 2.7.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -6
  3. data/CHANGELOG.md +17 -1
  4. data/README.md +1 -2
  5. data/Rakefile +1 -1
  6. data/doc/AST_FORMAT.md +22 -0
  7. data/lib/parser/ast/processor.rb +3 -0
  8. data/lib/parser/builders/default.rb +9 -0
  9. data/lib/parser/diagnostic.rb +1 -1
  10. data/lib/parser/diagnostic/engine.rb +1 -2
  11. data/lib/parser/lexer.rl +7 -0
  12. data/lib/parser/messages.rb +15 -0
  13. data/lib/parser/meta.rb +1 -1
  14. data/lib/parser/ruby28.y +62 -20
  15. data/lib/parser/runner.rb +21 -2
  16. data/lib/parser/runner/ruby_rewrite.rb +2 -2
  17. data/lib/parser/source/buffer.rb +3 -1
  18. data/lib/parser/source/comment/associator.rb +14 -4
  19. data/lib/parser/source/range.rb +7 -0
  20. data/lib/parser/source/tree_rewriter.rb +1 -1
  21. data/lib/parser/tree_rewriter.rb +1 -2
  22. data/lib/parser/version.rb +1 -1
  23. data/parser.gemspec +1 -1
  24. data/test/helper.rb +24 -6
  25. data/test/parse_helper.rb +9 -16
  26. data/test/test_ast_processor.rb +32 -0
  27. data/test/test_base.rb +1 -1
  28. data/test/test_diagnostic.rb +6 -7
  29. data/test/test_diagnostic_engine.rb +5 -8
  30. data/test/test_lexer.rb +5 -8
  31. data/test/test_meta.rb +12 -0
  32. data/test/test_parser.rb +84 -13
  33. data/test/test_runner_parse.rb +22 -1
  34. data/test/test_runner_rewrite.rb +1 -1
  35. data/test/test_source_buffer.rb +4 -1
  36. data/test/test_source_comment.rb +2 -2
  37. data/test/test_source_comment_associator.rb +47 -15
  38. data/test/test_source_map.rb +1 -2
  39. data/test/test_source_range.rb +16 -11
  40. data/test/test_source_rewriter.rb +4 -4
  41. data/test/test_source_rewriter_action.rb +2 -2
  42. data/test/test_source_tree_rewriter.rb +3 -3
  43. metadata +11 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e95a182ac672d1917e517628161ab896f34d4518df71430e0b349998817308e0
4
- data.tar.gz: 24175a8fa5f50956127930f697ba4f09ae0561c359d9fea4ea19883c8c7c072a
3
+ metadata.gz: bab855c4abc633b9ba13f855d816f20948ee539f27bea7878b574f4d0b3232f7
4
+ data.tar.gz: 929aa201a2c06738c6202c8efb2afd2dda77e630805738007c39f3b8274fa717
5
5
  SHA512:
6
- metadata.gz: 95e17dd645e2240c9b3f6d7972a03d2c0df0305548920643c9d930ed3fa8b6aee8da77211b11d80a4d3f651aa75c1c2d43e51daa46aca163bd292ba91e9bcc0e
7
- data.tar.gz: e9b989147352537fb1460a71585ace1165f5bca6d3baa01fde8f83a5b951c305bb37bab9ac93b019c70b728e6152ecd178f07935c335aab5e0f3a8ee4caf83a8
6
+ metadata.gz: 78e2e37d1ef61d2c125c1462ecb2e48ab8f76a06fd97bcba28ea4acd5fa60f77a296f859fd6ad67a787027b7a9e62d0fc34bedc8c23522bce21213d1ca49d780
7
+ data.tar.gz: 07fa5ad8fe63b8503ced8fa0143062385f1d2f99e542f80cc32375269a9ad75f07cc2b0e00838232a71a9e797ba5cab28b661d8a58765f558e29353462641f06
@@ -19,10 +19,10 @@ matrix:
19
19
  script: bundle exec rake test_cov
20
20
  - name: jruby-9.1.15.0 / Parser tests
21
21
  rvm: jruby-9.1.15.0
22
- script: bundle exec rake test_cov
23
- - name: rbx-2 / Parser tests
24
- rvm: rbx-2
25
- script: bundle exec rake test_cov
22
+ script: bundle exec rake test
23
+ - name: truffleruby / Parser tests
24
+ rvm: truffleruby
25
+ script: bundle exec rake test
26
26
  - name: 2.5.8 / Rubocop tests
27
27
  rvm: 2.5.8
28
28
  script: ./ci/run_rubocop_specs
@@ -34,9 +34,7 @@ matrix:
34
34
  script: ./ci/run_rubocop_specs
35
35
  allow_failures:
36
36
  - rvm: ruby-head
37
- - rvm: rbx-2
38
37
  - script: ./ci/run_rubocop_specs
39
38
  before_install:
40
- - gem install bundler -v '< 2'
41
39
  - bundle --version
42
40
  - gem --version
@@ -1,9 +1,25 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- Not released (2020-04-30)
4
+ Not released (2020-05-26)
5
5
  -------------------------
6
6
 
7
+ API modifications:
8
+ * fixed all warnings. tests are running in verbose mode now. (#685) (Ilya Bylich)
9
+
10
+ Features implemented:
11
+ * ruby-[parse, rewrite]: add legacy switches (#699) (Marc-André Lafortune)
12
+ * Added Parser::Source::Range#to_range. (#697) (Ilya Bylich)
13
+ * ruby28.y: support rescue modifier in endless method definition. (#696) (Ilya Bylich)
14
+ * ruby28.y: unify kwrest and no-kwrest rules. (#694) (Ilya Bylich)
15
+ * ruby28.y: add right hand assignment (#682) (Vladimir Dementyev)
16
+
17
+ Bugs fixed:
18
+ * fix Comment.associate for postfix conditions/loops (#688) (Marc-André Lafortune)
19
+
20
+ v2.7.1.2 (2020-04-30)
21
+ ---------------------
22
+
7
23
  Features implemented:
8
24
  * ruby28.y: endless method definition (#676) (Vladimir Dementyev)
9
25
  * ruby28.y: branch parser (#677) (Vladimir Dementyev)
data/README.md CHANGED
@@ -59,8 +59,7 @@ Parse a chunk of code and display all diagnostics:
59
59
  puts diag.render
60
60
  end
61
61
 
62
- buffer = Parser::Source::Buffer.new('(string)')
63
- buffer.source = "foo *bar"
62
+ buffer = Parser::Source::Buffer.new('(string)', source: "foo *bar")
64
63
 
65
64
  p parser.parse(buffer)
66
65
  # (string):1:5: warning: `*' interpreted as argument prefix
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ task :default => [:test]
11
11
  Rake::TestTask.new do |t|
12
12
  t.libs = %w(test/ lib/)
13
13
  t.test_files = FileList["test/**/test_*.rb"]
14
- t.warning = false
14
+ t.warning = true
15
15
  end
16
16
 
17
17
  task :test_cov do
@@ -730,6 +730,28 @@ Format:
730
730
 
731
731
  ~~~
732
732
 
733
+ ### Right-hand assignment
734
+
735
+ Format:
736
+
737
+ ~~~
738
+ (rasgn (int 1) (lvasgn :a))
739
+ "1 => a"
740
+ ~~~~~~ expression
741
+ ~~ operator
742
+ ~~~
743
+
744
+ #### Multiple right-hand assignment
745
+
746
+ Format:
747
+
748
+ ~~~
749
+ (mrasgn (send (int 13) :divmod (int 5)) (mlhs (lvasgn :a) (lvasgn :b)))
750
+ "13.divmod(5) => a,b"
751
+ ~~~~~~~~~~~~~~~~~~~ expression
752
+ ^^ operator
753
+ ~~~
754
+
733
755
  ## Class and module definition
734
756
 
735
757
  ### Module
@@ -75,6 +75,9 @@ module Parser
75
75
  alias on_mlhs process_regular_node
76
76
  alias on_masgn process_regular_node
77
77
 
78
+ alias on_rasgn process_regular_node
79
+ alias on_mrasgn process_regular_node
80
+
78
81
  def on_const(node)
79
82
  scope_node, name = *node
80
83
 
@@ -619,6 +619,15 @@ module Parser
619
619
  binary_op_map(lhs, eql_t, rhs))
620
620
  end
621
621
 
622
+ def rassign(lhs, assoc_t, rhs)
623
+ n(:rasgn, [lhs, rhs], binary_op_map(lhs, assoc_t, rhs))
624
+ end
625
+
626
+ def multi_rassign(lhs, assoc_t, rhs)
627
+ n(:mrasgn, [ lhs, rhs ],
628
+ binary_op_map(lhs, assoc_t, rhs))
629
+ end
630
+
622
631
  #
623
632
  # Class and module definition
624
633
  #
@@ -67,7 +67,7 @@ module Parser
67
67
  # @return [String] the rendered message.
68
68
  #
69
69
  def message
70
- MESSAGES[@reason] % @arguments
70
+ Messages.compile(@reason, @arguments)
71
71
  end
72
72
 
73
73
  ##
@@ -7,8 +7,7 @@ module Parser
7
7
  # diagnostics by delegating them to registered consumers.
8
8
  #
9
9
  # @example
10
- # buffer = Parser::Source::Buffer.new(__FILE__)
11
- # buffer.code = 'foobar'
10
+ # buffer = Parser::Source::Buffer.new(__FILE__, source: 'foobar')
12
11
  #
13
12
  # consumer = lambda do |diagnostic|
14
13
  # puts diagnostic.message
@@ -283,6 +283,13 @@ class Parser::Lexer
283
283
  %% write exec;
284
284
  # %
285
285
 
286
+ # Ragel creates a local variable called `testEof` but it doesn't use
287
+ # it in any assignment. This dead code is here to swallow the warning.
288
+ # It has no runtime cost because Ruby doesn't produce any instructions from it.
289
+ if false
290
+ testEof
291
+ end
292
+
286
293
  @p = p
287
294
 
288
295
  if @token_queue.any?
@@ -93,4 +93,19 @@ module Parser
93
93
  :crossing_insertions => 'the rewriting action on:',
94
94
  :crossing_insertions_conflict => 'is crossing that on:',
95
95
  }.freeze
96
+
97
+ # @api private
98
+ module Messages
99
+ # Formats the message, returns a raw template if there's nothing to interpolate
100
+ #
101
+ # Code like `format("", {})` gives a warning, and so this method tries interpolating
102
+ # only if `arguments` hash is not empty.
103
+ #
104
+ # @api private
105
+ def self.compile(reason, arguments)
106
+ template = MESSAGES[reason]
107
+ return template if Hash === arguments && arguments.empty?
108
+ format(template, arguments)
109
+ end
110
+ end
96
111
  end
@@ -12,7 +12,7 @@ module Parser
12
12
  sym dsym xstr regopt regexp array splat
13
13
  pair kwsplat hash irange erange self
14
14
  lvar ivar cvar gvar const defined? lvasgn
15
- ivasgn cvasgn gvasgn casgn mlhs masgn
15
+ ivasgn cvasgn gvasgn casgn mlhs masgn rasgn mrasgn
16
16
  op_asgn and_asgn ensure rescue arg_expr
17
17
  or_asgn back_ref nth_ref
18
18
  match_with_lvasgn match_current_line
@@ -206,8 +206,26 @@ rule
206
206
  {
207
207
  result = @builder.multi_assign(val[0], val[1], val[2])
208
208
  }
209
+ | rassign
209
210
  | expr
210
211
 
212
+ rassign: arg_value tASSOC lhs
213
+ {
214
+ result = @builder.rassign(val[0], val[1], val[2])
215
+ }
216
+ | arg_value tASSOC mlhs
217
+ {
218
+ result = @builder.multi_rassign(val[0], val[1], val[2])
219
+ }
220
+ | rassign tASSOC lhs
221
+ {
222
+ result = @builder.rassign(val[0], val[1], val[2])
223
+ }
224
+ | rassign tASSOC mlhs
225
+ {
226
+ result = @builder.multi_rassign(val[0], val[1], val[2])
227
+ }
228
+
211
229
  command_asgn: lhs tEQL command_rhs
212
230
  {
213
231
  result = @builder.assign(val[0], val[1], val[2])
@@ -851,6 +869,23 @@ rule
851
869
  result = @builder.def_endless_method(*val[0],
852
870
  val[1], val[2], val[3])
853
871
 
872
+ @lexer.cmdarg.pop
873
+ @lexer.cond.pop
874
+ @static_env.unextend
875
+ @context.pop
876
+ @current_arg_stack.pop
877
+ }
878
+ | defn_head f_paren_args tEQL arg kRESCUE_MOD arg
879
+ {
880
+ rescue_body = @builder.rescue_body(val[4],
881
+ nil, nil, nil,
882
+ nil, val[5])
883
+
884
+ method_body = @builder.begin_body(val[3], [ rescue_body ])
885
+
886
+ result = @builder.def_endless_method(*val[0],
887
+ val[1], val[2], method_body)
888
+
854
889
  @lexer.cmdarg.pop
855
890
  @lexer.cond.pop
856
891
  @static_env.unextend
@@ -862,6 +897,23 @@ rule
862
897
  result = @builder.def_endless_singleton(*val[0],
863
898
  val[1], val[2], val[3])
864
899
 
900
+ @lexer.cmdarg.pop
901
+ @lexer.cond.pop
902
+ @static_env.unextend
903
+ @context.pop
904
+ @current_arg_stack.pop
905
+ }
906
+ | defs_head f_paren_args tEQL arg kRESCUE_MOD arg
907
+ {
908
+ rescue_body = @builder.rescue_body(val[4],
909
+ nil, nil, nil,
910
+ nil, val[5])
911
+
912
+ method_body = @builder.begin_body(val[3], [ rescue_body ])
913
+
914
+ result = @builder.def_endless_singleton(*val[0],
915
+ val[1], val[2], method_body)
916
+
865
917
  @lexer.cmdarg.pop
866
918
  @lexer.cond.pop
867
919
  @static_env.unextend
@@ -1401,6 +1453,9 @@ rule
1401
1453
  result = @builder.restarg(val[0])
1402
1454
  }
1403
1455
 
1456
+ f_any_kwrest: f_kwrest
1457
+ | f_no_kwarg
1458
+
1404
1459
  block_args_tail: f_block_kwarg tCOMMA f_kwrest opt_f_block_arg
1405
1460
  {
1406
1461
  result = val[0].concat(val[2]).concat(val[3])
@@ -1409,11 +1464,7 @@ rule
1409
1464
  {
1410
1465
  result = val[0].concat(val[1])
1411
1466
  }
1412
- | f_kwrest opt_f_block_arg
1413
- {
1414
- result = val[0].concat(val[1])
1415
- }
1416
- | f_no_kwarg opt_f_block_arg
1467
+ | f_any_kwrest opt_f_block_arg
1417
1468
  {
1418
1469
  result = val[0].concat(val[1])
1419
1470
  }
@@ -2012,7 +2063,7 @@ opt_block_args_tail:
2012
2063
 
2013
2064
  p_arg: p_expr
2014
2065
 
2015
- p_kwargs: p_kwarg tCOMMA p_kwrest
2066
+ p_kwargs: p_kwarg tCOMMA p_any_kwrest
2016
2067
  {
2017
2068
  result = [ *val[0], *val[2] ]
2018
2069
  }
@@ -2024,18 +2075,10 @@ opt_block_args_tail:
2024
2075
  {
2025
2076
  result = val[0]
2026
2077
  }
2027
- | p_kwrest
2078
+ | p_any_kwrest
2028
2079
  {
2029
2080
  result = val[0]
2030
2081
  }
2031
- | p_kwarg tCOMMA p_kwnorest
2032
- {
2033
- result = [ *val[0], *val[2] ]
2034
- }
2035
- | p_kwnorest
2036
- {
2037
- result = [ *val[0], *val[2] ]
2038
- }
2039
2082
 
2040
2083
  p_kwarg: p_kw
2041
2084
  {
@@ -2079,6 +2122,9 @@ opt_block_args_tail:
2079
2122
  result = [ @builder.match_nil_pattern(val[0], val[1]) ]
2080
2123
  }
2081
2124
 
2125
+ p_any_kwrest: p_kwrest
2126
+ | p_kwnorest
2127
+
2082
2128
  p_value: p_primitive
2083
2129
  | p_primitive tDOT2 p_primitive
2084
2130
  {
@@ -2579,11 +2625,7 @@ keyword_variable: kNIL
2579
2625
  {
2580
2626
  result = val[0].concat(val[1])
2581
2627
  }
2582
- | f_kwrest opt_f_block_arg
2583
- {
2584
- result = val[0].concat(val[1])
2585
- }
2586
- | f_no_kwarg opt_f_block_arg
2628
+ | f_any_kwrest opt_f_block_arg
2587
2629
  {
2588
2630
  result = val[0].concat(val[1])
2589
2631
  }
@@ -14,9 +14,8 @@ module Parser
14
14
  end
15
15
 
16
16
  def initialize
17
- Parser::Builders::Default.modernize
18
-
19
17
  @option_parser = OptionParser.new { |opts| setup_option_parsing(opts) }
18
+ @legacy = {}
20
19
  @parser_class = nil
21
20
  @parser = nil
22
21
  @files = []
@@ -30,6 +29,7 @@ module Parser
30
29
 
31
30
  def execute(options)
32
31
  parse_options(options)
32
+ setup_builder_default
33
33
  prepare_parser
34
34
 
35
35
  process_all_input
@@ -37,6 +37,8 @@ module Parser
37
37
 
38
38
  private
39
39
 
40
+ LEGACY_MODES = %i[lambda procarg0 encoding index arg_inside_procarg0].freeze
41
+
40
42
  def runner_name
41
43
  raise NotImplementedError, "implement #{self.class}##{__callee__}"
42
44
  end
@@ -126,6 +128,17 @@ module Parser
126
128
  @parser_class = Parser::RubyMotion
127
129
  end
128
130
 
131
+ opts.on '--legacy', "Parse with all legacy modes" do
132
+ @legacy = Hash.new(true)
133
+ end
134
+
135
+ LEGACY_MODES.each do |mode|
136
+ opt_name = "--legacy-#{mode.to_s.gsub('_', '-')}"
137
+ opts.on opt_name, "Parse with legacy mode for emit_#{mode}" do
138
+ @legacy[mode] = true
139
+ end
140
+ end
141
+
129
142
  opts.on '-w', '--warnings', 'Enable warnings' do |w|
130
143
  @warnings = w
131
144
  end
@@ -164,6 +177,12 @@ module Parser
164
177
  end
165
178
  end
166
179
 
180
+ def setup_builder_default
181
+ LEGACY_MODES.each do |mode|
182
+ Parser::Builders::Default.send(:"emit_#{mode}=", !@legacy[mode])
183
+ end
184
+ end
185
+
167
186
  def prepare_parser
168
187
  @parser = @parser_class.new
169
188
 
@@ -55,8 +55,8 @@ module Parser
55
55
  new_source = rewriter.rewrite(buffer, ast)
56
56
 
57
57
  new_buffer = Source::Buffer.new(initial_buffer.name +
58
- '|after ' + rewriter_class.name)
59
- new_buffer.source = new_source
58
+ '|after ' + rewriter_class.name,
59
+ source: new_source)
60
60
 
61
61
  @parser.reset
62
62
  new_ast = @parser.parse(new_buffer)
@@ -102,7 +102,7 @@ module Parser
102
102
  end
103
103
  end
104
104
 
105
- def initialize(name, first_line = 1)
105
+ def initialize(name, first_line = 1, source: nil)
106
106
  @name = name.to_s
107
107
  @source = nil
108
108
  @first_line = first_line
@@ -116,6 +116,8 @@ module Parser
116
116
  # Cache for fast lookup
117
117
  @line_for_position = {}
118
118
  @column_for_position = {}
119
+
120
+ self.source = source if source
119
121
  end
120
122
 
121
123
  ##
@@ -107,6 +107,19 @@ module Parser
107
107
 
108
108
  private
109
109
 
110
+ POSTFIX_TYPES = Set[:if, :while, :while_post, :until, :until_post].freeze
111
+ def children_in_source_order(node)
112
+ if POSTFIX_TYPES.include?(node.type)
113
+ # All these types have either nodes with expressions, or `nil`
114
+ # so a compact will do, but they need to be sorted.
115
+ node.children.compact.sort_by { |child| child.loc.expression.begin_pos }
116
+ else
117
+ node.children.select do |child|
118
+ child.is_a?(AST::Node) && child.loc && child.loc.expression
119
+ end
120
+ end
121
+ end
122
+
110
123
  def do_associate
111
124
  @mapping = Hash.new { |h, k| h[k] = [] }
112
125
  @comment_num = -1
@@ -131,10 +144,7 @@ module Parser
131
144
  node_loc = node.location
132
145
  if @current_comment.location.line <= node_loc.last_line ||
133
146
  node_loc.is_a?(Map::Heredoc)
134
- node.children.each do |child|
135
- next unless child.is_a?(AST::Node) && child.loc && child.loc.expression
136
- visit(child)
137
- end
147
+ children_in_source_order(node).each { |child| visit(child) }
138
148
 
139
149
  process_trailing_comments(node)
140
150
  end