fn parse_optional_arguments()

in compiler/crates/graphql-syntax/src/parser.rs [1256:1426]


    fn parse_optional_arguments(&mut self) -> ParseResult<Option<List<Argument>>> {
        if self.peek_token_kind() != TokenKind::OpenParen {
            return Ok(None);
        }
        let start = self.parse_token();
        let mut items: Vec<Argument> = vec![];
        loop {
            let peek_kind = self.peek_token_kind();
            if peek_kind == TokenKind::CloseParen {
                break;
            } else if peek_kind == TokenKind::OpenBrace || peek_kind == TokenKind::CloseBrace {
                self.record_error(Diagnostic::error(
                    SyntaxError::Expected(TokenKind::CloseParen),
                    Location::new(self.source_location, self.peek().span),
                ));
                let span = Span::new(start.span.start, self.end_index);
                if items.is_empty() {
                    self.record_error(Diagnostic::error(
                        SyntaxError::ExpectedArgument,
                        Location::new(self.source_location, span),
                    ))
                }
                return Ok(Some(List {
                    span,
                    start,
                    items,
                    end: self.empty_token(),
                }));
            }
            // MaybeArgument Name ?: ?Value[?Const]
            let start = self.index();
            let name = if peek_kind == TokenKind::Identifier {
                self.parse_identifier()?
            } else {
                (|| {
                    if peek_kind == TokenKind::Colon && !items.is_empty() {
                        /*
                            (
                                arg:
                                arg2: $var
                                #   ^ We are at the second colon, and need to recover the identifier
                            )
                        */
                        let last_arg = items.last_mut().unwrap();
                        if let Value::Constant(ConstantValue::Enum(node)) = &last_arg.value {
                            self.record_error(Diagnostic::error(
                                SyntaxError::ExpectedValue,
                                Location::new(
                                    self.source_location,
                                    Span::new(last_arg.colon.span.end, last_arg.colon.span.end),
                                ),
                            ));
                            let name = Identifier {
                                span: node.token.span,
                                token: node.token.clone(),
                                value: node.value,
                            };
                            last_arg.span.end = last_arg.colon.span.end;
                            last_arg.value = Value::Constant(ConstantValue::Null(Token {
                                span: Span::new(last_arg.span.end, last_arg.span.end),
                                kind: TokenKind::Empty,
                            }));
                            return name;
                        }
                    }
                    /*
                        ($var)
                        (:$var)
                    */
                    self.record_error(Diagnostic::error(
                        SyntaxError::Expected(TokenKind::Identifier),
                        Location::new(
                            self.source_location,
                            Span::new(start, self.peek().span.start),
                        ),
                    ));
                    let empty_token = self.empty_token();
                    Identifier {
                        span: empty_token.span,
                        token: empty_token,
                        value: "".intern(),
                    }
                })()
            };

            let colon = self.parse_optional_kind(TokenKind::Colon);
            if let Some(colon) = colon {
                if self.peek_kind(TokenKind::CloseParen) {
                    self.record_error(Diagnostic::error(
                        SyntaxError::ExpectedValue,
                        Location::new(
                            self.source_location,
                            Span::new(name.span.end, self.end_index),
                        ),
                    ));
                    let span = Span::new(start, self.end_index);
                    let value = Value::Constant(ConstantValue::Null(self.empty_token()));
                    items.push(Argument {
                        span,
                        name,
                        colon,
                        value,
                    });
                } else {
                    let value = self.parse_value()?;
                    let span = Span::new(start, self.end_index);
                    items.push(Argument {
                        span,
                        name,
                        colon,
                        value,
                    });
                }
            } else {
                self.record_error(Diagnostic::error(
                    SyntaxError::Expected(TokenKind::Colon),
                    Location::new(self.source_location, Span::new(name.span.end, self.index())),
                ));
                // Continue parsing value if the next token looks like a value (except for Enum)
                // break early if the next token is not a valid token for the next argument
                let mut should_break = false;
                let value = match self.peek_token_kind() {
                    TokenKind::Dollar
                    | TokenKind::OpenBrace
                    | TokenKind::OpenBracket
                    | TokenKind::StringLiteral
                    | TokenKind::IntegerLiteral
                    | TokenKind::FloatLiteral => self.parse_value()?,
                    TokenKind::Identifier => {
                        let source = self.source(self.peek());
                        if source == "true" || source == "false" || source == "null" {
                            self.parse_value()?
                        } else {
                            Value::Constant(ConstantValue::Null(self.empty_token()))
                        }
                    }
                    TokenKind::CloseParen | TokenKind::Colon => {
                        Value::Constant(ConstantValue::Null(self.empty_token()))
                    }
                    _ => {
                        should_break = true;
                        Value::Constant(ConstantValue::Null(self.empty_token()))
                    }
                };
                let span = Span::new(start, self.end_index);
                items.push(Argument {
                    span,
                    name,
                    colon: self.empty_token(),
                    value,
                });
                if should_break {
                    break;
                }
            }
        }
        let end = self.parse_token();
        let span = Span::new(start.span.start, end.span.end);
        if items.is_empty() {
            self.record_error(Diagnostic::error(
                SyntaxError::ExpectedArgument,
                Location::new(self.source_location, span),
            ))
        }
        Ok(Some(List {
            span,
            start,
            items,
            end,
        }))
    }