parser 2.7.0.2 → 2.7.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb64651ef64daa71d4c55d8ab253c90ce470eaf85f3b0ce98dfe5f171a681c80
4
- data.tar.gz: a875a3b5c4a92b7fae3a58c379c300519e7c328641a7cb8d8a65e3f131118eed
3
+ metadata.gz: fb7c9797808fcd8920ef34ccce6dae7f416d0fe3084a863fa3fec7fa30b086fc
4
+ data.tar.gz: 828a937688a35e277c7d435197ca2d023458de366a8e94fbaa67bdd6d3a04145
5
5
  SHA512:
6
- metadata.gz: 8bd9f983ef31671217011a895e07b466f5327578f1dbc2288e5bcd08e367d038e0ee53c35ee1851323c8b04ab20e3fd27c1347a7615c869a99ebcebc8a949baf
7
- data.tar.gz: cab52019893fb86e6fe66aaa9391d9d6cd721daac62550f7a0d2303bafbcc6deac349c4366315f8bc4ba15f6309b7ead3f30e3ff8fcd0e15a394c63c86071db7
6
+ metadata.gz: 679bc103f3d8b2663a1ec2de6c3acac41c90c1d738b40bf4b5ed945b8e3f51e22359caa0789e55009f3bd2c45ac832d00182db9ba9be1f43b18e6fe0e0508278
7
+ data.tar.gz: b4f4d29766e23ca406644fb578e2134f28a9983d765d4aa45117dcb5a9ee012f27c2b8b65eccf11b7e3818b1543a62cc0483aa510a51352bc2260ad0fd650acd
data/.travis.yml CHANGED
@@ -2,26 +2,17 @@ dist: trusty
2
2
  language: ruby
3
3
  matrix:
4
4
  include:
5
- - name: 2.0.0 / Parser tests
6
- rvm: 2.0.0
5
+ - name: 2.4.10 / Parser tests
6
+ rvm: 2.4.10
7
7
  script: bundle exec rake test_cov
8
- - name: 2.2.10 / Parser tests
9
- rvm: 2.2.10
8
+ - name: 2.5.8 / Parser tests
9
+ rvm: 2.5.8
10
10
  script: bundle exec rake test_cov
11
- - name: 2.3.8 / Parser tests
12
- rvm: 2.3.8
11
+ - name: 2.6.6 / Parser tests
12
+ rvm: 2.6.6
13
13
  script: bundle exec rake test_cov
14
- - name: 2.4.9 / Parser tests
15
- rvm: 2.4.9
16
- script: bundle exec rake test_cov
17
- - name: 2.5.7 / Parser tests
18
- rvm: 2.5.7
19
- script: bundle exec rake test_cov
20
- - name: 2.6.5 / Parser tests
21
- rvm: 2.6.5
22
- script: bundle exec rake test_cov
23
- - name: 2.7.0 / Parser tests
24
- rvm: 2.7.0
14
+ - name: 2.7.1 / Parser tests
15
+ rvm: 2.7.1
25
16
  script: bundle exec rake test_cov
26
17
  - name: ruby-head / Parser tests
27
18
  rvm: ruby-head
@@ -32,14 +23,14 @@ matrix:
32
23
  - name: rbx-2 / Parser tests
33
24
  rvm: rbx-2
34
25
  script: bundle exec rake test_cov
35
- - name: 2.5.7 / Rubocop tests
36
- rvm: 2.5.7
26
+ - name: 2.5.8 / Rubocop tests
27
+ rvm: 2.5.8
37
28
  script: ./ci/run_rubocop_specs
38
- - name: 2.6.5 / Rubocop tests
39
- rvm: 2.6.5
29
+ - name: 2.6.6 / Rubocop tests
30
+ rvm: 2.6.6
40
31
  script: ./ci/run_rubocop_specs
41
- - name: 2.7.0 / Rubocop tests
42
- rvm: 2.7.0
32
+ - name: 2.7.1 / Rubocop tests
33
+ rvm: 2.7.1
43
34
  script: ./ci/run_rubocop_specs
44
35
  allow_failures:
45
36
  - rvm: ruby-head
data/CHANGELOG.md CHANGED
@@ -1,9 +1,41 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
- Not released (2020-01-08)
4
+ Not released (2020-04-15)
5
5
  -------------------------
6
6
 
7
+ Features implemented:
8
+ * Add Source::Range#eql? and hash (#675) (Marc-André Lafortune)
9
+ * Source::TreeRewriter: Add #merge, #merge! and #empty? (#674) (Marc-André Lafortune)
10
+
11
+ v2.7.1.0 (2020-04-03)
12
+ ---------------------
13
+
14
+ API modifications:
15
+ * Bump ruby versions to 2.4.10, 2.5.8, 2.6.6, 2.7.1. (#665) (Ilya Bylich)
16
+
17
+ Features implemented:
18
+ * ruby27.y: allow newlines inside braced pattern. (#664) (Ilya Bylich)
19
+ * ruby27.y: Allow trailing comma in hash pattern (#661) (Koichi ITO)
20
+
21
+ v2.7.0.5 (2020-03-20)
22
+ ---------------------
23
+
24
+ Features implemented:
25
+ * ruby27.y: fix array pattern with tail source map (#659) (Vladimir Dementyev)
26
+
27
+ Bugs fixed:
28
+ * builder.rb: fix constant_pattern source map (#660) (Vladimir Dementyev)
29
+
30
+ v2.7.0.4 (2020-03-02)
31
+ ---------------------
32
+
33
+ Bugs fixed:
34
+ * lexer.rl: allow spaces before comments-before-leading-dot. (#654) (Ilya Bylich)
35
+
36
+ v2.7.0.2 (2020-01-08)
37
+ ---------------------
38
+
7
39
  Bugs fixed:
8
40
  * lexer.rl: fix paren_nest for curly braces (#646) (Ilya Bylich)
9
41
 
data/README.md CHANGED
@@ -24,10 +24,11 @@ below for explanation of `emit_*` calls):
24
24
 
25
25
  require 'parser/current'
26
26
  # opt-in to most recent AST format:
27
- Parser::Builders::Default.emit_lambda = true
28
- Parser::Builders::Default.emit_procarg0 = true
29
- Parser::Builders::Default.emit_encoding = true
30
- Parser::Builders::Default.emit_index = true
27
+ Parser::Builders::Default.emit_lambda = true
28
+ Parser::Builders::Default.emit_procarg0 = true
29
+ Parser::Builders::Default.emit_encoding = true
30
+ Parser::Builders::Default.emit_index = true
31
+ Parser::Builders::Default.emit_arg_inside_procarg0 = true
31
32
 
32
33
  Parse a chunk of code:
33
34
 
@@ -234,7 +235,7 @@ Parser implements the MacRuby 0.12 and RubyMotion mid-2015 parsers precisely. Ho
234
235
 
235
236
  ## Known issues
236
237
 
237
- Adding support for the following Ruby MRI features in Parser would needlessly complicate it, and as they all are very specific and rarely occuring corner cases, this is not done.
238
+ Adding support for the following Ruby MRI features in Parser would needlessly complicate it, and as they all are very specific and rarely occurring corner cases, this is not done.
238
239
 
239
240
  Parser has been extensively tested; in particular, it parses almost entire [Rubygems][rg] corpus. For every issue, a breakdown of affected gems is offered.
240
241
 
data/doc/AST_FORMAT.md CHANGED
@@ -1870,6 +1870,27 @@ Format:
1870
1870
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression
1871
1871
  ~~~
1872
1872
 
1873
+ #### With empty else
1874
+
1875
+ Empty `else` differs from the missing (or _implicit_) `else` for pattern matching, since
1876
+ the latter one raises a `NoMatchingPattern` exception. Thus, we need a way to distinguish this
1877
+ two cases in the resulting AST.
1878
+
1879
+ Format:
1880
+
1881
+ ~~~
1882
+ (case-match,
1883
+ (str "str")
1884
+ (in-pattern
1885
+ (match-var :foo)
1886
+ (lvar :bar))
1887
+ (empty-else))
1888
+ "case "str"; in foo; bar; else; end"
1889
+ ~~~~ keyword ~~~~ else
1890
+ ~~~ end
1891
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ expression
1892
+ ~~~
1893
+
1873
1894
  ### In clause
1874
1895
 
1875
1896
  Format:
@@ -2047,7 +2068,7 @@ so a single item match with comma gets interpreted as an array.
2047
2068
  (array-pattern-with-tail
2048
2069
  (match-var :foo))
2049
2070
  "in foo,"
2050
- ~~~ expression
2071
+ ~~~~ expression
2051
2072
  ~~~
2052
2073
 
2053
2074
  ### Matching using hash pattern
@@ -2118,7 +2139,7 @@ Format:
2118
2139
  "in X[^foo bar]"
2119
2140
  ~ begin (const-pattern)
2120
2141
  ~ end (const-pattern)
2121
- ~~~~~~~~~~~ expression (const-pattern)
2142
+ ~~~~~~~~~~~~ expression (const-pattern)
2122
2143
  ~ name (const-pattern.const)
2123
2144
  ~ expression (const-pattern.const)
2124
2145
  ~~~
@@ -2136,7 +2157,24 @@ Format:
2136
2157
  "in X[foo:, bar:]"
2137
2158
  ~ begin (const-pattern)
2138
2159
  ~ end (const-pattern)
2139
- ~~~~~~~~~~~~ expression (const-pattern)
2160
+ ~~~~~~~~~~~~~ expression (const-pattern)
2161
+ ~ name (const-pattern.const)
2162
+ ~ expression (const-pattern.const)
2163
+ ~~~
2164
+
2165
+ #### With array pattern without elements
2166
+
2167
+ Format:
2168
+
2169
+ ~~~
2170
+ (const-pattern
2171
+ (const nil :X)
2172
+ (array-pattern))
2173
+ "in X[]"
2174
+ ~ begin (const-pattern)
2175
+ ~ end (const-pattern)
2176
+ ~~~ expression (const-pattern)
2140
2177
  ~ name (const-pattern.const)
2141
2178
  ~ expression (const-pattern.const)
2179
+ ~~ expression (const-pattern.array_pattern)
2142
2180
  ~~~
@@ -274,6 +274,10 @@ module Parser
274
274
  'Parser::AST::Processor#on_argument instead.'
275
275
  on_argument(node)
276
276
  end
277
+
278
+ def on_empty_else(node)
279
+ node
280
+ end
277
281
  end
278
282
  end
279
283
  end
@@ -1213,6 +1213,7 @@ module Parser
1213
1213
  #
1214
1214
 
1215
1215
  def case_match(case_t, expr, in_bodies, else_t, else_body, end_t)
1216
+ else_body = n(:empty_else, nil, token_map(else_t)) if else_t && !else_body
1216
1217
  n(:case_match, [ expr, *(in_bodies << else_body)],
1217
1218
  condition_map(case_t, expr, nil, nil, else_t, else_body, end_t))
1218
1219
  end
@@ -1313,9 +1314,11 @@ module Parser
1313
1314
  end
1314
1315
 
1315
1316
  def array_pattern(lbrack_t, elements, rbrack_t)
1317
+ return n(:array_pattern, nil, collection_map(lbrack_t, [], rbrack_t)) if elements.nil?
1318
+
1316
1319
  trailing_comma = false
1317
1320
 
1318
- elements = elements.map do |element|
1321
+ node_elements = elements.map do |element|
1319
1322
  if element.type == :match_with_trailing_comma
1320
1323
  trailing_comma = true
1321
1324
  element.children.first
@@ -1326,17 +1329,22 @@ module Parser
1326
1329
  end
1327
1330
 
1328
1331
  node_type = trailing_comma ? :array_pattern_with_tail : :array_pattern
1329
- n(node_type, elements,
1332
+
1333
+ n(node_type, node_elements,
1330
1334
  collection_map(lbrack_t, elements, rbrack_t))
1331
1335
  end
1332
1336
 
1333
- def match_with_trailing_comma(match)
1334
- n(:match_with_trailing_comma, [ match ], nil)
1337
+ def match_with_trailing_comma(match, comma_t)
1338
+ n(:match_with_trailing_comma, [ match ], expr_map(match.loc.expression.join(loc(comma_t))))
1335
1339
  end
1336
1340
 
1337
1341
  def const_pattern(const, ldelim_t, pattern, rdelim_t)
1338
1342
  n(:const_pattern, [const, pattern],
1339
- collection_map(ldelim_t, [pattern], rdelim_t))
1343
+ Source::Map::Collection.new(
1344
+ loc(ldelim_t), loc(rdelim_t),
1345
+ const.loc.expression.join(loc(rdelim_t))
1346
+ )
1347
+ )
1340
1348
  end
1341
1349
 
1342
1350
  def pin(pin_t, var)
@@ -1369,11 +1377,13 @@ module Parser
1369
1377
  pair_keyword(label, value)
1370
1378
  else
1371
1379
  begin_t, parts, end_t = label
1380
+ label_loc = loc(begin_t).join(loc(end_t))
1372
1381
 
1373
1382
  # quoted label like "label": value
1374
1383
  if (var_name = static_string(parts))
1375
- loc = loc(begin_t).join(loc(end_t))
1376
- check_duplicate_pattern_key(var_name, loc)
1384
+ check_duplicate_pattern_key(var_name, label_loc)
1385
+ else
1386
+ diagnostic :error, :pm_interp_in_var_name, nil, label_loc
1377
1387
  end
1378
1388
 
1379
1389
  pair_quoted(begin_t, parts, end_t, value)
@@ -48,7 +48,7 @@ module Parser
48
48
  CurrentRuby = Ruby23
49
49
 
50
50
  when /^2\.4\./
51
- current_version = '2.4.9'
51
+ current_version = '2.4.10'
52
52
  if RUBY_VERSION != current_version
53
53
  warn_syntax_deviation 'parser/ruby24', current_version
54
54
  end
@@ -57,7 +57,7 @@ module Parser
57
57
  CurrentRuby = Ruby24
58
58
 
59
59
  when /^2\.5\./
60
- current_version = '2.5.7'
60
+ current_version = '2.5.8'
61
61
  if RUBY_VERSION != current_version
62
62
  warn_syntax_deviation 'parser/ruby25', current_version
63
63
  end
@@ -66,7 +66,7 @@ module Parser
66
66
  CurrentRuby = Ruby25
67
67
 
68
68
  when /^2\.6\./
69
- current_version = '2.6.5'
69
+ current_version = '2.6.6'
70
70
  if RUBY_VERSION != current_version
71
71
  warn_syntax_deviation 'parser/ruby26', current_version
72
72
  end
@@ -75,7 +75,7 @@ module Parser
75
75
  CurrentRuby = Ruby26
76
76
 
77
77
  when /^2\.7\./
78
- current_version = '2.7.0'
78
+ current_version = '2.7.1'
79
79
  if RUBY_VERSION != current_version
80
80
  warn_syntax_deviation 'parser/ruby27', current_version
81
81
  end
data/lib/parser/lexer.rl CHANGED
@@ -2473,7 +2473,7 @@ class Parser::Lexer
2473
2473
 
2474
2474
  # Here we use '\n' instead of w_newline to not modify @newline_s
2475
2475
  # and eventually properly emit tNL
2476
- (w_space_comment '\n')+
2476
+ (c_space* w_space_comment '\n')+
2477
2477
  => {
2478
2478
  if @version < 27
2479
2479
  # Ruby before 2.7 doesn't support comments before leading dot.
@@ -70,6 +70,7 @@ module Parser
70
70
  :circular_argument_reference => 'circular argument reference %{var_name}',
71
71
  :pm_interp_in_var_name => 'symbol literal with interpolation is not allowed',
72
72
  :lvar_name => "`%{name}' is not allowed as a local variable name",
73
+ :undefined_lvar => "no such local variable: `%{name}'",
73
74
  :duplicate_variable_name => 'duplicate variable name %{name}',
74
75
  :duplicate_pattern_key => 'duplicate hash pattern key %{name}',
75
76
 
data/lib/parser/meta.rb CHANGED
@@ -31,6 +31,7 @@ module Parser
31
31
  match_var pin match_alt match_as match_rest
32
32
  array_pattern match_with_trailing_comma array_pattern_with_tail
33
33
  hash_pattern const_pattern if_guard unless_guard match_nil_pattern
34
+ empty_else
34
35
  ).map(&:to_sym).to_set.freeze
35
36
 
36
37
  end # Meta
data/lib/parser/ruby27.y CHANGED
@@ -1784,7 +1784,7 @@ opt_block_args_tail:
1784
1784
  # array patterns that end with comma
1785
1785
  # like 1, 2,
1786
1786
  # must be emitted as `array_pattern_with_tail`
1787
- item = @builder.match_with_trailing_comma(val[0])
1787
+ item = @builder.match_with_trailing_comma(val[0], val[1])
1788
1788
  result = @builder.array_pattern(nil, [ item ], nil)
1789
1789
  }
1790
1790
  | p_expr tCOMMA p_args
@@ -1841,7 +1841,8 @@ opt_block_args_tail:
1841
1841
  }
1842
1842
  | p_const tLPAREN2 rparen
1843
1843
  {
1844
- result = @builder.const_pattern(val[0], val[1], nil, val[2])
1844
+ pattern = @builder.array_pattern(val[1], nil, val[2])
1845
+ result = @builder.const_pattern(val[0], val[1], pattern, val[2])
1845
1846
  }
1846
1847
  | p_const p_lbracket p_args rbracket
1847
1848
  {
@@ -1857,7 +1858,8 @@ opt_block_args_tail:
1857
1858
  }
1858
1859
  | p_const tLBRACK2 rbracket
1859
1860
  {
1860
- result = @builder.const_pattern(val[0], val[1], nil, val[2])
1861
+ pattern = @builder.array_pattern(val[1], nil, val[2])
1862
+ result = @builder.const_pattern(val[0], val[1], pattern, val[2])
1861
1863
  }
1862
1864
  | tLBRACK
1863
1865
  {
@@ -1875,13 +1877,16 @@ opt_block_args_tail:
1875
1877
  | tLBRACE
1876
1878
  {
1877
1879
  @pattern_hash_keys.push
1880
+ result = @lexer.in_kwarg
1881
+ @lexer.in_kwarg = false
1878
1882
  }
1879
- p_kwargs tRCURLY
1883
+ p_kwargs rbrace
1880
1884
  {
1881
1885
  @pattern_hash_keys.pop
1886
+ @lexer.in_kwarg = val[1]
1882
1887
  result = @builder.hash_pattern(val[0], val[2], val[3])
1883
1888
  }
1884
- | tLBRACE tRCURLY
1889
+ | tLBRACE rbrace
1885
1890
  {
1886
1891
  result = @builder.hash_pattern(val[0], [], val[1])
1887
1892
  }
@@ -1932,7 +1937,7 @@ opt_block_args_tail:
1932
1937
  # array patterns that end with comma
1933
1938
  # like [1, 2,]
1934
1939
  # must be emitted as `array_pattern_with_tail`
1935
- item = @builder.match_with_trailing_comma(val[0])
1940
+ item = @builder.match_with_trailing_comma(val[0], val[1])
1936
1941
  result = [ item ]
1937
1942
  }
1938
1943
  | p_args_head p_arg tCOMMA
@@ -1940,7 +1945,7 @@ opt_block_args_tail:
1940
1945
  # array patterns that end with comma
1941
1946
  # like [1, 2,]
1942
1947
  # must be emitted as `array_pattern_with_tail`
1943
- last_item = @builder.match_with_trailing_comma(val[1])
1948
+ last_item = @builder.match_with_trailing_comma(val[1], val[2])
1944
1949
  result = [ *val[0], last_item ]
1945
1950
  }
1946
1951
 
@@ -1984,6 +1989,10 @@ opt_block_args_tail:
1984
1989
  {
1985
1990
  result = val[0]
1986
1991
  }
1992
+ | p_kwarg tCOMMA
1993
+ {
1994
+ result = val[0]
1995
+ }
1987
1996
  | p_kwrest
1988
1997
  {
1989
1998
  result = val[0]
@@ -2096,6 +2105,11 @@ opt_block_args_tail:
2096
2105
 
2097
2106
  p_var_ref: tCARET tIDENTIFIER
2098
2107
  {
2108
+ name = val[1][0]
2109
+ unless static_env.declared?(name)
2110
+ diagnostic :error, :undefined_lvar, { :name => name }, val[1]
2111
+ end
2112
+
2099
2113
  lvar = @builder.accessible(@builder.ident(val[1]))
2100
2114
  result = @builder.pin(val[0], lvar)
2101
2115
  }
@@ -2891,6 +2905,10 @@ keyword_variable: kNIL
2891
2905
  {
2892
2906
  result = val[1]
2893
2907
  }
2908
+ rbrace: opt_nl tRCURLY
2909
+ {
2910
+ result = val[1]
2911
+ }
2894
2912
  trailer: | tNL | tCOMMA
2895
2913
 
2896
2914
  term: tSEMI
@@ -298,6 +298,15 @@ module Parser
298
298
  (@end_pos <=> other.end_pos)
299
299
  end
300
300
 
301
+ alias_method :eql?, :==
302
+
303
+ ##
304
+ # Support for Ranges be used in as Hash indices and in Sets.
305
+ #
306
+ def hash
307
+ [@source_buffer, @begin_pos, @end_pos].hash
308
+ end
309
+
301
310
  ##
302
311
  # @return [String] a human-readable representation of this range.
303
312
  #
@@ -117,6 +117,43 @@ module Parser
117
117
  @action_root = TreeRewriter::Action.new(all_encompassing_range, @enforcer)
118
118
  end
119
119
 
120
+ ##
121
+ # Returns true iff no (non trivial) update has been recorded
122
+ #
123
+ # @return [Boolean]
124
+ #
125
+ def empty?
126
+ @action_root.empty?
127
+ end
128
+
129
+ ##
130
+ # Merges the updates of argument with the receiver.
131
+ # Policies of the receiver are used.
132
+ #
133
+ # @param [Rewriter] with
134
+ # @return [Rewriter] self
135
+ # @raise [ClobberingError] when clobbering is detected
136
+ #
137
+ def merge!(with)
138
+ raise 'TreeRewriter are not for the same source_buffer' unless
139
+ source_buffer == with.source_buffer
140
+
141
+ @action_root = @action_root.combine(with.action_root)
142
+ self
143
+ end
144
+
145
+ ##
146
+ # Returns a new rewriter that consists of the updates of the received
147
+ # and the given argument. Policies of the receiver are used.
148
+ #
149
+ # @param [Rewriter] with
150
+ # @return [Rewriter] merge of receiver and argument
151
+ # @raise [ClobberingError] when clobbering is detected
152
+ #
153
+ def merge(with)
154
+ dup.merge!(with)
155
+ end
156
+
120
157
  ##
121
158
  # Replaces the code of the source range `range` with `content`.
122
159
  #
@@ -203,10 +240,9 @@ module Parser
203
240
  ##
204
241
  # Provides a protected block where a sequence of multiple rewrite actions
205
242
  # are handled atomically. If any of the actions failed by clobbering,
206
- # all the actions are rolled back.
243
+ # all the actions are rolled back. Transactions can be nested.
207
244
  #
208
245
  # @raise [RuntimeError] when no block is passed
209
- # @raise [RuntimeError] when already in a transaction
210
246
  #
211
247
  def transaction
212
248
  unless block_given?
@@ -256,6 +292,10 @@ module Parser
256
292
 
257
293
  extend Deprecation
258
294
 
295
+ protected
296
+
297
+ attr_reader :action_root
298
+
259
299
  private
260
300
 
261
301
  ACTIONS = %i[accept warn raise].freeze
@@ -25,12 +25,18 @@ module Parser
25
25
  freeze
26
26
  end
27
27
 
28
- # Assumes action.children.empty?
29
28
  def combine(action)
30
- return self unless action.insertion? || action.replacement # Ignore empty action
29
+ return self if action.empty? # Ignore empty action
31
30
  do_combine(action)
32
31
  end
33
32
 
33
+ def empty?
34
+ @insert_before.empty? &&
35
+ @insert_after.empty? &&
36
+ @children.empty? &&
37
+ (@replacement == nil || (@replacement.empty? && @range.empty?))
38
+ end
39
+
34
40
  def ordered_replacements
35
41
  reps = []
36
42
  reps << [@range.begin, @insert_before] unless @insert_before.empty?
@@ -46,9 +52,11 @@ module Parser
46
52
 
47
53
  protected
48
54
 
49
- def with(range: @range, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
55
+ attr_reader :children
56
+
57
+ def with(range: @range, enforcer: @enforcer, children: @children, insert_before: @insert_before, replacement: @replacement, insert_after: @insert_after)
50
58
  children = swallow(children) if replacement
51
- self.class.new(range, @enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
59
+ self.class.new(range, enforcer, children: children, insert_before: insert_before, replacement: replacement, insert_after: insert_after)
52
60
  end
53
61
 
54
62
  # Assumes range.contains?(action.range) && action.children.empty?
@@ -56,11 +64,11 @@ module Parser
56
64
  if action.range == @range
57
65
  merge(action)
58
66
  else
59
- place_in_hierachy(action)
67
+ place_in_hierarchy(action)
60
68
  end
61
69
  end
62
70
 
63
- def place_in_hierachy(action)
71
+ def place_in_hierarchy(action)
64
72
  family = @children.group_by { |child| child.relationship_with(action) }
65
73
 
66
74
  if family[:fusible]
@@ -69,7 +77,8 @@ module Parser
69
77
  extra_sibbling = if family[:parent] # action should be a descendant of one of the children
70
78
  family[:parent][0].do_combine(action)
71
79
  elsif family[:child] # or it should become the parent of some of the children,
72
- action.with(children: family[:child])
80
+ action.with(children: family[:child], enforcer: @enforcer)
81
+ .combine_children(action.children)
73
82
  else # or else it should become an additional child
74
83
  action
75
84
  end
@@ -77,6 +86,13 @@ module Parser
77
86
  end
78
87
  end
79
88
 
89
+ # Assumes more_children all contained within @range
90
+ def combine_children(more_children)
91
+ more_children.inject(self) do |parent, new_child|
92
+ parent.place_in_hierarchy(new_child)
93
+ end
94
+ end
95
+
80
96
  def fuse_deletions(action, fusible, other_sibblings)
81
97
  without_fusible = with(children: other_sibblings)
82
98
  fused_range = [action, *fusible].map(&:range).inject(:join)
@@ -109,7 +125,7 @@ module Parser
109
125
  insert_before: "#{action.insert_before}#{insert_before}",
110
126
  replacement: action.replacement || @replacement,
111
127
  insert_after: "#{insert_after}#{action.insert_after}",
112
- )
128
+ ).combine_children(action.children)
113
129
  end
114
130
 
115
131
  def call_enforcer_for_merge(action)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Parser
4
- VERSION = '2.7.0.2'
4
+ VERSION = '2.7.1.1'
5
5
  end
data/parser.gemspec CHANGED
@@ -13,6 +13,13 @@ Gem::Specification.new do |spec|
13
13
  spec.homepage = 'https://github.com/whitequark/parser'
14
14
  spec.license = 'MIT'
15
15
 
16
+ spec.metadata = {
17
+ 'bug_tracker_uri' => 'https://github.com/whitequark/parser/issues',
18
+ 'changelog_uri' => "https://github.com/whitequark/parser/blob/v#{spec.version}/CHANGELOG.md",
19
+ 'documentation_uri' => "https://www.rubydoc.info/gems/parser/#{spec.version}",
20
+ 'source_code_uri' => "https://github.com/whitequark/parser/tree/v#{spec.version}"
21
+ }
22
+
16
23
  spec.files = `git ls-files`.split + %w(
17
24
  lib/parser/lexer.rb
18
25
  lib/parser/ruby18.rb
data/test/test_parser.rb CHANGED
@@ -7644,12 +7644,26 @@ class TestParser < Minitest::Test
7644
7644
  %q{},
7645
7645
  SINCE_2_7)
7646
7646
 
7647
+ assert_parses(
7648
+ s(:send,
7649
+ s(:send, nil, :a), :foo),
7650
+ %Q{a #\n #\n.foo\n},
7651
+ %q{},
7652
+ SINCE_2_7)
7653
+
7647
7654
  assert_parses(
7648
7655
  s(:csend,
7649
7656
  s(:send, nil, :a), :foo),
7650
7657
  %Q{a #\n#\n&.foo\n},
7651
7658
  %q{},
7652
7659
  SINCE_2_7)
7660
+
7661
+ assert_parses(
7662
+ s(:csend,
7663
+ s(:send, nil, :a), :foo),
7664
+ %Q{a #\n #\n&.foo\n},
7665
+ %q{},
7666
+ SINCE_2_7)
7653
7667
  end
7654
7668
 
7655
7669
  def test_comments_before_leading_dot__before_27
@@ -8408,7 +8422,7 @@ class TestParser < Minitest::Test
8408
8422
  nil,
8409
8423
  s(:nil)),
8410
8424
  %q{in x, then nil},
8411
- %q{ ~ expression (in_pattern.array_pattern_with_tail)}
8425
+ %q{ ~~ expression (in_pattern.array_pattern_with_tail)}
8412
8426
  )
8413
8427
 
8414
8428
  assert_parses_pattern_match(
@@ -8454,7 +8468,7 @@ class TestParser < Minitest::Test
8454
8468
  nil,
8455
8469
  s(:nil)),
8456
8470
  %q{in x, y, then nil},
8457
- %q{ ~~~~ expression (in_pattern.array_pattern_with_tail)}
8471
+ %q{ ~~~~~ expression (in_pattern.array_pattern_with_tail)}
8458
8472
  )
8459
8473
 
8460
8474
  assert_parses_pattern_match(
@@ -8661,6 +8675,18 @@ class TestParser < Minitest::Test
8661
8675
  | ~ end (in_pattern.hash_pattern)}
8662
8676
  )
8663
8677
 
8678
+ assert_parses_pattern_match(
8679
+ s(:in_pattern,
8680
+ s(:hash_pattern,
8681
+ s(:pair, s(:sym, :a), s(:int, 1))),
8682
+ nil,
8683
+ s(:true)),
8684
+ %q{in { a: 1, } then true},
8685
+ %q{ ~~~~~~~~~ expression (in_pattern.hash_pattern)
8686
+ | ~ begin (in_pattern.hash_pattern)
8687
+ | ~ end (in_pattern.hash_pattern)}
8688
+ )
8689
+
8664
8690
  assert_parses_pattern_match(
8665
8691
  s(:in_pattern,
8666
8692
  s(:hash_pattern,
@@ -8732,6 +8758,67 @@ class TestParser < Minitest::Test
8732
8758
  %q{in a: 1, _a:, ** then true},
8733
8759
  %q{ ~~~~~~~~~~~~~ expression (in_pattern.hash_pattern)}
8734
8760
  )
8761
+
8762
+ assert_parses_pattern_match(
8763
+ s(:in_pattern,
8764
+ s(:hash_pattern,
8765
+ s(:pair,
8766
+ s(:sym, :a),
8767
+ s(:int, 1))), nil,
8768
+ s(:false)),
8769
+ %q{
8770
+ in {a: 1
8771
+ }
8772
+ false
8773
+ },
8774
+ %q{}
8775
+ )
8776
+
8777
+
8778
+ assert_parses_pattern_match(
8779
+ s(:in_pattern,
8780
+ s(:hash_pattern,
8781
+ s(:pair,
8782
+ s(:sym, :a),
8783
+ s(:int, 2))), nil,
8784
+ s(:false)),
8785
+ %q{
8786
+ in {a:
8787
+ 2}
8788
+ false
8789
+ },
8790
+ %q{}
8791
+ )
8792
+
8793
+ assert_parses_pattern_match(
8794
+ s(:in_pattern,
8795
+ s(:hash_pattern,
8796
+ s(:pair,
8797
+ s(:sym, :a),
8798
+ s(:hash_pattern,
8799
+ s(:match_var, :b))),
8800
+ s(:match_var, :c)), nil,
8801
+ s(:send, nil, :p,
8802
+ s(:lvar, :c))),
8803
+ %q{
8804
+ in a: {b:}, c:
8805
+ p c
8806
+ },
8807
+ %q{}
8808
+ )
8809
+
8810
+ assert_parses_pattern_match(
8811
+ s(:in_pattern,
8812
+ s(:hash_pattern,
8813
+ s(:match_var, :a)), nil,
8814
+ s(:true)),
8815
+ %q{
8816
+ in {a:
8817
+ }
8818
+ true
8819
+ },
8820
+ %q{}
8821
+ )
8735
8822
  end
8736
8823
 
8737
8824
  def test_pattern_matching_hash_with_string_keys
@@ -8839,6 +8926,15 @@ class TestParser < Minitest::Test
8839
8926
  )
8840
8927
  end
8841
8928
 
8929
+ def test_pattern_matching_hash_with_string_interpolation_keys
8930
+ assert_diagnoses(
8931
+ [:error, :pm_interp_in_var_name],
8932
+ %q{case a; in "#{a}": 1; end},
8933
+ %q{ ~~~~~~~ location},
8934
+ SINCE_2_7
8935
+ )
8936
+ end
8937
+
8842
8938
  def test_pattern_matching_keyword_variable
8843
8939
  assert_parses_pattern_match(
8844
8940
  s(:in_pattern,
@@ -8970,7 +9066,7 @@ class TestParser < Minitest::Test
8970
9066
  nil,
8971
9067
  s(:true)),
8972
9068
  %q{in A(1, 2) then true},
8973
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9069
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
8974
9070
  | ~ begin (in_pattern.const_pattern)
8975
9071
  | ~ end (in_pattern.const_pattern)
8976
9072
  | ~ expression (in_pattern.const_pattern.const)
@@ -8986,7 +9082,7 @@ class TestParser < Minitest::Test
8986
9082
  nil,
8987
9083
  s(:true)),
8988
9084
  %q{in A(x:) then true},
8989
- %q{ ~~~~ expression (in_pattern.const_pattern)
9085
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
8990
9086
  | ~ begin (in_pattern.const_pattern)
8991
9087
  | ~ end (in_pattern.const_pattern)
8992
9088
  | ~ expression (in_pattern.const_pattern.const)
@@ -8997,13 +9093,15 @@ class TestParser < Minitest::Test
8997
9093
  s(:in_pattern,
8998
9094
  s(:const_pattern,
8999
9095
  s(:const, nil, :A),
9000
- nil),
9096
+ s(:array_pattern)),
9001
9097
  nil,
9002
9098
  s(:true)),
9003
9099
  %q{in A() then true},
9004
- %q{ ~~ expression (in_pattern.const_pattern)
9100
+ %q{ ~~~ expression (in_pattern.const_pattern)
9005
9101
  | ~ begin (in_pattern.const_pattern)
9006
- | ~ end (in_pattern.const_pattern)}
9102
+ | ~ end (in_pattern.const_pattern)
9103
+ | ~ expression (in_pattern.const_pattern.const)
9104
+ | ~~ expression (in_pattern.const_pattern.array_pattern)}
9007
9105
  )
9008
9106
 
9009
9107
  assert_parses_pattern_match(
@@ -9016,7 +9114,7 @@ class TestParser < Minitest::Test
9016
9114
  nil,
9017
9115
  s(:true)),
9018
9116
  %q{in A[1, 2] then true},
9019
- %q{ ~~~~~~ expression (in_pattern.const_pattern)
9117
+ %q{ ~~~~~~~ expression (in_pattern.const_pattern)
9020
9118
  | ~ begin (in_pattern.const_pattern)
9021
9119
  | ~ end (in_pattern.const_pattern)
9022
9120
  | ~ expression (in_pattern.const_pattern.const)
@@ -9032,7 +9130,7 @@ class TestParser < Minitest::Test
9032
9130
  nil,
9033
9131
  s(:true)),
9034
9132
  %q{in A[x:] then true},
9035
- %q{ ~~~~ expression (in_pattern.const_pattern)
9133
+ %q{ ~~~~~ expression (in_pattern.const_pattern)
9036
9134
  | ~ begin (in_pattern.const_pattern)
9037
9135
  | ~ end (in_pattern.const_pattern)
9038
9136
  | ~ expression (in_pattern.const_pattern.const)
@@ -9043,13 +9141,14 @@ class TestParser < Minitest::Test
9043
9141
  s(:in_pattern,
9044
9142
  s(:const_pattern,
9045
9143
  s(:const, nil, :A),
9046
- nil),
9144
+ s(:array_pattern)),
9047
9145
  nil,
9048
9146
  s(:true)),
9049
9147
  %q{in A[] then true},
9050
- %q{ ~~ expression (in_pattern.const_pattern)
9148
+ %q{ ~~~ expression (in_pattern.const_pattern)
9051
9149
  | ~ begin (in_pattern.const_pattern)
9052
- | ~ end (in_pattern.const_pattern)}
9150
+ | ~ end (in_pattern.const_pattern)
9151
+ | ~~ expression (in_pattern.const_pattern.array_pattern)}
9053
9152
  )
9054
9153
  end
9055
9154
 
@@ -9093,6 +9192,20 @@ class TestParser < Minitest::Test
9093
9192
  )
9094
9193
  end
9095
9194
 
9195
+ def test_pattern_matching_blank_else
9196
+ assert_parses(
9197
+ s(:case_match,
9198
+ s(:int, 1),
9199
+ s(:in_pattern,
9200
+ s(:int, 2), nil,
9201
+ s(:int, 3)),
9202
+ s(:empty_else)),
9203
+ %q{case 1; in 2; 3; else; end},
9204
+ %q{ ~~~~ else},
9205
+ SINCE_2_7
9206
+ )
9207
+ end
9208
+
9096
9209
  def assert_pattern_matching_defines_local_variables(match_code, lvar_names, versions = SINCE_2_7)
9097
9210
  code = "case 1; #{match_code}; then [#{lvar_names.join(', ')}]; end"
9098
9211
 
@@ -9295,6 +9408,14 @@ class TestParser < Minitest::Test
9295
9408
  SINCE_2_7)
9296
9409
  end
9297
9410
 
9411
+ def test_pattern_matching_required_bound_variable_before_pin
9412
+ assert_diagnoses(
9413
+ [:error, :undefined_lvar, { :name => 'a' }],
9414
+ %{case 0; in ^a; true; end},
9415
+ %{ ^ location},
9416
+ SINCE_2_7)
9417
+ end
9418
+
9298
9419
  def test_parser_bug_645
9299
9420
  assert_parses(
9300
9421
  s(:block,
@@ -31,16 +31,16 @@ class TestSourceCommentAssociator < Minitest::Test
31
31
  ast, associations = associate(<<-END)
32
32
  #!/usr/bin/env ruby
33
33
  # coding: utf-8
34
- # class preceeding
35
- # another class preceeding
34
+ # class preceding
35
+ # another class preceding
36
36
  class Foo # class keyword line
37
- # method foo preceeding
37
+ # method foo preceding
38
38
  def foo
39
39
  puts 'foo'
40
40
  end # method foo decorating
41
- # method bar preceeding
41
+ # method bar preceding
42
42
  def bar
43
- # expression preceeding
43
+ # expression preceding
44
44
  1 + # 1 decorating
45
45
  2
46
46
  # method bar sparse
@@ -58,8 +58,8 @@ end # class decorating
58
58
 
59
59
  assert_equal 6, associations.size
60
60
  assert_equal [
61
- '# class preceeding',
62
- '# another class preceeding',
61
+ '# class preceding',
62
+ '# another class preceding',
63
63
  '# class sparse',
64
64
  '# class decorating'
65
65
  ], associations[klass_node].map(&:text)
@@ -67,16 +67,16 @@ end # class decorating
67
67
  '# class keyword line'
68
68
  ], associations[klass_name_node].map(&:text)
69
69
  assert_equal [
70
- '# method foo preceeding',
70
+ '# method foo preceding',
71
71
  '# method foo decorating'
72
72
  ], associations[foo_node].map(&:text)
73
73
  assert_equal [
74
- '# method bar preceeding',
74
+ '# method bar preceding',
75
75
  '# method bar sparse',
76
76
  '# method bar decorating'
77
77
  ], associations[bar_node].map(&:text)
78
78
  assert_equal [
79
- '# expression preceeding'
79
+ '# expression preceding'
80
80
  ], associations[expr_node].map(&:text)
81
81
  assert_equal [
82
82
  '# 1 decorating'
@@ -112,16 +112,16 @@ end
112
112
  ast, associations = associate_locations(<<-END)
113
113
  #!/usr/bin/env ruby
114
114
  # coding: utf-8
115
- # class preceeding
116
- # another class preceeding
115
+ # class preceding
116
+ # another class preceding
117
117
  class Foo # class keyword line
118
- # method foo preceeding
118
+ # method foo preceding
119
119
  def foo
120
120
  puts 'foo'
121
121
  end # method foo decorating
122
- # method bar preceeding
122
+ # method bar preceding
123
123
  def bar
124
- # expression preceeding
124
+ # expression preceding
125
125
  1 + # 1 decorating
126
126
  2
127
127
  # method bar sparse
@@ -139,8 +139,8 @@ end # class decorating
139
139
 
140
140
  assert_equal 6, associations.size
141
141
  assert_equal [
142
- '# class preceeding',
143
- '# another class preceeding',
142
+ '# class preceding',
143
+ '# another class preceding',
144
144
  '# class sparse',
145
145
  '# class decorating'
146
146
  ], associations[klass_node.loc].map(&:text)
@@ -148,16 +148,16 @@ end # class decorating
148
148
  '# class keyword line'
149
149
  ], associations[klass_name_node.loc].map(&:text)
150
150
  assert_equal [
151
- '# method foo preceeding',
151
+ '# method foo preceding',
152
152
  '# method foo decorating'
153
153
  ], associations[foo_node.loc].map(&:text)
154
154
  assert_equal [
155
- '# method bar preceeding',
155
+ '# method bar preceding',
156
156
  '# method bar sparse',
157
157
  '# method bar decorating'
158
158
  ], associations[bar_node.loc].map(&:text)
159
159
  assert_equal [
160
- '# expression preceeding'
160
+ '# expression preceding'
161
161
  ], associations[expr_node.loc].map(&:text)
162
162
  assert_equal [
163
163
  '# 1 decorating'
@@ -169,4 +169,19 @@ class TestSourceRange < Minitest::Test
169
169
  assert_equal 1, sr3.begin_pos
170
170
  assert_equal 4, sr3.end_pos
171
171
  end
172
+
173
+ def test_eql_and_hash
174
+ assert_equal false, @sr1_3.eql?(@sr3_3)
175
+ assert @sr1_3.hash != @sr3_3.hash
176
+
177
+ also_1_3 = @sr3_3.with(begin_pos: 1)
178
+ assert_equal true, @sr1_3.eql?(also_1_3)
179
+ assert_equal @sr1_3.hash, also_1_3.hash
180
+
181
+ buf2 = Parser::Source::Buffer.new('(string)')
182
+ buf2.source = "foobar\nbaz"
183
+ from_other_buf = Parser::Source::Range.new(buf2, 1, 3)
184
+ assert_equal false, @sr1_3.eql?(from_other_buf)
185
+ assert @sr1_3.hash != from_other_buf.hash
186
+ end
172
187
  end
@@ -8,8 +8,10 @@ class TestSourceTreeRewriter < Minitest::Test
8
8
  @buf.source = 'puts(:hello, :world)'
9
9
 
10
10
  @hello = range(5, 6)
11
+ @ll = range(7, 2)
11
12
  @comma_space = range(11,2)
12
13
  @world = range(13,6)
14
+ @whole = range(0, @buf.source.length)
13
15
  end
14
16
 
15
17
  def range(from, len)
@@ -17,11 +19,11 @@ class TestSourceTreeRewriter < Minitest::Test
17
19
  end
18
20
 
19
21
  # Returns either:
20
- # - String (Normal operation)
22
+ # - yield rewriter
21
23
  # - [diagnostic, ...] (Diagnostics)
22
24
  # - Parser::ClobberingError
23
25
  #
24
- def apply(actions, **policy)
26
+ def build(actions, **policy)
25
27
  diagnostics = []
26
28
  diags = -> { diagnostics.flatten.map(&:strip).join("\n") }
27
29
  rewriter = Parser::Source::TreeRewriter.new(@buf, **policy)
@@ -30,7 +32,7 @@ class TestSourceTreeRewriter < Minitest::Test
30
32
  rewriter.public_send(action, range, *args)
31
33
  end
32
34
  if diagnostics.empty?
33
- rewriter.process
35
+ yield rewriter
34
36
  else
35
37
  diags.call
36
38
  end
@@ -38,6 +40,15 @@ class TestSourceTreeRewriter < Minitest::Test
38
40
  [::Parser::ClobberingError, diags.call]
39
41
  end
40
42
 
43
+ # Returns either:
44
+ # - String (Normal operation)
45
+ # - [diagnostic, ...] (Diagnostics)
46
+ # - Parser::ClobberingError
47
+ #
48
+ def apply(actions, **policy)
49
+ build(actions, **policy) { |rewriter| rewriter.process }
50
+ end
51
+
41
52
  # Expects ordered actions to be grouped together
42
53
  def check_actions(expected, grouped_actions, **policy)
43
54
  grouped_actions.permutation do |sequence|
@@ -170,4 +181,73 @@ DIAGNOSTIC
170
181
  rewriter = Parser::Source::TreeRewriter.new(@buf)
171
182
  assert_raises(IndexError) { rewriter.insert_before(range(0, 100), 'hola') }
172
183
  end
184
+
185
+ def test_empty
186
+ rewriter = Parser::Source::TreeRewriter.new(@buf)
187
+ assert_equal true, rewriter.empty?
188
+
189
+ # This is a trivial wrap
190
+ rewriter.wrap(range(2,3), '', '')
191
+ assert_equal true, rewriter.empty?
192
+
193
+ # This is a trivial deletion
194
+ rewriter.remove(range(2,0))
195
+ assert_equal true, rewriter.empty?
196
+
197
+ rewriter.remove(range(2,3))
198
+ assert_equal false, rewriter.empty?
199
+ end
200
+
201
+ # splits array into two groups, yield all such possible pairs of groups
202
+ # each_split([1, 2, 3, 4]) yields [1, 2], [3, 4];
203
+ # then [1, 3], [2, 4]
204
+ # ...
205
+ # and finally [3, 4], [1, 2]
206
+ def each_split(array)
207
+ n = array.size
208
+ first_split_size = n.div(2)
209
+ splitting = (0...n).to_set
210
+ splitting.to_a.combination(first_split_size) do |indices|
211
+ yield array.values_at(*indices),
212
+ array.values_at(*(splitting - indices))
213
+ end
214
+ end
215
+
216
+ # Checks that `actions+extra` give the same result when
217
+ # made in order or from subgroups that are later merged.
218
+ # The `extra` actions are always added at the end of the second group.
219
+ #
220
+ def check_all_merge_possibilities(actions, extra, **policy)
221
+ expected = apply(actions + extra, **policy)
222
+
223
+ each_split(actions) do |actions_1, actions_2|
224
+ build(actions_1, **policy) do |rewriter_1|
225
+ build(actions_2 + extra, **policy) do |rewriter_2|
226
+ result = rewriter_1.merge(rewriter_2).process
227
+ assert_equal(expected, result,
228
+ "Group 1: #{actions_1.inspect}\n\n" +
229
+ "Group 2: #{(actions_2 + extra).inspect}"
230
+ )
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ def test_merge
237
+ check_all_merge_possibilities([
238
+ [:wrap, @whole, '<', '>'],
239
+ [:replace, @comma_space, ' => '],
240
+ [:wrap, @hello, '!', '!'],
241
+ # Following two wraps must have same value as they
242
+ # will be applied in different orders...
243
+ [:wrap, @hello.join(@world), '{', '}'],
244
+ [:wrap, @hello.join(@world), '{', '}'],
245
+ [:remove, @ll],
246
+ [:replace, @world, ':everybody'],
247
+ [:wrap, @world, '[', ']']
248
+ ],
249
+ [ # ... but this one is always going to be applied last (extra)
250
+ [:wrap, @hello.join(@world), '@', '@'],
251
+ ])
252
+ end
173
253
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0.2
4
+ version: 2.7.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - whitequark
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-08 00:00:00.000000000 Z
11
+ date: 2020-04-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ast
@@ -295,7 +295,11 @@ files:
295
295
  homepage: https://github.com/whitequark/parser
296
296
  licenses:
297
297
  - MIT
298
- metadata: {}
298
+ metadata:
299
+ bug_tracker_uri: https://github.com/whitequark/parser/issues
300
+ changelog_uri: https://github.com/whitequark/parser/blob/v2.7.1.1/CHANGELOG.md
301
+ documentation_uri: https://www.rubydoc.info/gems/parser/2.7.1.1
302
+ source_code_uri: https://github.com/whitequark/parser/tree/v2.7.1.1
299
303
  post_install_message:
300
304
  rdoc_options: []
301
305
  require_paths: