From 67746016dcbbdbf038cc8bcbcb47502eeceb8ae3 Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Thu, 9 Apr 2020 23:57:33 +0300 Subject: [PATCH 1/8] Add support for descriptions to all AST nodes according to the draft specification --- ...ould_Not_Change_Inadvertently.approved.txt | 19 +- src/GraphQLParser.Tests/ParserTests.cs | 46 +++ src/GraphQLParser/AST/Enums.cs | 3 +- src/GraphQLParser/AST/GraphQLDescription.cs | 14 + .../AST/GraphQLObjectTypeDefinition.cs | 4 +- .../AST/GraphQLSchemaDefinition.cs | 6 +- .../AST/GraphQLTypeDefinition.cs | 6 +- src/GraphQLParser/AST/IHaveDescription.cs | 7 + src/GraphQLParser/ParserContext.cs | 280 ++++++++++-------- 9 files changed, 253 insertions(+), 132 deletions(-) create mode 100644 src/GraphQLParser/AST/GraphQLDescription.cs create mode 100644 src/GraphQLParser/AST/IHaveDescription.cs diff --git a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt index 5da446c8..de178d1b 100644 --- a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt +++ b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt @@ -47,6 +47,7 @@ namespace GraphQLParser.AST TypeExtensionDefinition = 35, DirectiveDefinition = 36, Comment = 37, + Description = 38, } public class GraphQLArgument : GraphQLParser.AST.ASTNode, GraphQLParser.AST.INamedNode { @@ -61,6 +62,12 @@ namespace GraphQLParser.AST public override GraphQLParser.AST.ASTNodeKind Kind { get; } public string Text { get; set; } } + public class GraphQLDescription : GraphQLParser.AST.ASTNode + { + public GraphQLDescription(string value) { } + public override GraphQLParser.AST.ASTNodeKind Kind { get; } + public string Value { get; set; } + } public class GraphQLDirective : GraphQLParser.AST.ASTNode, GraphQLParser.AST.INamedNode { public GraphQLDirective() { } @@ -207,7 +214,7 @@ namespace GraphQLParser.AST public GraphQLParser.AST.GraphQLName? Name { get; set; } public GraphQLParser.AST.GraphQLValue? Value { get; set; } } - public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode + public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHaveDescription { public GraphQLObjectTypeDefinition() { } public System.Collections.Generic.List? Directives { get; set; } @@ -251,9 +258,10 @@ namespace GraphQLParser.AST public string? Value { get; set; } public override string? ToString() { } } - public class GraphQLSchemaDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode + public class GraphQLSchemaDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHaveDescription { public GraphQLSchemaDefinition() { } + public GraphQLParser.AST.GraphQLDescription Description { get; set; } public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } public System.Collections.Generic.List? OperationTypes { get; set; } @@ -268,9 +276,10 @@ namespace GraphQLParser.AST { protected GraphQLType() { } } - public abstract class GraphQLTypeDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.INamedNode + public abstract class GraphQLTypeDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHaveDescription, GraphQLParser.AST.INamedNode { protected GraphQLTypeDefinition() { } + public GraphQLParser.AST.GraphQLDescription? Description { get; set; } public GraphQLParser.AST.GraphQLName? Name { get; set; } } public class GraphQLTypeExtensionDefinition : GraphQLParser.AST.GraphQLTypeDefinition @@ -308,6 +317,10 @@ namespace GraphQLParser.AST { System.Collections.Generic.List? Directives { get; set; } } + public interface IHaveDescription + { + GraphQLParser.AST.GraphQLDescription? Description { get; set; } + } public interface INamedNode { GraphQLParser.AST.GraphQLName? Name { get; set; } diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index db3b3781..60bff3a0 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -530,5 +530,51 @@ public void Should_Parse_Unions(string text) { new Parser(new Lexer()).Parse(new Source(text)).ShouldNotBeNull(); } + + [Theory] + [InlineData("\"schema description\" schema { query : Query }", "schema description")] + [InlineData("\"scalar description\" scalar DateTiem", "scalar description")] + [InlineData("\"type description\" type Query { name : String }", "type description")] + [InlineData("\"interface description\" interface Person { name : String }", "interface description")] + [InlineData("\"union description\" union Person = He | She", "union description")] + [InlineData("\"enum description\" enum Currency { EUR USD }", "enum description")] + [InlineData("\"input description\" input PersonInput { name : String }", "input description")] + [InlineData("\"directive description\" directive @example on FIELD", "directive description")] + public void Should_Parse_TypeDefinition_Description(string text, string expectedDescription) + { + var document = new Parser(new Lexer()).Parse(new Source(text)); + ((IHaveDescription)document.Definitions[0]).Description.Value.ShouldBe(expectedDescription); + } + + [Fact] + public void Should_Parse_FieldDefinition_Description() + { + var text = "type Query { \"field description\" name : String }"; + var document = new Parser(new Lexer()).Parse(new Source(text)); + var objectTypeDefinition = document.Definitions.OfType().First(); + objectTypeDefinition.Fields[0].Description.Value.ShouldBe("field description"); + } + + [Fact] + public void Should_Parse_InputValueDefinition_Description() + { + var text = "type Query { name(\"input value description\" inputValue: Int) : String }"; + var document = new Parser(new Lexer()).Parse(new Source(text)); + var objectTypeDefinition = document.Definitions.OfType().First(); + objectTypeDefinition.Fields[0] + .Arguments[0].Description.Value.ShouldBe("input value description"); + } + + [Fact] + public void Should_Parse_EnumValueDefinition_Description() + { + var text = "enum Currency { \"EUR value description\" EUR \"USD value description\" USD }"; + var document = new Parser(new Lexer()).Parse(new Source(text)); + var enumTypeDefinition = document.Definitions.OfType().First(); + enumTypeDefinition.Values + .Select(v=>v.Description.Value) + .ShouldBe(new []{"EUR value description", "USD value description"}); + } + } } diff --git a/src/GraphQLParser/AST/Enums.cs b/src/GraphQLParser/AST/Enums.cs index 55e56f9d..376a4898 100644 --- a/src/GraphQLParser/AST/Enums.cs +++ b/src/GraphQLParser/AST/Enums.cs @@ -40,6 +40,7 @@ public enum ASTNodeKind TypeExtensionDefinition, DirectiveDefinition, Comment, + Description } public enum OperationType @@ -48,4 +49,4 @@ public enum OperationType Mutation, Subscription } -} \ No newline at end of file +} diff --git a/src/GraphQLParser/AST/GraphQLDescription.cs b/src/GraphQLParser/AST/GraphQLDescription.cs new file mode 100644 index 00000000..1edb9105 --- /dev/null +++ b/src/GraphQLParser/AST/GraphQLDescription.cs @@ -0,0 +1,14 @@ +namespace GraphQLParser.AST +{ + public class GraphQLDescription : ASTNode + { + public GraphQLDescription(string value) + { + Value = value; + } + + public override ASTNodeKind Kind => ASTNodeKind.Description; + + public string Value { get; set; } + } +} diff --git a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs index 243c798a..aa033c8f 100644 --- a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs @@ -2,7 +2,7 @@ namespace GraphQLParser.AST { - public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode + public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHaveDescription { public List? Directives { get; set; } @@ -12,4 +12,4 @@ public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectives public override ASTNodeKind Kind => ASTNodeKind.ObjectTypeDefinition; } -} \ No newline at end of file +} diff --git a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs index fbdd42e2..4a4e8feb 100644 --- a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs @@ -2,12 +2,14 @@ namespace GraphQLParser.AST { - public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode + public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode, IHaveDescription { public List? Directives { get; set; } public override ASTNodeKind Kind => ASTNodeKind.SchemaDefinition; public List? OperationTypes { get; set; } + + public GraphQLDescription Description { get; set; } } -} \ No newline at end of file +} diff --git a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs index 8410a642..b27bec29 100644 --- a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs @@ -1,7 +1,9 @@ namespace GraphQLParser.AST { - public abstract class GraphQLTypeDefinition : ASTNode, INamedNode + public abstract class GraphQLTypeDefinition : ASTNode, INamedNode, IHaveDescription { public GraphQLName? Name { get; set; } + + public GraphQLDescription? Description { get; set; } } -} \ No newline at end of file +} diff --git a/src/GraphQLParser/AST/IHaveDescription.cs b/src/GraphQLParser/AST/IHaveDescription.cs new file mode 100644 index 00000000..0355483c --- /dev/null +++ b/src/GraphQLParser/AST/IHaveDescription.cs @@ -0,0 +1,7 @@ +namespace GraphQLParser.AST +{ + public interface IHaveDescription + { + GraphQLDescription? Description { get; set; } + } +} \ No newline at end of file diff --git a/src/GraphQLParser/ParserContext.cs b/src/GraphQLParser/ParserContext.cs index 4bdadc1f..fd451d00 100644 --- a/src/GraphQLParser/ParserContext.cs +++ b/src/GraphQLParser/ParserContext.cs @@ -1,14 +1,14 @@ -using GraphQLParser.AST; -using GraphQLParser.Exceptions; -using System; -using System.Collections.Generic; +using GraphQLParser.AST; +using GraphQLParser.Exceptions; +using System; +using System.Collections.Generic; using System.Linq; - + namespace GraphQLParser { // WARNING: mutable struct, pass it by reference to those methods that will change it internal struct ParserContext : IDisposable - { + { private delegate TResult ParseCallback(ref ParserContext context); private readonly ILexer _lexer; @@ -240,8 +240,8 @@ private GraphQLArgument ParseArgument() private List? ParseArgumentDefs() { - return Peek(TokenKind.PAREN_L) - ? Many(TokenKind.PAREN_L, (ref ParserContext context) => context.ParseInputValueDef(), TokenKind.PAREN_R) + return Peek(TokenKind.PAREN_L) + ? Many(TokenKind.PAREN_L, (ref ParserContext context) => context.ParseInputValueDef(), TokenKind.PAREN_R) : null; } @@ -268,20 +268,14 @@ private ASTNode ParseDefinition() { ParseComment(); - if (Peek(TokenKind.BRACE_L)) - { - return ParseOperationDefinition(); - } - - if (Peek(TokenKind.NAME)) + return _currentToken.Kind switch { - ASTNode? definition; - if ((definition = ParseNamedDefinition()) != null) - return definition; - } - - throw new GraphQLSyntaxErrorException( - $"Unexpected {_currentToken}", _source, _currentToken.Start); + TokenKind.BRACE_L => ParseOperationDefinition(), + TokenKind.STRING => ParseNamedDocumentedDefinition(throwOnMissing: true)!, + TokenKind.NAME => ParseNamedDefinition(), + _ => throw new GraphQLSyntaxErrorException( + $"Unexpected {_currentToken}", _source, _currentToken.Start) + }; } private List ParseDefinitionsIfNotEOF() @@ -316,19 +310,19 @@ private List ParseDefinitionsIfNotEOF() text.Add(_currentToken.Value); end = _currentToken.End; Advance(); - } + } while (_currentToken.Kind == TokenKind.COMMENT); - var comment = new GraphQLComment(string.Join(Environment.NewLine, text)) - { - Location = new GraphQLLocation - ( - start, - end - ) + var comment = new GraphQLComment(string.Join(Environment.NewLine, text)) + { + Location = new GraphQLLocation + ( + start, + end + ) }; - if (_comments == null) + if (_comments == null) _comments = new Stack(); _comments.Push(comment); @@ -336,6 +330,21 @@ private List ParseDefinitionsIfNotEOF() return comment; } + private GraphQLDescription? ParseDescription() + { + if (!Peek(TokenKind.STRING)) + return null; + + var documentation = new GraphQLDescription(_currentToken.Value!) + { + Location = GetLocation(_currentToken.Start) + }; + + Advance(); + + return documentation; + } + private GraphQLDirective ParseDirective() { int start = _currentToken.Start; @@ -346,18 +355,19 @@ private GraphQLDirective ParseDirective() Arguments = ParseArguments(), Location = GetLocation(start) }; - } - - /// - /// http://spec.graphql.org/draft/#DirectiveDefinition - /// DirectiveDefinition: - /// Description(opt) directive @ Name ArgumentsDefinition(opt) repeatable(opt) on DirectiveLocations - /// + } + + /// + /// http://spec.graphql.org/draft/#DirectiveDefinition + /// DirectiveDefinition: + /// Description(opt) directive @ Name ArgumentsDefinition(opt) repeatable(opt) on DirectiveLocations + /// + /// /// - private GraphQLDirectiveDefinition ParseDirectiveDefinition() + private GraphQLDirectiveDefinition ParseDirectiveDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("directive"); Expect(TokenKind.AT); @@ -370,6 +380,7 @@ private GraphQLDirectiveDefinition ParseDirectiveDefinition() return new GraphQLDirectiveDefinition { + Description = description, Comment = comment, Name = name, Repeatable = repeatable, @@ -377,32 +388,32 @@ private GraphQLDirectiveDefinition ParseDirectiveDefinition() Locations = locations, Location = GetLocation(start) }; - } - - private bool ParseRepeatable() - { - if (Peek(TokenKind.NAME)) - { - switch (_currentToken.Value) - { - case "repeatable": - Advance(); - return true; - case "on": - return false; - default: - throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start); - } - } - - return false; + } + + private bool ParseRepeatable() + { + if (Peek(TokenKind.NAME)) + { + switch (_currentToken.Value) + { + case "repeatable": + Advance(); + return true; + case "on": + return false; + default: + throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start); + } + } + + return false; } private List ParseDirectiveLocations() { - var locations = new List(); - - // Directive locations may be defined with an optional leading | character + var locations = new List(); + + // Directive locations may be defined with an optional leading | character // to aid formatting when representing a longer list of possible locations Skip(TokenKind.PIPE); @@ -432,14 +443,15 @@ private GraphQLDocument ParseDocument() return CreateDocument(start, definitions); } - private GraphQLEnumTypeDefinition ParseEnumTypeDefinition() + private GraphQLEnumTypeDefinition ParseEnumTypeDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("enum"); return new GraphQLEnumTypeDefinition { + Description = description, Comment = comment, Name = ParseName(), Directives = ParseDirectives(), @@ -462,9 +474,11 @@ private GraphQLEnumValueDefinition ParseEnumValueDefinition() { var comment = GetComment(); int start = _currentToken.Start; + var description = ParseDescription(); return new GraphQLEnumValueDefinition { + Description = description, Comment = comment, Name = ParseName(), Directives = ParseDirectives(), @@ -476,12 +490,14 @@ private GraphQLFieldDefinition ParseFieldDefinition() { var comment = GetComment(); int start = _currentToken.Start; + var description = ParseDescription(); var name = ParseName(); var args = ParseArgumentDefs(); Expect(TokenKind.COLON); return new GraphQLFieldDefinition { + Description = description, Comment = comment, Name = name, Arguments = args, @@ -492,7 +508,7 @@ private GraphQLFieldDefinition ParseFieldDefinition() } private GraphQLFieldSelection ParseFieldSelection() - { + { var comment = GetComment(); int start = _currentToken.Start; var nameOrAlias = ParseName(); @@ -530,8 +546,8 @@ private ASTNode ParseFragment() int start = _currentToken.Start; Expect(TokenKind.SPREAD); - return Peek(TokenKind.NAME) && !"on".Equals(_currentToken.Value) - ? CreateGraphQLFragmentSpread(start, comment) + return Peek(TokenKind.NAME) && !"on".Equals(_currentToken.Value) + ? CreateGraphQLFragmentSpread(start, comment) : CreateInlineFragment(start, comment); } @@ -581,14 +597,15 @@ private GraphQLName ParseFragmentName() return types; } - private GraphQLInputObjectTypeDefinition ParseInputObjectTypeDefinition() + private GraphQLInputObjectTypeDefinition ParseInputObjectTypeDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("input"); return new GraphQLInputObjectTypeDefinition { + Description = description, Comment = comment, Name = ParseName(), Directives = ParseDirectives(), @@ -601,11 +618,13 @@ private GraphQLInputValueDefinition ParseInputValueDef() { var comment = GetComment(); int start = _currentToken.Start; + var description = ParseDescription(); var name = ParseName(); Expect(TokenKind.COLON); return new GraphQLInputValueDefinition { + Description = description, Comment = comment, Name = name, Type = ParseType(), @@ -627,14 +646,15 @@ private GraphQLValue ParseInt(bool isConstant) }; } - private GraphQLInterfaceTypeDefinition ParseInterfaceTypeDefinition() + private GraphQLInterfaceTypeDefinition ParseInterfaceTypeDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("interface"); return new GraphQLInterfaceTypeDefinition { + Description = description, Comment = comment, Name = ParseName(), Directives = ParseDirectives(), @@ -645,9 +665,9 @@ private GraphQLInterfaceTypeDefinition ParseInterfaceTypeDefinition() private GraphQLValue ParseList(bool isConstant) { - int start = _currentToken.Start; - // the compiler caches these delegates in the generated code - ParseCallback constant = (ref ParserContext context) => context.ParseConstantValue(); + int start = _currentToken.Start; + // the compiler caches these delegates in the generated code + ParseCallback constant = (ref ParserContext context) => context.ParseConstantValue(); ParseCallback value = (ref ParserContext context) => context.ParseValueValue(); return new GraphQLListValue(ASTNodeKind.ListValue) @@ -672,24 +692,36 @@ private GraphQLName ParseName() }; } - private ASTNode? ParseNamedDefinition() - { - return _currentToken.Value switch + private ASTNode ParseNamedDefinition() => + _currentToken.Value switch { - "query" => ParseOperationDefinition(), - "mutation" => ParseOperationDefinition(), - "subscription" => ParseOperationDefinition(), + "query" => ParseOperationDefinition(), + "mutation" => ParseOperationDefinition(), + "subscription" => ParseOperationDefinition(), "fragment" => ParseFragmentDefinition(), - "schema" => ParseSchemaDefinition(), - "scalar" => ParseScalarTypeDefinition(), - "type" => ParseObjectTypeDefinition(), - "interface" => ParseInterfaceTypeDefinition(), - "union" => ParseUnionTypeDefinition(), - "enum" => ParseEnumTypeDefinition(), - "input" => ParseInputObjectTypeDefinition(), "extend" => ParseTypeExtensionDefinition(), - "directive" => ParseDirectiveDefinition(), - _ => null + _ => ParseNamedDocumentedDefinition() + ?? throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) + }; + + private ASTNode? ParseNamedDocumentedDefinition(bool throwOnMissing = false) + { + var description = ParseDescription(); + + return _currentToken.Value switch + { + "schema" => ParseSchemaDefinition(description), + "scalar" => ParseScalarTypeDefinition(description), + "type" => ParseObjectTypeDefinition(description), + "interface" => ParseInterfaceTypeDefinition(description), + "union" => ParseUnionTypeDefinition(description), + "enum" => ParseEnumTypeDefinition(description), + "input" => ParseInputObjectTypeDefinition(description), + "directive" => ParseDirectiveDefinition(description), + _ => throwOnMissing + ? throw new GraphQLSyntaxErrorException( + $"Unexpected {_currentToken}. Expecting schema, scalar, type, interface, union, enum, input or directive.", _source, _currentToken.Start) + : (ASTNode?)null }; } @@ -707,15 +739,15 @@ private GraphQLValue ParseNameValue(bool isConstant) { var token = _currentToken; - if ("true".Equals(token.Value) || "false".Equals(token.Value)) - { - return ParseBooleanValue(token); - } + if ("true".Equals(token.Value) || "false".Equals(token.Value)) + { + return ParseBooleanValue(token); + } else if (token.Value != null) - { - return token.Value.Equals("null") - ? ParseNullValue(token) - : ParseEnumValue(token); + { + return token.Value.Equals("null") + ? ParseNullValue(token) + : ParseEnumValue(token); } throw new GraphQLSyntaxErrorException( @@ -769,15 +801,16 @@ private List ParseObjectFields(bool isConstant) return fields; } - private GraphQLObjectTypeDefinition ParseObjectTypeDefinition() + private GraphQLObjectTypeDefinition ParseObjectTypeDefinition(GraphQLDescription? description = null) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("type"); return new GraphQLObjectTypeDefinition { + Description = description, Comment = comment, Name = ParseName(), Interfaces = ParseImplementsInterfaces(), @@ -804,7 +837,7 @@ private OperationType ParseOperationType() return token.Value switch { "mutation" => OperationType.Mutation, - "subscription" => OperationType.Subscription, + "subscription" => OperationType.Subscription, _ => OperationType.Query }; } @@ -824,16 +857,17 @@ private GraphQLOperationTypeDefinition ParseOperationTypeDefinition() }; } - private GraphQLScalarTypeDefinition ParseScalarTypeDefinition() + private GraphQLScalarTypeDefinition ParseScalarTypeDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("scalar"); var name = ParseName(); var directives = ParseDirectives(); return new GraphQLScalarTypeDefinition { + Description = description, Comment = comment, Name = name, Directives = directives, @@ -841,16 +875,17 @@ private GraphQLScalarTypeDefinition ParseScalarTypeDefinition() }; } - private GraphQLSchemaDefinition ParseSchemaDefinition() + private GraphQLSchemaDefinition ParseSchemaDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("schema"); var directives = ParseDirectives(); var operationTypes = Many(TokenKind.BRACE_L, (ref ParserContext context) => context.ParseOperationTypeDefinition(), TokenKind.BRACE_R); return new GraphQLSchemaDefinition { + Description = description, Comment = comment, Directives = directives, OperationTypes = operationTypes, @@ -905,12 +940,12 @@ private GraphQLType ParseType() type = ParseNamedType(); } - return Skip(TokenKind.BANG) + return Skip(TokenKind.BANG) ? new GraphQLNonNullType { Type = type, Location = GetLocation(start) - } + } : type; } @@ -932,10 +967,10 @@ private GraphQLTypeExtensionDefinition ParseTypeExtensionDefinition() private List ParseUnionMembers() { - var members = new List(); - - // Union members may be defined with an optional leading | character - // to aid formatting when representing a longer list of possible types + var members = new List(); + + // Union members may be defined with an optional leading | character + // to aid formatting when representing a longer list of possible types Skip(TokenKind.PIPE); do @@ -947,10 +982,10 @@ private List ParseUnionMembers() return members; } - private GraphQLUnionTypeDefinition ParseUnionTypeDefinition() + private GraphQLUnionTypeDefinition ParseUnionTypeDefinition(GraphQLDescription? description) { var comment = GetComment(); - int start = _currentToken.Start; + int start = description?.Location.Start ?? _currentToken.Start; ExpectKeyword("union"); var name = ParseName(); var directives = ParseDirectives(); @@ -959,6 +994,7 @@ private GraphQLUnionTypeDefinition ParseUnionTypeDefinition() return new GraphQLUnionTypeDefinition { + Description = description, Comment = comment, Name = name, Directives = directives, @@ -967,16 +1003,16 @@ private GraphQLUnionTypeDefinition ParseUnionTypeDefinition() }; } - private GraphQLValue ParseValueLiteral(bool isConstant) => _currentToken.Kind switch - { - TokenKind.BRACKET_L => ParseList(isConstant), - TokenKind.BRACE_L => ParseObject(isConstant), - TokenKind.INT => ParseInt(isConstant), - TokenKind.FLOAT => ParseFloat(isConstant), - TokenKind.STRING => ParseString(isConstant), - TokenKind.NAME => ParseNameValue(isConstant), - TokenKind.DOLLAR when !isConstant => ParseVariable(), - _ => throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) + private GraphQLValue ParseValueLiteral(bool isConstant) => _currentToken.Kind switch + { + TokenKind.BRACKET_L => ParseList(isConstant), + TokenKind.BRACE_L => ParseObject(isConstant), + TokenKind.INT => ParseInt(isConstant), + TokenKind.FLOAT => ParseFloat(isConstant), + TokenKind.STRING => ParseString(isConstant), + TokenKind.NAME => ParseNameValue(isConstant), + TokenKind.DOLLAR when !isConstant => ParseVariable(), + _ => throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) }; private GraphQLValue ParseValueValue() => ParseValueLiteral(false); @@ -994,7 +1030,7 @@ private GraphQLVariable ParseVariable() } private GraphQLVariableDefinition ParseVariableDefinition() - { + { var comment = GetComment(); int start = _currentToken.Start; From d8eaf12f34f69a1a3e957acd634efea811ca076c Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Fri, 10 Apr 2020 16:16:28 +0300 Subject: [PATCH 2/8] add missing nullable string specification --- src/GraphQLParser/AST/GraphQLSchemaDefinition.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs index 4a4e8feb..79f14d99 100644 --- a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs @@ -10,6 +10,6 @@ public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode, IHaveDescrip public List? OperationTypes { get; set; } - public GraphQLDescription Description { get; set; } + public GraphQLDescription? Description { get; set; } } } From 51db02d52f50f06ee01698b9c6e3350cd87c1740 Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Fri, 10 Apr 2020 17:55:28 +0300 Subject: [PATCH 3/8] Fix public interface test --- ...ests.Public_Api_Should_Not_Change_Inadvertently.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt index de178d1b..5296affd 100644 --- a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt +++ b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt @@ -261,7 +261,7 @@ namespace GraphQLParser.AST public class GraphQLSchemaDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHaveDescription { public GraphQLSchemaDefinition() { } - public GraphQLParser.AST.GraphQLDescription Description { get; set; } + public GraphQLParser.AST.GraphQLDescription? Description { get; set; } public System.Collections.Generic.List? Directives { get; set; } public override GraphQLParser.AST.ASTNodeKind Kind { get; } public System.Collections.Generic.List? OperationTypes { get; set; } From 35f0874d715afbea29a0c426baf3a42f087d0adc Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Sat, 11 Apr 2020 00:51:37 +0300 Subject: [PATCH 4/8] rename IHaveDescription -> IHasDescriptionNode based on code review --- ...pi_Should_Not_Change_Inadvertently.approved.txt | 14 +++++++------- src/GraphQLParser.Tests/ParserTests.cs | 2 +- .../AST/GraphQLObjectTypeDefinition.cs | 2 +- src/GraphQLParser/AST/GraphQLSchemaDefinition.cs | 2 +- src/GraphQLParser/AST/GraphQLTypeDefinition.cs | 2 +- ...{IHaveDescription.cs => IHasDescriptionNode.cs} | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename src/GraphQLParser/AST/{IHaveDescription.cs => IHasDescriptionNode.cs} (69%) diff --git a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt index 5296affd..80407b73 100644 --- a/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt +++ b/src/GraphQLParser.ApiTests/ApiApprovalTests.Public_Api_Should_Not_Change_Inadvertently.approved.txt @@ -214,7 +214,7 @@ namespace GraphQLParser.AST public GraphQLParser.AST.GraphQLName? Name { get; set; } public GraphQLParser.AST.GraphQLValue? Value { get; set; } } - public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHaveDescription + public class GraphQLObjectTypeDefinition : GraphQLParser.AST.GraphQLTypeDefinition, GraphQLParser.AST.IHasDescriptionNode, GraphQLParser.AST.IHasDirectivesNode { public GraphQLObjectTypeDefinition() { } public System.Collections.Generic.List? Directives { get; set; } @@ -258,7 +258,7 @@ namespace GraphQLParser.AST public string? Value { get; set; } public override string? ToString() { } } - public class GraphQLSchemaDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDirectivesNode, GraphQLParser.AST.IHaveDescription + public class GraphQLSchemaDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDescriptionNode, GraphQLParser.AST.IHasDirectivesNode { public GraphQLSchemaDefinition() { } public GraphQLParser.AST.GraphQLDescription? Description { get; set; } @@ -276,7 +276,7 @@ namespace GraphQLParser.AST { protected GraphQLType() { } } - public abstract class GraphQLTypeDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHaveDescription, GraphQLParser.AST.INamedNode + public abstract class GraphQLTypeDefinition : GraphQLParser.AST.ASTNode, GraphQLParser.AST.IHasDescriptionNode, GraphQLParser.AST.INamedNode { protected GraphQLTypeDefinition() { } public GraphQLParser.AST.GraphQLDescription? Description { get; set; } @@ -313,13 +313,13 @@ namespace GraphQLParser.AST public GraphQLParser.AST.GraphQLType? Type { get; set; } public GraphQLParser.AST.GraphQLVariable? Variable { get; set; } } - public interface IHasDirectivesNode + public interface IHasDescriptionNode { - System.Collections.Generic.List? Directives { get; set; } + GraphQLParser.AST.GraphQLDescription? Description { get; set; } } - public interface IHaveDescription + public interface IHasDirectivesNode { - GraphQLParser.AST.GraphQLDescription? Description { get; set; } + System.Collections.Generic.List? Directives { get; set; } } public interface INamedNode { diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index 60bff3a0..1fba607d 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -543,7 +543,7 @@ public void Should_Parse_Unions(string text) public void Should_Parse_TypeDefinition_Description(string text, string expectedDescription) { var document = new Parser(new Lexer()).Parse(new Source(text)); - ((IHaveDescription)document.Definitions[0]).Description.Value.ShouldBe(expectedDescription); + ((IHasDescriptionNode)document.Definitions[0]).Description.Value.ShouldBe(expectedDescription); } [Fact] diff --git a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs index aa033c8f..8d9ca7dc 100644 --- a/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLObjectTypeDefinition.cs @@ -2,7 +2,7 @@ namespace GraphQLParser.AST { - public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHaveDescription + public class GraphQLObjectTypeDefinition : GraphQLTypeDefinition, IHasDirectivesNode, IHasDescriptionNode { public List? Directives { get; set; } diff --git a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs index 79f14d99..88e38b32 100644 --- a/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLSchemaDefinition.cs @@ -2,7 +2,7 @@ namespace GraphQLParser.AST { - public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode, IHaveDescription + public class GraphQLSchemaDefinition : ASTNode, IHasDirectivesNode, IHasDescriptionNode { public List? Directives { get; set; } diff --git a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs index b27bec29..4997b196 100644 --- a/src/GraphQLParser/AST/GraphQLTypeDefinition.cs +++ b/src/GraphQLParser/AST/GraphQLTypeDefinition.cs @@ -1,6 +1,6 @@ namespace GraphQLParser.AST { - public abstract class GraphQLTypeDefinition : ASTNode, INamedNode, IHaveDescription + public abstract class GraphQLTypeDefinition : ASTNode, INamedNode, IHasDescriptionNode { public GraphQLName? Name { get; set; } diff --git a/src/GraphQLParser/AST/IHaveDescription.cs b/src/GraphQLParser/AST/IHasDescriptionNode.cs similarity index 69% rename from src/GraphQLParser/AST/IHaveDescription.cs rename to src/GraphQLParser/AST/IHasDescriptionNode.cs index 0355483c..0114ef67 100644 --- a/src/GraphQLParser/AST/IHaveDescription.cs +++ b/src/GraphQLParser/AST/IHasDescriptionNode.cs @@ -1,7 +1,7 @@ namespace GraphQLParser.AST { - public interface IHaveDescription + public interface IHasDescriptionNode { GraphQLDescription? Description { get; set; } } -} \ No newline at end of file +} From e1cb4a8233fa4986945c4da632910dfce9311a02 Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Sat, 11 Apr 2020 00:53:53 +0300 Subject: [PATCH 5/8] fix type in string Co-Authored-By: Ivan Maximov --- src/GraphQLParser.Tests/ParserTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index 1fba607d..0aba0069 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -533,7 +533,7 @@ public void Should_Parse_Unions(string text) [Theory] [InlineData("\"schema description\" schema { query : Query }", "schema description")] - [InlineData("\"scalar description\" scalar DateTiem", "scalar description")] + [InlineData("\"scalar description\" scalar DateTime", "scalar description")] [InlineData("\"type description\" type Query { name : String }", "type description")] [InlineData("\"interface description\" interface Person { name : String }", "interface description")] [InlineData("\"union description\" union Person = He | She", "union description")] From b5a84fd1dca5062108aaa9f5a1b3127209bbd046 Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Sat, 11 Apr 2020 00:56:25 +0300 Subject: [PATCH 6/8] Apply suggestions from code review Co-Authored-By: Ivan Maximov --- src/GraphQLParser.Tests/ParserTests.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GraphQLParser.Tests/ParserTests.cs b/src/GraphQLParser.Tests/ParserTests.cs index 0aba0069..dcacb59c 100644 --- a/src/GraphQLParser.Tests/ParserTests.cs +++ b/src/GraphQLParser.Tests/ParserTests.cs @@ -575,6 +575,5 @@ public void Should_Parse_EnumValueDefinition_Description() .Select(v=>v.Description.Value) .ShouldBe(new []{"EUR value description", "USD value description"}); } - } } From 4e10bdc8a0017bbe6ccfd0f48a0948a0b7f930c6 Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Sat, 11 Apr 2020 01:40:35 +0300 Subject: [PATCH 7/8] Undo whitespace changes --- src/GraphQLParser/ParserContext.cs | 156 ++++++++++++++--------------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/src/GraphQLParser/ParserContext.cs b/src/GraphQLParser/ParserContext.cs index fd451d00..42968a35 100644 --- a/src/GraphQLParser/ParserContext.cs +++ b/src/GraphQLParser/ParserContext.cs @@ -1,14 +1,14 @@ -using GraphQLParser.AST; -using GraphQLParser.Exceptions; -using System; -using System.Collections.Generic; +using GraphQLParser.AST; +using GraphQLParser.Exceptions; +using System; +using System.Collections.Generic; using System.Linq; - + namespace GraphQLParser { // WARNING: mutable struct, pass it by reference to those methods that will change it internal struct ParserContext : IDisposable - { + { private delegate TResult ParseCallback(ref ParserContext context); private readonly ILexer _lexer; @@ -240,8 +240,8 @@ private GraphQLArgument ParseArgument() private List? ParseArgumentDefs() { - return Peek(TokenKind.PAREN_L) - ? Many(TokenKind.PAREN_L, (ref ParserContext context) => context.ParseInputValueDef(), TokenKind.PAREN_R) + return Peek(TokenKind.PAREN_L) + ? Many(TokenKind.PAREN_L, (ref ParserContext context) => context.ParseInputValueDef(), TokenKind.PAREN_R) : null; } @@ -310,19 +310,19 @@ private List ParseDefinitionsIfNotEOF() text.Add(_currentToken.Value); end = _currentToken.End; Advance(); - } + } while (_currentToken.Kind == TokenKind.COMMENT); - var comment = new GraphQLComment(string.Join(Environment.NewLine, text)) - { - Location = new GraphQLLocation - ( - start, - end - ) + var comment = new GraphQLComment(string.Join(Environment.NewLine, text)) + { + Location = new GraphQLLocation + ( + start, + end + ) }; - if (_comments == null) + if (_comments == null) _comments = new Stack(); _comments.Push(comment); @@ -355,14 +355,14 @@ private GraphQLDirective ParseDirective() Arguments = ParseArguments(), Location = GetLocation(start) }; - } - - /// - /// http://spec.graphql.org/draft/#DirectiveDefinition - /// DirectiveDefinition: - /// Description(opt) directive @ Name ArgumentsDefinition(opt) repeatable(opt) on DirectiveLocations - /// /// + } + + /// + /// http://spec.graphql.org/draft/#DirectiveDefinition + /// DirectiveDefinition: + /// Description(opt) directive @ Name ArgumentsDefinition(opt) repeatable(opt) on DirectiveLocations + /// /// private GraphQLDirectiveDefinition ParseDirectiveDefinition(GraphQLDescription? description) { @@ -388,32 +388,32 @@ private GraphQLDirectiveDefinition ParseDirectiveDefinition(GraphQLDescription? Locations = locations, Location = GetLocation(start) }; - } - - private bool ParseRepeatable() - { - if (Peek(TokenKind.NAME)) - { - switch (_currentToken.Value) - { - case "repeatable": - Advance(); - return true; - case "on": - return false; - default: - throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start); - } - } - - return false; + } + + private bool ParseRepeatable() + { + if (Peek(TokenKind.NAME)) + { + switch (_currentToken.Value) + { + case "repeatable": + Advance(); + return true; + case "on": + return false; + default: + throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start); + } + } + + return false; } private List ParseDirectiveLocations() { - var locations = new List(); - - // Directive locations may be defined with an optional leading | character + var locations = new List(); + + // Directive locations may be defined with an optional leading | character // to aid formatting when representing a longer list of possible locations Skip(TokenKind.PIPE); @@ -508,7 +508,7 @@ private GraphQLFieldDefinition ParseFieldDefinition() } private GraphQLFieldSelection ParseFieldSelection() - { + { var comment = GetComment(); int start = _currentToken.Start; var nameOrAlias = ParseName(); @@ -546,8 +546,8 @@ private ASTNode ParseFragment() int start = _currentToken.Start; Expect(TokenKind.SPREAD); - return Peek(TokenKind.NAME) && !"on".Equals(_currentToken.Value) - ? CreateGraphQLFragmentSpread(start, comment) + return Peek(TokenKind.NAME) && !"on".Equals(_currentToken.Value) + ? CreateGraphQLFragmentSpread(start, comment) : CreateInlineFragment(start, comment); } @@ -665,9 +665,9 @@ private GraphQLInterfaceTypeDefinition ParseInterfaceTypeDefinition(GraphQLDescr private GraphQLValue ParseList(bool isConstant) { - int start = _currentToken.Start; - // the compiler caches these delegates in the generated code - ParseCallback constant = (ref ParserContext context) => context.ParseConstantValue(); + int start = _currentToken.Start; + // the compiler caches these delegates in the generated code + ParseCallback constant = (ref ParserContext context) => context.ParseConstantValue(); ParseCallback value = (ref ParserContext context) => context.ParseValueValue(); return new GraphQLListValue(ASTNodeKind.ListValue) @@ -739,15 +739,15 @@ private GraphQLValue ParseNameValue(bool isConstant) { var token = _currentToken; - if ("true".Equals(token.Value) || "false".Equals(token.Value)) - { - return ParseBooleanValue(token); - } + if ("true".Equals(token.Value) || "false".Equals(token.Value)) + { + return ParseBooleanValue(token); + } else if (token.Value != null) - { - return token.Value.Equals("null") - ? ParseNullValue(token) - : ParseEnumValue(token); + { + return token.Value.Equals("null") + ? ParseNullValue(token) + : ParseEnumValue(token); } throw new GraphQLSyntaxErrorException( @@ -837,7 +837,7 @@ private OperationType ParseOperationType() return token.Value switch { "mutation" => OperationType.Mutation, - "subscription" => OperationType.Subscription, + "subscription" => OperationType.Subscription, _ => OperationType.Query }; } @@ -940,12 +940,12 @@ private GraphQLType ParseType() type = ParseNamedType(); } - return Skip(TokenKind.BANG) + return Skip(TokenKind.BANG) ? new GraphQLNonNullType { Type = type, Location = GetLocation(start) - } + } : type; } @@ -967,10 +967,10 @@ private GraphQLTypeExtensionDefinition ParseTypeExtensionDefinition() private List ParseUnionMembers() { - var members = new List(); - - // Union members may be defined with an optional leading | character - // to aid formatting when representing a longer list of possible types + var members = new List(); + + // Union members may be defined with an optional leading | character + // to aid formatting when representing a longer list of possible types Skip(TokenKind.PIPE); do @@ -1003,16 +1003,16 @@ private GraphQLUnionTypeDefinition ParseUnionTypeDefinition(GraphQLDescription? }; } - private GraphQLValue ParseValueLiteral(bool isConstant) => _currentToken.Kind switch - { - TokenKind.BRACKET_L => ParseList(isConstant), - TokenKind.BRACE_L => ParseObject(isConstant), - TokenKind.INT => ParseInt(isConstant), - TokenKind.FLOAT => ParseFloat(isConstant), - TokenKind.STRING => ParseString(isConstant), - TokenKind.NAME => ParseNameValue(isConstant), - TokenKind.DOLLAR when !isConstant => ParseVariable(), - _ => throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) + private GraphQLValue ParseValueLiteral(bool isConstant) => _currentToken.Kind switch + { + TokenKind.BRACKET_L => ParseList(isConstant), + TokenKind.BRACE_L => ParseObject(isConstant), + TokenKind.INT => ParseInt(isConstant), + TokenKind.FLOAT => ParseFloat(isConstant), + TokenKind.STRING => ParseString(isConstant), + TokenKind.NAME => ParseNameValue(isConstant), + TokenKind.DOLLAR when !isConstant => ParseVariable(), + _ => throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) }; private GraphQLValue ParseValueValue() => ParseValueLiteral(false); @@ -1030,7 +1030,7 @@ private GraphQLVariable ParseVariable() } private GraphQLVariableDefinition ParseVariableDefinition() - { + { var comment = GetComment(); int start = _currentToken.Start; From 22060e2cb48d16a417399bdeda15ec15fac25d2f Mon Sep 17 00:00:00 2001 From: Serhii Almazov Date: Sat, 11 Apr 2020 09:45:18 +0300 Subject: [PATCH 8/8] Make ParseNamedDocumentedDefinition more explicit about whether it needs to read the description --- src/GraphQLParser/ParserContext.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/GraphQLParser/ParserContext.cs b/src/GraphQLParser/ParserContext.cs index 42968a35..eebcd888 100644 --- a/src/GraphQLParser/ParserContext.cs +++ b/src/GraphQLParser/ParserContext.cs @@ -271,7 +271,7 @@ private ASTNode ParseDefinition() return _currentToken.Kind switch { TokenKind.BRACE_L => ParseOperationDefinition(), - TokenKind.STRING => ParseNamedDocumentedDefinition(throwOnMissing: true)!, + TokenKind.STRING => ParseNamedDocumentedDefinition(hasDescription: true)!, TokenKind.NAME => ParseNamedDefinition(), _ => throw new GraphQLSyntaxErrorException( $"Unexpected {_currentToken}", _source, _currentToken.Start) @@ -700,13 +700,18 @@ private ASTNode ParseNamedDefinition() => "subscription" => ParseOperationDefinition(), "fragment" => ParseFragmentDefinition(), "extend" => ParseTypeExtensionDefinition(), - _ => ParseNamedDocumentedDefinition() + _ => ParseNamedDocumentedDefinition(hasDescription: false) ?? throw new GraphQLSyntaxErrorException($"Unexpected {_currentToken}", _source, _currentToken.Start) }; - private ASTNode? ParseNamedDocumentedDefinition(bool throwOnMissing = false) + /// + /// Parses the named definitions which may contain a description. + /// + /// Specifies whether the current position is at the string literal which should be parsed as a description. + /// Parsed node. + private ASTNode? ParseNamedDocumentedDefinition(bool hasDescription) { - var description = ParseDescription(); + var description = hasDescription ? ParseDescription() : null; return _currentToken.Value switch { @@ -718,7 +723,7 @@ private ASTNode ParseNamedDefinition() => "enum" => ParseEnumTypeDefinition(description), "input" => ParseInputObjectTypeDefinition(description), "directive" => ParseDirectiveDefinition(description), - _ => throwOnMissing + _ => hasDescription ? throw new GraphQLSyntaxErrorException( $"Unexpected {_currentToken}. Expecting schema, scalar, type, interface, union, enum, input or directive.", _source, _currentToken.Start) : (ASTNode?)null