syntax_tree 2.0.0 → 2.1.1

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.
@@ -163,6 +163,13 @@ module SyntaxTree
163
163
  line_counts[lineno - 1][column]
164
164
  end
165
165
 
166
+ # This represents the current column we're in relative to the beginning of
167
+ # the current line.
168
+ def current_column
169
+ line = line_counts[lineno - 1]
170
+ line[column].to_i - line.start
171
+ end
172
+
166
173
  # As we build up a list of tokens, we'll periodically need to go backwards
167
174
  # and find the ones that we've already hit in order to determine the
168
175
  # location information for nodes that use them. For example, if you have a
@@ -251,10 +258,13 @@ module SyntaxTree
251
258
  def on_BEGIN(statements)
252
259
  lbrace = find_token(LBrace)
253
260
  rbrace = find_token(RBrace)
261
+ start_char = find_next_statement_start(lbrace.location.end_char)
254
262
 
255
263
  statements.bind(
256
- find_next_statement_start(lbrace.location.end_char),
257
- rbrace.location.start_char
264
+ start_char,
265
+ start_char - line_counts[lbrace.location.start_line - 1].start,
266
+ rbrace.location.start_char,
267
+ rbrace.location.start_column,
258
268
  )
259
269
 
260
270
  keyword = find_token(Kw, "BEGIN")
@@ -271,7 +281,7 @@ module SyntaxTree
271
281
  def on_CHAR(value)
272
282
  CHAR.new(
273
283
  value: value,
274
- location: Location.token(line: lineno, char: char_pos, size: value.size)
284
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
275
285
  )
276
286
  end
277
287
 
@@ -280,10 +290,13 @@ module SyntaxTree
280
290
  def on_END(statements)
281
291
  lbrace = find_token(LBrace)
282
292
  rbrace = find_token(RBrace)
293
+ start_char = find_next_statement_start(lbrace.location.end_char)
283
294
 
284
295
  statements.bind(
285
- find_next_statement_start(lbrace.location.end_char),
286
- rbrace.location.start_char
296
+ start_char,
297
+ start_char - line_counts[lbrace.location.start_line - 1].start,
298
+ rbrace.location.start_char,
299
+ rbrace.location.start_column
287
300
  )
288
301
 
289
302
  keyword = find_token(Kw, "END")
@@ -301,7 +314,7 @@ module SyntaxTree
301
314
  @__end__ =
302
315
  EndContent.new(
303
316
  value: source[(char_pos + value.length)..-1],
304
- location: Location.token(line: lineno, char: char_pos, size: value.size)
317
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
305
318
  )
306
319
  end
307
320
 
@@ -401,12 +414,19 @@ module SyntaxTree
401
414
  # (false | untyped) block
402
415
  # ) -> Args
403
416
  def on_args_add_block(arguments, block)
417
+ # First, see if there is an & operator that could potentially be
418
+ # associated with the block part of this args_add_block. If there is not,
419
+ # then just return the arguments.
404
420
  operator = find_token(Op, "&", consume: false)
405
-
406
- # If we can't find the & operator, then there's no block to add to the
407
- # list, so we're just going to return the arguments as-is.
408
421
  return arguments unless operator
409
422
 
423
+ # If there are any arguments and the operator we found from the list is
424
+ # not after them, then we're going to return the arguments as-is because
425
+ # we're looking at an & that occurs before the arguments are done.
426
+ if arguments.parts.any? && operator.location.start_char < arguments.location.end_char
427
+ return arguments
428
+ end
429
+
410
430
  # Now we know we have an & operator, so we're going to delete it from the
411
431
  # list of tokens to make sure it doesn't get confused with anything else.
412
432
  tokens.delete(operator)
@@ -415,13 +435,6 @@ module SyntaxTree
415
435
  location = operator.location
416
436
  location = operator.location.to(block.location) if block
417
437
 
418
- # If there are any arguments and the operator we found from the list is
419
- # not after them, then we're going to return the arguments as-is because
420
- # we're looking at an & that occurs before the arguments are done.
421
- if arguments.parts.any? && location.start_char < arguments.location.end_char
422
- return arguments
423
- end
424
-
425
438
  # Otherwise, we're looking at an actual block argument (with or without a
426
439
  # block, which could be missing because it could be a bare & since 3.1.0).
427
440
  arg_block = ArgBlock.new(value: block, location: location)
@@ -465,7 +478,7 @@ module SyntaxTree
465
478
  # :call-seq:
466
479
  # on_args_new: () -> Args
467
480
  def on_args_new
468
- Args.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
481
+ Args.new(parts: [], location: Location.fixed(line: lineno, column: current_column, char: char_pos))
469
482
  end
470
483
 
471
484
  # :call-seq:
@@ -551,7 +564,7 @@ module SyntaxTree
551
564
  def on_backref(value)
552
565
  Backref.new(
553
566
  value: value,
554
- location: Location.token(line: lineno, char: char_pos, size: value.size)
567
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
555
568
  )
556
569
  end
557
570
 
@@ -561,7 +574,7 @@ module SyntaxTree
561
574
  node =
562
575
  Backtick.new(
563
576
  value: value,
564
- location: Location.token(line: lineno, char: char_pos, size: value.size)
577
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
565
578
  )
566
579
 
567
580
  tokens << node
@@ -592,15 +605,20 @@ module SyntaxTree
592
605
  PinnedBegin.new(statement: bodystmt, location: location)
593
606
  else
594
607
  keyword = find_token(Kw, "begin")
595
- end_char =
608
+ end_location =
596
609
  if bodystmt.rescue_clause || bodystmt.ensure_clause ||
597
610
  bodystmt.else_clause
598
- bodystmt.location.end_char
611
+ bodystmt.location
599
612
  else
600
- find_token(Kw, "end").location.end_char
613
+ find_token(Kw, "end").location
601
614
  end
602
615
 
603
- bodystmt.bind(keyword.location.end_char, end_char)
616
+ bodystmt.bind(
617
+ keyword.location.end_char,
618
+ keyword.location.end_column,
619
+ end_location.end_char,
620
+ end_location.end_column
621
+ )
604
622
  location = keyword.location.to(bodystmt.location)
605
623
 
606
624
  Begin.new(bodystmt: bodystmt, location: location)
@@ -682,9 +700,10 @@ module SyntaxTree
682
700
  BodyStmt.new(
683
701
  statements: statements,
684
702
  rescue_clause: rescue_clause,
703
+ else_keyword: else_clause && find_token(Kw, "else"),
685
704
  else_clause: else_clause,
686
705
  ensure_clause: ensure_clause,
687
- location: Location.fixed(line: lineno, char: char_pos)
706
+ location: Location.fixed(line: lineno, char: char_pos, column: current_column)
688
707
  )
689
708
  end
690
709
 
@@ -696,18 +715,24 @@ module SyntaxTree
696
715
  def on_brace_block(block_var, statements)
697
716
  lbrace = find_token(LBrace)
698
717
  rbrace = find_token(RBrace)
718
+ location = (block_var || lbrace).location
719
+ start_char = find_next_statement_start(location.end_char)
699
720
 
700
721
  statements.bind(
701
- find_next_statement_start((block_var || lbrace).location.end_char),
702
- rbrace.location.start_char
722
+ start_char,
723
+ start_char - line_counts[location.start_line - 1].start,
724
+ rbrace.location.start_char,
725
+ rbrace.location.start_column
703
726
  )
704
727
 
705
728
  location =
706
729
  Location.new(
707
730
  start_line: lbrace.location.start_line,
708
731
  start_char: lbrace.location.start_char,
732
+ start_column: lbrace.location.start_column,
709
733
  end_line: [rbrace.location.end_line, statements.location.end_line].max,
710
- end_char: rbrace.location.end_char
734
+ end_char: rbrace.location.end_char,
735
+ end_column: rbrace.location.end_column
711
736
  )
712
737
 
713
738
  BraceBlock.new(
@@ -736,8 +761,14 @@ module SyntaxTree
736
761
  # (:call | Backtick | Const | Ident | Op) message
737
762
  # ) -> Call
738
763
  def on_call(receiver, operator, message)
739
- ending = message
740
- ending = operator if message == :call
764
+ ending =
765
+ if message != :call
766
+ message
767
+ elsif operator != :"::"
768
+ operator
769
+ else
770
+ receiver
771
+ end
741
772
 
742
773
  Call.new(
743
774
  receiver: receiver,
@@ -781,10 +812,14 @@ module SyntaxTree
781
812
  def on_class(constant, superclass, bodystmt)
782
813
  beginning = find_token(Kw, "class")
783
814
  ending = find_token(Kw, "end")
815
+ location = (superclass || constant).location
816
+ start_char = find_next_statement_start(location.end_char)
784
817
 
785
818
  bodystmt.bind(
786
- find_next_statement_start((superclass || constant).location.end_char),
787
- ending.location.start_char
819
+ start_char,
820
+ start_char - line_counts[location.start_line - 1].start,
821
+ ending.location.start_char,
822
+ ending.location.start_column
788
823
  )
789
824
 
790
825
  ClassDeclaration.new(
@@ -801,7 +836,7 @@ module SyntaxTree
801
836
  node =
802
837
  Comma.new(
803
838
  value: value,
804
- location: Location.token(line: lineno, char: char_pos, size: value.size)
839
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
805
840
  )
806
841
 
807
842
  tokens << node
@@ -846,7 +881,7 @@ module SyntaxTree
846
881
  value: value.chomp,
847
882
  inline: value.strip != lines[line - 1].strip,
848
883
  location:
849
- Location.token(line: line, char: char_pos, size: value.size - 1)
884
+ Location.token(line: line, char: char_pos, column: current_column, size: value.size - 1)
850
885
  )
851
886
 
852
887
  @comments << comment
@@ -858,7 +893,7 @@ module SyntaxTree
858
893
  def on_const(value)
859
894
  Const.new(
860
895
  value: value,
861
- location: Location.token(line: lineno, char: char_pos, size: value.size)
896
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
862
897
  )
863
898
  end
864
899
 
@@ -893,7 +928,7 @@ module SyntaxTree
893
928
  def on_cvar(value)
894
929
  CVar.new(
895
930
  value: value,
896
- location: Location.token(line: lineno, char: char_pos, size: value.size)
931
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
897
932
  )
898
933
  end
899
934
 
@@ -917,12 +952,15 @@ module SyntaxTree
917
952
  # location information
918
953
  if params.is_a?(Params) && params.empty?
919
954
  end_char = name.location.end_char
955
+ end_column = name.location.end_column
920
956
  location =
921
957
  Location.new(
922
958
  start_line: params.location.start_line,
923
959
  start_char: end_char,
960
+ start_column: end_column,
924
961
  end_line: params.location.end_line,
925
- end_char: end_char
962
+ end_char: end_char,
963
+ end_column: end_column
926
964
  )
927
965
 
928
966
  params = Params.new(location: location)
@@ -932,9 +970,13 @@ module SyntaxTree
932
970
 
933
971
  if ending
934
972
  tokens.delete(ending)
973
+ start_char = find_next_statement_start(params.location.end_char)
974
+
935
975
  bodystmt.bind(
936
- find_next_statement_start(params.location.end_char),
937
- ending.location.start_char
976
+ start_char,
977
+ start_char - line_counts[params.location.start_line - 1].start,
978
+ ending.location.start_char,
979
+ ending.location.start_column
938
980
  )
939
981
 
940
982
  Def.new(
@@ -992,12 +1034,15 @@ module SyntaxTree
992
1034
  # location information
993
1035
  if params.is_a?(Params) && params.empty?
994
1036
  end_char = name.location.end_char
1037
+ end_column = name.location.end_column
995
1038
  location =
996
1039
  Location.new(
997
1040
  start_line: params.location.start_line,
998
1041
  start_char: end_char,
1042
+ start_column: end_column,
999
1043
  end_line: params.location.end_line,
1000
- end_char: end_char
1044
+ end_char: end_char,
1045
+ end_column: end_column
1001
1046
  )
1002
1047
 
1003
1048
  params = Params.new(location: location)
@@ -1008,9 +1053,13 @@ module SyntaxTree
1008
1053
 
1009
1054
  if ending
1010
1055
  tokens.delete(ending)
1056
+ start_char = find_next_statement_start(params.location.end_char)
1057
+
1011
1058
  bodystmt.bind(
1012
- find_next_statement_start(params.location.end_char),
1013
- ending.location.start_char
1059
+ start_char,
1060
+ start_char - line_counts[params.location.start_line - 1].start,
1061
+ ending.location.start_char,
1062
+ ending.location.start_column
1014
1063
  )
1015
1064
 
1016
1065
  Defs.new(
@@ -1042,10 +1091,14 @@ module SyntaxTree
1042
1091
  def on_do_block(block_var, bodystmt)
1043
1092
  beginning = find_token(Kw, "do")
1044
1093
  ending = find_token(Kw, "end")
1094
+ location = (block_var || beginning).location
1095
+ start_char = find_next_statement_start(location.end_char)
1045
1096
 
1046
1097
  bodystmt.bind(
1047
- find_next_statement_start((block_var || beginning).location.end_char),
1048
- ending.location.start_char
1098
+ start_char,
1099
+ start_char - line_counts[location.start_line - 1].start,
1100
+ ending.location.start_char,
1101
+ ending.location.start_column
1049
1102
  )
1050
1103
 
1051
1104
  DoBlock.new(
@@ -1115,7 +1168,7 @@ module SyntaxTree
1115
1168
  # :call-seq:
1116
1169
  # on_else: (Statements statements) -> Else
1117
1170
  def on_else(statements)
1118
- beginning = find_token(Kw, "else")
1171
+ keyword = find_token(Kw, "else")
1119
1172
 
1120
1173
  # else can either end with an end keyword (in which case we'll want to
1121
1174
  # consume that event) or it can end with an ensure keyword (in which case
@@ -1127,13 +1180,19 @@ module SyntaxTree
1127
1180
 
1128
1181
  node = tokens[index]
1129
1182
  ending = node.value == "end" ? tokens.delete_at(index) : node
1130
- # ending = node
1183
+ start_char = find_next_statement_start(keyword.location.end_char)
1131
1184
 
1132
- statements.bind(beginning.location.end_char, ending.location.start_char)
1185
+ statements.bind(
1186
+ start_char,
1187
+ start_char - line_counts[keyword.location.start_line - 1].start,
1188
+ ending.location.start_char,
1189
+ ending.location.start_column
1190
+ )
1133
1191
 
1134
1192
  Else.new(
1193
+ keyword: keyword,
1135
1194
  statements: statements,
1136
- location: beginning.location.to(ending.location)
1195
+ location: keyword.location.to(ending.location)
1137
1196
  )
1138
1197
  end
1139
1198
 
@@ -1147,7 +1206,12 @@ module SyntaxTree
1147
1206
  beginning = find_token(Kw, "elsif")
1148
1207
  ending = consequent || find_token(Kw, "end")
1149
1208
 
1150
- statements.bind(predicate.location.end_char, ending.location.start_char)
1209
+ statements.bind(
1210
+ predicate.location.end_char,
1211
+ predicate.location.end_column,
1212
+ ending.location.start_char,
1213
+ ending.location.start_column
1214
+ )
1151
1215
 
1152
1216
  Elsif.new(
1153
1217
  predicate: predicate,
@@ -1170,7 +1234,7 @@ module SyntaxTree
1170
1234
  @embdoc =
1171
1235
  EmbDoc.new(
1172
1236
  value: value,
1173
- location: Location.fixed(line: lineno, char: char_pos)
1237
+ location: Location.fixed(line: lineno, column: current_column, char: char_pos)
1174
1238
  )
1175
1239
  end
1176
1240
 
@@ -1185,8 +1249,10 @@ module SyntaxTree
1185
1249
  Location.new(
1186
1250
  start_line: location.start_line,
1187
1251
  start_char: location.start_char,
1252
+ start_column: location.start_column,
1188
1253
  end_line: lineno,
1189
- end_char: char_pos + value.length - 1
1254
+ end_char: char_pos + value.length - 1,
1255
+ end_column: current_column + value.length - 1
1190
1256
  )
1191
1257
  )
1192
1258
 
@@ -1202,7 +1268,7 @@ module SyntaxTree
1202
1268
  node =
1203
1269
  EmbExprBeg.new(
1204
1270
  value: value,
1205
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1271
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1206
1272
  )
1207
1273
 
1208
1274
  tokens << node
@@ -1215,7 +1281,7 @@ module SyntaxTree
1215
1281
  node =
1216
1282
  EmbExprEnd.new(
1217
1283
  value: value,
1218
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1284
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1219
1285
  )
1220
1286
 
1221
1287
  tokens << node
@@ -1228,7 +1294,7 @@ module SyntaxTree
1228
1294
  node =
1229
1295
  EmbVar.new(
1230
1296
  value: value,
1231
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1297
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1232
1298
  )
1233
1299
 
1234
1300
  tokens << node
@@ -1243,9 +1309,12 @@ module SyntaxTree
1243
1309
  # We don't want to consume the :@kw event, because that would break
1244
1310
  # def..ensure..end chains.
1245
1311
  ending = find_token(Kw, "end", consume: false)
1312
+ start_char = find_next_statement_start(keyword.location.end_char)
1246
1313
  statements.bind(
1247
- find_next_statement_start(keyword.location.end_char),
1248
- ending.location.start_char
1314
+ start_char,
1315
+ start_char - line_counts[keyword.location.start_line - 1].start,
1316
+ ending.location.start_char,
1317
+ ending.location.start_column
1249
1318
  )
1250
1319
 
1251
1320
  Ensure.new(
@@ -1292,7 +1361,7 @@ module SyntaxTree
1292
1361
  def on_float(value)
1293
1362
  FloatLiteral.new(
1294
1363
  value: value,
1295
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1364
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1296
1365
  )
1297
1366
  end
1298
1367
 
@@ -1337,7 +1406,9 @@ module SyntaxTree
1337
1406
 
1338
1407
  statements.bind(
1339
1408
  (keyword || collection).location.end_char,
1340
- ending.location.start_char
1409
+ (keyword || collection).location.end_column,
1410
+ ending.location.start_char,
1411
+ ending.location.start_column
1341
1412
  )
1342
1413
 
1343
1414
  if index.is_a?(MLHS)
@@ -1358,7 +1429,7 @@ module SyntaxTree
1358
1429
  def on_gvar(value)
1359
1430
  GVar.new(
1360
1431
  value: value,
1361
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1432
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1362
1433
  )
1363
1434
  end
1364
1435
 
@@ -1379,7 +1450,7 @@ module SyntaxTree
1379
1450
  # on_heredoc_beg: (String value) -> HeredocBeg
1380
1451
  def on_heredoc_beg(value)
1381
1452
  location =
1382
- Location.token(line: lineno, char: char_pos, size: value.size + 1)
1453
+ Location.token(line: lineno, char: char_pos, column: current_column, size: value.size + 1)
1383
1454
 
1384
1455
  # Here we're going to artificially create an extra node type so that if
1385
1456
  # there are comments after the declaration of a heredoc, they get printed.
@@ -1415,8 +1486,10 @@ module SyntaxTree
1415
1486
  Location.new(
1416
1487
  start_line: heredoc.location.start_line,
1417
1488
  start_char: heredoc.location.start_char,
1489
+ start_column: heredoc.location.start_column,
1418
1490
  end_line: lineno,
1419
- end_char: char_pos
1491
+ end_char: char_pos,
1492
+ end_column: current_column,
1420
1493
  )
1421
1494
  )
1422
1495
  end
@@ -1428,13 +1501,19 @@ module SyntaxTree
1428
1501
  # (nil | VarField) keyword_rest
1429
1502
  # ) -> HshPtn
1430
1503
  def on_hshptn(constant, keywords, keyword_rest)
1431
- parts = [constant, keywords, keyword_rest].flatten(2).compact
1504
+ parts = [constant, *keywords&.flatten(1), keyword_rest].compact
1505
+ location =
1506
+ if parts.empty?
1507
+ find_token(LBrace).location.to(find_token(RBrace).location)
1508
+ else
1509
+ parts[0].location.to(parts[-1].location)
1510
+ end
1432
1511
 
1433
1512
  HshPtn.new(
1434
1513
  constant: constant,
1435
- keywords: keywords,
1514
+ keywords: keywords || [],
1436
1515
  keyword_rest: keyword_rest,
1437
- location: parts[0].location.to(parts[-1].location)
1516
+ location: location
1438
1517
  )
1439
1518
  end
1440
1519
 
@@ -1443,7 +1522,7 @@ module SyntaxTree
1443
1522
  def on_ident(value)
1444
1523
  Ident.new(
1445
1524
  value: value,
1446
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1525
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1447
1526
  )
1448
1527
  end
1449
1528
 
@@ -1457,7 +1536,12 @@ module SyntaxTree
1457
1536
  beginning = find_token(Kw, "if")
1458
1537
  ending = consequent || find_token(Kw, "end")
1459
1538
 
1460
- statements.bind(predicate.location.end_char, ending.location.start_char)
1539
+ statements.bind(
1540
+ predicate.location.end_char,
1541
+ predicate.location.end_column,
1542
+ ending.location.start_char,
1543
+ ending.location.start_column
1544
+ )
1461
1545
 
1462
1546
  If.new(
1463
1547
  predicate: predicate,
@@ -1503,7 +1587,7 @@ module SyntaxTree
1503
1587
  def on_imaginary(value)
1504
1588
  Imaginary.new(
1505
1589
  value: value,
1506
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1590
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1507
1591
  )
1508
1592
  end
1509
1593
 
@@ -1527,9 +1611,12 @@ module SyntaxTree
1527
1611
  statements_start = token
1528
1612
  end
1529
1613
 
1614
+ start_char = find_next_statement_start(statements_start.location.end_char)
1530
1615
  statements.bind(
1531
- find_next_statement_start(statements_start.location.end_char),
1532
- ending.location.start_char
1616
+ start_char,
1617
+ start_char - line_counts[statements_start.location.start_line - 1].start,
1618
+ ending.location.start_char,
1619
+ ending.location.start_column
1533
1620
  )
1534
1621
 
1535
1622
  In.new(
@@ -1545,7 +1632,7 @@ module SyntaxTree
1545
1632
  def on_int(value)
1546
1633
  Int.new(
1547
1634
  value: value,
1548
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1635
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1549
1636
  )
1550
1637
  end
1551
1638
 
@@ -1554,7 +1641,7 @@ module SyntaxTree
1554
1641
  def on_ivar(value)
1555
1642
  IVar.new(
1556
1643
  value: value,
1557
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1644
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1558
1645
  )
1559
1646
  end
1560
1647
 
@@ -1564,7 +1651,7 @@ module SyntaxTree
1564
1651
  node =
1565
1652
  Kw.new(
1566
1653
  value: value,
1567
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1654
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1568
1655
  )
1569
1656
 
1570
1657
  tokens << node
@@ -1585,7 +1672,7 @@ module SyntaxTree
1585
1672
  def on_label(value)
1586
1673
  Label.new(
1587
1674
  value: value,
1588
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1675
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1589
1676
  )
1590
1677
  end
1591
1678
 
@@ -1595,7 +1682,7 @@ module SyntaxTree
1595
1682
  node =
1596
1683
  LabelEnd.new(
1597
1684
  value: value,
1598
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1685
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1599
1686
  )
1600
1687
 
1601
1688
  tokens << node
@@ -1621,7 +1708,12 @@ module SyntaxTree
1621
1708
  closing = find_token(Kw, "end")
1622
1709
  end
1623
1710
 
1624
- statements.bind(opening.location.end_char, closing.location.start_char)
1711
+ statements.bind(
1712
+ opening.location.end_char,
1713
+ opening.location.end_column,
1714
+ closing.location.start_char,
1715
+ closing.location.start_column
1716
+ )
1625
1717
 
1626
1718
  Lambda.new(
1627
1719
  params: params,
@@ -1636,7 +1728,7 @@ module SyntaxTree
1636
1728
  node =
1637
1729
  LBrace.new(
1638
1730
  value: value,
1639
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1731
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1640
1732
  )
1641
1733
 
1642
1734
  tokens << node
@@ -1649,7 +1741,7 @@ module SyntaxTree
1649
1741
  node =
1650
1742
  LBracket.new(
1651
1743
  value: value,
1652
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1744
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1653
1745
  )
1654
1746
 
1655
1747
  tokens << node
@@ -1662,7 +1754,7 @@ module SyntaxTree
1662
1754
  node =
1663
1755
  LParen.new(
1664
1756
  value: value,
1665
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1757
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1666
1758
  )
1667
1759
 
1668
1760
  tokens << node
@@ -1761,7 +1853,7 @@ module SyntaxTree
1761
1853
  # :call-seq:
1762
1854
  # on_mlhs_new: () -> MLHS
1763
1855
  def on_mlhs_new
1764
- MLHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
1856
+ MLHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos, column: current_column))
1765
1857
  end
1766
1858
 
1767
1859
  # :call-seq:
@@ -1787,10 +1879,13 @@ module SyntaxTree
1787
1879
  def on_module(constant, bodystmt)
1788
1880
  beginning = find_token(Kw, "module")
1789
1881
  ending = find_token(Kw, "end")
1882
+ start_char = find_next_statement_start(constant.location.end_char)
1790
1883
 
1791
1884
  bodystmt.bind(
1792
- find_next_statement_start(constant.location.end_char),
1793
- ending.location.start_char
1885
+ start_char,
1886
+ start_char - line_counts[constant.location.start_line - 1].start,
1887
+ ending.location.start_char,
1888
+ ending.location.start_column
1794
1889
  )
1795
1890
 
1796
1891
  ModuleDeclaration.new(
@@ -1803,7 +1898,7 @@ module SyntaxTree
1803
1898
  # :call-seq:
1804
1899
  # on_mrhs_new: () -> MRHS
1805
1900
  def on_mrhs_new
1806
- MRHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
1901
+ MRHS.new(parts: [], location: Location.fixed(line: lineno, char: char_pos, column: current_column))
1807
1902
  end
1808
1903
 
1809
1904
  # :call-seq:
@@ -1872,7 +1967,7 @@ module SyntaxTree
1872
1967
  node =
1873
1968
  Op.new(
1874
1969
  value: value,
1875
- location: Location.token(line: lineno, char: char_pos, size: value.size)
1970
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
1876
1971
  )
1877
1972
 
1878
1973
  tokens << node
@@ -1931,7 +2026,7 @@ module SyntaxTree
1931
2026
  if parts.any?
1932
2027
  parts[0].location.to(parts[-1].location)
1933
2028
  else
1934
- Location.fixed(line: lineno, char: char_pos)
2029
+ Location.fixed(line: lineno, char: char_pos, column: current_column)
1935
2030
  end
1936
2031
 
1937
2032
  Params.new(
@@ -1954,12 +2049,15 @@ module SyntaxTree
1954
2049
 
1955
2050
  if contents && contents.is_a?(Params)
1956
2051
  location = contents.location
2052
+ start_char = find_next_statement_start(lparen.location.end_char)
1957
2053
  location =
1958
2054
  Location.new(
1959
2055
  start_line: location.start_line,
1960
- start_char: find_next_statement_start(lparen.location.end_char),
2056
+ start_char: start_char,
2057
+ start_column: start_char - line_counts[lparen.location.start_line - 1].start,
1961
2058
  end_line: location.end_line,
1962
- end_char: rparen.location.start_char
2059
+ end_char: rparen.location.start_char,
2060
+ end_column: rparen.location.start_column
1963
2061
  )
1964
2062
 
1965
2063
  contents =
@@ -1997,23 +2095,26 @@ module SyntaxTree
1997
2095
  def on_period(value)
1998
2096
  Period.new(
1999
2097
  value: value,
2000
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2098
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2001
2099
  )
2002
2100
  end
2003
2101
 
2004
2102
  # :call-seq:
2005
2103
  # on_program: (Statements statements) -> Program
2006
2104
  def on_program(statements)
2105
+ last_column = source.length - line_counts[lines.length - 1].start
2007
2106
  location =
2008
2107
  Location.new(
2009
2108
  start_line: 1,
2010
2109
  start_char: 0,
2110
+ start_column: 0,
2011
2111
  end_line: lines.length,
2012
- end_char: source.length
2112
+ end_char: source.length,
2113
+ end_column: last_column
2013
2114
  )
2014
2115
 
2015
2116
  statements.body << @__end__ if @__end__
2016
- statements.bind(0, source.length)
2117
+ statements.bind(0, 0, source.length, last_column)
2017
2118
 
2018
2119
  program = Program.new(statements: statements, location: location)
2019
2120
  attach_comments(program, @comments)
@@ -2126,7 +2227,7 @@ module SyntaxTree
2126
2227
  node =
2127
2228
  QSymbolsBeg.new(
2128
2229
  value: value,
2129
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2230
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2130
2231
  )
2131
2232
 
2132
2233
  tokens << node
@@ -2161,7 +2262,7 @@ module SyntaxTree
2161
2262
  node =
2162
2263
  QWordsBeg.new(
2163
2264
  value: value,
2164
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2265
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2165
2266
  )
2166
2267
 
2167
2268
  tokens << node
@@ -2181,7 +2282,7 @@ module SyntaxTree
2181
2282
  def on_rational(value)
2182
2283
  RationalLiteral.new(
2183
2284
  value: value,
2184
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2285
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2185
2286
  )
2186
2287
  end
2187
2288
 
@@ -2191,7 +2292,7 @@ module SyntaxTree
2191
2292
  node =
2192
2293
  RBrace.new(
2193
2294
  value: value,
2194
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2295
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2195
2296
  )
2196
2297
 
2197
2298
  tokens << node
@@ -2204,7 +2305,7 @@ module SyntaxTree
2204
2305
  node =
2205
2306
  RBracket.new(
2206
2307
  value: value,
2207
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2308
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2208
2309
  )
2209
2310
 
2210
2311
  tokens << node
@@ -2238,7 +2339,7 @@ module SyntaxTree
2238
2339
  node =
2239
2340
  RegexpBeg.new(
2240
2341
  value: value,
2241
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2342
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2242
2343
  )
2243
2344
 
2244
2345
  tokens << node
@@ -2250,7 +2351,7 @@ module SyntaxTree
2250
2351
  def on_regexp_end(value)
2251
2352
  RegexpEnd.new(
2252
2353
  value: value,
2253
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2354
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2254
2355
  )
2255
2356
  end
2256
2357
 
@@ -2292,9 +2393,12 @@ module SyntaxTree
2292
2393
  exceptions = exceptions[0] if exceptions.is_a?(Array)
2293
2394
 
2294
2395
  last_node = variable || exceptions || keyword
2396
+ start_char = find_next_statement_start(last_node.location.end_char)
2295
2397
  statements.bind(
2296
- find_next_statement_start(last_node.location.end_char),
2297
- char_pos
2398
+ start_char,
2399
+ start_char - line_counts[last_node.location.start_line - 1].start,
2400
+ char_pos,
2401
+ current_column
2298
2402
  )
2299
2403
 
2300
2404
  # We add an additional inner node here that ripper doesn't provide so that
@@ -2309,13 +2413,16 @@ module SyntaxTree
2309
2413
  Location.new(
2310
2414
  start_line: keyword.location.start_line,
2311
2415
  start_char: keyword.location.end_char + 1,
2416
+ start_column: keyword.location.end_column + 1,
2312
2417
  end_line: last_node.location.end_line,
2313
- end_char: last_node.location.end_char
2418
+ end_char: last_node.location.end_char,
2419
+ end_column: last_node.location.end_column
2314
2420
  )
2315
2421
  )
2316
2422
  end
2317
2423
 
2318
2424
  Rescue.new(
2425
+ keyword: keyword,
2319
2426
  exception: rescue_ex,
2320
2427
  statements: statements,
2321
2428
  consequent: consequent,
@@ -2323,8 +2430,10 @@ module SyntaxTree
2323
2430
  Location.new(
2324
2431
  start_line: keyword.location.start_line,
2325
2432
  start_char: keyword.location.start_char,
2433
+ start_column: keyword.location.start_column,
2326
2434
  end_line: lineno,
2327
- end_char: char_pos
2435
+ end_char: char_pos,
2436
+ end_column: current_column
2328
2437
  )
2329
2438
  )
2330
2439
  end
@@ -2383,7 +2492,7 @@ module SyntaxTree
2383
2492
  node =
2384
2493
  RParen.new(
2385
2494
  value: value,
2386
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2495
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2387
2496
  )
2388
2497
 
2389
2498
  tokens << node
@@ -2395,10 +2504,13 @@ module SyntaxTree
2395
2504
  def on_sclass(target, bodystmt)
2396
2505
  beginning = find_token(Kw, "class")
2397
2506
  ending = find_token(Kw, "end")
2507
+ start_char = find_next_statement_start(target.location.end_char)
2398
2508
 
2399
2509
  bodystmt.bind(
2400
- find_next_statement_start(target.location.end_char),
2401
- ending.location.start_char
2510
+ start_char,
2511
+ start_char - line_counts[target.location.start_line - 1].start,
2512
+ ending.location.start_char,
2513
+ ending.location.start_column
2402
2514
  )
2403
2515
 
2404
2516
  SClass.new(
@@ -2437,7 +2549,7 @@ module SyntaxTree
2437
2549
  Statements.new(
2438
2550
  self,
2439
2551
  body: [],
2440
- location: Location.fixed(line: lineno, char: char_pos)
2552
+ location: Location.fixed(line: lineno, char: char_pos, column: current_column)
2441
2553
  )
2442
2554
  end
2443
2555
 
@@ -2471,7 +2583,7 @@ module SyntaxTree
2471
2583
  def on_string_content
2472
2584
  StringContent.new(
2473
2585
  parts: [],
2474
- location: Location.fixed(line: lineno, char: char_pos)
2586
+ location: Location.fixed(line: lineno, char: char_pos, column: current_column)
2475
2587
  )
2476
2588
  end
2477
2589
 
@@ -2494,18 +2606,22 @@ module SyntaxTree
2494
2606
 
2495
2607
  statements.bind(
2496
2608
  embexpr_beg.location.end_char,
2497
- embexpr_end.location.start_char
2609
+ embexpr_beg.location.end_column,
2610
+ embexpr_end.location.start_char,
2611
+ embexpr_end.location.start_column
2498
2612
  )
2499
2613
 
2500
2614
  location =
2501
2615
  Location.new(
2502
2616
  start_line: embexpr_beg.location.start_line,
2503
2617
  start_char: embexpr_beg.location.start_char,
2618
+ start_column: embexpr_beg.location.start_column,
2504
2619
  end_line: [
2505
2620
  embexpr_end.location.end_line,
2506
2621
  statements.location.end_line
2507
2622
  ].max,
2508
- end_char: embexpr_end.location.end_char
2623
+ end_char: embexpr_end.location.end_char,
2624
+ end_column: embexpr_end.location.end_column
2509
2625
  )
2510
2626
 
2511
2627
  StringEmbExpr.new(statements: statements, location: location)
@@ -2533,11 +2649,13 @@ module SyntaxTree
2533
2649
  Location.new(
2534
2650
  start_line: tstring_beg.location.start_line,
2535
2651
  start_char: tstring_beg.location.start_char,
2652
+ start_column: tstring_beg.location.start_column,
2536
2653
  end_line: [
2537
2654
  tstring_end.location.end_line,
2538
2655
  string.location.end_line
2539
2656
  ].max,
2540
- end_char: tstring_end.location.end_char
2657
+ end_char: tstring_end.location.end_char,
2658
+ end_column: tstring_end.location.end_column
2541
2659
  )
2542
2660
 
2543
2661
  StringLiteral.new(
@@ -2566,7 +2684,7 @@ module SyntaxTree
2566
2684
  node =
2567
2685
  SymBeg.new(
2568
2686
  value: value,
2569
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2687
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2570
2688
  )
2571
2689
 
2572
2690
  tokens << node
@@ -2620,7 +2738,7 @@ module SyntaxTree
2620
2738
  node =
2621
2739
  SymbolsBeg.new(
2622
2740
  value: value,
2623
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2741
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2624
2742
  )
2625
2743
 
2626
2744
  tokens << node
@@ -2645,7 +2763,7 @@ module SyntaxTree
2645
2763
  node =
2646
2764
  TLambda.new(
2647
2765
  value: value,
2648
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2766
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2649
2767
  )
2650
2768
 
2651
2769
  tokens << node
@@ -2658,7 +2776,7 @@ module SyntaxTree
2658
2776
  node =
2659
2777
  TLamBeg.new(
2660
2778
  value: value,
2661
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2779
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2662
2780
  )
2663
2781
 
2664
2782
  tokens << node
@@ -2693,7 +2811,7 @@ module SyntaxTree
2693
2811
  node =
2694
2812
  TStringBeg.new(
2695
2813
  value: value,
2696
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2814
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2697
2815
  )
2698
2816
 
2699
2817
  tokens << node
@@ -2705,7 +2823,7 @@ module SyntaxTree
2705
2823
  def on_tstring_content(value)
2706
2824
  TStringContent.new(
2707
2825
  value: value,
2708
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2826
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2709
2827
  )
2710
2828
  end
2711
2829
 
@@ -2715,7 +2833,7 @@ module SyntaxTree
2715
2833
  node =
2716
2834
  TStringEnd.new(
2717
2835
  value: value,
2718
- location: Location.token(line: lineno, char: char_pos, size: value.size)
2836
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
2719
2837
  )
2720
2838
 
2721
2839
  tokens << node
@@ -2731,19 +2849,17 @@ module SyntaxTree
2731
2849
  # parentheses they don't get reported as a paren node for some reason.
2732
2850
 
2733
2851
  beginning = find_token(Kw, "not")
2734
- ending = statement
2735
-
2736
- range = beginning.location.end_char...statement.location.start_char
2737
- paren = source[range].include?("(")
2852
+ ending = statement || beginning
2853
+ parentheses = source[beginning.location.end_char] == "("
2738
2854
 
2739
- if paren
2855
+ if parentheses
2740
2856
  find_token(LParen)
2741
2857
  ending = find_token(RParen)
2742
2858
  end
2743
2859
 
2744
2860
  Not.new(
2745
2861
  statement: statement,
2746
- parentheses: paren,
2862
+ parentheses: parentheses,
2747
2863
  location: beginning.location.to(ending.location)
2748
2864
  )
2749
2865
  else
@@ -2789,7 +2905,12 @@ module SyntaxTree
2789
2905
  beginning = find_token(Kw, "unless")
2790
2906
  ending = consequent || find_token(Kw, "end")
2791
2907
 
2792
- statements.bind(predicate.location.end_char, ending.location.start_char)
2908
+ statements.bind(
2909
+ predicate.location.end_char,
2910
+ predicate.location.end_column,
2911
+ ending.location.start_char,
2912
+ ending.location.start_column
2913
+ )
2793
2914
 
2794
2915
  Unless.new(
2795
2916
  predicate: predicate,
@@ -2826,7 +2947,12 @@ module SyntaxTree
2826
2947
  end
2827
2948
 
2828
2949
  # Update the Statements location information
2829
- statements.bind(predicate.location.end_char, ending.location.start_char)
2950
+ statements.bind(
2951
+ predicate.location.end_char,
2952
+ predicate.location.end_column,
2953
+ ending.location.start_char,
2954
+ ending.location.start_column
2955
+ )
2830
2956
 
2831
2957
  Until.new(
2832
2958
  predicate: predicate,
@@ -2865,12 +2991,12 @@ module SyntaxTree
2865
2991
  # ) -> VarField
2866
2992
  def on_var_field(value)
2867
2993
  location =
2868
- if value
2994
+ if value && value != :nil
2869
2995
  value.location
2870
2996
  else
2871
2997
  # You can hit this pattern if you're assigning to a splat using
2872
2998
  # pattern matching syntax in Ruby 2.7+
2873
- Location.fixed(line: lineno, char: char_pos)
2999
+ Location.fixed(line: lineno, char: char_pos, column: current_column)
2874
3000
  end
2875
3001
 
2876
3002
  VarField.new(value: value, location: location)
@@ -2898,7 +3024,7 @@ module SyntaxTree
2898
3024
  # :call-seq:
2899
3025
  # on_void_stmt: () -> VoidStmt
2900
3026
  def on_void_stmt
2901
- VoidStmt.new(location: Location.fixed(line: lineno, char: char_pos))
3027
+ VoidStmt.new(location: Location.fixed(line: lineno, char: char_pos, column: current_column))
2902
3028
  end
2903
3029
 
2904
3030
  # :call-seq:
@@ -2917,9 +3043,13 @@ module SyntaxTree
2917
3043
  statements_start = token
2918
3044
  end
2919
3045
 
3046
+ start_char = find_next_statement_start(statements_start.location.end_char)
3047
+
2920
3048
  statements.bind(
2921
- find_next_statement_start(statements_start.location.end_char),
2922
- ending.location.start_char
3049
+ start_char,
3050
+ start_char - line_counts[statements_start.location.start_line - 1].start,
3051
+ ending.location.start_char,
3052
+ ending.location.start_column
2923
3053
  )
2924
3054
 
2925
3055
  When.new(
@@ -2945,7 +3075,12 @@ module SyntaxTree
2945
3075
  end
2946
3076
 
2947
3077
  # Update the Statements location information
2948
- statements.bind(predicate.location.end_char, ending.location.start_char)
3078
+ statements.bind(
3079
+ predicate.location.end_char,
3080
+ predicate.location.end_column,
3081
+ ending.location.start_char,
3082
+ ending.location.start_column
3083
+ )
2949
3084
 
2950
3085
  While.new(
2951
3086
  predicate: predicate,
@@ -2981,7 +3116,7 @@ module SyntaxTree
2981
3116
  # :call-seq:
2982
3117
  # on_word_new: () -> Word
2983
3118
  def on_word_new
2984
- Word.new(parts: [], location: Location.fixed(line: lineno, char: char_pos))
3119
+ Word.new(parts: [], location: Location.fixed(line: lineno, char: char_pos, column: current_column))
2985
3120
  end
2986
3121
 
2987
3122
  # :call-seq:
@@ -3000,7 +3135,7 @@ module SyntaxTree
3000
3135
  node =
3001
3136
  WordsBeg.new(
3002
3137
  value: value,
3003
- location: Location.token(line: lineno, char: char_pos, size: value.size)
3138
+ location: Location.token(line: lineno, char: char_pos, column: current_column, size: value.size)
3004
3139
  )
3005
3140
 
3006
3141
  tokens << node