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,
}))
}