src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

equal.go (2668B)


      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 }