// Copyright 2019, The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE.md file. package cmp import "reflect" // valueNode represents a single node within a report, which is a // structured representation of the value tree, containing information // regarding which nodes are equal or not. type valueNode struct { parent *valueNode Type reflect.Type ValueX reflect.Value ValueY reflect.Value // NumSame is the number of leaf nodes that are equal. // All descendants are equal only if NumDiff is 0. NumSame int // NumDiff is the number of leaf nodes that are not equal. NumDiff int // NumIgnored is the number of leaf nodes that are ignored. NumIgnored int // NumCompared is the number of leaf nodes that were compared // using an Equal method or Comparer function. NumCompared int // NumTransformed is the number of non-leaf nodes that were transformed. NumTransformed int // NumChildren is the number of transitive descendants of this node. // This counts from zero; thus, leaf nodes have no descendants. NumChildren int // MaxDepth is the maximum depth of the tree. This counts from zero; // thus, leaf nodes have a depth of zero. MaxDepth int // Records is a list of struct fields, slice elements, or map entries. Records []reportRecord // If populated, implies Value is not populated // Value is the result of a transformation, pointer indirect, of // type assertion. Value *valueNode // If populated, implies Records is not populated // TransformerName is the name of the transformer. TransformerName string // If non-empty, implies Value is populated } type reportRecord struct { Key reflect.Value // Invalid for slice element Value *valueNode } func (parent *valueNode) PushStep(ps PathStep) (child *valueNode) { vx, vy := ps.Values() child = &valueNode{parent: parent, Type: ps.Type(), ValueX: vx, ValueY: vy} switch s := ps.(type) { case StructField: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Key: reflect.ValueOf(s.Name()), Value: child}) case SliceIndex: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Value: child}) case MapIndex: assert(parent.Value == nil) parent.Records = append(parent.Records, reportRecord{Key: s.Key(), Value: child}) case Indirect: assert(parent.Value == nil && parent.Records == nil) parent.Value = child case TypeAssertion: assert(parent.Value == nil && parent.Records == nil) parent.Value = child case Transform: assert(parent.Value == nil && parent.Records == nil) parent.Value = child parent.TransformerName = s.Name() parent.NumTransformed++ default: assert(parent == nil) // Must be the root step } return child } func (r *valueNode) Report(rs Result) { assert(r.MaxDepth == 0) // May only be called on leaf nodes if rs.ByIgnore() { r.NumIgnored++ } else { if rs.Equal() { r.NumSame++ } else { r.NumDiff++ } } assert(r.NumSame+r.NumDiff+r.NumIgnored == 1) if rs.ByMethod() { r.NumCompared++ } if rs.ByFunc() { r.NumCompared++ } assert(r.NumCompared <= 1) } func (child *valueNode) PopStep() (parent *valueNode) { if child.parent == nil { return nil } parent = child.parent parent.NumSame += child.NumSame parent.NumDiff += child.NumDiff parent.NumIgnored += child.NumIgnored parent.NumCompared += child.NumCompared parent.NumTransformed += child.NumTransformed parent.NumChildren += child.NumChildren + 1 if parent.MaxDepth < child.MaxDepth+1 { parent.MaxDepth = child.MaxDepth + 1 } return parent }