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  // EqualSyntax reports whether x and y are equal.
    30  // Identifiers are considered equal if they are spelled the same.
    31  // Comments are ignored.
    32  func EqualSyntax(x, y ast.Expr) bool {
    33  	sameName := func(x, y *ast.Ident) bool { return x.Name == y.Name }
    34  	return Equal(x, y, sameName)
    35  }
    36  
    37  func equal(x, y reflect.Value, identical func(x, y *ast.Ident) bool) bool {
    38  	// Ensure types are the same
    39  	if x.Type() != y.Type() {
    40  		return false
    41  	}
    42  	switch x.Kind() {
    43  	case reflect.Pointer:
    44  		if x.IsNil() || y.IsNil() {
    45  			return x.IsNil() == y.IsNil()
    46  		}
    47  		switch t := x.Interface().(type) {
    48  		// Skip fields of types potentially involved in cycles.
    49  		case *ast.Object, *ast.Scope, *ast.CommentGroup:
    50  			return true
    51  		case *ast.Ident:
    52  			return identical(t, y.Interface().(*ast.Ident))
    53  		default:
    54  			return equal(x.Elem(), y.Elem(), identical)
    55  		}
    56  
    57  	case reflect.Interface:
    58  		if x.IsNil() || y.IsNil() {
    59  			return x.IsNil() == y.IsNil()
    60  		}
    61  		return equal(x.Elem(), y.Elem(), identical)
    62  
    63  	case reflect.Struct:
    64  		for i := range x.NumField() {
    65  			xf := x.Field(i)
    66  			yf := y.Field(i)
    67  			// Skip position fields.
    68  			if xpos, ok := xf.Interface().(token.Pos); ok {
    69  				ypos := yf.Interface().(token.Pos)
    70  				// Numeric value of a Pos is not significant but its "zeroness" is,
    71  				// because it is often significant, e.g. CallExpr.Variadic(Ellipsis), ChanType.Arrow.
    72  				if xpos.IsValid() != ypos.IsValid() {
    73  					return false
    74  				}
    75  			} else if !equal(xf, yf, identical) {
    76  				return false
    77  			}
    78  		}
    79  		return true
    80  
    81  	case reflect.Slice:
    82  		if x.IsNil() || y.IsNil() {
    83  			return x.IsNil() == y.IsNil()
    84  		}
    85  		if x.Len() != y.Len() {
    86  			return false
    87  		}
    88  		for i := range x.Len() {
    89  			if !equal(x.Index(i), y.Index(i), identical) {
    90  				return false
    91  			}
    92  		}
    93  		return true
    94  
    95  	case reflect.String:
    96  		return x.String() == y.String()
    97  
    98  	case reflect.Bool:
    99  		return x.Bool() == y.Bool()
   100  
   101  	case reflect.Int:
   102  		return x.Int() == y.Int()
   103  
   104  	default:
   105  		panic(x)
   106  	}
   107  }
   108  

View as plain text