Skip to main content

solar_parse/parser/
stmt.rs

1use super::item::VarFlags;
2use crate::{PResult, Parser, parser::SeqSep};
3use smallvec::SmallVec;
4use solar_ast::{token::*, *};
5use solar_data_structures::CollectAndApply;
6use solar_interface::{Ident, Span, SpannedOption, Symbol, kw, sym};
7
8impl<'sess, 'ast, 'cb> Parser<'sess, 'ast, 'cb> {
9    /// Parses a statement.
10    #[instrument(level = "trace", skip_all)]
11    pub fn parse_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
12        self.with_recursion_limit("statement", |this| {
13            let docs = this.parse_doc_comments();
14            this.parse_spanned(Self::parse_stmt_kind).map(|(span, kind)| Stmt { docs, kind, span })
15        })
16    }
17
18    /// Parses a statement into a new allocation.
19    pub fn parse_stmt_boxed(&mut self) -> PResult<'sess, Box<'ast, Stmt<'ast>>> {
20        self.parse_stmt().map(|stmt| self.alloc(stmt))
21    }
22
23    /// Parses a statement kind.
24    fn parse_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
25        let mut semi = true;
26        let kind = if self.eat_keyword(kw::If) {
27            semi = false;
28            self.parse_stmt_if()
29        } else if self.eat_keyword(kw::While) {
30            semi = false;
31            self.parse_stmt_while()
32        } else if self.eat_keyword(kw::Do) {
33            self.parse_stmt_do_while()
34        } else if self.eat_keyword(kw::For) {
35            semi = false;
36            self.parse_stmt_for()
37        } else if self.eat_keyword(kw::Unchecked) {
38            semi = false;
39            self.parse_block().map(StmtKind::UncheckedBlock)
40        } else if self.check(TokenKind::OpenDelim(Delimiter::Brace)) {
41            semi = false;
42            self.parse_block().map(StmtKind::Block)
43        } else if self.eat_keyword(kw::Continue) {
44            Ok(StmtKind::Continue)
45        } else if self.eat_keyword(kw::Break) {
46            Ok(StmtKind::Break)
47        } else if self.eat_keyword(kw::Return) {
48            let expr = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
49            Ok(StmtKind::Return(expr))
50        } else if self.eat_keyword(kw::Throw) {
51            let msg = "`throw` statements have been removed; use `revert`, `require`, or `assert` instead";
52            Err(self.dcx().err(msg).span(self.prev_token.span))
53        } else if self.eat_keyword(kw::Try) {
54            semi = false;
55            self.parse_stmt_try().map(|stmt| StmtKind::Try(self.alloc(stmt)))
56        } else if self.eat_keyword(kw::Assembly) {
57            semi = false;
58            self.parse_stmt_assembly().map(StmtKind::Assembly)
59        } else if self.eat_keyword(kw::Emit) {
60            self.parse_path_call().map(|(path, params)| StmtKind::Emit(path, params))
61        } else if self.check_keyword(kw::Revert) && self.look_ahead(1).is_ident() {
62            self.bump(); // `revert`
63            self.parse_path_call().map(|(path, params)| StmtKind::Revert(path, params))
64        } else if self.check_keyword(sym::underscore) && self.look_ahead(1).kind == TokenKind::Semi
65        {
66            self.bump(); // `_`
67            Ok(StmtKind::Placeholder)
68        } else {
69            self.parse_simple_stmt_kind()
70        };
71        if semi && kind.is_ok() {
72            self.expect_semi()?;
73        }
74        kind
75    }
76
77    /// Parses a block of statements.
78    pub(super) fn parse_block(&mut self) -> PResult<'sess, Block<'ast>> {
79        let lo = self.token.span;
80        self.parse_delim_seq(Delimiter::Brace, SeqSep::none(), true, Self::parse_stmt)
81            .map(|stmts| Block { span: lo.to(self.prev_token.span), stmts })
82    }
83
84    /// Parses an if statement.
85    fn parse_stmt_if(&mut self) -> PResult<'sess, StmtKind<'ast>> {
86        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
87        let expr = self.parse_expr()?;
88        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
89        let true_stmt = self.parse_stmt()?;
90        let else_stmt =
91            if self.eat_keyword(kw::Else) { Some(self.parse_stmt_boxed()?) } else { None };
92        Ok(StmtKind::If(expr, self.alloc(true_stmt), else_stmt))
93    }
94
95    /// Parses a while statement.
96    fn parse_stmt_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
97        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
98        let expr = self.parse_expr()?;
99        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
100        let stmt = self.parse_stmt()?;
101        Ok(StmtKind::While(expr, self.alloc(stmt)))
102    }
103
104    /// Parses a do-while statement.
105    fn parse_stmt_do_while(&mut self) -> PResult<'sess, StmtKind<'ast>> {
106        let stmt = self.parse_stmt()?;
107        let stmt = self.alloc(stmt);
108        self.expect_keyword(kw::While)?;
109        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
110        let expr = self.parse_expr()?;
111        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
112        Ok(StmtKind::DoWhile(stmt, expr))
113    }
114
115    /// Parses a for statement.
116    fn parse_stmt_for(&mut self) -> PResult<'sess, StmtKind<'ast>> {
117        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
118
119        let init = if self.check(TokenKind::Semi) { None } else { Some(self.parse_simple_stmt()?) };
120        self.expect(TokenKind::Semi)?;
121
122        let cond = if self.check(TokenKind::Semi) { None } else { Some(self.parse_expr()?) };
123        self.expect_semi()?;
124
125        let next = if self.check_noexpect(TokenKind::CloseDelim(Delimiter::Parenthesis)) {
126            None
127        } else {
128            Some(self.parse_expr()?)
129        };
130        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
131        let body = self.parse_stmt_boxed()?;
132        Ok(StmtKind::For { init: init.map(|init| self.alloc(init)), cond, next, body })
133    }
134
135    /// Parses a try statement.
136    fn parse_stmt_try(&mut self) -> PResult<'sess, StmtTry<'ast>> {
137        let expr = self.parse_expr()?;
138        let mut clauses = SmallVec::<[_; 4]>::new();
139
140        let mut lo = self.token.span;
141        let returns = if self.eat_keyword(kw::Returns) {
142            self.parse_parameter_list(false, VarFlags::FUNCTION)?
143        } else {
144            Default::default()
145        };
146        let block = self.parse_block()?;
147        let span = lo.to(self.prev_token.span);
148        clauses.push(TryCatchClause { name: None, args: returns, block, span });
149
150        lo = self.token.span;
151        self.expect_keyword(kw::Catch)?;
152        loop {
153            let name = self.parse_ident_opt()?;
154            let args = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
155                self.parse_parameter_list(false, VarFlags::FUNCTION)?
156            } else {
157                Default::default()
158            };
159            let block = self.parse_block()?;
160            let span = lo.to(self.prev_token.span);
161            clauses.push(TryCatchClause { name, args, block, span });
162            lo = self.token.span;
163            if !self.eat_keyword(kw::Catch) {
164                break;
165            }
166        }
167
168        let clauses = self.alloc_smallvec(clauses);
169        Ok(StmtTry { expr, clauses })
170    }
171
172    /// Parses an assembly block.
173    fn parse_stmt_assembly(&mut self) -> PResult<'sess, StmtAssembly<'ast>> {
174        let dialect = self.parse_str_lit_opt();
175        let flags = if self.check(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
176            self.parse_paren_comma_seq(false, Self::parse_str_lit)?
177        } else {
178            Default::default()
179        };
180        let block = self.parse_yul_block()?;
181        Ok(StmtAssembly { dialect, flags, block })
182    }
183
184    /// Parses a simple statement. These are just variable declarations and expressions.
185    fn parse_simple_stmt(&mut self) -> PResult<'sess, Stmt<'ast>> {
186        let docs = self.parse_doc_comments();
187        self.parse_spanned(Self::parse_simple_stmt_kind).map(|(span, kind)| Stmt {
188            docs,
189            kind,
190            span,
191        })
192    }
193
194    /// Parses a simple statement kind. These are just variable declarations and expressions.
195    ///
196    /// Also used in the for loop initializer. Does not parse the trailing semicolon.
197    fn parse_simple_stmt_kind(&mut self) -> PResult<'sess, StmtKind<'ast>> {
198        let lo = self.token.span;
199        if self.eat(TokenKind::OpenDelim(Delimiter::Parenthesis)) {
200            let mut none_elements = SmallVec::<[_; 8]>::new();
201            while self.eat(TokenKind::Comma) {
202                none_elements.push(self.prev_token.span.shrink_to_hi());
203            }
204
205            let (statement_type, iap) = self.try_parse_iap()?;
206            match statement_type {
207                LookAheadInfo::VariableDeclaration => {
208                    let mut variables = none_elements
209                        .into_iter()
210                        .map(SpannedOption::None)
211                        .collect::<SmallVec<[_; 8]>>();
212                    let ty = iap.into_ty(self);
213                    variables.push(SpannedOption::Some(
214                        self.parse_variable_definition_with(VarFlags::FUNCTION, ty)?,
215                    ));
216                    self.parse_optional_items_seq_required(
217                        Delimiter::Parenthesis,
218                        &mut variables,
219                        |this| this.parse_variable_definition(VarFlags::FUNCTION),
220                    )?;
221                    self.expect(TokenKind::Eq)?;
222                    let expr = self.parse_expr()?;
223                    Ok(StmtKind::DeclMulti(self.alloc_smallvec(variables), expr))
224                }
225                LookAheadInfo::Expression => {
226                    let mut components = none_elements
227                        .into_iter()
228                        .map(SpannedOption::None)
229                        .collect::<SmallVec<[_; 8]>>();
230                    let expr = iap.into_expr(self);
231                    components.push(SpannedOption::Some(self.parse_expr_with(expr)?));
232                    self.parse_optional_items_seq_required(
233                        Delimiter::Parenthesis,
234                        &mut components,
235                        Self::parse_expr,
236                    )?;
237                    let partially_parsed = Expr {
238                        span: lo.to(self.prev_token.span),
239                        kind: ExprKind::Tuple(self.alloc_smallvec(components)),
240                    };
241                    self.parse_expr_with(Some(self.alloc(partially_parsed))).map(StmtKind::Expr)
242                }
243                LookAheadInfo::IndexAccessStructure => unreachable!(),
244            }
245        } else {
246            let (statement_type, iap) = self.try_parse_iap()?;
247            match statement_type {
248                LookAheadInfo::VariableDeclaration => {
249                    let ty = iap.into_ty(self);
250                    self.parse_variable_definition_with(VarFlags::VAR, ty)
251                        .map(|var| StmtKind::DeclSingle(self.alloc(var)))
252                }
253                LookAheadInfo::Expression => {
254                    let expr = iap.into_expr(self);
255                    self.parse_expr_with(expr).map(StmtKind::Expr)
256                }
257                LookAheadInfo::IndexAccessStructure => unreachable!(),
258            }
259        }
260    }
261
262    /// Parses a `delim`-delimited, comma-separated list of maybe-optional items.
263    /// E.g. `(a, b) => [Some, Some]`, `(, a,, b,) => [None, Some, None, Some, None]`.
264    ///
265    /// All elements are wrapped in a `SpannedOption<T>`, so that even for uninformed elements,
266    /// AST consumers can be aware of the location of the separators.
267    pub(super) fn parse_optional_items_seq<T>(
268        &mut self,
269        delim: Delimiter,
270        mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
271    ) -> PResult<'sess, SmallVec<[SpannedOption<T>; 8]>> {
272        self.expect(TokenKind::OpenDelim(delim))?;
273
274        let mut out = SmallVec::<[_; 8]>::new();
275
276        // Handle leading commas, e.g., `(, a, b)`.
277        while self.eat(TokenKind::Comma) {
278            out.push(SpannedOption::None(self.prev_token.span.shrink_to_lo()));
279        }
280
281        // Handle the first potential item. If the list is not closing,
282        // we assume there's at least one item.
283        if !self.check(TokenKind::CloseDelim(delim)) {
284            out.push(SpannedOption::Some(f(self)?));
285        }
286
287        // Call the helper to parse the rest of the sequence.
288        self.parse_optional_items_seq_required(delim, &mut out, f)?;
289        Ok(out)
290    }
291
292    fn parse_optional_items_seq_required<T>(
293        &mut self,
294        delim: Delimiter,
295        out: &mut SmallVec<[SpannedOption<T>; 8]>,
296        mut f: impl FnMut(&mut Self) -> PResult<'sess, T>,
297    ) -> PResult<'sess, ()> {
298        let (comma, close) = (TokenKind::Comma, TokenKind::CloseDelim(delim));
299
300        // Handle early close delimiter.
301        if self.eat(close) {
302            return Ok(());
303        }
304
305        // Expect comma separator.
306        self.expect(comma)?;
307
308        // Handle subsequent elements until finding the close token.
309        loop {
310            let item = if self.check(comma) || self.check(close) { None } else { Some(f(self)?) };
311            if self.eat(comma) {
312                let element = match item {
313                    Some(val) => SpannedOption::Some(val),
314                    None => SpannedOption::None(self.prev_token.span.shrink_to_lo()),
315                };
316                out.push(element);
317            } else if self.eat(close) {
318                let element = match item {
319                    Some(val) => SpannedOption::Some(val),
320                    None => SpannedOption::None(self.prev_token.span.shrink_to_lo()),
321                };
322                out.push(element);
323                return Ok(());
324            } else {
325                return self.unexpected();
326            }
327        }
328    }
329
330    /// Parses a path and a list of call arguments.
331    fn parse_path_call(&mut self) -> PResult<'sess, (AstPath<'ast>, CallArgs<'ast>)> {
332        let path = self.parse_path()?;
333        let params = self.parse_call_args()?;
334        Ok((path, params))
335    }
336
337    /// Never returns `LookAheadInfo::IndexAccessStructure`.
338    fn try_parse_iap(&mut self) -> PResult<'sess, (LookAheadInfo, IndexAccessedPath<'ast>)> {
339        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L1961
340        if let ty @ (LookAheadInfo::VariableDeclaration | LookAheadInfo::Expression) =
341            self.peek_statement_type()
342        {
343            return Ok((ty, IndexAccessedPath::default()));
344        }
345
346        let iap = self.parse_iap()?;
347        let ty = if self.token.is_non_reserved_ident(self.in_yul)
348            || self.token.is_location_specifier()
349        {
350            // `a.b memory`, `a[b] c`
351            LookAheadInfo::VariableDeclaration
352        } else {
353            LookAheadInfo::Expression
354        };
355        Ok((ty, iap))
356    }
357
358    fn peek_statement_type(&mut self) -> LookAheadInfo {
359        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2528
360        if self.token.is_keyword_any(&[kw::Mapping, kw::Function]) {
361            return LookAheadInfo::VariableDeclaration;
362        }
363
364        if self.check_nr_ident() || self.check_elementary_type() {
365            let next = self.look_ahead(1);
366            if self.token.is_elementary_type() && next.is_ident_where(|id| id.name == kw::Payable) {
367                return LookAheadInfo::VariableDeclaration;
368            }
369            if next.is_non_reserved_ident(self.in_yul)
370                || next.is_location_specifier()
371                // These aren't valid but we include them for a better error message.
372                || next.is_mutability_specifier()
373                || next.is_visibility_specifier()
374            {
375                return LookAheadInfo::VariableDeclaration;
376            }
377            if matches!(next.kind, TokenKind::OpenDelim(Delimiter::Bracket) | TokenKind::Dot) {
378                return LookAheadInfo::IndexAccessStructure;
379            }
380        }
381        LookAheadInfo::Expression
382    }
383
384    fn parse_iap(&mut self) -> PResult<'sess, IndexAccessedPath<'ast>> {
385        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2559
386        let mut path = SmallVec::<[_; 4]>::new();
387        if self.check_nr_ident() {
388            path.push(IapKind::Member(self.parse_ident()?));
389            while self.eat(TokenKind::Dot) {
390                let id = match self.ident_or_err(true) {
391                    Ok(id) => id,
392                    Err(err) => {
393                        err.emit();
394                        path.push(IapKind::Member(Ident::new(
395                            Symbol::DUMMY,
396                            self.prev_token.span.shrink_to_hi(),
397                        )));
398                        break;
399                    }
400                };
401                if id.name != kw::Address && id.is_reserved(self.in_yul) {
402                    self.expected_ident_found_err().emit();
403                }
404                self.bump(); // `id`
405                path.push(IapKind::Member(id));
406            }
407        } else if self.check_elementary_type() {
408            let (span, kind) = self.parse_spanned(Self::parse_elementary_type)?;
409            path.push(IapKind::MemberTy(span, kind));
410        } else {
411            return self.unexpected();
412        }
413        let n_idents = path
414            .iter()
415            .take_while(|kind| matches!(kind, IapKind::Member(_) | IapKind::MemberTy(..)))
416            .count();
417
418        while self.check(TokenKind::OpenDelim(Delimiter::Bracket)) {
419            let (span, kind) = self.parse_spanned(Self::parse_expr_index_kind)?;
420            path.push(IapKind::Index(span, kind));
421        }
422
423        Ok(IndexAccessedPath { path, n_idents })
424    }
425}
426
427#[derive(Debug)]
428enum LookAheadInfo {
429    /// `a.b`, `a[b]`
430    IndexAccessStructure,
431    VariableDeclaration,
432    Expression,
433}
434
435#[derive(Debug)]
436enum IapKind<'ast> {
437    /// `[...]`
438    Index(Span, IndexKind<'ast>),
439    /// `<ident>` or `.<ident>`
440    Member(Ident),
441    /// `<ty>`
442    MemberTy(Span, ElementaryType),
443}
444
445#[derive(Debug, Default)]
446struct IndexAccessedPath<'ast> {
447    path: SmallVec<[IapKind<'ast>; 4]>,
448    /// The number of elements in `path` that are `IapKind::Member[Ty]` at the start.
449    n_idents: usize,
450}
451
452impl<'ast> IndexAccessedPath<'ast> {
453    fn into_ty(self, parser: &mut Parser<'_, 'ast, '_>) -> Option<Type<'ast>> {
454        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2617
455        let mut path = self.path.into_iter();
456        let first = path.next()?;
457
458        let mut ty = if let IapKind::MemberTy(span, kind) = first {
459            debug_assert_eq!(self.n_idents, 1);
460            Type { span, kind: TypeKind::Elementary(kind) }
461        } else {
462            debug_assert!(self.n_idents >= 1);
463            let first = std::iter::once(&first);
464            let path = first
465                .chain(path.as_slice())
466                .map(|x| match x {
467                    IapKind::Member(id) => *id,
468                    kind => unreachable!("{kind:?}"),
469                })
470                .take(self.n_idents);
471            let path = CollectAndApply::collect_and_apply(path, |path| parser.alloc_path(path));
472            Type { span: path.span(), kind: TypeKind::Custom(path) }
473        };
474
475        for index in path.skip(self.n_idents - 1) {
476            let (span, kind) = match index {
477                IapKind::Index(span, kind) => (span, kind),
478                IapKind::Member(_) | IapKind::MemberTy(..) => panic!("parsed too much"),
479            };
480            let size = match kind {
481                IndexKind::Index(expr) => expr,
482                IndexKind::Range(l, r) => {
483                    let msg = "expected array length, got range expression";
484                    parser.dcx().emit_err(span, msg);
485                    l.or(r)
486                }
487            };
488            let span = ty.span.to(span);
489            ty =
490                Type { span, kind: TypeKind::Array(parser.alloc(TypeArray { element: ty, size })) };
491        }
492
493        Some(ty)
494    }
495
496    fn into_expr(self, parser: &mut Parser<'_, 'ast, '_>) -> Option<Box<'ast, Expr<'ast>>> {
497        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/libsolidity/parsing/Parser.cpp#L2658
498        let mut path = self.path.into_iter();
499
500        let mut expr = parser.alloc(match path.next()? {
501            IapKind::Member(ident) => Expr::from_ident(ident),
502            IapKind::MemberTy(span, kind) => {
503                Expr { span, kind: ExprKind::Type(Type { span, kind: TypeKind::Elementary(kind) }) }
504            }
505            IapKind::Index(..) => panic!("should not happen"),
506        });
507        for index in path {
508            expr = parser.alloc(match index {
509                IapKind::Member(ident) => {
510                    Expr { span: expr.span.to(ident.span), kind: ExprKind::Member(expr, ident) }
511                }
512                IapKind::MemberTy(..) => panic!("should not happen"),
513                IapKind::Index(span, kind) => {
514                    Expr { span: expr.span.to(span), kind: ExprKind::Index(expr, kind) }
515                }
516            });
517        }
518        Some(expr)
519    }
520}
521
522#[cfg(test)]
523mod tests {
524    use super::*;
525    use solar_interface::{Result, Session, source_map::FileName};
526
527    #[test]
528    fn optional_items_seq() {
529        #[allow(clippy::type_complexity)]
530        fn check(tests: &[(&str, &[Option<&str>])]) {
531            let sess = Session::builder().with_test_emitter().single_threaded().build();
532            sess.enter_sequential(|| -> Result {
533                for &(s, results) in tests.iter() {
534                    let name = s.to_string();
535                    let arena = Arena::new();
536                    let mut parser =
537                        Parser::from_source_code(&sess, &arena, FileName::Custom(name), s)?;
538
539                    let list = parser
540                        .parse_optional_items_seq(Delimiter::Parenthesis, Parser::parse_ident)
541                        .map_err(|e| e.emit())
542                        .unwrap_or_else(|_| panic!("src: {s:?}"));
543                    sess.dcx.has_errors().unwrap();
544
545                    let formatted: Vec<_> = list
546                        .iter()
547                        .map(|item| match item {
548                            SpannedOption::Some(ident) => {
549                                let data = Some(ident.as_str());
550                                let snip = sess.source_map().span_to_snippet(ident.span).unwrap();
551                                (data, snip)
552                            }
553                            SpannedOption::None(span) => {
554                                let data = None;
555                                let snip = sess.source_map().span_to_snippet(*span).unwrap();
556                                (data, snip)
557                            }
558                        })
559                        .collect();
560
561                    // Format the expected results for comparison
562                    let expected: Vec<_> = results
563                        .iter()
564                        .map(|&data| (data, data.unwrap_or("").to_string()))
565                        .collect();
566
567                    assert_eq!(formatted, expected, "{s:?}");
568                }
569                Ok(())
570            })
571            .unwrap();
572        }
573
574        check(&[
575            ("()", &[]),
576            ("(a)", &[Some("a")]),
577            // invalid syntax
578            // ("(,)", &[None, None]),
579            ("(a,)", &[Some("a"), None]),
580            ("(,b)", &[None, Some("b")]),
581            ("(a,b)", &[Some("a"), Some("b")]),
582            ("(a,b,)", &[Some("a"), Some("b"), None]),
583            // invalid syntax
584            // ("(,,)", &[None, None, None]),
585            ("(a,,)", &[Some("a"), None, None]),
586            ("(a,b,)", &[Some("a"), Some("b"), None]),
587            ("(a,b,c)", &[Some("a"), Some("b"), Some("c")]),
588            ("(,b,c)", &[None, Some("b"), Some("c")]),
589            ("(,,c)", &[None, None, Some("c")]),
590            ("(a,,c)", &[Some("a"), None, Some("c")]),
591        ]);
592    }
593}