validation.go

  1package validation
  2
  3import (
  4	"fmt"
  5	"math"
  6	"reflect"
  7	"strconv"
  8	"strings"
  9	"text/scanner"
 10
 11	"github.com/vektah/gqlgen/neelance/common"
 12	"github.com/vektah/gqlgen/neelance/errors"
 13	"github.com/vektah/gqlgen/neelance/query"
 14	"github.com/vektah/gqlgen/neelance/schema"
 15)
 16
 17type varSet map[*common.InputValue]struct{}
 18
 19type selectionPair struct{ a, b query.Selection }
 20
 21type fieldInfo struct {
 22	sf     *schema.Field
 23	parent schema.NamedType
 24}
 25
 26type context struct {
 27	schema           *schema.Schema
 28	doc              *query.Document
 29	errs             []*errors.QueryError
 30	opErrs           map[*query.Operation][]*errors.QueryError
 31	usedVars         map[*query.Operation]varSet
 32	fieldMap         map[*query.Field]fieldInfo
 33	overlapValidated map[selectionPair]struct{}
 34}
 35
 36func (c *context) addErr(loc errors.Location, rule string, format string, a ...interface{}) {
 37	c.addErrMultiLoc([]errors.Location{loc}, rule, format, a...)
 38}
 39
 40func (c *context) addErrMultiLoc(locs []errors.Location, rule string, format string, a ...interface{}) {
 41	c.errs = append(c.errs, &errors.QueryError{
 42		Message:   fmt.Sprintf(format, a...),
 43		Locations: locs,
 44		Rule:      rule,
 45	})
 46}
 47
 48type opContext struct {
 49	*context
 50	ops []*query.Operation
 51}
 52
 53func Validate(s *schema.Schema, doc *query.Document) []*errors.QueryError {
 54	c := &context{
 55		schema:           s,
 56		doc:              doc,
 57		opErrs:           make(map[*query.Operation][]*errors.QueryError),
 58		usedVars:         make(map[*query.Operation]varSet),
 59		fieldMap:         make(map[*query.Field]fieldInfo),
 60		overlapValidated: make(map[selectionPair]struct{}),
 61	}
 62
 63	opNames := make(nameSet)
 64	fragUsedBy := make(map[*query.FragmentDecl][]*query.Operation)
 65	for _, op := range doc.Operations {
 66		c.usedVars[op] = make(varSet)
 67		opc := &opContext{c, []*query.Operation{op}}
 68
 69		if op.Name.Name == "" && len(doc.Operations) != 1 {
 70			c.addErr(op.Loc, "LoneAnonymousOperation", "This anonymous operation must be the only defined operation.")
 71		}
 72		if op.Name.Name != "" {
 73			validateName(c, opNames, op.Name, "UniqueOperationNames", "operation")
 74		}
 75
 76		validateDirectives(opc, string(op.Type), op.Directives)
 77
 78		varNames := make(nameSet)
 79		for _, v := range op.Vars {
 80			validateName(c, varNames, v.Name, "UniqueVariableNames", "variable")
 81
 82			t := resolveType(c, v.Type)
 83			if !canBeInput(t) {
 84				c.addErr(v.TypeLoc, "VariablesAreInputTypes", "Variable %q cannot be non-input type %q.", "$"+v.Name.Name, t)
 85			}
 86
 87			if v.Default != nil {
 88				validateLiteral(opc, v.Default)
 89
 90				if t != nil {
 91					if nn, ok := t.(*common.NonNull); ok {
 92						c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q is required and will not use the default value. Perhaps you meant to use type %q.", "$"+v.Name.Name, t, nn.OfType)
 93					}
 94
 95					if ok, reason := validateValueType(opc, v.Default, t); !ok {
 96						c.addErr(v.Default.Location(), "DefaultValuesOfCorrectType", "Variable %q of type %q has invalid default value %s.\n%s", "$"+v.Name.Name, t, v.Default, reason)
 97					}
 98				}
 99			}
100		}
101
102		var entryPoint schema.NamedType
103		switch op.Type {
104		case query.Query:
105			entryPoint = s.EntryPoints["query"]
106		case query.Mutation:
107			entryPoint = s.EntryPoints["mutation"]
108		case query.Subscription:
109			entryPoint = s.EntryPoints["subscription"]
110		default:
111			panic("unreachable")
112		}
113
114		validateSelectionSet(opc, op.Selections, entryPoint)
115
116		fragUsed := make(map[*query.FragmentDecl]struct{})
117		markUsedFragments(c, op.Selections, fragUsed)
118		for frag := range fragUsed {
119			fragUsedBy[frag] = append(fragUsedBy[frag], op)
120		}
121	}
122
123	fragNames := make(nameSet)
124	fragVisited := make(map[*query.FragmentDecl]struct{})
125	for _, frag := range doc.Fragments {
126		opc := &opContext{c, fragUsedBy[frag]}
127
128		validateName(c, fragNames, frag.Name, "UniqueFragmentNames", "fragment")
129		validateDirectives(opc, "FRAGMENT_DEFINITION", frag.Directives)
130
131		t := unwrapType(resolveType(c, &frag.On))
132		// continue even if t is nil
133		if t != nil && !canBeFragment(t) {
134			c.addErr(frag.On.Loc, "FragmentsOnCompositeTypes", "Fragment %q cannot condition on non composite type %q.", frag.Name.Name, t)
135			continue
136		}
137
138		validateSelectionSet(opc, frag.Selections, t)
139
140		if _, ok := fragVisited[frag]; !ok {
141			detectFragmentCycle(c, frag.Selections, fragVisited, nil, map[string]int{frag.Name.Name: 0})
142		}
143	}
144
145	for _, frag := range doc.Fragments {
146		if len(fragUsedBy[frag]) == 0 {
147			c.addErr(frag.Loc, "NoUnusedFragments", "Fragment %q is never used.", frag.Name.Name)
148		}
149	}
150
151	for _, op := range doc.Operations {
152		c.errs = append(c.errs, c.opErrs[op]...)
153
154		opUsedVars := c.usedVars[op]
155		for _, v := range op.Vars {
156			if _, ok := opUsedVars[v]; !ok {
157				opSuffix := ""
158				if op.Name.Name != "" {
159					opSuffix = fmt.Sprintf(" in operation %q", op.Name.Name)
160				}
161				c.addErr(v.Loc, "NoUnusedVariables", "Variable %q is never used%s.", "$"+v.Name.Name, opSuffix)
162			}
163		}
164	}
165
166	return c.errs
167}
168
169func validateSelectionSet(c *opContext, sels []query.Selection, t schema.NamedType) {
170	for _, sel := range sels {
171		validateSelection(c, sel, t)
172	}
173
174	for i, a := range sels {
175		for _, b := range sels[i+1:] {
176			c.validateOverlap(a, b, nil, nil)
177		}
178	}
179}
180
181func validateSelection(c *opContext, sel query.Selection, t schema.NamedType) {
182	switch sel := sel.(type) {
183	case *query.Field:
184		validateDirectives(c, "FIELD", sel.Directives)
185
186		fieldName := sel.Name.Name
187		var f *schema.Field
188		switch fieldName {
189		case "__typename":
190			f = &schema.Field{
191				Name: "__typename",
192				Type: c.schema.Types["String"],
193			}
194		case "__schema":
195			f = &schema.Field{
196				Name: "__schema",
197				Type: c.schema.Types["__Schema"],
198			}
199		case "__type":
200			f = &schema.Field{
201				Name: "__type",
202				Args: common.InputValueList{
203					&common.InputValue{
204						Name: common.Ident{Name: "name"},
205						Type: &common.NonNull{OfType: c.schema.Types["String"]},
206					},
207				},
208				Type: c.schema.Types["__Type"],
209			}
210		default:
211			f = fields(t).Get(fieldName)
212			if f == nil && t != nil {
213				suggestion := makeSuggestion("Did you mean", fields(t).Names(), fieldName)
214				c.addErr(sel.Alias.Loc, "FieldsOnCorrectType", "Cannot query field %q on type %q.%s", fieldName, t, suggestion)
215			}
216		}
217		c.fieldMap[sel] = fieldInfo{sf: f, parent: t}
218
219		validateArgumentLiterals(c, sel.Arguments)
220		if f != nil {
221			validateArgumentTypes(c, sel.Arguments, f.Args, sel.Alias.Loc,
222				func() string { return fmt.Sprintf("field %q of type %q", fieldName, t) },
223				func() string { return fmt.Sprintf("Field %q", fieldName) },
224			)
225		}
226
227		var ft common.Type
228		if f != nil {
229			ft = f.Type
230			sf := hasSubfields(ft)
231			if sf && sel.Selections == nil {
232				c.addErr(sel.Alias.Loc, "ScalarLeafs", "Field %q of type %q must have a selection of subfields. Did you mean \"%s { ... }\"?", fieldName, ft, fieldName)
233			}
234			if !sf && sel.Selections != nil {
235				c.addErr(sel.SelectionSetLoc, "ScalarLeafs", "Field %q must not have a selection since type %q has no subfields.", fieldName, ft)
236			}
237		}
238		if sel.Selections != nil {
239			validateSelectionSet(c, sel.Selections, unwrapType(ft))
240		}
241
242	case *query.InlineFragment:
243		validateDirectives(c, "INLINE_FRAGMENT", sel.Directives)
244		if sel.On.Name != "" {
245			fragTyp := unwrapType(resolveType(c.context, &sel.On))
246			if fragTyp != nil && !compatible(t, fragTyp) {
247				c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment cannot be spread here as objects of type %q can never be of type %q.", t, fragTyp)
248			}
249			t = fragTyp
250			// continue even if t is nil
251		}
252		if t != nil && !canBeFragment(t) {
253			c.addErr(sel.On.Loc, "FragmentsOnCompositeTypes", "Fragment cannot condition on non composite type %q.", t)
254			return
255		}
256		validateSelectionSet(c, sel.Selections, unwrapType(t))
257
258	case *query.FragmentSpread:
259		validateDirectives(c, "FRAGMENT_SPREAD", sel.Directives)
260		frag := c.doc.Fragments.Get(sel.Name.Name)
261		if frag == nil {
262			c.addErr(sel.Name.Loc, "KnownFragmentNames", "Unknown fragment %q.", sel.Name.Name)
263			return
264		}
265		fragTyp := c.schema.Types[frag.On.Name]
266		if !compatible(t, fragTyp) {
267			c.addErr(sel.Loc, "PossibleFragmentSpreads", "Fragment %q cannot be spread here as objects of type %q can never be of type %q.", frag.Name.Name, t, fragTyp)
268		}
269
270	default:
271		panic("unreachable")
272	}
273}
274
275func compatible(a, b common.Type) bool {
276	for _, pta := range possibleTypes(a) {
277		for _, ptb := range possibleTypes(b) {
278			if pta == ptb {
279				return true
280			}
281		}
282	}
283	return false
284}
285
286func possibleTypes(t common.Type) []*schema.Object {
287	switch t := t.(type) {
288	case *schema.Object:
289		return []*schema.Object{t}
290	case *schema.Interface:
291		return t.PossibleTypes
292	case *schema.Union:
293		return t.PossibleTypes
294	default:
295		return nil
296	}
297}
298
299func markUsedFragments(c *context, sels []query.Selection, fragUsed map[*query.FragmentDecl]struct{}) {
300	for _, sel := range sels {
301		switch sel := sel.(type) {
302		case *query.Field:
303			if sel.Selections != nil {
304				markUsedFragments(c, sel.Selections, fragUsed)
305			}
306
307		case *query.InlineFragment:
308			markUsedFragments(c, sel.Selections, fragUsed)
309
310		case *query.FragmentSpread:
311			frag := c.doc.Fragments.Get(sel.Name.Name)
312			if frag == nil {
313				return
314			}
315
316			if _, ok := fragUsed[frag]; ok {
317				return
318			}
319			fragUsed[frag] = struct{}{}
320			markUsedFragments(c, frag.Selections, fragUsed)
321
322		default:
323			panic("unreachable")
324		}
325	}
326}
327
328func detectFragmentCycle(c *context, sels []query.Selection, fragVisited map[*query.FragmentDecl]struct{}, spreadPath []*query.FragmentSpread, spreadPathIndex map[string]int) {
329	for _, sel := range sels {
330		detectFragmentCycleSel(c, sel, fragVisited, spreadPath, spreadPathIndex)
331	}
332}
333
334func detectFragmentCycleSel(c *context, sel query.Selection, fragVisited map[*query.FragmentDecl]struct{}, spreadPath []*query.FragmentSpread, spreadPathIndex map[string]int) {
335	switch sel := sel.(type) {
336	case *query.Field:
337		if sel.Selections != nil {
338			detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex)
339		}
340
341	case *query.InlineFragment:
342		detectFragmentCycle(c, sel.Selections, fragVisited, spreadPath, spreadPathIndex)
343
344	case *query.FragmentSpread:
345		frag := c.doc.Fragments.Get(sel.Name.Name)
346		if frag == nil {
347			return
348		}
349
350		spreadPath = append(spreadPath, sel)
351		if i, ok := spreadPathIndex[frag.Name.Name]; ok {
352			cyclePath := spreadPath[i:]
353			via := ""
354			if len(cyclePath) > 1 {
355				names := make([]string, len(cyclePath)-1)
356				for i, frag := range cyclePath[:len(cyclePath)-1] {
357					names[i] = frag.Name.Name
358				}
359				via = " via " + strings.Join(names, ", ")
360			}
361
362			locs := make([]errors.Location, len(cyclePath))
363			for i, frag := range cyclePath {
364				locs[i] = frag.Loc
365			}
366			c.addErrMultiLoc(locs, "NoFragmentCycles", "Cannot spread fragment %q within itself%s.", frag.Name.Name, via)
367			return
368		}
369
370		if _, ok := fragVisited[frag]; ok {
371			return
372		}
373		fragVisited[frag] = struct{}{}
374
375		spreadPathIndex[frag.Name.Name] = len(spreadPath)
376		detectFragmentCycle(c, frag.Selections, fragVisited, spreadPath, spreadPathIndex)
377		delete(spreadPathIndex, frag.Name.Name)
378
379	default:
380		panic("unreachable")
381	}
382}
383
384func (c *context) validateOverlap(a, b query.Selection, reasons *[]string, locs *[]errors.Location) {
385	if a == b {
386		return
387	}
388
389	if _, ok := c.overlapValidated[selectionPair{a, b}]; ok {
390		return
391	}
392	c.overlapValidated[selectionPair{a, b}] = struct{}{}
393	c.overlapValidated[selectionPair{b, a}] = struct{}{}
394
395	switch a := a.(type) {
396	case *query.Field:
397		switch b := b.(type) {
398		case *query.Field:
399			if b.Alias.Loc.Before(a.Alias.Loc) {
400				a, b = b, a
401			}
402			if reasons2, locs2 := c.validateFieldOverlap(a, b); len(reasons2) != 0 {
403				locs2 = append(locs2, a.Alias.Loc, b.Alias.Loc)
404				if reasons == nil {
405					c.addErrMultiLoc(locs2, "OverlappingFieldsCanBeMerged", "Fields %q conflict because %s. Use different aliases on the fields to fetch both if this was intentional.", a.Alias.Name, strings.Join(reasons2, " and "))
406					return
407				}
408				for _, r := range reasons2 {
409					*reasons = append(*reasons, fmt.Sprintf("subfields %q conflict because %s", a.Alias.Name, r))
410				}
411				*locs = append(*locs, locs2...)
412			}
413
414		case *query.InlineFragment:
415			for _, sel := range b.Selections {
416				c.validateOverlap(a, sel, reasons, locs)
417			}
418
419		case *query.FragmentSpread:
420			if frag := c.doc.Fragments.Get(b.Name.Name); frag != nil {
421				for _, sel := range frag.Selections {
422					c.validateOverlap(a, sel, reasons, locs)
423				}
424			}
425
426		default:
427			panic("unreachable")
428		}
429
430	case *query.InlineFragment:
431		for _, sel := range a.Selections {
432			c.validateOverlap(sel, b, reasons, locs)
433		}
434
435	case *query.FragmentSpread:
436		if frag := c.doc.Fragments.Get(a.Name.Name); frag != nil {
437			for _, sel := range frag.Selections {
438				c.validateOverlap(sel, b, reasons, locs)
439			}
440		}
441
442	default:
443		panic("unreachable")
444	}
445}
446
447func (c *context) validateFieldOverlap(a, b *query.Field) ([]string, []errors.Location) {
448	if a.Alias.Name != b.Alias.Name {
449		return nil, nil
450	}
451
452	if asf := c.fieldMap[a].sf; asf != nil {
453		if bsf := c.fieldMap[b].sf; bsf != nil {
454			if !typesCompatible(asf.Type, bsf.Type) {
455				return []string{fmt.Sprintf("they return conflicting types %s and %s", asf.Type, bsf.Type)}, nil
456			}
457		}
458	}
459
460	at := c.fieldMap[a].parent
461	bt := c.fieldMap[b].parent
462	if at == nil || bt == nil || at == bt {
463		if a.Name.Name != b.Name.Name {
464			return []string{fmt.Sprintf("%s and %s are different fields", a.Name.Name, b.Name.Name)}, nil
465		}
466
467		if argumentsConflict(a.Arguments, b.Arguments) {
468			return []string{"they have differing arguments"}, nil
469		}
470	}
471
472	var reasons []string
473	var locs []errors.Location
474	for _, a2 := range a.Selections {
475		for _, b2 := range b.Selections {
476			c.validateOverlap(a2, b2, &reasons, &locs)
477		}
478	}
479	return reasons, locs
480}
481
482func argumentsConflict(a, b common.ArgumentList) bool {
483	if len(a) != len(b) {
484		return true
485	}
486	for _, argA := range a {
487		valB, ok := b.Get(argA.Name.Name)
488		if !ok || !reflect.DeepEqual(argA.Value.Value(nil), valB.Value(nil)) {
489			return true
490		}
491	}
492	return false
493}
494
495func fields(t common.Type) schema.FieldList {
496	switch t := t.(type) {
497	case *schema.Object:
498		return t.Fields
499	case *schema.Interface:
500		return t.Fields
501	default:
502		return nil
503	}
504}
505
506func unwrapType(t common.Type) schema.NamedType {
507	if t == nil {
508		return nil
509	}
510	for {
511		switch t2 := t.(type) {
512		case schema.NamedType:
513			return t2
514		case *common.List:
515			t = t2.OfType
516		case *common.NonNull:
517			t = t2.OfType
518		default:
519			panic("unreachable")
520		}
521	}
522}
523
524func resolveType(c *context, t common.Type) common.Type {
525	t2, err := common.ResolveType(t, c.schema.Resolve)
526	if err != nil {
527		c.errs = append(c.errs, err)
528	}
529	return t2
530}
531
532func validateDirectives(c *opContext, loc string, directives common.DirectiveList) {
533	directiveNames := make(nameSet)
534	for _, d := range directives {
535		dirName := d.Name.Name
536		validateNameCustomMsg(c.context, directiveNames, d.Name, "UniqueDirectivesPerLocation", func() string {
537			return fmt.Sprintf("The directive %q can only be used once at this location.", dirName)
538		})
539
540		validateArgumentLiterals(c, d.Args)
541
542		dd, ok := c.schema.Directives[dirName]
543		if !ok {
544			c.addErr(d.Name.Loc, "KnownDirectives", "Unknown directive %q.", dirName)
545			continue
546		}
547
548		locOK := false
549		for _, allowedLoc := range dd.Locs {
550			if loc == allowedLoc {
551				locOK = true
552				break
553			}
554		}
555		if !locOK {
556			c.addErr(d.Name.Loc, "KnownDirectives", "Directive %q may not be used on %s.", dirName, loc)
557		}
558
559		validateArgumentTypes(c, d.Args, dd.Args, d.Name.Loc,
560			func() string { return fmt.Sprintf("directive %q", "@"+dirName) },
561			func() string { return fmt.Sprintf("Directive %q", "@"+dirName) },
562		)
563	}
564}
565
566type nameSet map[string]errors.Location
567
568func validateName(c *context, set nameSet, name common.Ident, rule string, kind string) {
569	validateNameCustomMsg(c, set, name, rule, func() string {
570		return fmt.Sprintf("There can be only one %s named %q.", kind, name.Name)
571	})
572}
573
574func validateNameCustomMsg(c *context, set nameSet, name common.Ident, rule string, msg func() string) {
575	if loc, ok := set[name.Name]; ok {
576		c.addErrMultiLoc([]errors.Location{loc, name.Loc}, rule, msg())
577		return
578	}
579	set[name.Name] = name.Loc
580}
581
582func validateArgumentTypes(c *opContext, args common.ArgumentList, argDecls common.InputValueList, loc errors.Location, owner1, owner2 func() string) {
583	for _, selArg := range args {
584		arg := argDecls.Get(selArg.Name.Name)
585		if arg == nil {
586			c.addErr(selArg.Name.Loc, "KnownArgumentNames", "Unknown argument %q on %s.", selArg.Name.Name, owner1())
587			continue
588		}
589		value := selArg.Value
590		if ok, reason := validateValueType(c, value, arg.Type); !ok {
591			c.addErr(value.Location(), "ArgumentsOfCorrectType", "Argument %q has invalid value %s.\n%s", arg.Name.Name, value, reason)
592		}
593	}
594	for _, decl := range argDecls {
595		if _, ok := decl.Type.(*common.NonNull); ok {
596			if _, ok := args.Get(decl.Name.Name); !ok {
597				c.addErr(loc, "ProvidedNonNullArguments", "%s argument %q of type %q is required but not provided.", owner2(), decl.Name.Name, decl.Type)
598			}
599		}
600	}
601}
602
603func validateArgumentLiterals(c *opContext, args common.ArgumentList) {
604	argNames := make(nameSet)
605	for _, arg := range args {
606		validateName(c.context, argNames, arg.Name, "UniqueArgumentNames", "argument")
607		validateLiteral(c, arg.Value)
608	}
609}
610
611func validateLiteral(c *opContext, l common.Literal) {
612	switch l := l.(type) {
613	case *common.ObjectLit:
614		fieldNames := make(nameSet)
615		for _, f := range l.Fields {
616			validateName(c.context, fieldNames, f.Name, "UniqueInputFieldNames", "input field")
617			validateLiteral(c, f.Value)
618		}
619	case *common.ListLit:
620		for _, entry := range l.Entries {
621			validateLiteral(c, entry)
622		}
623	case *common.Variable:
624		for _, op := range c.ops {
625			v := op.Vars.Get(l.Name)
626			if v == nil {
627				byOp := ""
628				if op.Name.Name != "" {
629					byOp = fmt.Sprintf(" by operation %q", op.Name.Name)
630				}
631				c.opErrs[op] = append(c.opErrs[op], &errors.QueryError{
632					Message:   fmt.Sprintf("Variable %q is not defined%s.", "$"+l.Name, byOp),
633					Locations: []errors.Location{l.Loc, op.Loc},
634					Rule:      "NoUndefinedVariables",
635				})
636				continue
637			}
638			c.usedVars[op][v] = struct{}{}
639		}
640	}
641}
642
643func validateValueType(c *opContext, v common.Literal, t common.Type) (bool, string) {
644	if v, ok := v.(*common.Variable); ok {
645		for _, op := range c.ops {
646			if v2 := op.Vars.Get(v.Name); v2 != nil {
647				t2, err := common.ResolveType(v2.Type, c.schema.Resolve)
648				if _, ok := t2.(*common.NonNull); !ok && v2.Default != nil {
649					t2 = &common.NonNull{OfType: t2}
650				}
651				if err == nil && !typeCanBeUsedAs(t2, t) {
652					c.addErrMultiLoc([]errors.Location{v2.Loc, v.Loc}, "VariablesInAllowedPosition", "Variable %q of type %q used in position expecting type %q.", "$"+v.Name, t2, t)
653				}
654			}
655		}
656		return true, ""
657	}
658
659	if nn, ok := t.(*common.NonNull); ok {
660		if isNull(v) {
661			return false, fmt.Sprintf("Expected %q, found null.", t)
662		}
663		t = nn.OfType
664	}
665	if isNull(v) {
666		return true, ""
667	}
668
669	switch t := t.(type) {
670	case *schema.Scalar, *schema.Enum:
671		if lit, ok := v.(*common.BasicLit); ok {
672			if validateBasicLit(lit, t) {
673				return true, ""
674			}
675		} else {
676			// custom complex scalars will be validated when unmarshaling
677			return true, ""
678		}
679
680	case *common.List:
681		list, ok := v.(*common.ListLit)
682		if !ok {
683			return validateValueType(c, v, t.OfType) // single value instead of list
684		}
685		for i, entry := range list.Entries {
686			if ok, reason := validateValueType(c, entry, t.OfType); !ok {
687				return false, fmt.Sprintf("In element #%d: %s", i, reason)
688			}
689		}
690		return true, ""
691
692	case *schema.InputObject:
693		v, ok := v.(*common.ObjectLit)
694		if !ok {
695			return false, fmt.Sprintf("Expected %q, found not an object.", t)
696		}
697		for _, f := range v.Fields {
698			name := f.Name.Name
699			iv := t.Values.Get(name)
700			if iv == nil {
701				return false, fmt.Sprintf("In field %q: Unknown field.", name)
702			}
703			if ok, reason := validateValueType(c, f.Value, iv.Type); !ok {
704				return false, fmt.Sprintf("In field %q: %s", name, reason)
705			}
706		}
707		for _, iv := range t.Values {
708			found := false
709			for _, f := range v.Fields {
710				if f.Name.Name == iv.Name.Name {
711					found = true
712					break
713				}
714			}
715			if !found {
716				if _, ok := iv.Type.(*common.NonNull); ok && iv.Default == nil {
717					return false, fmt.Sprintf("In field %q: Expected %q, found null.", iv.Name.Name, iv.Type)
718				}
719			}
720		}
721		return true, ""
722	}
723
724	return false, fmt.Sprintf("Expected type %q, found %s.", t, v)
725}
726
727func validateBasicLit(v *common.BasicLit, t common.Type) bool {
728	switch t := t.(type) {
729	case *schema.Scalar:
730		switch t.Name {
731		case "Int":
732			if v.Type != scanner.Int {
733				return false
734			}
735			f, err := strconv.ParseFloat(v.Text, 64)
736			if err != nil {
737				panic(err)
738			}
739			return f >= math.MinInt32 && f <= math.MaxInt32
740		case "Float":
741			return v.Type == scanner.Int || v.Type == scanner.Float
742		case "String":
743			return v.Type == scanner.String
744		case "Boolean":
745			return v.Type == scanner.Ident && (v.Text == "true" || v.Text == "false")
746		case "ID":
747			return v.Type == scanner.Int || v.Type == scanner.String
748		default:
749			//TODO: Type-check against expected type by Unmarshaling
750			return true
751		}
752
753	case *schema.Enum:
754		if v.Type != scanner.Ident {
755			return false
756		}
757		for _, option := range t.Values {
758			if option.Name == v.Text {
759				return true
760			}
761		}
762		return false
763	}
764
765	return false
766}
767
768func canBeFragment(t common.Type) bool {
769	switch t.(type) {
770	case *schema.Object, *schema.Interface, *schema.Union:
771		return true
772	default:
773		return false
774	}
775}
776
777func canBeInput(t common.Type) bool {
778	switch t := t.(type) {
779	case *schema.InputObject, *schema.Scalar, *schema.Enum:
780		return true
781	case *common.List:
782		return canBeInput(t.OfType)
783	case *common.NonNull:
784		return canBeInput(t.OfType)
785	default:
786		return false
787	}
788}
789
790func hasSubfields(t common.Type) bool {
791	switch t := t.(type) {
792	case *schema.Object, *schema.Interface, *schema.Union:
793		return true
794	case *common.List:
795		return hasSubfields(t.OfType)
796	case *common.NonNull:
797		return hasSubfields(t.OfType)
798	default:
799		return false
800	}
801}
802
803func isLeaf(t common.Type) bool {
804	switch t.(type) {
805	case *schema.Scalar, *schema.Enum:
806		return true
807	default:
808		return false
809	}
810}
811
812func isNull(lit interface{}) bool {
813	_, ok := lit.(*common.NullLit)
814	return ok
815}
816
817func typesCompatible(a, b common.Type) bool {
818	al, aIsList := a.(*common.List)
819	bl, bIsList := b.(*common.List)
820	if aIsList || bIsList {
821		return aIsList && bIsList && typesCompatible(al.OfType, bl.OfType)
822	}
823
824	ann, aIsNN := a.(*common.NonNull)
825	bnn, bIsNN := b.(*common.NonNull)
826	if aIsNN || bIsNN {
827		return aIsNN && bIsNN && typesCompatible(ann.OfType, bnn.OfType)
828	}
829
830	if isLeaf(a) || isLeaf(b) {
831		return a == b
832	}
833
834	return true
835}
836
837func typeCanBeUsedAs(t, as common.Type) bool {
838	nnT, okT := t.(*common.NonNull)
839	if okT {
840		t = nnT.OfType
841	}
842
843	nnAs, okAs := as.(*common.NonNull)
844	if okAs {
845		as = nnAs.OfType
846		if !okT {
847			return false // nullable can not be used as non-null
848		}
849	}
850
851	if t == as {
852		return true
853	}
854
855	if lT, ok := t.(*common.List); ok {
856		if lAs, ok := as.(*common.List); ok {
857			return typeCanBeUsedAs(lT.OfType, lAs.OfType)
858		}
859	}
860	return false
861}