Create a separate `validateMoveStatementGraph` function so that
`ValidateMoves` and `ApplyMoves` both check the same conditions. Since
we're not using the builtin `graph.Validate` method, because we may have
multiple roots and want better cycle diagnostics, we need to add checks
for self references too. While multiple roots are an error enforced by
`Validate` for the concurrent walk, they are OK when using
`TransitiveReduction` and `ReverseDepthFirstWalk`, so we can skip that
check.
Apply moves must first use `TransitiveReduction` to reduce the graph,
otherwise nodes may be skipped if they are passed over by a transitive
edge.
// Reporting cycles is awkward because there isn't any definitive
// way to decide which of the objects in the cycle is the cause of
// the problem. Therefore we'll just list them all out and leave
// the user to figure it out. :(
stmtStrs:=make([]string,0,len(cycle))
for_,stmtI:=rangecycle{
// move statement graph nodes are pointers to move statements
stmt:=stmtI.(*MoveStatement)
stmtStrs=append(stmtStrs,fmt.Sprintf(
"\n - %s: %s → %s",
stmt.DeclRange.StartString(),
stmt.From.String(),
stmt.To.String(),
))
}
sort.Strings(stmtStrs)// just to make the order deterministic
diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Cyclic dependency in move statements",
fmt.Sprintf(
"The following chained move statements form a cycle, and so there is no final location to move objects to:%s\n\nA chain of move statements must end with an address that doesn't appear in any other statements, and which typically also refers to an object still declared in the configuration.",
strings.Join(stmtStrs,""),
),
))
}
// Look for cycles to self.
// A user shouldn't be able to create self-references, but we cannot
// correctly process a graph with them.
for_,e:=rangeg.Edges(){
src:=e.Source()
ifsrc==e.Target(){
diags=diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Cyclic dependency in move statements",
"Self reference in move statements",
fmt.Sprintf(
"The following chained move statements form a cycle, and so there is no final location to move objects to:%s\n\nA chain of move statements must end with an address that doesn't appear in any other statements, and which typically also refers to an object still declared in the configuration.",
strings.Join(stmtStrs,""),
"The move statement %s refers to itself the move dependency graph, which is invalid. This is a bug in Terraform; please report it!",