Source file src/cmd/vendor/golang.org/x/tools/internal/astutil/equal.go

     1  // Copyright 2023 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package astutil
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  	"reflect"
    11  )
    12  
    13  // Equal reports whether two nodes are structurally equal,
    14  // ignoring fields of type [token.Pos], [ast.Object],
    15  // and [ast.Scope], and comments.
    16  //
    17  // The operands x and y may be nil.
    18  // A nil slice is not equal to an empty slice.
    19  //
    20  // The provided function determines whether two identifiers
    21  // should be considered identical.
    22  func Equal(x, y ast.Node, identical func(x, y *ast.Ident) bool) bool {
    23  	if x == nil || y == nil {
    24  		return x == y
    25  	}
    26  	return equal(reflect.ValueOf(x), reflect.ValueOf(y), identical)
    27  }
    28  
    29  func equal(x, y reflect.Value, identical func(x, y *ast.Ident) bool) bool {
    30  	// Ensure types are the same
    31  	if x.Type() != y.Type() {
    32  		return false
    33  	}
    34  	switch x.Kind() {
    35  	case reflect.Pointer:
    36  		if x.IsNil() || y.IsNil() {
    37  			return x.IsNil() == y.IsNil()
    38  		}
    39  		switch t := x.Interface().(type) {
    40  		// Skip fields of types potentially involved in cycles.
    41  		case *ast.Object, *ast.Scope, *ast.CommentGroup:
    42  			return true
    43  		case *ast.Ident:
    44  			return identical(t, y.Interface().(*ast.Ident))
    45  		default:
    46  			return equal(x.Elem(), y.Elem(), identical)
    47  		}
    48  
    49  	case reflect.Interface:
    50  		if x.IsNil() || y.IsNil() {
    51  			return x.IsNil() == y.IsNil()
    52  		}
    53  		return equal(x.Elem(), y.Elem(), identical)
    54  
    55  	case reflect.Struct:
    56  		for i := range x.NumField() {
    57  			xf := x.Field(i)
    58  			yf := y.Field(i)
    59  			// Skip position fields.
    60  			if xpos, ok := xf.Interface().(token.Pos); ok {
    61  				ypos := yf.Interface().(token.Pos)
    62  				// Numeric value of a Pos is not significant but its "zeroness" is,
    63  				// because it is often significant, e.g. CallExpr.Variadic(Ellipsis), ChanType.Arrow.
    64  				if xpos.IsValid() != ypos.IsValid() {
    65  					return false
    66  				}
    67  			} else if !equal(xf, yf, identical) {
    68  				return false
    69  			}
    70  		}
    71  		return true
    72  
    73  	case reflect.Slice:
    74  		if x.IsNil() || y.IsNil() {
    75  			return x.IsNil() == y.IsNil()
    76  		}
    77  		if x.Len() != y.Len() {
    78  			return false
    79  		}
    80  		for i := range x.Len() {
    81  			if !equal(x.Index(i), y.Index(i), identical) {
    82  				return false
    83  			}
    84  		}
    85  		return true
    86  
    87  	case reflect.String:
    88  		return x.String() == y.String()
    89  
    90  	case reflect.Bool:
    91  		return x.Bool() == y.Bool()
    92  
    93  	case reflect.Int:
    94  		return x.Int() == y.Int()
    95  
    96  	default:
    97  		panic(x)
    98  	}
    99  }
   100  

View as plain text