Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle operation parsing error GraphQL #2434

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/biome_graphql_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,10 +507,10 @@ impl<'src> GraphqlLexer<'src> {
b'\\' => self.consume_escape_sequence_in_string(state),
b'\n' | b'\r' => (
LexStringState::Terminated,
Some(
ParseDiagnostic::new("Missing closing quote", start..self.text_position())
.with_detail(self.position..self.position + 1, "line breaks here"),
),
Some(ParseDiagnostic::new(
"Missing closing quote",
start..self.text_position(),
)),
),
_ => {
self.advance_char_unchecked();
Expand Down
18 changes: 16 additions & 2 deletions crates/biome_graphql_parser/src/parser/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use biome_parser::{
};

use super::{
definitions::is_at_selection_set_end,
directive::is_at_directive,
is_at_name,
parse_error::{expected_argument, expected_value},
value::parse_value,
Expand All @@ -22,7 +24,7 @@ impl ParseRecovery for ArgumentListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_ARGUMENT;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_name(p)
is_at_name(p) || is_at_argument_list_end(p)
}
}

Expand All @@ -40,7 +42,7 @@ impl ParseNodeList for ArgumentList {
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T![')'])
is_at_argument_list_end(p)
}

fn recover(
Expand Down Expand Up @@ -81,3 +83,15 @@ fn parse_argument(p: &mut GraphqlParser) -> ParsedSyntax {

Present(m.complete(p, GRAPHQL_ARGUMENT))
}

/// Arguments are only allowed in the following cases:
/// - Inside a selection set
/// - In a directive
#[inline]
pub(crate) fn is_at_argument_list_end(p: &GraphqlParser<'_>) -> bool {
p.at(T![')'])
// also handle the start of a selection set
|| is_at_selection_set_end(p)
// at the start of a new directive
|| is_at_directive(p)
}
10 changes: 8 additions & 2 deletions crates/biome_graphql_parser/src/parser/definitions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use biome_parser::{
};

use self::operation::{is_at_operation, parse_operation_definition};
pub(crate) use operation::is_at_selection_set_end;

struct DefinitionListParseRecovery;

Expand All @@ -17,8 +18,7 @@ impl ParseRecovery for DefinitionListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS_DEFINITION;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
// TODO: recover at any definition
is_at_operation(p)
is_at_definition(p)
}
}

Expand Down Expand Up @@ -56,3 +56,9 @@ fn parse_definition(p: &mut GraphqlParser) -> ParsedSyntax {
_ => Absent,
}
}

#[inline]
fn is_at_definition(p: &GraphqlParser<'_>) -> bool {
// TODO: recover at any definition
is_at_operation(p)
}
27 changes: 20 additions & 7 deletions crates/biome_graphql_parser/src/parser/definitions/operation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{
argument::parse_arguments,
directive::DirectiveList,
directive::{is_at_directive, DirectiveList},
is_at_name,
parse_error::{
expected_any_selection, expected_name, expected_named_type, expected_type, expected_value,
Expand All @@ -21,6 +21,8 @@ use biome_parser::{
prelude::ParsedSyntax::*, token_set, Parser, TokenSet,
};

use super::is_at_definition;

const OPERATION_TYPE: TokenSet<GraphqlSyntaxKind> =
token_set![T![query], T![mutation], T![subscription]];

Expand All @@ -38,7 +40,7 @@ impl ParseNodeList for SelectionList {
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['}'])
is_at_selection_set_end(p)
}

fn recover(
Expand All @@ -58,7 +60,7 @@ impl ParseRecovery for SelectionListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS_SELECTION;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_selection(p)
is_at_selection(p) || is_at_selection_set_end(p)
}
}

Expand All @@ -76,7 +78,7 @@ impl ParseNodeList for VariableDefinitionList {
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T![')'])
is_at_variable_definitions_end(p)
}

fn recover(
Expand All @@ -97,10 +99,10 @@ struct VariableDefinitionListParseRecovery;
impl ParseRecovery for VariableDefinitionListParseRecovery {
type Kind = GraphqlSyntaxKind;
type Parser<'source> = GraphqlParser<'source>;
const RECOVERED_KIND: Self::Kind = GRAPHQL_VARIABLE_DEFINITION;
const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_variable(p)
is_at_variable(p) || is_at_variable_definitions_end(p)
}
}

Expand Down Expand Up @@ -262,15 +264,26 @@ fn parse_default_value(p: &mut GraphqlParser) -> ParsedSyntax {
}

#[inline]
pub(crate) fn is_at_operation(p: &mut GraphqlParser<'_>) -> bool {
pub(crate) fn is_at_operation(p: &GraphqlParser<'_>) -> bool {
p.at_ts(OPERATION_TYPE) || is_at_selection_set(p)
}

#[inline]
fn is_at_variable_definitions_end(p: &GraphqlParser) -> bool {
p.at(T![')']) || is_at_directive(p) || is_at_selection_set(p)
}

#[inline]
fn is_at_selection_set(p: &GraphqlParser) -> bool {
p.at(T!['{'])
}

#[inline]
pub(crate) fn is_at_selection_set_end(p: &GraphqlParser) -> bool {
// stop at closing brace or at the start of a new definition
p.at(T!['}']) || is_at_definition(p)
}

#[inline]
fn is_at_selection(p: &GraphqlParser) -> bool {
is_at_field(p) || is_at_fragment(p)
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_graphql_parser/src/parser/directive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ pub(crate) fn parse_directive(p: &mut GraphqlParser) -> ParsedSyntax {
}

#[inline]
fn is_at_directive(p: &mut GraphqlParser<'_>) -> bool {
pub(crate) fn is_at_directive(p: &GraphqlParser<'_>) -> bool {
p.at(T![@])
}
25 changes: 21 additions & 4 deletions crates/biome_graphql_parser/src/parser/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use biome_parser::{
};

use super::{
argument::is_at_argument_list_end,
is_at_name,
parse_error::{expected_object_field, expected_value},
variable::{is_at_variable, parse_variable},
Expand All @@ -26,7 +27,7 @@ impl ParseRecovery for ListValueElementListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS_VALUE;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_value(p)
is_at_value(p) || is_at_list_end(p)
}
}

Expand All @@ -44,7 +45,7 @@ impl ParseNodeList for ListValueElementList {
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T![']'])
is_at_list_end(p)
}

fn recover(
Expand All @@ -64,7 +65,7 @@ impl ParseRecovery for ObjectValueMemberListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_OBJECT_FIELD;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
is_at_name(p)
is_at_name(p) || is_at_object_end(p)
}
}

Expand All @@ -82,7 +83,7 @@ impl ParseNodeList for ObjectValueMemberList {
}

fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['}'])
is_at_object_end(p)
}

fn recover(
Expand Down Expand Up @@ -267,6 +268,15 @@ fn is_at_list(p: &GraphqlParser) -> bool {
p.at(T!['['])
}

#[inline]
fn is_at_list_end(p: &mut GraphqlParser) -> bool {
p.at(T![']'])
// at next argument
|| p.lookahead() == T![:]
// value is only used in argument
|| is_at_argument_list_end(p)
}

#[inline]
fn is_at_object(p: &GraphqlParser) -> bool {
p.at(T!['{'])
Expand All @@ -276,3 +286,10 @@ fn is_at_object(p: &GraphqlParser) -> bool {
fn is_at_object_field(p: &GraphqlParser) -> bool {
is_at_name(p)
}

#[inline]
fn is_at_object_end(p: &GraphqlParser) -> bool {
p.at(T!['}'])
// value is only used in argument
|| is_at_argument_list_end(p)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
hero @
}

{
hero @ @
}

{
hero @deprecated(: "Deprecated")
}

{
hero @deprecated(reason:)
}

{
hero @deprecated(reason: "Deprecated"
}

{
hero @(reason: "Deprecated"
}

{
hero @(: "Deprecated"
}

{
hero @(:
}

{
hero @(:)
}
Loading