github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/internal/code/compare.go (about)

     1  package code
     2  
     3  import (
     4  	"fmt"
     5  	"go/types"
     6  )
     7  
     8  // CompatibleTypes isnt a strict comparison, it allows for pointer differences
     9  func CompatibleTypes(expected types.Type, actual types.Type) error {
    10  	//fmt.Println("Comparing ", expected.String(), actual.String())
    11  
    12  	// Special case to deal with pointer mismatches
    13  	{
    14  		expectedPtr, expectedIsPtr := expected.(*types.Pointer)
    15  		actualPtr, actualIsPtr := actual.(*types.Pointer)
    16  
    17  		if expectedIsPtr && actualIsPtr {
    18  			return CompatibleTypes(expectedPtr.Elem(), actualPtr.Elem())
    19  		}
    20  		if expectedIsPtr && !actualIsPtr {
    21  			return CompatibleTypes(expectedPtr.Elem(), actual)
    22  		}
    23  		if !expectedIsPtr && actualIsPtr {
    24  			return CompatibleTypes(expected, actualPtr.Elem())
    25  		}
    26  	}
    27  
    28  	switch expected := expected.(type) {
    29  	case *types.Slice:
    30  		if actual, ok := actual.(*types.Slice); ok {
    31  			return CompatibleTypes(expected.Elem(), actual.Elem())
    32  		}
    33  
    34  	case *types.Array:
    35  		if actual, ok := actual.(*types.Array); ok {
    36  			if expected.Len() != actual.Len() {
    37  				return fmt.Errorf("array length differs")
    38  			}
    39  
    40  			return CompatibleTypes(expected.Elem(), actual.Elem())
    41  		}
    42  
    43  	case *types.Basic:
    44  		if actual, ok := actual.(*types.Basic); ok {
    45  			if actual.Kind() != expected.Kind() {
    46  				return fmt.Errorf("basic kind differs, %s != %s", expected.Name(), actual.Name())
    47  			}
    48  
    49  			return nil
    50  		}
    51  
    52  	case *types.Struct:
    53  		if actual, ok := actual.(*types.Struct); ok {
    54  			if expected.NumFields() != actual.NumFields() {
    55  				return fmt.Errorf("number of struct fields differ")
    56  			}
    57  
    58  			for i := 0; i < expected.NumFields(); i++ {
    59  				if expected.Field(i).Name() != actual.Field(i).Name() {
    60  					return fmt.Errorf("struct field %d name differs, %s != %s", i, expected.Field(i).Name(), actual.Field(i).Name())
    61  				}
    62  				if err := CompatibleTypes(expected.Field(i).Type(), actual.Field(i).Type()); err != nil {
    63  					return err
    64  				}
    65  			}
    66  			return nil
    67  		}
    68  
    69  	case *types.Tuple:
    70  		if actual, ok := actual.(*types.Tuple); ok {
    71  			if expected.Len() != actual.Len() {
    72  				return fmt.Errorf("tuple length differs, %d != %d", expected.Len(), actual.Len())
    73  			}
    74  
    75  			for i := 0; i < expected.Len(); i++ {
    76  				if err := CompatibleTypes(expected.At(i).Type(), actual.At(i).Type()); err != nil {
    77  					return err
    78  				}
    79  			}
    80  
    81  			return nil
    82  		}
    83  
    84  	case *types.Signature:
    85  		if actual, ok := actual.(*types.Signature); ok {
    86  			if err := CompatibleTypes(expected.Params(), actual.Params()); err != nil {
    87  				return err
    88  			}
    89  			if err := CompatibleTypes(expected.Results(), actual.Results()); err != nil {
    90  				return err
    91  			}
    92  
    93  			return nil
    94  		}
    95  	case *types.Interface:
    96  		if actual, ok := actual.(*types.Interface); ok {
    97  			if expected.NumMethods() != actual.NumMethods() {
    98  				return fmt.Errorf("interface method count differs, %d != %d", expected.NumMethods(), actual.NumMethods())
    99  			}
   100  
   101  			for i := 0; i < expected.NumMethods(); i++ {
   102  				if expected.Method(i).Name() != actual.Method(i).Name() {
   103  					return fmt.Errorf("interface method %d name differs, %s != %s", i, expected.Method(i).Name(), actual.Method(i).Name())
   104  				}
   105  				if err := CompatibleTypes(expected.Method(i).Type(), actual.Method(i).Type()); err != nil {
   106  					return err
   107  				}
   108  			}
   109  
   110  			return nil
   111  		}
   112  
   113  	case *types.Map:
   114  		if actual, ok := actual.(*types.Map); ok {
   115  			if err := CompatibleTypes(expected.Key(), actual.Key()); err != nil {
   116  				return err
   117  			}
   118  
   119  			if err := CompatibleTypes(expected.Elem(), actual.Elem()); err != nil {
   120  				return err
   121  			}
   122  
   123  			return nil
   124  		}
   125  
   126  	case *types.Chan:
   127  		if actual, ok := actual.(*types.Chan); ok {
   128  			return CompatibleTypes(expected.Elem(), actual.Elem())
   129  		}
   130  
   131  	case *types.Named:
   132  		if actual, ok := actual.(*types.Named); ok {
   133  			if NormalizeVendor(expected.Obj().Pkg().Path()) != NormalizeVendor(actual.Obj().Pkg().Path()) {
   134  				return fmt.Errorf(
   135  					"package name of named type differs, %s != %s",
   136  					NormalizeVendor(expected.Obj().Pkg().Path()),
   137  					NormalizeVendor(actual.Obj().Pkg().Path()),
   138  				)
   139  			}
   140  
   141  			if expected.Obj().Name() != actual.Obj().Name() {
   142  				return fmt.Errorf(
   143  					"named type name differs, %s != %s",
   144  					NormalizeVendor(expected.Obj().Name()),
   145  					NormalizeVendor(actual.Obj().Name()),
   146  				)
   147  			}
   148  
   149  			return nil
   150  		}
   151  
   152  		// Before models are generated all missing references will be Invalid Basic references.
   153  		// lets assume these are valid too.
   154  		if actual, ok := actual.(*types.Basic); ok && actual.Kind() == types.Invalid {
   155  			return nil
   156  		}
   157  
   158  	default:
   159  		return fmt.Errorf("missing support for %T", expected)
   160  	}
   161  
   162  	return fmt.Errorf("type mismatch %T != %T", expected, actual)
   163  }