Skip to content

Commit

Permalink
Avoid parse ambiguity on type extensions
Browse files Browse the repository at this point in the history
Partial fix to #564, adds lookahead constraints to type system extensions to remove ambiguity or inefficiency from the grammar.

The GraphQL grammar should be parsed in linear type with at most one lookahead. Since extensions have an optional `{ }` body, finding the token `{` should always attempt to parse the relevant portion of the type extension rather than completing the type extension and attempting to parse the query shorthand SelectionSet.
  • Loading branch information
leebyron committed Apr 8, 2021
1 parent adee896 commit c9c5973
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 19 deletions.
32 changes: 24 additions & 8 deletions spec/Appendix B -- Grammar Summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ SchemaDefinition : Description? schema Directives[Const]? { RootOperationTypeDef

SchemaExtension :
- extend schema Directives[Const]? { RootOperationTypeDefinition+ }
- extend schema Directives[Const]
- extend schema Directives[Const] [lookahead != `{`]

RootOperationTypeDefinition : OperationType : NamedType

Expand All @@ -249,12 +249,14 @@ ScalarTypeDefinition : Description? scalar Name Directives[Const]?
ScalarTypeExtension :
- extend scalar Name Directives[Const]

ObjectTypeDefinition : Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
ObjectTypeDefinition :
- Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`]

ObjectTypeExtension :
- extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- extend type Name ImplementsInterfaces? Directives[Const]
- extend type Name ImplementsInterfaces
- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`]
- extend type Name ImplementsInterfaces [lookahead != `{`]

ImplementsInterfaces :
- ImplementsInterfaces & NamedType
Expand All @@ -268,12 +270,22 @@ ArgumentsDefinition : ( InputValueDefinition+ )

InputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]?

<<<<<<< HEAD
InterfaceTypeDefinition : Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?

InterfaceTypeExtension :
- extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- extend interface Name ImplementsInterfaces? Directives[Const]
- extend interface Name ImplementsInterfaces
=======
InterfaceTypeDefinition :
- Description? interface Name Directives[Const]? FieldsDefinition
- Description? interface Name Directives[Const]? [lookahead != `{`]

InterfaceTypeExtension :
- extend interface Name Directives[Const]? FieldsDefinition
- extend interface Name Directives[Const] [lookahead != `{`]
>>>>>>> 3bc2b08... Avoid parse ambiguity on type extensions
UnionTypeDefinition : Description? union Name Directives[Const]? UnionMemberTypes?

Expand All @@ -285,23 +297,27 @@ UnionTypeExtension :
- extend union Name Directives[Const]? UnionMemberTypes
- extend union Name Directives[Const]

EnumTypeDefinition : Description? enum Name Directives[Const]? EnumValuesDefinition?
EnumTypeDefinition :
- Description? enum Name Directives[Const]? EnumValuesDefinition
- Description? enum Name Directives[Const]? [lookahead != `{`]

EnumValuesDefinition : { EnumValueDefinition+ }

EnumValueDefinition : Description? EnumValue Directives[Const]?

EnumTypeExtension :
- extend enum Name Directives[Const]? EnumValuesDefinition
- extend enum Name Directives[Const]
- extend enum Name Directives[Const] [lookahead != `{`]

InputObjectTypeDefinition : Description? input Name Directives[Const]? InputFieldsDefinition?
InputObjectTypeDefinition :
- Description? input Name Directives[Const]? InputFieldsDefinition
- Description? input Name Directives[Const]? [lookahead != `{`]

InputFieldsDefinition : { InputValueDefinition+ }

InputObjectTypeExtension :
- extend input Name Directives[Const]? InputFieldsDefinition
- extend input Name Directives[Const]
- extend input Name Directives[Const] [lookahead != `{`]

DirectiveDefinition : Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations

Expand Down
34 changes: 23 additions & 11 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,16 @@ type Query {

SchemaExtension :
- extend schema Directives[Const]? { RootOperationTypeDefinition+ }
- extend schema Directives[Const]
- extend schema Directives[Const] [lookahead != `{`]

Schema extensions are used to represent a schema which has been extended from
an original schema. For example, this might be used by a GraphQL service which
adds additional operation types, or additional directives to an existing schema.

Note: Schema extensions without additional operation type definitions must not
be followed by a {`{`} (such as a query shorthand) to avoid parsing ambiguity.
This same limitation applies to the type definitions and extensions below.

**Schema Validation**

Schema extensions have the potential to be invalid if incorrectly defined.
Expand Down Expand Up @@ -574,7 +578,9 @@ Scalar type extensions have the potential to be invalid if incorrectly defined.

## Objects

ObjectTypeDefinition : Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
ObjectTypeDefinition :
- Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`]

ImplementsInterfaces :
- ImplementsInterfaces & NamedType
Expand Down Expand Up @@ -949,8 +955,8 @@ type ExampleType {

ObjectTypeExtension :
- extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- extend type Name ImplementsInterfaces? Directives[Const]
- extend type Name ImplementsInterfaces
- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`]
- extend type Name ImplementsInterfaces [lookahead != `{`]

Object type extensions are used to represent a type which has been extended from
some original type. For example, this might be used to represent local data, or
Expand Down Expand Up @@ -992,7 +998,9 @@ Object type extensions have the potential to be invalid if incorrectly defined.

## Interfaces

InterfaceTypeDefinition : Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition?
InterfaceTypeDefinition :
- Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`]

GraphQL interfaces represent a list of named fields and their arguments. GraphQL
objects and interfaces can then implement these interfaces which requires that
Expand Down Expand Up @@ -1182,8 +1190,8 @@ Interface types have the potential to be invalid if incorrectly defined.

InterfaceTypeExtension :
- extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition
- extend interface Name ImplementsInterfaces? Directives[Const]
- extend interface Name ImplementsInterfaces
- extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != `{`]
- extend interface Name ImplementsInterfaces [lookahead != `{`]

Interface type extensions are used to represent an interface which has been
extended from some original interface. For example, this might be used to
Expand Down Expand Up @@ -1361,7 +1369,9 @@ Union type extensions have the potential to be invalid if incorrectly defined.

## Enums

EnumTypeDefinition : Description? enum Name Directives[Const]? EnumValuesDefinition?
EnumTypeDefinition :
- Description? enum Name Directives[Const]? EnumValuesDefinition
- Description? enum Name Directives[Const]? [lookahead != `{`]

EnumValuesDefinition : { EnumValueDefinition+ }

Expand Down Expand Up @@ -1411,7 +1421,7 @@ Enum types have the potential to be invalid if incorrectly defined.

EnumTypeExtension :
- extend enum Name Directives[Const]? EnumValuesDefinition
- extend enum Name Directives[Const]
- extend enum Name Directives[Const] [lookahead != `{`]

Enum type extensions are used to represent an enum type which has been
extended from some original enum type. For example, this might be used to
Expand All @@ -1432,7 +1442,9 @@ Enum type extensions have the potential to be invalid if incorrectly defined.

## Input Objects

InputObjectTypeDefinition : Description? input Name Directives[Const]? InputFieldsDefinition?
InputObjectTypeDefinition :
- Description? input Name Directives[Const]? InputFieldsDefinition
- Description? input Name Directives[Const]? [lookahead != `{`]

InputFieldsDefinition : { InputValueDefinition+ }

Expand Down Expand Up @@ -1599,7 +1611,7 @@ Literal Value | Variables | Coerced Value

InputObjectTypeExtension :
- extend input Name Directives[Const]? InputFieldsDefinition
- extend input Name Directives[Const]
- extend input Name Directives[Const] [lookahead != `{`]

Input object type extensions are used to represent an input object type which
has been extended from some original input object type. For example, this might
Expand Down

0 comments on commit c9c5973

Please sign in to comment.