Skip to content

Commit 1fda4fe

Browse files
committed
Add \exhaustive\ annotation to exhaustive match blocks
Ponyc recently added an \exhaustive\ annotation for match expressions. When present, the compiler will fail compilation if the match is not exhaustive. This protects against future breakage if new variants are added to a union type.
1 parent 20625cc commit 1fda4fe

10 files changed

Lines changed: 76 additions & 76 deletions

examples/close-codes/main.pony

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ actor CloseHandler is ws.WebSocketServerActor
8686
else ""
8787
end
8888

89-
match close_status
89+
match \exhaustive\ close_status
9090
| let c: ws.CloseCode =>
9191
_out.print("Closed with code: " + c.string() + reason_suffix)
9292
| let _: ws.CloseNoStatusReceived =>

examples/request-filter/main.pony

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ actor FilterHandler is ws.WebSocketServerActor
6262
fun ref _websocket(): ws.WebSocketServer => _ws
6363

6464
fun ref on_upgrade_request(request: ws.UpgradeRequest val): Bool =>
65-
let origin = match request.header("Origin")
65+
let origin = match \exhaustive\ request.header("Origin")
6666
| let o: String val => o
6767
| None => ""
6868
end

mare/_frame_parser.pony

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class _FrameParser
2121
let frames = recover iso Array[_ParsedFrame val] end
2222

2323
while _buf.size() > 0 do
24-
match _try_parse_frame()
24+
match \exhaustive\ _try_parse_frame()
2525
| let frame: _ParsedFrame val =>
2626
frames.push(frame)
2727
| let err: _FrameError =>

mare/_handshake_parser.pony

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ class _HandshakeParser
2929
return HandshakeRequestTooLarge
3030
end
3131

32-
match _find_header_end()
32+
match \exhaustive\ _find_header_end()
3333
| None => _HandshakeNeedMore
3434
| let pos: USize => _parse_request(pos)
3535
end
@@ -133,13 +133,13 @@ class _HandshakeParser
133133
if not has_upgrade then return HandshakeMissingUpgrade end
134134
if not has_connection_upgrade then return HandshakeMissingUpgrade end
135135

136-
match websocket_version
136+
match \exhaustive\ websocket_version
137137
| let v: String val =>
138138
if v != "13" then return HandshakeWrongVersion end
139139
| None => return HandshakeWrongVersion
140140
end
141141

142-
match websocket_key
142+
match \exhaustive\ websocket_key
143143
| let key: String val =>
144144
let decoded_size =
145145
try

mare/_test_close_status.pony

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class \nodoc\ _TestExtractorPropertyRoundtrip is Property1[U16]
170170
end
171171
(let status, _) = _CloseStatusExtractor.from_payload(payload)
172172
// Verify the extracted code matches the input regardless of type
173-
let extracted_code = match status
173+
let extracted_code = match \exhaustive\ status
174174
| let s: CloseNormal => s.code()
175175
| let s: CloseGoingAway => s.code()
176176
| let s: CloseProtocolError => s.code()

mare/_test_fragment_reassembler.pony

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class \nodoc\ iso _TestReassemblerSingleText is UnitTest
88
fun apply(h: TestHelper) =>
99
let reassembler = _FragmentReassembler
1010
let payload: Array[U8] val = "Hello".array()
11-
match reassembler.frame(true, 0x01, payload, 1_048_576)
11+
match \exhaustive\ reassembler.frame(true, 0x01, payload, 1_048_576)
1212
| let msg: _CompleteMessage =>
1313
h.assert_true(msg.is_text)
1414
h.assert_eq[USize](5, msg.data.size())
@@ -23,7 +23,7 @@ class \nodoc\ iso _TestReassemblerSingleBinary is UnitTest
2323
fun apply(h: TestHelper) =>
2424
let reassembler = _FragmentReassembler
2525
let payload: Array[U8] val = recover val [as U8: 0x01; 0x02] end
26-
match reassembler.frame(true, 0x02, payload, 1_048_576)
26+
match \exhaustive\ reassembler.frame(true, 0x02, payload, 1_048_576)
2727
| let msg: _CompleteMessage =>
2828
h.assert_false(msg.is_text)
2929
h.assert_eq[USize](2, msg.data.size())
@@ -40,23 +40,23 @@ class \nodoc\ iso _TestReassemblerMultiFragment is UnitTest
4040

4141
// First fragment: text, FIN=0
4242
let frag1: Array[U8] val = "Hel".array()
43-
match reassembler.frame(false, 0x01, frag1, 1_048_576)
43+
match \exhaustive\ reassembler.frame(false, 0x01, frag1, 1_048_576)
4444
| _FragmentContinue => None // expected
4545
| let _: _CompleteMessage => h.fail("too early")
4646
| let err: _ReassemblyError => h.fail("unexpected error")
4747
end
4848

4949
// Continuation: FIN=0
5050
let frag2: Array[U8] val = "lo ".array()
51-
match reassembler.frame(false, 0x00, frag2, 1_048_576)
51+
match \exhaustive\ reassembler.frame(false, 0x00, frag2, 1_048_576)
5252
| _FragmentContinue => None // expected
5353
| let _: _CompleteMessage => h.fail("too early")
5454
| let err: _ReassemblyError => h.fail("unexpected error")
5555
end
5656

5757
// Final continuation: FIN=1
5858
let frag3: Array[U8] val = "World".array()
59-
match reassembler.frame(true, 0x00, frag3, 1_048_576)
59+
match \exhaustive\ reassembler.frame(true, 0x00, frag3, 1_048_576)
6060
| let msg: _CompleteMessage =>
6161
h.assert_true(msg.is_text)
6262
h.assert_eq[USize](11, msg.data.size())
@@ -83,7 +83,7 @@ class \nodoc\ iso _TestReassemblerInterleavedData is UnitTest
8383

8484
// Attempt to start a new text message (interleaved)
8585
let frag2: Array[U8] val = "World".array()
86-
match reassembler.frame(true, 0x01, frag2, 1_048_576)
86+
match \exhaustive\ reassembler.frame(true, 0x01, frag2, 1_048_576)
8787
| let err: _ReassemblyError =>
8888
h.assert_is[CloseCode](CloseProtocolError, err.code)
8989
| _FragmentContinue => h.fail("expected error")
@@ -102,7 +102,7 @@ class \nodoc\ iso _TestReassemblerMaxSize is UnitTest
102102
while i < 100 do a.push(0x41); i = i + 1 end
103103
a
104104
end
105-
match reassembler.frame(true, 0x02, payload, 50) // max=50
105+
match \exhaustive\ reassembler.frame(true, 0x02, payload, 50) // max=50
106106
| let err: _ReassemblyError =>
107107
h.assert_is[CloseCode](CloseMessageTooBig, err.code)
108108
| _FragmentContinue => h.fail("expected error")
@@ -116,7 +116,7 @@ class \nodoc\ iso _TestReassemblerInvalidUtf8 is UnitTest
116116
fun apply(h: TestHelper) =>
117117
let reassembler = _FragmentReassembler
118118
let payload: Array[U8] val = recover val [as U8: 0xFF; 0xFE] end
119-
match reassembler.frame(true, 0x01, payload, 1_048_576)
119+
match \exhaustive\ reassembler.frame(true, 0x01, payload, 1_048_576)
120120
| let err: _ReassemblyError =>
121121
h.assert_is[CloseCode](CloseInvalidPayload, err.code)
122122
| _FragmentContinue => h.fail("expected error")
@@ -132,7 +132,7 @@ class \nodoc\ iso _TestReassemblerValidUtf8 is UnitTest
132132
// Valid UTF-8: "café" = 63 61 66 C3 A9
133133
let payload: Array[U8] val =
134134
recover val [as U8: 0x63; 0x61; 0x66; 0xC3; 0xA9] end
135-
match reassembler.frame(true, 0x01, payload, 1_048_576)
135+
match \exhaustive\ reassembler.frame(true, 0x01, payload, 1_048_576)
136136
| let msg: _CompleteMessage =>
137137
h.assert_true(msg.is_text)
138138
h.assert_eq[USize](5, msg.data.size())
@@ -147,7 +147,7 @@ class \nodoc\ iso _TestReassemblerContinuationWithoutStart is UnitTest
147147
fun apply(h: TestHelper) =>
148148
let reassembler = _FragmentReassembler
149149
let payload: Array[U8] val = "data".array()
150-
match reassembler.frame(true, 0x00, payload, 1_048_576)
150+
match \exhaustive\ reassembler.frame(true, 0x00, payload, 1_048_576)
151151
| let err: _ReassemblyError =>
152152
h.assert_is[CloseCode](CloseProtocolError, err.code)
153153
| _FragmentContinue => h.fail("expected error")
@@ -197,15 +197,15 @@ class \nodoc\ iso _TestReassemblerPropertyRoundtrip is Property1[USize]
197197
end
198198

199199
let reassembler = _FragmentReassembler
200-
match reassembler.frame(false, 0x02, frag1, total_size + 1)
200+
match \exhaustive\ reassembler.frame(false, 0x02, frag1, total_size + 1)
201201
| _FragmentContinue => None
202202
else h.fail("expected continue for frag1")
203203
end
204-
match reassembler.frame(false, 0x00, frag2, total_size + 1)
204+
match \exhaustive\ reassembler.frame(false, 0x00, frag2, total_size + 1)
205205
| _FragmentContinue => None
206206
else h.fail("expected continue for frag2")
207207
end
208-
match reassembler.frame(true, 0x00, frag3, total_size + 1)
208+
match \exhaustive\ reassembler.frame(true, 0x00, frag3, total_size + 1)
209209
| let msg: _CompleteMessage =>
210210
h.assert_false(msg.is_text)
211211
h.assert_eq[USize](total_size, msg.data.size())

mare/_test_frame_encoder.pony

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class \nodoc\ iso _TestFrameEncoderPropertyRoundtrip is Property1[USize]
158158

159159
// Parse
160160
let parser = _FrameParser
161-
match parser.parse(masked)
161+
match \exhaustive\ parser.parse(masked)
162162
| let frames: Array[_ParsedFrame val] val =>
163163
h.assert_eq[USize](1, frames.size())
164164
let parsed = frames(0)?

mare/_test_frame_parser.pony

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ class \nodoc\ iso _TestFrameParserText is UnitTest
102102
let frame = _TestFrameHelper.masked_frame(
103103
true, 0x01, "Hello".array())
104104
let parser = _FrameParser
105-
match parser.parse(frame)
105+
match \exhaustive\ parser.parse(frame)
106106
| let frames: Array[_ParsedFrame val] val =>
107107
h.assert_eq[USize](1, frames.size())
108108
h.assert_true(frames(0)?.fin)
@@ -120,7 +120,7 @@ class \nodoc\ iso _TestFrameParserBinary is UnitTest
120120
let payload: Array[U8] val = recover val [as U8: 0x01; 0x02; 0x03] end
121121
let frame = _TestFrameHelper.masked_frame(true, 0x02, payload)
122122
let parser = _FrameParser
123-
match parser.parse(frame)
123+
match \exhaustive\ parser.parse(frame)
124124
| let frames: Array[_ParsedFrame val] val =>
125125
h.assert_eq[USize](1, frames.size())
126126
h.assert_eq[U8](0x02, frames(0)?.opcode)
@@ -138,7 +138,7 @@ class \nodoc\ iso _TestFrameParserPing is UnitTest
138138
let payload: Array[U8] val = recover val [as U8: 0xAA; 0xBB] end
139139
let frame = _TestFrameHelper.masked_frame(true, 0x09, payload)
140140
let parser = _FrameParser
141-
match parser.parse(frame)
141+
match \exhaustive\ parser.parse(frame)
142142
| let frames: Array[_ParsedFrame val] val =>
143143
h.assert_eq[USize](1, frames.size())
144144
h.assert_eq[U8](0x09, frames(0)?.opcode)
@@ -154,7 +154,7 @@ class \nodoc\ iso _TestFrameParserPong is UnitTest
154154
let frame = _TestFrameHelper.masked_frame(
155155
true, 0x0A, recover val Array[U8] end)
156156
let parser = _FrameParser
157-
match parser.parse(frame)
157+
match \exhaustive\ parser.parse(frame)
158158
| let frames: Array[_ParsedFrame val] val =>
159159
h.assert_eq[USize](1, frames.size())
160160
h.assert_eq[U8](0x0A, frames(0)?.opcode)
@@ -171,7 +171,7 @@ class \nodoc\ iso _TestFrameParserCloseWithCode is UnitTest
171171
recover val [as U8: 0x03; 0xE8; 'b'; 'y'; 'e'] end
172172
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
173173
let parser = _FrameParser
174-
match parser.parse(frame)
174+
match \exhaustive\ parser.parse(frame)
175175
| let frames: Array[_ParsedFrame val] val =>
176176
h.assert_eq[USize](1, frames.size())
177177
h.assert_eq[U8](0x08, frames(0)?.opcode)
@@ -190,7 +190,7 @@ class \nodoc\ iso _TestFrameParserCloseEmpty is UnitTest
190190
let frame = _TestFrameHelper.masked_frame(
191191
true, 0x08, recover val Array[U8] end)
192192
let parser = _FrameParser
193-
match parser.parse(frame)
193+
match \exhaustive\ parser.parse(frame)
194194
| let frames: Array[_ParsedFrame val] val =>
195195
h.assert_eq[USize](1, frames.size())
196196
h.assert_eq[U8](0x08, frames(0)?.opcode)
@@ -206,7 +206,7 @@ class \nodoc\ iso _TestFrameParserCloseOneByte is UnitTest
206206
let payload: Array[U8] val = recover val [as U8: 0x03] end
207207
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
208208
let parser = _FrameParser
209-
match parser.parse(frame)
209+
match \exhaustive\ parser.parse(frame)
210210
| let frames: Array[_ParsedFrame val] val =>
211211
h.fail("expected error for 1-byte close payload")
212212
| let err: _FrameError =>
@@ -223,7 +223,7 @@ class \nodoc\ iso _TestFrameParserCloseInvalidUtf8Reason is UnitTest
223223
recover val [as U8: 0x03; 0xE8; 0xFF] end
224224
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
225225
let parser = _FrameParser
226-
match parser.parse(frame)
226+
match \exhaustive\ parser.parse(frame)
227227
| let frames: Array[_ParsedFrame val] val =>
228228
h.fail("expected error for invalid UTF-8 in close reason")
229229
| let err: _FrameError =>
@@ -240,7 +240,7 @@ class \nodoc\ iso _TestFrameParserCloseValidUtf8Reason is UnitTest
240240
recover val [as U8: 0x03; 0xE8; 'O'; 'K'] end
241241
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
242242
let parser = _FrameParser
243-
match parser.parse(frame)
243+
match \exhaustive\ parser.parse(frame)
244244
| let frames: Array[_ParsedFrame val] val =>
245245
h.assert_eq[USize](1, frames.size())
246246
h.assert_eq[U8](0x08, frames(0)?.opcode)
@@ -260,7 +260,7 @@ class \nodoc\ iso _TestFrameParserLength16Bit is UnitTest
260260
end
261261
let frame = _TestFrameHelper.masked_frame(true, 0x02, payload)
262262
let parser = _FrameParser
263-
match parser.parse(frame)
263+
match \exhaustive\ parser.parse(frame)
264264
| let frames: Array[_ParsedFrame val] val =>
265265
h.assert_eq[USize](1, frames.size())
266266
h.assert_eq[USize](200, frames(0)?.payload.size())
@@ -281,7 +281,7 @@ class \nodoc\ iso _TestFrameParserLength64Bit is UnitTest
281281
end
282282
let frame = _TestFrameHelper.masked_frame(true, 0x02, payload)
283283
let parser = _FrameParser
284-
match parser.parse(frame)
284+
match \exhaustive\ parser.parse(frame)
285285
| let frames: Array[_ParsedFrame val] val =>
286286
h.assert_eq[USize](1, frames.size())
287287
h.assert_eq[USize](size, frames(0)?.payload.size())
@@ -309,7 +309,7 @@ class \nodoc\ iso _TestFrameParserLength64BitMsbSet is UnitTest
309309
f
310310
end
311311
let parser = _FrameParser
312-
match parser.parse(raw)
312+
match \exhaustive\ parser.parse(raw)
313313
| let frames: Array[_ParsedFrame val] val =>
314314
h.fail("expected error for 64-bit length with MSB set")
315315
| let err: _FrameError =>
@@ -324,7 +324,7 @@ class \nodoc\ iso _TestFrameParserUnmasked is UnitTest
324324
let frame = _TestFrameHelper.unmasked_frame(
325325
true, 0x01, "Hello".array())
326326
let parser = _FrameParser
327-
match parser.parse(frame)
327+
match \exhaustive\ parser.parse(frame)
328328
| let frames: Array[_ParsedFrame val] val =>
329329
h.fail("expected error for unmasked frame")
330330
| let err: _FrameError =>
@@ -345,7 +345,7 @@ class \nodoc\ iso _TestFrameParserNonZeroRsv is UnitTest
345345
f
346346
end
347347
let parser = _FrameParser
348-
match parser.parse(raw)
348+
match \exhaustive\ parser.parse(raw)
349349
| let frames: Array[_ParsedFrame val] val =>
350350
h.fail("expected error for RSV bits")
351351
| let err: _FrameError =>
@@ -360,7 +360,7 @@ class \nodoc\ iso _TestFrameParserFragmentedControl is UnitTest
360360
let frame = _TestFrameHelper.masked_frame(
361361
false, 0x09, recover val Array[U8] end)
362362
let parser = _FrameParser
363-
match parser.parse(frame)
363+
match \exhaustive\ parser.parse(frame)
364364
| let frames: Array[_ParsedFrame val] val =>
365365
h.fail("expected error for fragmented control frame")
366366
| let err: _FrameError =>
@@ -380,7 +380,7 @@ class \nodoc\ iso _TestFrameParserControlTooLarge is UnitTest
380380
end
381381
let frame = _TestFrameHelper.masked_frame(true, 0x09, payload)
382382
let parser = _FrameParser
383-
match parser.parse(frame)
383+
match \exhaustive\ parser.parse(frame)
384384
| let frames: Array[_ParsedFrame val] val =>
385385
h.fail("expected error for oversized control frame")
386386
| let err: _FrameError =>
@@ -395,7 +395,7 @@ class \nodoc\ iso _TestFrameParserUnknownOpcode is UnitTest
395395
let frame = _TestFrameHelper.masked_frame(
396396
true, 0x03, recover val Array[U8] end)
397397
let parser = _FrameParser
398-
match parser.parse(frame)
398+
match \exhaustive\ parser.parse(frame)
399399
| let frames: Array[_ParsedFrame val] val =>
400400
h.fail("expected error for unknown opcode")
401401
| let err: _FrameError =>
@@ -415,7 +415,7 @@ class \nodoc\ iso _TestFrameParserIncremental is UnitTest
415415
var i: USize = 0
416416
while i < (full_frame.size() - 1) do
417417
let byte: Array[U8] val = recover val [as U8: full_frame(i)?] end
418-
match parser.parse(byte)
418+
match \exhaustive\ parser.parse(byte)
419419
| let frames: Array[_ParsedFrame val] val =>
420420
h.assert_eq[USize](0, frames.size())
421421
| let err: _FrameError => h.fail("unexpected error at byte " +
@@ -426,7 +426,7 @@ class \nodoc\ iso _TestFrameParserIncremental is UnitTest
426426

427427
// Feed last byte — should complete the frame
428428
let last: Array[U8] val = recover val [as U8: full_frame(i)?] end
429-
match parser.parse(last)
429+
match \exhaustive\ parser.parse(last)
430430
| let frames: Array[_ParsedFrame val] val =>
431431
h.assert_eq[USize](1, frames.size())
432432
h.assert_eq[U8](0x01, frames(0)?.opcode)
@@ -454,7 +454,7 @@ class \nodoc\ iso _TestFrameParserMultipleFrames is UnitTest
454454
end
455455

456456
let parser = _FrameParser
457-
match parser.parse(combined)
457+
match \exhaustive\ parser.parse(combined)
458458
| let frames: Array[_ParsedFrame val] val =>
459459
h.assert_eq[USize](2, frames.size())
460460
h.assert_eq[U8](0x01, frames(0)?.opcode)
@@ -479,7 +479,7 @@ class \nodoc\ iso _TestFrameParserCloseValidCodes is Property1[U16]
479479
recover val [as U8: (code >> 8).u8(); code.u8()] end
480480
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
481481
let parser = _FrameParser
482-
match parser.parse(frame)
482+
match \exhaustive\ parser.parse(frame)
483483
| let frames: Array[_ParsedFrame val] val =>
484484
h.assert_eq[USize](1, frames.size())
485485
h.assert_eq[U8](0x08, frames(0)?.opcode)
@@ -507,7 +507,7 @@ class \nodoc\ iso _TestFrameParserCloseInvalidCodes is Property1[U16]
507507
recover val [as U8: (code >> 8).u8(); code.u8()] end
508508
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
509509
let parser = _FrameParser
510-
match parser.parse(frame)
510+
match \exhaustive\ parser.parse(frame)
511511
| let frames: Array[_ParsedFrame val] val =>
512512
h.fail("expected error for invalid code " + code.string())
513513
| let err: _FrameError =>
@@ -543,7 +543,7 @@ class \nodoc\ iso _TestFrameParserCloseMixedCodes is Property1[U16]
543543
recover val [as U8: (code >> 8).u8(); code.u8()] end
544544
let frame = _TestFrameHelper.masked_frame(true, 0x08, payload)
545545
let parser = _FrameParser
546-
match parser.parse(frame)
546+
match \exhaustive\ parser.parse(frame)
547547
| let frames: Array[_ParsedFrame val] val =>
548548
h.assert_true(valid, "code " + code.string() + " should be rejected")
549549
| let err: _FrameError =>
@@ -566,7 +566,7 @@ class \nodoc\ iso _TestFrameParserPropertyRandom is Property1[USize]
566566
end
567567
let frame = _TestFrameHelper.masked_frame(true, 0x02, payload)
568568
let parser = _FrameParser
569-
match parser.parse(frame)
569+
match \exhaustive\ parser.parse(frame)
570570
| let frames: Array[_ParsedFrame val] val =>
571571
h.assert_eq[USize](1, frames.size())
572572
h.assert_eq[U8](0x02, frames(0)?.opcode)

0 commit comments

Comments
 (0)