Skip to content

Commit

Permalink
Handle operation parsing error GraphQL (#2434)
Browse files Browse the repository at this point in the history
  • Loading branch information
vohoanglong0107 authored Apr 18, 2024
1 parent 3d0b473 commit b584a88
Show file tree
Hide file tree
Showing 17 changed files with 5,730 additions and 39 deletions.
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

0 comments on commit b584a88

Please sign in to comment.