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}