From a91fdd55583a6bb9b372a6485c2358d1979f9b23 Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Mon, 22 Jul 2019 18:11:33 -0700 Subject: [PATCH] Avoid parse ambiguity on type extensions 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. --- spec/Appendix B -- Grammar Summary.md | 30 ++++++++++++++--------- spec/Section 3 -- Type System.md | 34 ++++++++++++++++++--------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index 32fd1a031..d4f1c12aa 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -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 @@ -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 @@ -268,12 +270,14 @@ ArgumentsDefinition : ( InputValueDefinition+ ) InputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]? -InterfaceTypeDefinition : Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition? +InterfaceTypeDefinition : + - Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition + - Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] 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 != `{`] UnionTypeDefinition : Description? union Name Directives[Const]? UnionMemberTypes? @@ -285,7 +289,9 @@ 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+ } @@ -293,15 +299,17 @@ 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 diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 141464905..cec81fb1a 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -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. @@ -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 @@ -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 @@ -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 @@ -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 @@ -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+ } @@ -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 @@ -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+ } @@ -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