hcl: ExprCall function

This accompanies ExprList, ExprMap, and AbsTraversalForExpr to
complete the set of static analysis interfaces for digging down into the
expression syntax structures without evaluation.

The intent of this function is to be a little like AbsTraversalForExpr
but for function calls. However, it's also similar to ExprList in that
it gives access to the raw expression objects for the arguments, allowing
for recursive analysis.
This commit is contained in:
Martin Atkins 2018-03-04 14:04:54 -08:00
parent 92456935b8
commit 5956048526
3 changed files with 84 additions and 0 deletions

46
hcl/expr_call.go Normal file
View File

@ -0,0 +1,46 @@
package hcl
// ExprCall tests if the given expression is a function call and,
// if so, extracts the function name and the expressions that represent
// the arguments. If the given expression is not statically a function call,
// error diagnostics are returned.
//
// A particular Expression implementation can support this function by
// offering a method called ExprCall that takes no arguments and returns
// *StaticCall. This method should return nil if a static call cannot
// be extracted. Alternatively, an implementation can support
// UnwrapExpression to delegate handling of this function to a wrapped
// Expression object.
func ExprCall(expr Expression) (*StaticCall, Diagnostics) {
type exprCall interface {
ExprCall() *StaticCall
}
physExpr := UnwrapExpressionUntil(expr, func(expr Expression) bool {
_, supported := expr.(exprCall)
return supported
})
if exC, supported := physExpr.(exprCall); supported {
if call := exC.ExprCall(); call != nil {
return call, nil
}
}
return nil, Diagnostics{
&Diagnostic{
Severity: DiagError,
Summary: "Invalid expression",
Detail: "A static function call is required.",
Subject: expr.StartRange().Ptr(),
},
}
}
// StaticCall represents a function call that was extracted statically from
// an expression using ExprCall.
type StaticCall struct {
Name string
NameRange Range
Arguments []Expression
ArgsRange Range
}

View File

@ -417,6 +417,21 @@ func (e *FunctionCallExpr) StartRange() hcl.Range {
return hcl.RangeBetween(e.NameRange, e.OpenParenRange)
}
// Implementation for hcl.ExprCall.
func (e *FunctionCallExpr) ExprCall() *hcl.StaticCall {
ret := &hcl.StaticCall{
Name: e.Name,
NameRange: e.NameRange,
Arguments: make([]hcl.Expression, len(e.Args)),
ArgsRange: hcl.RangeBetween(e.OpenParenRange, e.CloseParenRange),
}
// Need to convert our own Expression objects into hcl.Expression.
for i, arg := range e.Args {
ret.Arguments[i] = arg
}
return ret
}
type ConditionalExpr struct {
Condition Expression
TrueResult Expression

View File

@ -558,6 +558,29 @@ func (e *expression) AsTraversal() hcl.Traversal {
}
}
// Implementation for hcl.ExprCall.
func (e *expression) ExprCall() *hcl.StaticCall {
// In JSON-based syntax a static call is given as a string containing
// an expression in the native syntax that also supports ExprCall.
switch v := e.src.(type) {
case *stringVal:
expr, diags := hclsyntax.ParseExpression([]byte(v.Value), v.SrcRange.Filename, v.SrcRange.Start)
if diags.HasErrors() {
return nil
}
call, diags := hcl.ExprCall(expr)
if diags.HasErrors() {
return nil
}
return call
default:
return nil
}
}
// Implementation for hcl.ExprList.
func (e *expression) ExprList() []hcl.Expression {
switch v := e.src.(type) {