1use super::core::Parser;
20use super::core::Rec;
21use super::core::Result;
22use super::error::Error;
23use super::error::SyntaxError;
24use super::lex::Keyword::{Case, Esac, In};
25use super::lex::Operator::{Bar, CloseParen, Newline, OpenParen};
26use super::lex::TokenId::{self, EndOfInput, Operator, Token};
27use crate::syntax::CaseItem;
28use crate::syntax::CompoundCommand;
29
30impl Parser<'_, '_> {
31 pub async fn case_item(&mut self) -> Result<Option<(CaseItem, bool)>> {
36 fn pattern_error_cause(token_id: TokenId) -> SyntaxError {
37 match token_id {
38 Token(_) => unreachable!(),
39 Operator(CloseParen) | Operator(Bar) | Operator(Newline) | EndOfInput => {
40 SyntaxError::MissingPattern
41 }
42 _ => SyntaxError::InvalidPattern,
43 }
44 }
45
46 let first_token = loop {
47 while self.newline_and_here_doc_contents().await? {}
48
49 if self.peek_token().await?.id == Token(Some(Esac)) {
50 return Ok(None);
51 }
52
53 match self.take_token_manual(false).await? {
54 Rec::AliasSubstituted => (),
55 Rec::Parsed(token) => break token,
56 }
57 };
58
59 let first_pattern = match first_token.id {
60 Token(_) => first_token.word,
61 Operator(OpenParen) => {
62 let next_token = self.take_token_auto(&[Esac]).await?;
63 match next_token.id {
64 Token(_) => next_token.word,
65 _ => {
66 let cause = pattern_error_cause(next_token.id).into();
67 let location = next_token.word.location;
68 return Err(Error { cause, location });
69 }
70 }
71 }
72 _ => {
73 let cause = pattern_error_cause(first_token.id).into();
74 let location = first_token.word.location;
75 return Err(Error { cause, location });
76 }
77 };
78
79 let mut patterns = vec![first_pattern];
80 loop {
81 let separator = self.take_token_auto(&[]).await?;
82 match separator.id {
83 Operator(CloseParen) => break,
84 Operator(Bar) => {
85 let pattern = self.take_token_auto(&[]).await?;
86 match pattern.id {
87 Token(_) => patterns.push(pattern.word),
88 _ => {
89 let cause = pattern_error_cause(pattern.id).into();
90 let location = pattern.word.location;
91 return Err(Error { cause, location });
92 }
93 }
94 }
95 _ => {
96 let cause = SyntaxError::UnclosedPatternList.into();
97 let location = separator.word.location;
98 return Err(Error { cause, location });
99 }
100 }
101 }
102
103 let body = self.maybe_compound_list_boxed().await?;
104
105 let continuation = match self.peek_token().await?.id {
106 Operator(op) => op.try_into().ok(),
107 _ => None,
108 };
109 let continued = continuation.is_some();
110 let continuation = continuation.unwrap_or_default();
111 if continued {
112 self.take_token_raw().await?;
113 }
115
116 Ok(Some((
117 CaseItem {
118 patterns,
119 body,
120 continuation,
121 },
122 continued,
123 )))
124 }
125
126 pub async fn case_command(&mut self) -> Result<CompoundCommand> {
134 let open = self.take_token_raw().await?;
135 assert_eq!(open.id, Token(Some(Case)));
136
137 let subject = self.take_token_auto(&[]).await?;
138 match subject.id {
139 Token(_) => (),
140 Operator(Newline) | EndOfInput => {
141 let cause = SyntaxError::MissingCaseSubject.into();
142 let location = subject.word.location;
143 return Err(Error { cause, location });
144 }
145 _ => {
146 let cause = SyntaxError::InvalidCaseSubject.into();
147 let location = subject.word.location;
148 return Err(Error { cause, location });
149 }
150 }
151 let subject = subject.word;
152
153 loop {
154 while self.newline_and_here_doc_contents().await? {}
155
156 let next_token = self.take_token_auto(&[In]).await?;
157 match next_token.id {
158 Token(Some(In)) => break,
159 Operator(Newline) => (),
160 _ => {
161 let opening_location = open.word.location;
162 let cause = SyntaxError::MissingIn { opening_location }.into();
163 let location = next_token.word.location;
164 return Err(Error { cause, location });
165 }
166 }
167 }
168
169 let mut items = Vec::new();
170 while let Some((item, continued)) = self.case_item().await? {
171 items.push(item);
172 if !continued {
173 break;
174 }
175 }
176
177 let close = self.take_token_raw().await?;
178 if close.id != Token(Some(Esac)) {
179 let opening_location = open.word.location;
180 let cause = SyntaxError::UnclosedCase { opening_location }.into();
181 let location = close.word.location;
182 return Err(Error { cause, location });
183 }
184
185 Ok(CompoundCommand::Case { subject, items })
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::super::error::ErrorCause;
192 use super::super::lex::Lexer;
193 use super::*;
194 use crate::alias::{AliasSet, EmptyGlossary, HashEntry};
195 use crate::source::Location;
196 use crate::source::Source;
197 use crate::syntax::CaseContinuation;
198 use assert_matches::assert_matches;
199 use futures_util::FutureExt;
200
201 #[test]
202 fn parser_case_item_esac() {
203 let mut lexer = Lexer::from_memory("\nESAC", Source::Unknown);
204 #[allow(clippy::mutable_key_type)]
205 let mut aliases = AliasSet::new();
206 let origin = Location::dummy("");
207 aliases.insert(HashEntry::new(
208 "ESAC".to_string(),
209 "\n\nesac".to_string(),
210 true,
211 origin.clone(),
212 ));
213 aliases.insert(HashEntry::new(
214 "esac".to_string(),
215 "&&".to_string(),
216 true,
217 origin,
218 ));
219 let mut parser = Parser::new(&mut lexer, &aliases);
220
221 let option = parser.case_item().now_or_never().unwrap().unwrap();
222 assert_eq!(option, None);
223
224 let next = parser.peek_token().now_or_never().unwrap().unwrap();
225 assert_eq!(next.id, Token(Some(Esac)));
226 }
227
228 #[test]
229 fn parser_case_item_minimum() {
230 let mut lexer = Lexer::from_memory("foo)", Source::Unknown);
231 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
232
233 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
234 assert_eq!(item.patterns.len(), 1);
235 assert_eq!(item.patterns[0].to_string(), "foo");
236 assert_eq!(item.body.0, []);
237 assert_eq!(item.continuation, CaseContinuation::Break);
238 assert!(!continued);
239
240 let next = parser.peek_token().now_or_never().unwrap().unwrap();
241 assert_eq!(next.id, EndOfInput);
242 }
243
244 #[test]
245 fn parser_case_item_with_open_paren() {
246 let mut lexer = Lexer::from_memory("(foo)", Source::Unknown);
247 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
248
249 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
250 assert_eq!(item.patterns.len(), 1);
251 assert_eq!(item.patterns[0].to_string(), "foo");
252 assert_eq!(item.body.0, []);
253 assert_eq!(item.continuation, CaseContinuation::Break);
254 assert!(!continued);
255
256 let next = parser.peek_token().now_or_never().unwrap().unwrap();
257 assert_eq!(next.id, EndOfInput);
258 }
259
260 #[test]
261 fn parser_case_item_many_patterns() {
262 let mut lexer = Lexer::from_memory("1 | esac | $three)", Source::Unknown);
263 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
264
265 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
266 assert_eq!(item.patterns.len(), 3);
267 assert_eq!(item.patterns[0].to_string(), "1");
268 assert_eq!(item.patterns[1].to_string(), "esac");
269 assert_eq!(item.patterns[2].to_string(), "$three");
270 assert_eq!(item.body.0, []);
271 assert_eq!(item.continuation, CaseContinuation::Break);
272 assert!(!continued);
273
274 let next = parser.peek_token().now_or_never().unwrap().unwrap();
275 assert_eq!(next.id, EndOfInput);
276 }
277
278 #[test]
279 fn parser_case_item_non_empty_body() {
280 let mut lexer = Lexer::from_memory("foo)\necho ok\n:&\n", Source::Unknown);
281 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
282
283 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
284 assert_eq!(item.patterns.len(), 1);
285 assert_eq!(item.patterns[0].to_string(), "foo");
286 assert_eq!(item.body.0.len(), 2);
287 assert_eq!(item.body.0[0].to_string(), "echo ok");
288 assert_eq!(item.body.0[1].to_string(), ":&");
289 assert_eq!(item.continuation, CaseContinuation::Break);
290 assert!(!continued);
291
292 let next = parser.peek_token().now_or_never().unwrap().unwrap();
293 assert_eq!(next.id, EndOfInput);
294 }
295
296 #[test]
297 fn parser_case_item_with_double_semicolon() {
298 let mut lexer = Lexer::from_memory("foo);;", Source::Unknown);
299 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
300
301 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
302 assert_eq!(item.patterns.len(), 1);
303 assert_eq!(item.patterns[0].to_string(), "foo");
304 assert_eq!(item.body.0, []);
305 assert_eq!(item.continuation, CaseContinuation::Break);
306 assert!(continued);
307
308 let next = parser.peek_token().now_or_never().unwrap().unwrap();
309 assert_eq!(next.id, EndOfInput);
310 }
311
312 #[test]
313 fn parser_case_item_with_non_empty_body_and_double_semicolon() {
314 let mut lexer = Lexer::from_memory("foo):;\n;;", Source::Unknown);
315 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
316
317 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
318 assert_eq!(item.patterns.len(), 1);
319 assert_eq!(item.patterns[0].to_string(), "foo");
320 assert_eq!(item.body.0.len(), 1);
321 assert_eq!(item.body.0[0].to_string(), ":");
322 assert_eq!(item.continuation, CaseContinuation::Break);
323 assert!(continued);
324
325 let next = parser.peek_token().now_or_never().unwrap().unwrap();
326 assert_eq!(next.id, EndOfInput);
327 }
328
329 #[test]
330 fn parser_case_item_with_semicolon_and() {
331 let mut lexer = Lexer::from_memory("foo);&", Source::Unknown);
332 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
333
334 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
335 assert_eq!(item.patterns.len(), 1);
336 assert_eq!(item.patterns[0].to_string(), "foo");
337 assert_eq!(item.body.0, []);
338 assert_eq!(item.continuation, CaseContinuation::FallThrough);
339 assert!(continued);
340
341 let next = parser.peek_token().now_or_never().unwrap().unwrap();
342 assert_eq!(next.id, EndOfInput);
343 }
344
345 #[test]
346 fn parser_case_item_missing_pattern_without_open_paren() {
347 let mut lexer = Lexer::from_memory(")", Source::Unknown);
348 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
349
350 let e = parser.case_item().now_or_never().unwrap().unwrap_err();
351 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingPattern));
352 assert_eq!(*e.location.code.value.borrow(), ")");
353 assert_eq!(e.location.code.start_line_number.get(), 1);
354 assert_eq!(*e.location.code.source, Source::Unknown);
355 assert_eq!(e.location.range, 0..1);
356 }
357
358 #[test]
359 fn parser_case_item_esac_after_paren() {
360 let mut lexer = Lexer::from_memory("(esac)", Source::Unknown);
361 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
362
363 let (item, continued) = parser.case_item().now_or_never().unwrap().unwrap().unwrap();
364 assert_eq!(item.patterns.len(), 1);
365 assert_eq!(item.patterns[0].to_string(), "esac");
366 assert_eq!(item.body.0, []);
367 assert_eq!(item.continuation, CaseContinuation::Break);
368 assert!(!continued);
369 }
370
371 #[test]
372 fn parser_case_item_first_pattern_not_word_after_open_paren() {
373 let mut lexer = Lexer::from_memory("(&", Source::Unknown);
374 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
375
376 let e = parser.case_item().now_or_never().unwrap().unwrap_err();
377 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidPattern));
378 assert_eq!(*e.location.code.value.borrow(), "(&");
379 assert_eq!(e.location.code.start_line_number.get(), 1);
380 assert_eq!(*e.location.code.source, Source::Unknown);
381 assert_eq!(e.location.range, 1..2);
382 }
383
384 #[test]
385 fn parser_case_item_missing_pattern_after_bar() {
386 let mut lexer = Lexer::from_memory("(foo| |", Source::Unknown);
387 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
388
389 let e = parser.case_item().now_or_never().unwrap().unwrap_err();
390 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingPattern));
391 assert_eq!(*e.location.code.value.borrow(), "(foo| |");
392 assert_eq!(e.location.code.start_line_number.get(), 1);
393 assert_eq!(*e.location.code.source, Source::Unknown);
394 assert_eq!(e.location.range, 6..7);
395 }
396
397 #[test]
398 fn parser_case_item_missing_close_paren() {
399 let mut lexer = Lexer::from_memory("(foo bar", Source::Unknown);
400 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
401
402 let e = parser.case_item().now_or_never().unwrap().unwrap_err();
403 assert_eq!(
404 e.cause,
405 ErrorCause::Syntax(SyntaxError::UnclosedPatternList)
406 );
407 assert_eq!(*e.location.code.value.borrow(), "(foo bar");
408 assert_eq!(e.location.code.start_line_number.get(), 1);
409 assert_eq!(*e.location.code.source, Source::Unknown);
410 assert_eq!(e.location.range, 5..8);
411 }
412
413 #[test]
414 fn parser_case_command_minimum() {
415 let mut lexer = Lexer::from_memory("case foo in esac", Source::Unknown);
416 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
417
418 let result = parser.compound_command().now_or_never().unwrap();
419 let compound_command = result.unwrap().unwrap();
420 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
421 assert_eq!(subject.to_string(), "foo");
422 assert_eq!(items, []);
423 });
424
425 let next = parser.peek_token().now_or_never().unwrap().unwrap();
426 assert_eq!(next.id, EndOfInput);
427 }
428
429 #[test]
430 fn parser_case_command_newline_before_in() {
431 let mut lexer = Lexer::from_memory("CASE_X IN_ESAC", Source::Unknown);
433 #[allow(clippy::mutable_key_type)]
434 let mut aliases = AliasSet::new();
435 let origin = Location::dummy("");
436 aliases.insert(HashEntry::new(
437 "CASE_X".to_string(),
438 " case x \n\n ".to_string(),
439 false,
440 origin.clone(),
441 ));
442 aliases.insert(HashEntry::new(
443 "IN_ESAC".to_string(),
444 "\nin esac".to_string(),
445 false,
446 origin,
447 ));
448 let mut parser = Parser::new(&mut lexer, &aliases);
449
450 let result = parser.take_token_manual(true).now_or_never().unwrap();
451 assert_matches!(result, Ok(Rec::AliasSubstituted));
452
453 let result = parser.compound_command().now_or_never().unwrap();
454 let compound_command = result.unwrap().unwrap();
455 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
456 assert_eq!(subject.to_string(), "x");
457 assert_eq!(items, []);
458 });
459
460 let next = parser.peek_token().now_or_never().unwrap().unwrap();
461 assert_eq!(next.id, EndOfInput);
462 }
463
464 #[test]
465 fn parser_case_command_alias_on_subject() {
466 let mut lexer = Lexer::from_memory("CASE in a|b) esac", Source::Unknown);
468 #[allow(clippy::mutable_key_type)]
469 let mut aliases = AliasSet::new();
470 let origin = Location::dummy("");
471 aliases.insert(HashEntry::new(
472 "CASE".to_string(),
473 " case ".to_string(),
474 false,
475 origin.clone(),
476 ));
477 aliases.insert(HashEntry::new(
478 "in".to_string(),
479 " in in ".to_string(),
480 false,
481 origin,
482 ));
483 let mut parser = Parser::new(&mut lexer, &aliases);
484
485 let result = parser.take_token_manual(true).now_or_never().unwrap();
486 assert_matches!(result, Ok(Rec::AliasSubstituted));
487
488 let result = parser.compound_command().now_or_never().unwrap();
489 let compound_command = result.unwrap().unwrap();
490 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
491 assert_eq!(subject.to_string(), "in");
492 assert_eq!(items.len(), 1);
493 assert_eq!(items[0].to_string(), "(a | b) ;;");
494 });
495
496 let next = parser.peek_token().now_or_never().unwrap().unwrap();
497 assert_eq!(next.id, EndOfInput);
498 }
499
500 #[test]
501 fn parser_case_command_alias_on_in() {
502 let mut lexer = Lexer::from_memory("CASE_X in esac", Source::Unknown);
504 #[allow(clippy::mutable_key_type)]
505 let mut aliases = AliasSet::new();
506 let origin = Location::dummy("");
507 aliases.insert(HashEntry::new(
508 "CASE_X".to_string(),
509 "case x ".to_string(),
510 false,
511 origin.clone(),
512 ));
513 aliases.insert(HashEntry::new(
514 "in".to_string(),
515 "in a)".to_string(),
516 false,
517 origin,
518 ));
519 let mut parser = Parser::new(&mut lexer, &aliases);
520
521 let result = parser.take_token_manual(true).now_or_never().unwrap();
522 assert_matches!(result, Ok(Rec::AliasSubstituted));
523
524 let result = parser.compound_command().now_or_never().unwrap();
525 let compound_command = result.unwrap().unwrap();
526 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
527 assert_eq!(subject.to_string(), "x");
528 assert_eq!(items, []);
529 });
530
531 let next = parser.peek_token().now_or_never().unwrap().unwrap();
532 assert_eq!(next.id, EndOfInput);
533 }
534
535 #[test]
536 fn parser_case_command_one_item() {
537 let mut lexer = Lexer::from_memory("case foo in bar) esac", Source::Unknown);
538 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
539
540 let result = parser.compound_command().now_or_never().unwrap();
541 let compound_command = result.unwrap().unwrap();
542 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
543 assert_eq!(subject.to_string(), "foo");
544 assert_eq!(items.len(), 1);
545 assert_eq!(items[0].to_string(), "(bar) ;;");
546 });
547
548 let next = parser.peek_token().now_or_never().unwrap().unwrap();
549 assert_eq!(next.id, EndOfInput);
550 }
551
552 #[test]
553 fn parser_case_command_many_items_without_final_double_semicolon() {
554 let mut lexer = Lexer::from_memory(
555 "case x in\n\na) ;; (b|c):&:; ;;\n d)echo\nesac",
556 Source::Unknown,
557 );
558 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
559
560 let result = parser.compound_command().now_or_never().unwrap();
561 let compound_command = result.unwrap().unwrap();
562 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
563 assert_eq!(subject.to_string(), "x");
564 assert_eq!(items.len(), 3);
565 assert_eq!(items[0].to_string(), "(a) ;;");
566 assert_eq!(items[1].to_string(), "(b | c) :& :;;");
567 assert_eq!(items[2].to_string(), "(d) echo;;");
568 });
569
570 let next = parser.peek_token().now_or_never().unwrap().unwrap();
571 assert_eq!(next.id, EndOfInput);
572 }
573
574 #[test]
575 fn parser_case_command_many_items_with_final_double_semicolon() {
576 let mut lexer = Lexer::from_memory("case x in(1);; 2)echo\n\n;;\n\nesac", Source::Unknown);
577 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
578
579 let result = parser.compound_command().now_or_never().unwrap();
580 let compound_command = result.unwrap().unwrap();
581 assert_matches!(compound_command, CompoundCommand::Case { subject, items } => {
582 assert_eq!(subject.to_string(), "x");
583 assert_eq!(items.len(), 2);
584 assert_eq!(items[0].to_string(), "(1) ;;");
585 assert_eq!(items[1].to_string(), "(2) echo;;");
586 });
587
588 let next = parser.peek_token().now_or_never().unwrap().unwrap();
589 assert_eq!(next.id, EndOfInput);
590 }
591
592 #[test]
593 fn parser_case_command_missing_subject() {
594 let mut lexer = Lexer::from_memory(" case ", Source::Unknown);
595 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
596
597 let result = parser.compound_command().now_or_never().unwrap();
598 let e = result.unwrap_err();
599 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::MissingCaseSubject));
600 assert_eq!(*e.location.code.value.borrow(), " case ");
601 assert_eq!(e.location.code.start_line_number.get(), 1);
602 assert_eq!(*e.location.code.source, Source::Unknown);
603 assert_eq!(e.location.range, 7..7);
604 }
605
606 #[test]
607 fn parser_case_command_invalid_subject() {
608 let mut lexer = Lexer::from_memory(" case ; ", Source::Unknown);
609 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
610
611 let result = parser.compound_command().now_or_never().unwrap();
612 let e = result.unwrap_err();
613 assert_eq!(e.cause, ErrorCause::Syntax(SyntaxError::InvalidCaseSubject));
614 assert_eq!(*e.location.code.value.borrow(), " case ; ");
615 assert_eq!(e.location.code.start_line_number.get(), 1);
616 assert_eq!(*e.location.code.source, Source::Unknown);
617 assert_eq!(e.location.range, 6..7);
618 }
619
620 #[test]
621 fn parser_case_command_missing_in() {
622 let mut lexer = Lexer::from_memory(" case x esac", Source::Unknown);
623 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
624
625 let result = parser.compound_command().now_or_never().unwrap();
626 let e = result.unwrap_err();
627 assert_matches!(e.cause,
628 ErrorCause::Syntax(SyntaxError::MissingIn { opening_location }) => {
629 assert_eq!(*opening_location.code.value.borrow(), " case x esac");
630 assert_eq!(opening_location.code.start_line_number.get(), 1);
631 assert_eq!(*opening_location.code.source, Source::Unknown);
632 assert_eq!(opening_location.range, 1..5);
633 });
634 assert_eq!(*e.location.code.value.borrow(), " case x esac");
635 assert_eq!(e.location.code.start_line_number.get(), 1);
636 assert_eq!(*e.location.code.source, Source::Unknown);
637 assert_eq!(e.location.range, 8..12);
638 }
639
640 #[test]
641 fn parser_case_command_missing_esac() {
642 let mut lexer = Lexer::from_memory("case x in a) }", Source::Unknown);
643 let mut parser = Parser::new(&mut lexer, &EmptyGlossary);
644
645 let result = parser.compound_command().now_or_never().unwrap();
646 let e = result.unwrap_err();
647 assert_matches!(e.cause,
648 ErrorCause::Syntax(SyntaxError::UnclosedCase { opening_location }) => {
649 assert_eq!(*opening_location.code.value.borrow(), "case x in a) }");
650 assert_eq!(opening_location.code.start_line_number.get(), 1);
651 assert_eq!(*opening_location.code.source, Source::Unknown);
652 assert_eq!(opening_location.range, 0..4);
653 });
654 assert_eq!(*e.location.code.value.borrow(), "case x in a) }");
655 assert_eq!(e.location.code.start_line_number.get(), 1);
656 assert_eq!(*e.location.code.source, Source::Unknown);
657 assert_eq!(e.location.range, 13..14);
658 }
659}