syntax_tree 5.2.0 → 5.3.0

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.
@@ -775,6 +775,10 @@ module SyntaxTree
775
775
  other.is_a?(ArgParen) && arguments === other.arguments
776
776
  end
777
777
 
778
+ def arity
779
+ arguments&.arity || 0
780
+ end
781
+
778
782
  private
779
783
 
780
784
  def trailing_comma?
@@ -848,6 +852,21 @@ module SyntaxTree
848
852
  def ===(other)
849
853
  other.is_a?(Args) && ArrayMatch.call(parts, other.parts)
850
854
  end
855
+
856
+ def arity
857
+ parts.sum do |part|
858
+ case part
859
+ when ArgStar, ArgsForward
860
+ Float::INFINITY
861
+ when BareAssocHash
862
+ part.assocs.sum do |assoc|
863
+ assoc.is_a?(AssocSplat) ? Float::INFINITY : 1
864
+ end
865
+ else
866
+ 1
867
+ end
868
+ end
869
+ end
851
870
  end
852
871
 
853
872
  # ArgBlock represents using a block operator on an expression.
@@ -1008,6 +1027,10 @@ module SyntaxTree
1008
1027
  def ===(other)
1009
1028
  other.is_a?(ArgsForward)
1010
1029
  end
1030
+
1031
+ def arity
1032
+ Float::INFINITY
1033
+ end
1011
1034
  end
1012
1035
 
1013
1036
  # ArrayLiteral represents an array literal, which can optionally contain
@@ -1080,58 +1103,6 @@ module SyntaxTree
1080
1103
  end
1081
1104
  end
1082
1105
 
1083
- # Formats an array that contains only a list of variable references. To make
1084
- # things simpler, if there are a bunch, we format them all using the "fill"
1085
- # algorithm as opposed to breaking them into a ton of lines. For example,
1086
- #
1087
- # [foo, bar, baz]
1088
- #
1089
- # instead of becoming:
1090
- #
1091
- # [
1092
- # foo,
1093
- # bar,
1094
- # baz
1095
- # ]
1096
- #
1097
- # would instead become:
1098
- #
1099
- # [
1100
- # foo, bar,
1101
- # baz
1102
- # ]
1103
- #
1104
- # provided the line length was hit between `bar` and `baz`.
1105
- class VarRefsFormatter
1106
- # The separator for the fill algorithm.
1107
- class Separator
1108
- def call(q)
1109
- q.text(",")
1110
- q.fill_breakable
1111
- end
1112
- end
1113
-
1114
- # [Args] the contents of the array
1115
- attr_reader :contents
1116
-
1117
- def initialize(contents)
1118
- @contents = contents
1119
- end
1120
-
1121
- def format(q)
1122
- q.text("[")
1123
- q.group do
1124
- q.indent do
1125
- q.breakable_empty
1126
- q.seplist(contents.parts, Separator.new) { |part| q.format(part) }
1127
- q.if_break { q.text(",") } if q.trailing_comma?
1128
- end
1129
- q.breakable_empty
1130
- end
1131
- q.text("]")
1132
- end
1133
- end
1134
-
1135
1106
  # This is a special formatter used if the array literal contains no values
1136
1107
  # but _does_ contain comments. In this case we do some special formatting to
1137
1108
  # make sure the comments gets indented properly.
@@ -1206,19 +1177,17 @@ module SyntaxTree
1206
1177
  end
1207
1178
 
1208
1179
  def format(q)
1209
- if qwords?
1210
- QWordsFormatter.new(contents).format(q)
1211
- return
1212
- end
1213
-
1214
- if qsymbols?
1215
- QSymbolsFormatter.new(contents).format(q)
1216
- return
1217
- end
1180
+ if lbracket.comments.empty? && contents && contents.comments.empty? &&
1181
+ contents.parts.length > 1
1182
+ if qwords?
1183
+ QWordsFormatter.new(contents).format(q)
1184
+ return
1185
+ end
1218
1186
 
1219
- if var_refs?(q)
1220
- VarRefsFormatter.new(contents).format(q)
1221
- return
1187
+ if qsymbols?
1188
+ QSymbolsFormatter.new(contents).format(q)
1189
+ return
1190
+ end
1222
1191
  end
1223
1192
 
1224
1193
  if empty_with_comments?
@@ -1250,39 +1219,24 @@ module SyntaxTree
1250
1219
  private
1251
1220
 
1252
1221
  def qwords?
1253
- lbracket.comments.empty? && contents && contents.comments.empty? &&
1254
- contents.parts.length > 1 &&
1255
- contents.parts.all? do |part|
1256
- case part
1257
- when StringLiteral
1258
- part.comments.empty? && part.parts.length == 1 &&
1259
- part.parts.first.is_a?(TStringContent) &&
1260
- !part.parts.first.value.match?(/[\s\[\]\\]/)
1261
- when CHAR
1262
- !part.value.match?(/[\[\]\\]/)
1263
- else
1264
- false
1265
- end
1222
+ contents.parts.all? do |part|
1223
+ case part
1224
+ when StringLiteral
1225
+ part.comments.empty? && part.parts.length == 1 &&
1226
+ part.parts.first.is_a?(TStringContent) &&
1227
+ !part.parts.first.value.match?(/[\s\[\]\\]/)
1228
+ when CHAR
1229
+ !part.value.match?(/[\[\]\\]/)
1230
+ else
1231
+ false
1266
1232
  end
1233
+ end
1267
1234
  end
1268
1235
 
1269
1236
  def qsymbols?
1270
- lbracket.comments.empty? && contents && contents.comments.empty? &&
1271
- contents.parts.length > 1 &&
1272
- contents.parts.all? do |part|
1273
- part.is_a?(SymbolLiteral) && part.comments.empty?
1274
- end
1275
- end
1276
-
1277
- def var_refs?(q)
1278
- lbracket.comments.empty? && contents && contents.comments.empty? &&
1279
- contents.parts.all? do |part|
1280
- part.is_a?(VarRef) && part.comments.empty?
1281
- end &&
1282
- (
1283
- contents.parts.sum { |part| part.value.value.length + 2 } >
1284
- q.maxwidth * 2
1285
- )
1237
+ contents.parts.all? do |part|
1238
+ part.is_a?(SymbolLiteral) && part.comments.empty?
1239
+ end
1286
1240
  end
1287
1241
 
1288
1242
  # If we have an empty array that contains only comments, then we're going
@@ -3068,6 +3022,10 @@ module SyntaxTree
3068
3022
  end
3069
3023
  end
3070
3024
  end
3025
+
3026
+ def arity
3027
+ arguments&.arity || 0
3028
+ end
3071
3029
  end
3072
3030
 
3073
3031
  # Case represents the beginning of a case chain.
@@ -3481,6 +3439,10 @@ module SyntaxTree
3481
3439
  arguments === other.arguments && block === other.block
3482
3440
  end
3483
3441
 
3442
+ def arity
3443
+ arguments.arity
3444
+ end
3445
+
3484
3446
  private
3485
3447
 
3486
3448
  def align(q, node, &block)
@@ -3646,6 +3608,10 @@ module SyntaxTree
3646
3608
  arguments === other.arguments && block === other.block
3647
3609
  end
3648
3610
 
3611
+ def arity
3612
+ arguments&.arity || 0
3613
+ end
3614
+
3649
3615
  private
3650
3616
 
3651
3617
  def argument_alignment(q, doc)
@@ -4175,6 +4141,17 @@ module SyntaxTree
4175
4141
  def endless?
4176
4142
  !bodystmt.is_a?(BodyStmt)
4177
4143
  end
4144
+
4145
+ def arity
4146
+ case params
4147
+ when Params
4148
+ params.arity
4149
+ when Paren
4150
+ params.contents.arity
4151
+ else
4152
+ 0..0
4153
+ end
4154
+ end
4178
4155
  end
4179
4156
 
4180
4157
  # Defined represents the use of the +defined?+ operator. It can be used with
@@ -4362,6 +4339,15 @@ module SyntaxTree
4362
4339
  opening.is_a?(Kw)
4363
4340
  end
4364
4341
 
4342
+ def arity
4343
+ case block_var
4344
+ when BlockVar
4345
+ block_var.params.arity
4346
+ else
4347
+ 0..0
4348
+ end
4349
+ end
4350
+
4365
4351
  private
4366
4352
 
4367
4353
  # If this is nested anywhere inside certain nodes, then we can't change
@@ -6153,7 +6139,7 @@ module SyntaxTree
6153
6139
  module Ternaryable
6154
6140
  class << self
6155
6141
  def call(q, node)
6156
- return false if ENV["STREE_FAST_FORMAT"]
6142
+ return false if ENV["STREE_FAST_FORMAT"] || q.disable_auto_ternary?
6157
6143
 
6158
6144
  # If this is a conditional inside of a parentheses as the only content,
6159
6145
  # then we don't want to transform it into a ternary. Presumably the user
@@ -6496,9 +6482,26 @@ module SyntaxTree
6496
6482
 
6497
6483
  def format(q)
6498
6484
  force_flat = [
6499
- AliasNode, Assign, Break, Command, CommandCall, Heredoc, IfNode, IfOp,
6500
- Lambda, MAssign, Next, OpAssign, RescueMod, ReturnNode, Super, Undef,
6501
- UnlessNode, VoidStmt, YieldNode, ZSuper
6485
+ AliasNode,
6486
+ Assign,
6487
+ Break,
6488
+ Command,
6489
+ CommandCall,
6490
+ Heredoc,
6491
+ IfNode,
6492
+ IfOp,
6493
+ Lambda,
6494
+ MAssign,
6495
+ Next,
6496
+ OpAssign,
6497
+ RescueMod,
6498
+ ReturnNode,
6499
+ Super,
6500
+ Undef,
6501
+ UnlessNode,
6502
+ VoidStmt,
6503
+ YieldNode,
6504
+ ZSuper
6502
6505
  ]
6503
6506
 
6504
6507
  if q.parent.is_a?(Paren) || force_flat.include?(truthy.class) ||
@@ -8325,6 +8328,29 @@ module SyntaxTree
8325
8328
  keyword_rest === other.keyword_rest && block === other.block
8326
8329
  end
8327
8330
 
8331
+ # Returns a range representing the possible number of arguments accepted
8332
+ # by this params node not including the block. For example:
8333
+ #
8334
+ # def foo(a, b = 1, c:, d: 2, &block)
8335
+ # ...
8336
+ # end
8337
+ #
8338
+ # has arity 2..4.
8339
+ #
8340
+ def arity
8341
+ optional_keywords = keywords.count { |_label, value| value }
8342
+
8343
+ lower_bound =
8344
+ requireds.length + posts.length + keywords.length - optional_keywords
8345
+
8346
+ upper_bound =
8347
+ if keyword_rest.nil? && rest.nil?
8348
+ lower_bound + optionals.length + optional_keywords
8349
+ end
8350
+
8351
+ lower_bound..upper_bound
8352
+ end
8353
+
8328
8354
  private
8329
8355
 
8330
8356
  def format_contents(q, parts)
@@ -11594,6 +11620,10 @@ module SyntaxTree
11594
11620
  def access_control?
11595
11621
  @access_control ||= %w[private protected public].include?(value.value)
11596
11622
  end
11623
+
11624
+ def arity
11625
+ 0
11626
+ end
11597
11627
  end
11598
11628
 
11599
11629
  # VoidStmt represents an empty lexical block of code.
@@ -1103,6 +1103,7 @@ module SyntaxTree
1103
1103
  # :call-seq:
1104
1104
  # on_comment: (String value) -> Comment
1105
1105
  def on_comment(value)
1106
+ # char is the index of the # character in the source.
1106
1107
  char = char_pos
1107
1108
  location =
1108
1109
  Location.token(
@@ -1112,8 +1113,24 @@ module SyntaxTree
1112
1113
  size: value.size - 1
1113
1114
  )
1114
1115
 
1115
- index = source.rindex(/[^\t ]/, char - 1) if char != 0
1116
- inline = index && (source[index] != "\n")
1116
+ # Loop backward in the source string, starting from the beginning of the
1117
+ # comment, and find the first character that is not a space or a tab. If
1118
+ # index is -1, this indicates that we've checked all of the characters
1119
+ # back to the start of the source, so this comment must be at the
1120
+ # beginning of the file.
1121
+ #
1122
+ # We are purposefully not using rindex or regular expressions here because
1123
+ # they check if there are invalid characters, which is actually possible
1124
+ # with the use of __END__ syntax.
1125
+ index = char - 1
1126
+ while index > -1 && (source[index] == "\t" || source[index] == " ")
1127
+ index -= 1
1128
+ end
1129
+
1130
+ # If we found a character that was not a space or a tab before the comment
1131
+ # and it's a newline, then this comment is inline. Otherwise, it stands on
1132
+ # its own and can be attached as its own node in the tree.
1133
+ inline = index != -1 && source[index] != "\n"
1117
1134
  comment =
1118
1135
  Comment.new(value: value.chomp, inline: inline, location: location)
1119
1136
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ class Formatter
5
+ DISABLE_TERNARY = true
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "5.2.0"
4
+ VERSION = "5.3.0"
5
5
  end
@@ -62,22 +62,26 @@ module SyntaxTree
62
62
  "constant-from"
63
63
  ].freeze
64
64
 
65
- attr_reader :filepath
65
+ attr_reader :lines
66
66
 
67
- def initialize(filepath)
68
- @filepath = filepath
67
+ def initialize(lines)
68
+ @lines = lines
69
69
  end
70
70
 
71
71
  def assemble
72
72
  iseq = InstructionSequence.new("<main>", "<compiled>", 1, :top)
73
- assemble_iseq(iseq, File.readlines(filepath, chomp: true))
73
+ assemble_iseq(iseq, lines)
74
74
 
75
75
  iseq.compile!
76
76
  iseq
77
77
  end
78
78
 
79
- def self.assemble(filepath)
80
- new(filepath).assemble
79
+ def self.assemble(source)
80
+ new(source.lines(chomp: true)).assemble
81
+ end
82
+
83
+ def self.assemble_file(filepath)
84
+ new(File.readlines(filepath, chomp: true)).assemble
81
85
  end
82
86
 
83
87
  private
@@ -285,7 +285,7 @@ module SyntaxTree
285
285
  # if we need to return the value of the last statement.
286
286
  attr_reader :last_statement
287
287
 
288
- def initialize(options)
288
+ def initialize(options = Options.new)
289
289
  @options = options
290
290
  @iseq = nil
291
291
  @last_statement = false
@@ -97,7 +97,7 @@ module SyntaxTree
97
97
  clause << Next(Args([]))
98
98
  when Leave
99
99
  value = Args([clause.pop])
100
- clause << (iseq.type == :top ? Break(value) : ReturnNode(value))
100
+ clause << (iseq.type != :top ? Break(value) : ReturnNode(value))
101
101
  when OptAnd, OptDiv, OptEq, OptGE, OptGT, OptLE, OptLT, OptLTLT,
102
102
  OptMinus, OptMod, OptMult, OptOr, OptPlus
103
103
  left, right = clause.pop(2)
@@ -70,7 +70,7 @@ module SyntaxTree
70
70
  [Fiddle::TYPE_VOIDP] * 3,
71
71
  Fiddle::TYPE_VOIDP
72
72
  )
73
- rescue NameError
73
+ rescue NameError, Fiddle::DLError
74
74
  end
75
75
 
76
76
  # This object is used to track the size of the stack at any given time. It