Skip to content

Commit

Permalink
fix(biome_graphql_parser): allow keyword to be used as identifier (#2814
Browse files Browse the repository at this point in the history
)
  • Loading branch information
vohoanglong0107 authored May 13, 2024
1 parent a2c1733 commit 7095ed0
Show file tree
Hide file tree
Showing 58 changed files with 5,733 additions and 4,312 deletions.
10 changes: 7 additions & 3 deletions crates/biome_graphql_parser/src/parser/argument.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use biome_parser::{
};

use super::{
definitions::is_at_selection_set_end,
directive::is_at_directive,
is_nth_at_name,
parse_error::{expected_argument, expected_value},
Expand Down Expand Up @@ -90,8 +89,13 @@ fn parse_argument(p: &mut GraphqlParser) -> ParsedSyntax {
#[inline]
pub(crate) fn is_at_argument_list_end(p: &mut GraphqlParser<'_>) -> bool {
p.at(T![')'])
// also handle the start of a selection set
|| is_at_selection_set_end(p)
// at the start af a new arguments list
|| p.at(T!['('])
// at a selection set or body of a definition
|| p.at(T!['{'])
|| p.at(T!['}'])
// at the start of a new directive
|| is_at_directive(p)
// if we can't find any of the above, we can't be sure if we're outside of
// an argument list
}
30 changes: 18 additions & 12 deletions crates/biome_graphql_parser/src/parser/definitions/enum.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{
directive::{is_at_directive, DirectiveList},
is_nth_at_name, parse_description,
is_nth_at_name, is_nth_at_non_kw_name, parse_description,
parse_error::expected_name,
parse_name,
value::{is_at_string, parse_enum_value},
Expand All @@ -15,8 +15,6 @@ use biome_parser::{
prelude::ParsedSyntax::*, Parser,
};

use super::is_at_definition;

#[inline]
pub(crate) fn parse_enum_type_definition(p: &mut GraphqlParser) -> ParsedSyntax {
if !is_at_enum_type_definition(p) {
Expand Down Expand Up @@ -87,9 +85,6 @@ impl ParseRecovery for EnumValueListParseRecovery {
const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS;

fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
// After a enum definition is a new type definition so it's safe to
// assume any name we see before a new type definition is a enum
// value
is_nth_at_name(p, 0) || is_at_enum_values_end(p)
}
}
Expand All @@ -116,22 +111,33 @@ pub(crate) fn is_at_enum_type_definition(p: &mut GraphqlParser) -> bool {
p.at(T![enum]) || (is_at_string(p) && p.nth_at(1, T![enum]))
}

/// Either a `{`, `|`, or a non kw name token must be present, else this is
/// likely the start of a new type definition
#[inline]
fn is_at_enum_values(p: &mut GraphqlParser) -> bool {
p.at(T!['{'])
// After an enum definition is a new type definition
// so it's safe to assume any name we see before a new type definition is
// an enum value
|| is_nth_at_name(p, 0)
|| (is_at_string(p) && is_nth_at_name(p, 1))
|| is_nth_at_non_kw_name(p, 0)
|| (is_at_string(p) && is_nth_at_non_kw_name(p, 1))
|| is_at_directive(p)
}

#[inline]
fn is_at_enum_value(p: &mut GraphqlParser) -> bool {
is_nth_at_name(p, 0) || (is_at_string(p) && is_nth_at_name(p, 1)) || is_at_directive(p)
}

/// Any name except `true`, `false`, `null`, is a valid enum value
/// so if the current token is still a name, it's still a valid enum value
#[inline]
fn is_at_enum_values_end(p: &mut GraphqlParser) -> bool {
p.at(T!['}']) || is_at_definition(p)
if p.at(T!['}']) {
return true;
}
// whether the current token is a description
// the directive check is for missing enum value case
if is_at_string(p) {
!is_nth_at_name(p, 1) && !p.nth_at(1, T![@])
} else {
!is_nth_at_name(p, 0) && !is_at_directive(p)
}
}
13 changes: 7 additions & 6 deletions crates/biome_graphql_parser/src/parser/definitions/field.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{
directive::DirectiveList,
is_nth_at_name, parse_description,
is_nth_at_name, is_nth_at_non_kw_name, parse_description,
parse_error::{expected_field_definition, expected_name, expected_type},
parse_name,
r#type::parse_type,
Expand All @@ -16,8 +16,6 @@ use biome_parser::{
prelude::ParsedSyntax::*, Parser,
};

use super::is_at_definition;

#[inline]
pub(super) fn parse_fields_definition(p: &mut GraphqlParser) -> ParsedSyntax {
if !is_at_fields(p) {
Expand Down Expand Up @@ -182,7 +180,9 @@ pub(super) fn is_at_fields(p: &mut GraphqlParser<'_>) -> bool {

#[inline]
pub(super) fn is_at_fields_end(p: &mut GraphqlParser<'_>) -> bool {
p.at(T!['}']) || is_at_definition(p)
p.at(T!['}'])
// start at a new definition body, as this rule doesn't allow nested fields
|| p.at(T!['{'])
}

/// Currently at a field definition, allowing some small errors.
Expand All @@ -208,8 +208,9 @@ pub(super) fn is_at_input_value_definition(p: &mut GraphqlParser<'_>) -> bool {
// missing name
|| p.at(T![:])
|| (is_at_string(p) && p.nth_at(1, T![:]))
// missing colon: `name String`
|| (is_nth_at_name(p, 0) && is_nth_at_name(p, 1))
// missing colon: `name String`. Must be non-keyword name, else it could
// be a new type definition.
|| (is_nth_at_non_kw_name(p, 0) && is_nth_at_non_kw_name(p, 1))
|| (is_at_string(p) && is_nth_at_name(p, 1) && is_nth_at_name(p, 2))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{
directive::DirectiveList,
parse_error::{expected_name, expected_named_type},
parse_error::{expected_name, expected_named_type, fragment_name_must_not_be_on},
parse_name,
r#type::parse_named_type,
GraphqlParser,
Expand All @@ -22,6 +22,9 @@ pub(crate) fn parse_fragment_definition(p: &mut GraphqlParser) -> ParsedSyntax {
let m = p.start();
p.bump(T![fragment]);

if p.at(T![on]) {
p.error(fragment_name_must_not_be_on(p, p.cur_range()));
}
parse_name(p).or_add_diagnostic(p, expected_name);
parse_type_condition(p);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ use biome_parser::{
prelude::ParsedSyntax::*, Parser,
};

use super::{
field::{is_at_input_value_definition, parse_input_value_definition},
is_at_definition,
};
use super::field::{is_at_input_value_definition, parse_input_value_definition};

#[inline]
pub(crate) fn parse_input_object_type_definition(p: &mut GraphqlParser) -> ParsedSyntax {
Expand Down Expand Up @@ -104,5 +101,7 @@ fn is_at_input_fields_definition(p: &mut GraphqlParser) -> bool {

#[inline]
fn is_input_fields_end(p: &mut GraphqlParser) -> bool {
p.at(T!['}']) || is_at_definition(p)
p.at(T!['}'])
// start of next definition body, since input fields can't be nested
|| p.at(T!['{'])
}
29 changes: 15 additions & 14 deletions crates/biome_graphql_parser/src/parser/definitions/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ use biome_graphql_syntax::{
GraphqlSyntaxKind::{self, *},
T,
};
use biome_parser::parse_lists::ParseNodeList;
use biome_parser::prelude::TokenSource;
use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList};
use biome_parser::{
parse_lists::ParseSeparatedList, parse_recovery::ParseRecovery, parsed_syntax::ParsedSyntax,
prelude::ParsedSyntax::*, Parser,
parse_recovery::ParseRecovery, parsed_syntax::ParsedSyntax, prelude::ParsedSyntax::*, Parser,
};

use super::field::{is_at_fields, is_at_fields_end, parse_fields_definition};
use super::{
field::{is_at_fields, is_at_fields_end, parse_fields_definition},
is_at_definition,
};

#[inline]
pub(super) fn parse_interface_type_definition(p: &mut GraphqlParser) -> ParsedSyntax {
Expand Down Expand Up @@ -54,16 +55,8 @@ pub(super) fn parse_implements_interface(p: &mut GraphqlParser) -> ParsedSyntax

p.bump(T![implements]);

// & is optional
p.eat(T![&]);

let position = p.source().position();
ImplementsInterfaceList.parse_list(p);

if position == p.source().position() {
p.error(expected_named_type(p, p.cur_range()));
}

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

Expand Down Expand Up @@ -103,6 +96,14 @@ impl ParseSeparatedList for ImplementsInterfaceList {
fn allow_trailing_separating_element(&self) -> bool {
false
}

fn allow_empty(&self) -> bool {
false
}

fn allow_leading_seperating_element(&self) -> bool {
true
}
}

struct ImplementsInterfaceListParseRecovery;
Expand All @@ -129,5 +130,5 @@ fn is_at_implements_interface(p: &mut GraphqlParser<'_>) -> bool {

#[inline]
fn is_at_implements_interface_end(p: &mut GraphqlParser<'_>) -> bool {
is_at_directive(p) || is_at_fields(p) || is_at_fields_end(p)
is_at_directive(p) || is_at_fields(p) || is_at_fields_end(p) || is_at_definition(p)
}
1 change: 0 additions & 1 deletion crates/biome_graphql_parser/src/parser/definitions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ use self::{
schema::{is_at_schema_definition, parse_schema_definition},
union::{is_at_union_type_definition, parse_union_type_definition},
};
pub(crate) use operation::is_at_selection_set_end;

struct DefinitionListParseRecovery;

Expand Down
25 changes: 15 additions & 10 deletions crates/biome_graphql_parser/src/parser/definitions/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ use biome_parser::{
prelude::ParsedSyntax::*, token_set, Parser, TokenSet,
};

use super::{
fragment::{is_at_type_condition, parse_type_condition},
is_at_definition,
};
use super::fragment::{is_at_type_condition, parse_type_condition};

pub(crate) const OPERATION_TYPE: TokenSet<GraphqlSyntaxKind> =
token_set![T![query], T![mutation], T![subscription]];
Expand Down Expand Up @@ -169,8 +166,15 @@ fn parse_field(p: &mut GraphqlParser) -> ParsedSyntax {
// otherwise we parse it as a normal field name
if is_at_alias(p) {
let m = p.start();
if p.at(T![:]) {
p.error(expected_name(p, p.cur_range()));
} else if is_nth_at_name(p, 0) {
parse_name(p).ok();
} else {
p.error(expected_name(p, p.cur_range()));
p.bump_any();
}

parse_name(p).or_add_diagnostic(p, expected_name);
p.bump(T![:]);
m.complete(p, GRAPHQL_ALIAS);

Expand All @@ -196,7 +200,7 @@ fn parse_fragment(p: &mut GraphqlParser) -> ParsedSyntax {
}
let m = p.start();
p.expect(DOT3);
if is_nth_at_name(p, 0) {
if is_nth_at_name(p, 0) && !p.nth_at(0, T![on]) {
// name is checked for in `is_at_name`
parse_name(p).ok();
DirectiveList.parse_list(p);
Expand Down Expand Up @@ -272,7 +276,7 @@ fn is_at_variable_definitions_end(p: &GraphqlParser) -> bool {
fn is_at_variable_definition(p: &mut GraphqlParser) -> bool {
is_at_variable(p)
// malformed variable
|| is_nth_at_name(p, 0)
|| (is_nth_at_name(p, 0) && p.nth_at(1, T![:]))
// malformed variable,but not inside selection set
|| (p.nth_at(1, T![:]) && !p.at(T!['{']))
// missing entire variable
Expand All @@ -287,10 +291,11 @@ fn is_at_selection_set(p: &GraphqlParser) -> bool {
p.at(T!['{'])
}

// Since keywords are valid names, we could only be sure that we are at the end
// of a selection set if we are at a closing curly brace
#[inline]
pub(crate) fn is_at_selection_set_end(p: &mut GraphqlParser) -> bool {
// stop at closing brace or at the start of a new definition
p.at(T!['}']) || is_at_definition(p)
fn is_at_selection_set_end(p: &mut GraphqlParser) -> bool {
p.at(T!['}'])
}

#[inline]
Expand Down
Loading

0 comments on commit 7095ed0

Please sign in to comment.