hclsyntax: Remove hclsyntax.Transform

This was always a bit of an outlier here because the rest of the API is
intentionally designed to encourage treating AST nodes as immutable.

Although the transforming walkers were functionally correct, they were
causing false positives in the race detector if two walks run concurrently.

We may later introduce something similar to this in the hclwrite package,
where the AST nodes are explicitly mutable.
This commit is contained in:
Martin Atkins 2018-09-26 07:38:43 -07:00
parent 3f1c5474d4
commit cce5ae6cc5
7 changed files with 41 additions and 79 deletions

View File

@ -132,7 +132,7 @@ type RelativeTraversalExpr struct {
}
func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) {
e.Source = w(e.Source).(Expression)
w(e.Source)
}
func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
@ -181,8 +181,8 @@ type FunctionCallExpr struct {
}
func (e *FunctionCallExpr) walkChildNodes(w internalWalkFunc) {
for i, arg := range e.Args {
e.Args[i] = w(arg).(Expression)
for _, arg := range e.Args {
w(arg)
}
}
@ -463,9 +463,9 @@ type ConditionalExpr struct {
}
func (e *ConditionalExpr) walkChildNodes(w internalWalkFunc) {
e.Condition = w(e.Condition).(Expression)
e.TrueResult = w(e.TrueResult).(Expression)
e.FalseResult = w(e.FalseResult).(Expression)
w(e.Condition)
w(e.TrueResult)
w(e.FalseResult)
}
func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
@ -593,8 +593,8 @@ type IndexExpr struct {
}
func (e *IndexExpr) walkChildNodes(w internalWalkFunc) {
e.Collection = w(e.Collection).(Expression)
e.Key = w(e.Key).(Expression)
w(e.Collection)
w(e.Key)
}
func (e *IndexExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
@ -625,8 +625,8 @@ type TupleConsExpr struct {
}
func (e *TupleConsExpr) walkChildNodes(w internalWalkFunc) {
for i, expr := range e.Exprs {
e.Exprs[i] = w(expr).(Expression)
for _, expr := range e.Exprs {
w(expr)
}
}
@ -674,9 +674,9 @@ type ObjectConsItem struct {
}
func (e *ObjectConsExpr) walkChildNodes(w internalWalkFunc) {
for i, item := range e.Items {
e.Items[i].KeyExpr = w(item.KeyExpr).(Expression)
e.Items[i].ValueExpr = w(item.ValueExpr).(Expression)
for _, item := range e.Items {
w(item.KeyExpr)
w(item.ValueExpr)
}
}
@ -792,7 +792,7 @@ func (e *ObjectConsKeyExpr) walkChildNodes(w internalWalkFunc) {
// We only treat our wrapped expression as a real expression if we're
// not going to interpret it as a literal.
if e.literalName() == "" {
e.Wrapped = w(e.Wrapped).(Expression)
w(e.Wrapped)
}
}
@ -1157,7 +1157,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
}
func (e *ForExpr) walkChildNodes(w internalWalkFunc) {
e.CollExpr = w(e.CollExpr).(Expression)
w(e.CollExpr)
scopeNames := map[string]struct{}{}
if e.KeyVar != "" {
@ -1170,17 +1170,17 @@ func (e *ForExpr) walkChildNodes(w internalWalkFunc) {
if e.KeyExpr != nil {
w(ChildScope{
LocalNames: scopeNames,
Expr: &e.KeyExpr,
Expr: e.KeyExpr,
})
}
w(ChildScope{
LocalNames: scopeNames,
Expr: &e.ValExpr,
Expr: e.ValExpr,
})
if e.CondExpr != nil {
w(ChildScope{
LocalNames: scopeNames,
Expr: &e.CondExpr,
Expr: e.CondExpr,
})
}
}
@ -1266,8 +1266,8 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
}
func (e *SplatExpr) walkChildNodes(w internalWalkFunc) {
e.Source = w(e.Source).(Expression)
e.Each = w(e.Each).(Expression)
w(e.Source)
w(e.Each)
}
func (e *SplatExpr) Range() hcl.Range {

View File

@ -129,8 +129,8 @@ type BinaryOpExpr struct {
}
func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) {
e.LHS = w(e.LHS).(Expression)
e.RHS = w(e.RHS).(Expression)
w(e.LHS)
w(e.RHS)
}
func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
@ -212,7 +212,7 @@ type UnaryOpExpr struct {
}
func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) {
e.Val = w(e.Val).(Expression)
w(e.Val)
}
func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {

View File

@ -16,8 +16,8 @@ type TemplateExpr struct {
}
func (e *TemplateExpr) walkChildNodes(w internalWalkFunc) {
for i, part := range e.Parts {
e.Parts[i] = w(part).(Expression)
for _, part := range e.Parts {
w(part)
}
}
@ -98,7 +98,7 @@ type TemplateJoinExpr struct {
}
func (e *TemplateJoinExpr) walkChildNodes(w internalWalkFunc) {
e.Tuple = w(e.Tuple).(Expression)
w(e.Tuple)
}
func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
@ -184,7 +184,7 @@ type TemplateWrapExpr struct {
}
func (e *TemplateWrapExpr) walkChildNodes(w internalWalkFunc) {
e.Wrapped = w(e.Wrapped).(Expression)
w(e.Wrapped)
}
func (e *TemplateWrapExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {

View File

@ -19,4 +19,4 @@ type Node interface {
Range() hcl.Range
}
type internalWalkFunc func(Node) Node
type internalWalkFunc func(Node)

View File

@ -47,8 +47,8 @@ type Body struct {
var assertBodyImplBody hcl.Body = &Body{}
func (b *Body) walkChildNodes(w internalWalkFunc) {
b.Attributes = w(b.Attributes).(Attributes)
b.Blocks = w(b.Blocks).(Blocks)
w(b.Attributes)
w(b.Blocks)
}
func (b *Body) Range() hcl.Range {
@ -286,8 +286,8 @@ func (b *Body) MissingItemRange() hcl.Range {
type Attributes map[string]*Attribute
func (a Attributes) walkChildNodes(w internalWalkFunc) {
for k, attr := range a {
a[k] = w(attr).(*Attribute)
for _, attr := range a {
w(attr)
}
}
@ -321,7 +321,7 @@ type Attribute struct {
}
func (a *Attribute) walkChildNodes(w internalWalkFunc) {
a.Expr = w(a.Expr).(Expression)
w(a.Expr)
}
func (a *Attribute) Range() hcl.Range {
@ -346,8 +346,8 @@ func (a *Attribute) AsHCLAttribute() *hcl.Attribute {
type Blocks []*Block
func (bs Blocks) walkChildNodes(w internalWalkFunc) {
for i, block := range bs {
bs[i] = w(block).(*Block)
for _, block := range bs {
w(block)
}
}
@ -378,7 +378,7 @@ type Block struct {
}
func (b *Block) walkChildNodes(w internalWalkFunc) {
b.Body = w(b.Body).(*Body)
w(b.Body)
}
func (b *Block) Range() hcl.Range {

View File

@ -72,15 +72,15 @@ func (w *variablesWalker) Exit(n Node) hcl.Diagnostics {
// that the child scope struct wraps.
type ChildScope struct {
LocalNames map[string]struct{}
Expr *Expression // pointer because it can be replaced on walk
Expr Expression
}
func (e ChildScope) walkChildNodes(w internalWalkFunc) {
*(e.Expr) = w(*(e.Expr)).(Expression)
w(e.Expr)
}
// Range returns the range of the expression that the ChildScope is
// encapsulating. It isn't really very useful to call Range on a ChildScope.
func (e ChildScope) Range() hcl.Range {
return (*e.Expr).Range()
return e.Expr.Range()
}

View File

@ -15,9 +15,8 @@ type VisitFunc func(node Node) hcl.Diagnostics
// and returned as a single set.
func VisitAll(node Node, f VisitFunc) hcl.Diagnostics {
diags := f(node)
node.walkChildNodes(func(node Node) Node {
node.walkChildNodes(func(node Node) {
diags = append(diags, VisitAll(node, f)...)
return node
})
return diags
}
@ -33,47 +32,10 @@ type Walker interface {
// Enter and Exit functions.
func Walk(node Node, w Walker) hcl.Diagnostics {
diags := w.Enter(node)
node.walkChildNodes(func(node Node) Node {
node.walkChildNodes(func(node Node) {
diags = append(diags, Walk(node, w)...)
return node
})
moreDiags := w.Exit(node)
diags = append(diags, moreDiags...)
return diags
}
// Transformer is an interface used with Transform
type Transformer interface {
// Transform accepts a node and returns a replacement node along with
// a flag for whether to also visit child nodes. If the flag is false,
// none of the child nodes will be visited and the TransformExit method
// will not be called for the node.
//
// It is acceptable and appropriate for Transform to return the same node
// it was given, for situations where no transform is needed.
Transform(node Node) (Node, bool, hcl.Diagnostics)
// TransformExit signals the end of transformations of child nodes of the
// given node. If Transform returned a new node, the given node is the
// node that was returned, rather than the node that was originally
// encountered.
TransformExit(node Node) hcl.Diagnostics
}
// Transform allows for in-place transformations of an AST starting with a
// particular node. The provider Transformer implementation drives the
// transformation process. The return value is the node that replaced the
// given top-level node.
func Transform(node Node, t Transformer) (Node, hcl.Diagnostics) {
newNode, descend, diags := t.Transform(node)
if !descend {
return newNode, diags
}
node.walkChildNodes(func(node Node) Node {
newNode, newDiags := Transform(node, t)
diags = append(diags, newDiags...)
return newNode
})
diags = append(diags, t.TransformExit(newNode)...)
return newNode, diags
}