You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: transform_destroy_edge.go
    path of file: ./repos/terraform/internal/terraform
    
    the code of the file until where you have to start completion: // Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package terraform

import (
	"log"

	"github.com/hashicorp/terraform/internal/addrs"
	"github.com/hashicorp/terraform/internal/dag"
	"github.com/hashicorp/terraform/internal/plans"
)

// GraphNodeDestroyer must be implemented by nodes that destroy resources.
type GraphNodeDestroyer interface {
	dag.Vertex

	// DestroyAddr is the address of the resource that is being
	// destroyed by this node. If this returns nil, then this node
	// is not destroying anything.
	DestroyAddr() *addrs.AbsResourceInstance
}

// GraphNodeCreator must be implemented by nodes that create OR update resources.
type GraphNodeCreator interface {
	// CreateAddr is the address of the resource being created or updated
	CreateAddr() *addrs.AbsResourceInstance
}

// DestroyEdgeTransformer is a GraphTransformer that creates the proper
// references for destroy resources. Destroy resources are more complex
// in that they must be depend on the destruction of resources that
// in turn depend on the CREATION of the node being destroy.
//
// That is complicated. Visually:
//
//	B_d -> A_d -> A -> B
//
// Notice that A destroy depends on B destroy, while B create depends on
// A create. They're inverted. This must be done for example because often
// dependent resources will block parent resources from deleting. Concrete
// example: VPC with subnets, the VPC can't be deleted while there are
// still subnets.
type DestroyEdgeTransformer struct {
	// FIXME: GraphNodeCreators are not always applying changes, and should not
	// participate in the destroy graph if there are no operations which could
	// interract with destroy nodes. We need Changes for now to detect the
	// action type, but perhaps this should be indicated somehow by the
	// DiffTransformer which was intended to be the only transformer operating
	// from the change set.
	Changes *plans.Changes

	// FIXME: Operation will not be needed here one we can better track
	// inter-provider dependencies and remove the cycle checks in
	// tryInterProviderDestroyEdge.
	Operation walkOperation
}

// tryInterProviderDestroyEdge checks if we're inserting a destroy edge
// across a provider boundary, and only adds the edge if it results in no cycles.

    
func (t *pruneUnusedNodesTransformer) Transform(g *Graph) error {
	if t.skip {
		return nil
	}

	// We need a reverse depth first walk of modules, processing them in order
	// from the leaf modules to the root. This allows us to remove unneeded
	// dependencies from child modules, freeing up nodes in the parent module
	// to also be removed.

	nodes := g.Vertices()

	for removed := true; removed; {
		removed = false

		for i := 0; i < len(nodes); i++ {
			// run this in a closure, so we can return early rather than
			// dealing with complex looping and labels
			func() {
				n := nodes[i]
				switch n := n.(type) {

				case *nodeExpandCheck:
					// We always execute check blocks, and they never have
					// anything referencing them. We have to explicitly list
					// them here otherwise they'll be deleted.
					return

				case graphNodeTemporaryValue:
					// root module outputs indicate they are not temporary by
					// returning false here.
					if !n.temporaryValue() {
						return
					}

					// temporary values, which consist of variables, locals,
					// and outputs, must be kept if anything refers to them.
					for _, v := range g.UpEdges(n) {
						// keep any value which is connected through a
						// reference
						if _, ok := v.(GraphNodeReferencer); ok {
							return
						}
					}

				case graphNodeExpandsInstances:
					// Any nodes that expand instances are kept when their
					// instances may need to be evaluated.
					for _, v := range g.UpEdges(n) {
						switch v.(type) {
						case graphNodeExpandsInstances:
							// Root module output values (which the following
							// condition matches) are exempt because we know
							// there is only ever exactly one instance of the
							// root module, and so it's not actually important
							// to expand it and so this lets us do a bit more
							// pruning than we'd be able to do otherwise.
							if tmp, ok := v.(graphNodeTemporaryValue); ok && !tmp.temporaryValue() {
								continue
							}

							// expanders can always depend on module expansion
							// themselves
							return
						case GraphNodeResourceInstance:
							// resource instances always depend on their
							// resource node, which is an expander
							return
						}
					}

				case GraphNodeProvider:
					// Only keep providers for evaluation if they have
					// resources to handle.
					// The provider transformers removed most unused providers
					// earlier, however there may be more to prune now based on
					// targeting or a destroy with no related instances in the
					// state.
					des, _ := g.Descendents(n)
					for _, v := range des {
						switch v.(type) {
						case GraphNodeProviderConsumer:
							return
						}
					}

				default:
					return
				}

				log.Printf("[DEBUG] pruneUnusedNodes: %s is no longer needed, removing", dag.VertexName(n))
				g.Remove(n)
				removed = true

				// remove the node from our iteration as well
				last := len(nodes) - 1
				nodes[i], nodes[last] = nodes[last], nodes[i]
				nodes = nodes[:last]
			}()
		}
	}

	return nil
}