syntax_tree 2.0.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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