Skip to content

Commit 45d8c4c

Browse files
committed
Enforce types in the test suite
1 parent 72c4f5c commit 45d8c4c

File tree

9 files changed

+173
-120
lines changed

9 files changed

+173
-120
lines changed

lib/syntax_tree/node.rb

Lines changed: 62 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1131,7 +1131,8 @@ def format(q)
11311131
end
11321132
end
11331133

1134-
# [LBracket] the bracket that opens this array
1134+
# [nil | LBracket | QSymbolsBeg | QWordsBeg | SymbolsBeg | WordsBeg] the
1135+
# bracket that opens this array
11351136
attr_reader :lbracket
11361137

11371138
# [nil | Args] the contents of the array
@@ -1485,7 +1486,7 @@ class Assoc < Node
14851486
# [Node] the key of this pair
14861487
attr_reader :key
14871488

1488-
# [Node] the value of this pair
1489+
# [nil | Node] the value of this pair
14891490
attr_reader :value
14901491

14911492
# [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -3508,16 +3509,16 @@ def align(q, node, &block)
35083509
# object.method argument
35093510
#
35103511
class CommandCall < Node
3511-
# [Node] the receiver of the message
3512+
# [nil | Node] the receiver of the message
35123513
attr_reader :receiver
35133514

3514-
# [:"::" | Op | Period] the operator used to send the message
3515+
# [nil | :"::" | Op | Period] the operator used to send the message
35153516
attr_reader :operator
35163517

3517-
# [Const | Ident | Op] the message being send
3518+
# [:call | Const | Ident | Op] the message being send
35183519
attr_reader :message
35193520

3520-
# [nil | Args] the arguments going along with the message
3521+
# [nil | Args | ArgParen] the arguments going along with the message
35213522
attr_reader :arguments
35223523

35233524
# [nil | BlockNode] the block associated with this method call
@@ -4603,7 +4604,7 @@ class DynaSymbol < Node
46034604
# dynamic symbol
46044605
attr_reader :parts
46054606

4606-
# [String] the quote used to delimit the dynamic symbol
4607+
# [nil | String] the quote used to delimit the dynamic symbol
46074608
attr_reader :quote
46084609

46094610
# [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -5947,7 +5948,7 @@ def comments
59475948
end
59485949

59495950
def format(q)
5950-
q.format(key)
5951+
HashKeyFormatter::Labels.new.format_key(q, key)
59515952

59525953
if value
59535954
q.text(" ")
@@ -5978,8 +5979,8 @@ def format(q)
59785979
# [nil | Node] the optional constant wrapper
59795980
attr_reader :constant
59805981

5981-
# [Array[ [Label, Node] ]] the set of tuples representing the keywords
5982-
# that should be matched against in the pattern
5982+
# [Array[ [DynaSymbol | Label, nil | Node] ]] the set of tuples
5983+
# representing the keywords that should be matched against in the pattern
59835984
attr_reader :keywords
59845985

59855986
# [nil | VarField] an optional parameter to gather up all remaining keywords
@@ -7510,7 +7511,7 @@ def ===(other)
75107511
# method {}
75117512
#
75127513
class MethodAddBlock < Node
7513-
# [CallNode | Command | CommandCall] the method call
7514+
# [ARef | CallNode | Command | CommandCall | Super | ZSuper] the method call
75147515
attr_reader :call
75157516

75167517
# [BlockNode] the block being sent with the method call
@@ -7585,8 +7586,12 @@ def format_contents(q)
75857586
# first, second, third = value
75867587
#
75877588
class MLHS < Node
7588-
# [Array[ARefField | ArgStar | Field | Ident | MLHSParen | VarField]] the
7589-
# parts of the left-hand side of a multiple assignment
7589+
# [
7590+
# Array[
7591+
# ARefField | ArgStar | ConstPathField | Field | Ident | MLHSParen |
7592+
# TopConstField | VarField
7593+
# ]
7594+
# ] the parts of the left-hand side of a multiple assignment
75907595
attr_reader :parts
75917596

75927597
# [boolean] whether or not there is a trailing comma at the end of this
@@ -8211,7 +8216,7 @@ def format(q)
82118216
end
82128217
end
82138218

8214-
# [Array[ Ident ]] any required parameters
8219+
# [Array[ Ident | MLHSParen ]] any required parameters
82158220
attr_reader :requireds
82168221

82178222
# [Array[ [ Ident, Node ] ]] any optional parameters and their default
@@ -8226,11 +8231,12 @@ def format(q)
82268231
# parameter
82278232
attr_reader :posts
82288233

8229-
# [Array[ [ Ident, nil | Node ] ]] any keyword parameters and their
8234+
# [Array[ [ Label, nil | Node ] ]] any keyword parameters and their
82308235
# optional default values
82318236
attr_reader :keywords
82328237

8233-
# [nil | :nil | KwRestParam] the optional keyword rest parameter
8238+
# [nil | :nil | ArgsForward | KwRestParam] the optional keyword rest
8239+
# parameter
82348240
attr_reader :keyword_rest
82358241

82368242
# [nil | BlockArg] the optional block parameter
@@ -9268,7 +9274,7 @@ def ambiguous?(q)
92689274
# end
92699275
#
92709276
class RescueEx < Node
9271-
# [Node] the list of exceptions being rescued
9277+
# [nil | Node] the list of exceptions being rescued
92729278
attr_reader :exceptions
92739279

92749280
# [nil | Field | VarField] the expression being used to capture the raised
@@ -9346,7 +9352,7 @@ class Rescue < Node
93469352
# [Kw] the rescue keyword
93479353
attr_reader :keyword
93489354

9349-
# [RescueEx] the exceptions being rescued
9355+
# [nil | RescueEx] the exceptions being rescued
93509356
attr_reader :exception
93519357

93529358
# [Statements] the expressions to evaluate when an error is rescued
@@ -9995,9 +10001,13 @@ class StringContent < Node
999510001
# string
999610002
attr_reader :parts
999710003

10004+
# [Array[ Comment | EmbDoc ]] the comments attached to this node
10005+
attr_reader :comments
10006+
999810007
def initialize(parts:, location:)
999910008
@parts = parts
1000010009
@location = location
10010+
@comments = []
1000110011
end
1000210012

1000310013
def accept(visitor)
@@ -10024,6 +10034,33 @@ def deconstruct_keys(_keys)
1002410034
def ===(other)
1002510035
other.is_a?(StringContent) && ArrayMatch.call(parts, other.parts)
1002610036
end
10037+
10038+
def format(q)
10039+
q.text(q.quote)
10040+
q.group do
10041+
parts.each do |part|
10042+
if part.is_a?(TStringContent)
10043+
value = Quotes.normalize(part.value, q.quote)
10044+
first = true
10045+
10046+
value.each_line(chomp: true) do |line|
10047+
if first
10048+
first = false
10049+
else
10050+
q.breakable_return
10051+
end
10052+
10053+
q.text(line)
10054+
end
10055+
10056+
q.breakable_return if value.end_with?("\n")
10057+
else
10058+
q.format(part)
10059+
end
10060+
end
10061+
end
10062+
q.text(q.quote)
10063+
end
1002710064
end
1002810065

1002910066
# StringConcat represents concatenating two strings together using a backward
@@ -10033,7 +10070,8 @@ def ===(other)
1003310070
# "second"
1003410071
#
1003510072
class StringConcat < Node
10036-
# [StringConcat | StringLiteral] the left side of the concatenation
10073+
# [Heredoc | StringConcat | StringLiteral] the left side of the
10074+
# concatenation
1003710075
attr_reader :left
1003810076

1003910077
# [StringLiteral] the right side of the concatenation
@@ -10230,7 +10268,7 @@ class StringLiteral < Node
1023010268
# string literal
1023110269
attr_reader :parts
1023210270

10233-
# [String] which quote was used by the string literal
10271+
# [nil | String] which quote was used by the string literal
1023410272
attr_reader :quote
1023510273

1023610274
# [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -10475,8 +10513,8 @@ def ===(other)
1047510513
# :symbol
1047610514
#
1047710515
class SymbolLiteral < Node
10478-
# [Backtick | Const | CVar | GVar | Ident | IVar | Kw | Op] the value of the
10479-
# symbol
10516+
# [Backtick | Const | CVar | GVar | Ident | IVar | Kw | Op | TStringContent]
10517+
# the value of the symbol
1048010518
attr_reader :value
1048110519

1048210520
# [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -11430,7 +11468,7 @@ def modifier?
1143011468
#
1143111469
# In the example above, the VarField node represents the +variable+ token.
1143211470
class VarField < Node
11433-
# [nil | Const | CVar | GVar | Ident | IVar] the target of this node
11471+
# [nil | :nil | Const | CVar | GVar | Ident | IVar] the target of this node
1143411472
attr_reader :value
1143511473

1143611474
# [Array[ Comment | EmbDoc ]] the comments attached to this node
@@ -11569,7 +11607,7 @@ def pin(parent, pin)
1156911607
# This can be a plain local variable like the example above. It can also be a
1157011608
# a class variable, a global variable, or an instance variable.
1157111609
class PinnedVarRef < Node
11572-
# [VarRef] the value of this node
11610+
# [Const | CVar | GVar | Ident | IVar] the value of this node
1157311611
attr_reader :value
1157411612

1157511613
# [Array[ Comment | EmbDoc ]] the comments attached to this node

lib/syntax_tree/parser.rb

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,13 @@ def on_blockarg(name)
908908
# (nil | Ensure) ensure_clause
909909
# ) -> BodyStmt
910910
def on_bodystmt(statements, rescue_clause, else_clause, ensure_clause)
911+
# In certain versions of Ruby, the `statements` argument can be any node
912+
# in the case that we're inside of an endless method definition. In this
913+
# case we'll wrap it in a Statements node to be consistent.
914+
unless statements.is_a?(Statements)
915+
statements = Statements.new(self, body: [statements], location: statements.location)
916+
end
917+
911918
parts = [statements, rescue_clause, else_clause, ensure_clause].compact
912919

913920
BodyStmt.new(
@@ -1157,13 +1164,23 @@ def on_const(value)
11571164
end
11581165

11591166
# :call-seq:
1160-
# on_const_path_field: (untyped parent, Const constant) -> ConstPathField
1167+
# on_const_path_field: (untyped parent, Const constant) ->
1168+
# ConstPathField | Field
11611169
def on_const_path_field(parent, constant)
1162-
ConstPathField.new(
1163-
parent: parent,
1164-
constant: constant,
1165-
location: parent.location.to(constant.location)
1166-
)
1170+
if constant.is_a?(Const)
1171+
ConstPathField.new(
1172+
parent: parent,
1173+
constant: constant,
1174+
location: parent.location.to(constant.location)
1175+
)
1176+
else
1177+
Field.new(
1178+
parent: parent,
1179+
operator: consume_operator(:"::"),
1180+
name: constant,
1181+
location: parent.location.to(constant.location)
1182+
)
1183+
end
11671184
end
11681185

11691186
# :call-seq:
@@ -1866,10 +1883,40 @@ def on_heredoc_end(value)
18661883
# :call-seq:
18671884
# on_hshptn: (
18681885
# (nil | untyped) constant,
1869-
# Array[[Label, untyped]] keywords,
1886+
# Array[[Label | StringContent, untyped]] keywords,
18701887
# (nil | VarField) keyword_rest
18711888
# ) -> HshPtn
18721889
def on_hshptn(constant, keywords, keyword_rest)
1890+
keywords =
1891+
(keywords || []).map do |(label, value)|
1892+
if label.is_a?(Label)
1893+
[label, value]
1894+
else
1895+
tstring_beg_index =
1896+
tokens.rindex do |token|
1897+
token.is_a?(TStringBeg) && token.location.start_char < label.location.start_char
1898+
end
1899+
1900+
tstring_beg = tokens.delete_at(tstring_beg_index)
1901+
1902+
label_end_index =
1903+
tokens.rindex do |token|
1904+
token.is_a?(LabelEnd) && token.location.start_char == label.location.end_char
1905+
end
1906+
1907+
label_end = tokens.delete_at(label_end_index)
1908+
1909+
[
1910+
DynaSymbol.new(
1911+
parts: label.parts,
1912+
quote: label_end.value[0],
1913+
location: tstring_beg.location.to(label_end.location)
1914+
),
1915+
value
1916+
]
1917+
end
1918+
end
1919+
18731920
if keyword_rest
18741921
# We're doing this to delete the token from the list so that it doesn't
18751922
# confuse future patterns by thinking they have an extra ** on the end.
@@ -1882,7 +1929,7 @@ def on_hshptn(constant, keywords, keyword_rest)
18821929
keyword_rest = VarField.new(value: nil, location: token.location)
18831930
end
18841931

1885-
parts = [constant, *keywords&.flatten(1), keyword_rest].compact
1932+
parts = [constant, *keywords.flatten(1), keyword_rest].compact
18861933

18871934
# If there's no constant, there may be braces, so we're going to look for
18881935
# those to get our bounds.
@@ -1899,7 +1946,7 @@ def on_hshptn(constant, keywords, keyword_rest)
18991946

19001947
HshPtn.new(
19011948
constant: constant,
1902-
keywords: keywords || [],
1949+
keywords: keywords,
19031950
keyword_rest: keyword_rest,
19041951
location: parts[0].location.to(parts[-1].location)
19051952
)
@@ -2379,7 +2426,7 @@ def on_method_add_block(call, block)
23792426
location = call.location.to(block.location)
23802427

23812428
case call
2382-
when Break, Next
2429+
when Break, Next, ReturnNode
23832430
parts = call.arguments.parts
23842431

23852432
node = parts.pop

lib/syntax_tree/reflection.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ def initialize(type)
2121
def ===(value)
2222
value.is_a?(Array) && value.all? { type === _1 }
2323
end
24+
25+
def inspect
26+
"Array<#{type.inspect}>"
27+
end
2428
end
2529

2630
# Represents a tuple type that holds a number of types in order.
@@ -35,6 +39,10 @@ def ===(value)
3539
value.is_a?(Array) && value.length == types.length &&
3640
value.zip(types).all? { _2 === _1 }
3741
end
42+
43+
def inspect
44+
"[#{types.map(&:inspect).join(", ")}]"
45+
end
3846
end
3947

4048
# Represents a union type that can be one of a number of types.
@@ -48,6 +56,10 @@ def initialize(types)
4856
def ===(value)
4957
types.any? { _1 === value }
5058
end
59+
60+
def inspect
61+
types.map(&:inspect).join(" | ")
62+
end
5163
end
5264

5365
class << self

0 commit comments

Comments
 (0)