diff --git a/graphql_test.go b/graphql_test.go index 8e581afb..b2b07825 100644 --- a/graphql_test.go +++ b/graphql_test.go @@ -581,6 +581,10 @@ func TestIncludeDirective(t *testing.T) { type testDeprecatedDirectiveResolver struct{} +func (r *testDeprecatedDirectiveResolver) Test() string { + return "" +} + func (r *testDeprecatedDirectiveResolver) A() int32 { return 0 } @@ -643,6 +647,7 @@ func TestDeprecatedDirective(t *testing.T) { } type Query { + test: String! } enum Test { diff --git a/internal/common/arguments.go b/internal/common/arguments.go new file mode 100644 index 00000000..309ae7b4 --- /dev/null +++ b/internal/common/arguments.go @@ -0,0 +1,38 @@ +package common + +type Argument struct { + Name Ident + Value Literal +} + +type ArgumentList []Argument + +func (l ArgumentList) Get(name string) (Literal, bool) { + for _, arg := range l { + if arg.Name.Name == name { + return arg.Value, true + } + } + return nil, false +} + +func (l ArgumentList) MustGet(name string) Literal { + value, ok := l.Get(name) + if !ok { + panic("argument not found") + } + return value +} + +func ParseArguments(l *Lexer) ArgumentList { + var args ArgumentList + l.ConsumeToken('(') + for l.Peek() != ')' { + name := l.ConsumeIdentWithLoc() + l.ConsumeToken(':') + value := ParseLiteral(l, false) + args = append(args, Argument{Name: name, Value: value}) + } + l.ConsumeToken(')') + return args +} diff --git a/internal/common/values.go b/internal/common/input_values.go similarity index 52% rename from internal/common/values.go rename to internal/common/input_values.go index 794f68de..5d70f28e 100644 --- a/internal/common/values.go +++ b/internal/common/input_values.go @@ -1,6 +1,8 @@ package common import ( + "fmt" + "github.com/neelance/graphql-go/errors" ) @@ -39,39 +41,27 @@ func ParseInputValue(l *Lexer) *InputValue { return p } -type Argument struct { - Name Ident - Value Literal -} - -type ArgumentList []Argument - -func (l ArgumentList) Get(name string) (Literal, bool) { - for _, arg := range l { - if arg.Name.Name == name { - return arg.Value, true +func ParseArgumentDeclList(l *Lexer) InputValueList { + var args InputValueList + if l.Peek() == '(' { + l.ConsumeToken('(') + for l.Peek() != ')' { + args = append(args, ParseInputValue(l)) } + l.ConsumeToken(')') } - return nil, false + return args } -func (l ArgumentList) MustGet(name string) Literal { - value, ok := l.Get(name) - if !ok { - panic("argument not found") +func ParseInputFieldList(typeName string, l *Lexer) InputValueList { + l.ConsumeToken('{') + var list InputValueList + for l.Peek() != '}' { + list = append(list, ParseInputValue(l)) } - return value -} - -func ParseArguments(l *Lexer) ArgumentList { - var args ArgumentList - l.ConsumeToken('(') - for l.Peek() != ')' { - name := l.ConsumeIdentWithLoc() - l.ConsumeToken(':') - value := ParseLiteral(l, false) - args = append(args, Argument{Name: name, Value: value}) + if len(list) == 0 { + l.SyntaxError(fmt.Sprintf(`input type %q must define one or more fields`, typeName)) } - l.ConsumeToken(')') - return args + l.ConsumeToken('}') + return list } diff --git a/internal/schema/schema.go b/internal/schema/schema.go index 0cada3a9..43d38b84 100644 --- a/internal/schema/schema.go +++ b/internal/schema/schema.go @@ -363,18 +363,14 @@ func parseObjectDecl(l *common.Lexer) *Object { } } } - l.ConsumeToken('{') - o.Fields = parseFields(l) - l.ConsumeToken('}') + o.Fields = parseFields("object", o.Name, l) return o } func parseInterfaceDecl(l *common.Lexer) *Interface { i := &Interface{} i.Name = l.ConsumeIdent() - l.ConsumeToken('{') - i.Fields = parseFields(l) - l.ConsumeToken('}') + i.Fields = parseFields("interface", i.Name, l) return i } @@ -393,11 +389,7 @@ func parseUnionDecl(l *common.Lexer) *Union { func parseInputDecl(l *common.Lexer) *InputObject { i := &InputObject{} i.Name = l.ConsumeIdent() - l.ConsumeToken('{') - for l.Peek() != '}' { - i.Values = append(i.Values, common.ParseInputValue(l)) - } - l.ConsumeToken('}') + i.Values = common.ParseInputFieldList(i.Name, l) return i } @@ -420,14 +412,7 @@ func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { d := &DirectiveDecl{} l.ConsumeToken('@') d.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - v := common.ParseInputValue(l) - d.Args = append(d.Args, v) - } - l.ConsumeToken(')') - } + d.Args = common.ParseArgumentDeclList(l) l.ConsumeKeyword("on") for { loc := l.ConsumeIdent() @@ -440,23 +425,22 @@ func parseDirectiveDecl(l *common.Lexer) *DirectiveDecl { return d } -func parseFields(l *common.Lexer) FieldList { +func parseFields(declType, typeName string, l *common.Lexer) FieldList { var fields FieldList + l.ConsumeToken('{') for l.Peek() != '}' { f := &Field{} f.Desc = l.DescComment() f.Name = l.ConsumeIdent() - if l.Peek() == '(' { - l.ConsumeToken('(') - for l.Peek() != ')' { - f.Args = append(f.Args, common.ParseInputValue(l)) - } - l.ConsumeToken(')') - } + f.Args = common.ParseArgumentDeclList(l) l.ConsumeToken(':') f.Type = common.ParseType(l) f.Directives = common.ParseDirectives(l) fields = append(fields, f) } + if len(fields) == 0 { + l.SyntaxError(fmt.Sprintf(`%s type %q must define one or more fields`, declType, typeName)) + } + l.ConsumeToken('}') return fields }