github.com/myhau/pulumi/pkg/v3@v3.70.2-0.20221116134521-f2775972e587/codegen/hcl2/model/visitor.go (about)

     1  // Copyright 2016-2020, Pulumi Corporation.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package model
    16  
    17  import (
    18  	"github.com/hashicorp/hcl/v2"
    19  	"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
    20  )
    21  
    22  // A BodyItemVisitor is a function that visits and optionally replaces the contents of a body item.
    23  type BodyItemVisitor func(n BodyItem) (BodyItem, hcl.Diagnostics)
    24  
    25  func BodyItemIdentityVisitor(n BodyItem) (BodyItem, hcl.Diagnostics) {
    26  	return n, nil
    27  }
    28  
    29  func visitBlock(n *Block, pre, post BodyItemVisitor) (BodyItem, hcl.Diagnostics) {
    30  	var diagnostics hcl.Diagnostics
    31  
    32  	var items []BodyItem
    33  	for _, item := range n.Body.Items {
    34  		newItem, diags := VisitBodyItem(item, pre, post)
    35  		diagnostics = append(diagnostics, diags...)
    36  
    37  		if newItem != nil {
    38  			items = append(items, newItem)
    39  		}
    40  	}
    41  	n.Body.Items = items
    42  
    43  	block, diags := post(n)
    44  	return block, append(diagnostics, diags...)
    45  }
    46  
    47  func VisitBodyItem(n BodyItem, pre, post BodyItemVisitor) (BodyItem, hcl.Diagnostics) {
    48  	if n == nil {
    49  		return nil, nil
    50  	}
    51  
    52  	if pre == nil {
    53  		pre = BodyItemIdentityVisitor
    54  	}
    55  
    56  	nn, preDiags := pre(n)
    57  
    58  	var postDiags hcl.Diagnostics
    59  	if post != nil {
    60  		switch n := nn.(type) {
    61  		case *Attribute:
    62  			nn, postDiags = post(n)
    63  		case *Block:
    64  			nn, postDiags = visitBlock(n, pre, post)
    65  		default:
    66  			contract.Failf("unexpected node type in visitExpression: %T", n)
    67  			return nil, nil
    68  		}
    69  	}
    70  
    71  	return nn, append(preDiags, postDiags...)
    72  }
    73  
    74  // An ExpressionVisitor is a function that visits and optionally replaces a node in an expression tree.
    75  type ExpressionVisitor func(n Expression) (Expression, hcl.Diagnostics)
    76  
    77  // IdentityVisitor is a ExpressionVisitor that returns the input node unchanged.
    78  func IdentityVisitor(n Expression) (Expression, hcl.Diagnostics) {
    79  	return n, nil
    80  }
    81  
    82  func visitAnonymousFunction(n *AnonymousFunctionExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
    83  	var diagnostics hcl.Diagnostics
    84  
    85  	body, diags := VisitExpression(n.Body, pre, post)
    86  	diagnostics = append(diagnostics, diags...)
    87  
    88  	n.Body = body
    89  
    90  	expr, diags := post(n)
    91  	return expr, append(diagnostics, diags...)
    92  }
    93  
    94  func visitBinaryOp(n *BinaryOpExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
    95  	var diagnostics hcl.Diagnostics
    96  
    97  	left, diags := VisitExpression(n.LeftOperand, pre, post)
    98  	diagnostics = append(diagnostics, diags...)
    99  
   100  	right, diags := VisitExpression(n.RightOperand, pre, post)
   101  	diagnostics = append(diagnostics, diags...)
   102  
   103  	n.LeftOperand, n.RightOperand = left, right
   104  
   105  	expr, diags := post(n)
   106  	return expr, append(diagnostics, diags...)
   107  }
   108  
   109  func visitConditional(n *ConditionalExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   110  	var diagnostics hcl.Diagnostics
   111  
   112  	condition, diags := VisitExpression(n.Condition, pre, post)
   113  	diagnostics = append(diagnostics, diags...)
   114  
   115  	trueResult, diags := VisitExpression(n.TrueResult, pre, post)
   116  	diagnostics = append(diagnostics, diags...)
   117  
   118  	falseResult, diags := VisitExpression(n.FalseResult, pre, post)
   119  	diagnostics = append(diagnostics, diags...)
   120  
   121  	n.Condition, n.TrueResult, n.FalseResult = condition, trueResult, falseResult
   122  
   123  	expr, diags := post(n)
   124  	return expr, append(diagnostics, diags...)
   125  }
   126  
   127  func visitFor(n *ForExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   128  	var diagnostics hcl.Diagnostics
   129  
   130  	collection, diags := VisitExpression(n.Collection, pre, post)
   131  	diagnostics = append(diagnostics, diags...)
   132  
   133  	key, diags := VisitExpression(n.Key, pre, post)
   134  	diagnostics = append(diagnostics, diags...)
   135  
   136  	value, diags := VisitExpression(n.Value, pre, post)
   137  	diagnostics = append(diagnostics, diags...)
   138  
   139  	condition, diags := VisitExpression(n.Condition, pre, post)
   140  	diagnostics = append(diagnostics, diags...)
   141  
   142  	n.Collection, n.Key, n.Value, n.Condition = collection, key, value, condition
   143  
   144  	expr, diags := post(n)
   145  	return expr, append(diagnostics, diags...)
   146  }
   147  
   148  func visitFunctionCall(n *FunctionCallExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   149  	var diagnostics hcl.Diagnostics
   150  
   151  	args, diags := visitExpressions(n.Args, pre, post)
   152  	diagnostics = append(diagnostics, diags...)
   153  
   154  	n.Args = args
   155  
   156  	expr, diags := post(n)
   157  	return expr, append(diagnostics, diags...)
   158  }
   159  
   160  func visitIndex(n *IndexExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   161  	var diagnostics hcl.Diagnostics
   162  
   163  	collection, diags := VisitExpression(n.Collection, pre, post)
   164  	diagnostics = append(diagnostics, diags...)
   165  
   166  	key, diags := VisitExpression(n.Key, pre, post)
   167  	diagnostics = append(diagnostics, diags...)
   168  
   169  	n.Collection, n.Key = collection, key
   170  
   171  	expr, diags := post(n)
   172  	return expr, append(diagnostics, diags...)
   173  }
   174  
   175  func visitObjectCons(n *ObjectConsExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   176  	var diagnostics hcl.Diagnostics
   177  
   178  	for i, item := range n.Items {
   179  		key, diags := VisitExpression(item.Key, pre, post)
   180  		diagnostics = append(diagnostics, diags...)
   181  
   182  		value, diags := VisitExpression(item.Value, pre, post)
   183  		diagnostics = append(diagnostics, diags...)
   184  
   185  		n.Items[i] = ObjectConsItem{Key: key, Value: value}
   186  	}
   187  
   188  	expr, diags := post(n)
   189  	return expr, append(diagnostics, diags...)
   190  }
   191  
   192  func visitRelativeTraversal(n *RelativeTraversalExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   193  	var diagnostics hcl.Diagnostics
   194  
   195  	source, diags := VisitExpression(n.Source, pre, post)
   196  	diagnostics = append(diagnostics, diags...)
   197  
   198  	n.Source = source
   199  
   200  	expr, diags := post(n)
   201  	return expr, append(diagnostics, diags...)
   202  }
   203  
   204  func visitSplat(n *SplatExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   205  	var diagnostics hcl.Diagnostics
   206  
   207  	source, diags := VisitExpression(n.Source, pre, post)
   208  	diagnostics = append(diagnostics, diags...)
   209  
   210  	each, diags := VisitExpression(n.Each, pre, post)
   211  	diagnostics = append(diagnostics, diags...)
   212  
   213  	n.Source, n.Each = source, each
   214  
   215  	expr, diags := post(n)
   216  	return expr, append(diagnostics, diags...)
   217  }
   218  
   219  func visitTemplate(n *TemplateExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   220  	var diagnostics hcl.Diagnostics
   221  
   222  	parts, diags := visitExpressions(n.Parts, pre, post)
   223  	diagnostics = append(diagnostics, diags...)
   224  
   225  	n.Parts = parts
   226  
   227  	expr, diags := post(n)
   228  	return expr, append(diagnostics, diags...)
   229  }
   230  
   231  func visitTemplateJoin(n *TemplateJoinExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   232  	var diagnostics hcl.Diagnostics
   233  
   234  	tuple, diags := VisitExpression(n.Tuple, pre, post)
   235  	diagnostics = append(diagnostics, diags...)
   236  
   237  	n.Tuple = tuple
   238  
   239  	expr, diags := post(n)
   240  	return expr, append(diagnostics, diags...)
   241  }
   242  
   243  func visitTupleCons(n *TupleConsExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   244  	var diagnostics hcl.Diagnostics
   245  
   246  	expressions, diags := visitExpressions(n.Expressions, pre, post)
   247  	diagnostics = append(diagnostics, diags...)
   248  
   249  	n.Expressions = expressions
   250  
   251  	expr, diags := post(n)
   252  	return expr, append(diagnostics, diags...)
   253  }
   254  
   255  func visitUnaryOp(n *UnaryOpExpression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   256  	var diagnostics hcl.Diagnostics
   257  
   258  	operand, diags := VisitExpression(n.Operand, pre, post)
   259  	diagnostics = append(diagnostics, diags...)
   260  
   261  	n.Operand = operand
   262  
   263  	expr, diags := post(n)
   264  	return expr, append(diagnostics, diags...)
   265  }
   266  
   267  func visitExpressions(ns []Expression, pre, post ExpressionVisitor) ([]Expression, hcl.Diagnostics) {
   268  	var diagnostics hcl.Diagnostics
   269  
   270  	nils := 0
   271  	for i, e := range ns {
   272  		ee, diags := VisitExpression(e, pre, post)
   273  		diagnostics = append(diagnostics, diags...)
   274  		if ee == nil {
   275  			nils++
   276  		}
   277  		ns[i] = ee
   278  	}
   279  	if nils == 0 {
   280  		return ns, diagnostics
   281  	} else if nils == len(ns) {
   282  		return []Expression{}, diagnostics
   283  	}
   284  
   285  	nns := make([]Expression, 0, len(ns)-nils)
   286  	for _, e := range ns {
   287  		if e != nil {
   288  			nns = append(nns, e)
   289  		}
   290  	}
   291  	return nns, diagnostics
   292  }
   293  
   294  // VisitExpression visits each node in an expression tree using the given pre- and post-order visitors. If the preorder
   295  // visitor returns a new node, that node's descendents will be visited. VisitExpression returns the result of the
   296  // post-order visitor. All diagnostics are accumulated.
   297  func VisitExpression(n Expression, pre, post ExpressionVisitor) (Expression, hcl.Diagnostics) {
   298  	if n == nil {
   299  		return nil, nil
   300  	}
   301  
   302  	if pre == nil {
   303  		pre = IdentityVisitor
   304  	}
   305  
   306  	nn, preDiags := pre(n)
   307  
   308  	var postDiags hcl.Diagnostics
   309  	if post != nil {
   310  		switch n := nn.(type) {
   311  		case *AnonymousFunctionExpression:
   312  			nn, postDiags = visitAnonymousFunction(n, pre, post)
   313  		case *BinaryOpExpression:
   314  			nn, postDiags = visitBinaryOp(n, pre, post)
   315  		case *ConditionalExpression:
   316  			nn, postDiags = visitConditional(n, pre, post)
   317  		case *ErrorExpression:
   318  			nn, postDiags = post(n)
   319  		case *ForExpression:
   320  			nn, postDiags = visitFor(n, pre, post)
   321  		case *FunctionCallExpression:
   322  			nn, postDiags = visitFunctionCall(n, pre, post)
   323  		case *IndexExpression:
   324  			nn, postDiags = visitIndex(n, pre, post)
   325  		case *LiteralValueExpression:
   326  			nn, postDiags = post(n)
   327  		case *ObjectConsExpression:
   328  			nn, postDiags = visitObjectCons(n, pre, post)
   329  		case *RelativeTraversalExpression:
   330  			nn, postDiags = visitRelativeTraversal(n, pre, post)
   331  		case *ScopeTraversalExpression:
   332  			nn, postDiags = post(n)
   333  		case *SplatExpression:
   334  			nn, postDiags = visitSplat(n, pre, post)
   335  		case *TemplateExpression:
   336  			nn, postDiags = visitTemplate(n, pre, post)
   337  		case *TemplateJoinExpression:
   338  			nn, postDiags = visitTemplateJoin(n, pre, post)
   339  		case *TupleConsExpression:
   340  			nn, postDiags = visitTupleCons(n, pre, post)
   341  		case *UnaryOpExpression:
   342  			nn, postDiags = visitUnaryOp(n, pre, post)
   343  		default:
   344  			contract.Failf("unexpected node type in visitExpression: %T", n)
   345  			return nil, nil
   346  		}
   347  	}
   348  
   349  	return nn, append(preDiags, postDiags...)
   350  }
   351  
   352  func visitBlockExpressions(n *Block, pre, post ExpressionVisitor) hcl.Diagnostics {
   353  	var diagnostics hcl.Diagnostics
   354  
   355  	for _, item := range n.Body.Items {
   356  		diags := VisitExpressions(item, pre, post)
   357  		diagnostics = append(diagnostics, diags...)
   358  	}
   359  
   360  	return diagnostics
   361  }
   362  
   363  // VisitExpressions visits each expression that descends from the given body item.
   364  func VisitExpressions(n BodyItem, pre, post ExpressionVisitor) hcl.Diagnostics {
   365  	if n == nil {
   366  		return nil
   367  	}
   368  
   369  	if pre == nil {
   370  		pre = IdentityVisitor
   371  	}
   372  
   373  	switch n := n.(type) {
   374  	case *Attribute:
   375  		v, diags := VisitExpression(n.Value, pre, post)
   376  		n.Value = v
   377  		return diags
   378  	case *Block:
   379  		return visitBlockExpressions(n, pre, post)
   380  	default:
   381  		contract.Failf("unexpected node type in visitExpression: %T", n)
   382  		return nil
   383  	}
   384  }