govendor fetch github.com/hashicorp/hil/...
This commit is contained in:
parent
b28fb766db
commit
edb362cfb3
|
@ -77,3 +77,12 @@ func (n *LiteralNode) String() string {
|
||||||
func (n *LiteralNode) Type(Scope) (Type, error) {
|
func (n *LiteralNode) Type(Scope) (Type, error) {
|
||||||
return n.Typex, nil
|
return n.Typex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsUnknown returns true either if the node's value is itself unknown
|
||||||
|
// of if it is a collection containing any unknown elements, deeply.
|
||||||
|
func (n *LiteralNode) IsUnknown() bool {
|
||||||
|
return IsUnknown(Variable{
|
||||||
|
Type: n.Typex,
|
||||||
|
Value: n.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -3,54 +3,61 @@ package ast
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) {
|
func VariableListElementTypesAreHomogenous(variableName string, list []Variable) (Type, error) {
|
||||||
listTypes := make(map[Type]struct{})
|
if len(list) == 0 {
|
||||||
|
return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
elemType := TypeUnknown
|
||||||
for _, v := range list {
|
for _, v := range list {
|
||||||
// Allow unknown
|
|
||||||
if v.Type == TypeUnknown {
|
if v.Type == TypeUnknown {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := listTypes[v.Type]; ok {
|
if elemType == TypeUnknown {
|
||||||
|
elemType = v.Type
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
listTypes[v.Type] = struct{}{}
|
|
||||||
|
if v.Type != elemType {
|
||||||
|
return TypeInvalid, fmt.Errorf(
|
||||||
|
"list %q does not have homogenous types. found %s and then %s",
|
||||||
|
variableName,
|
||||||
|
elemType, v.Type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
elemType = v.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(listTypes) != 1 && len(list) != 0 {
|
return elemType, nil
|
||||||
return TypeInvalid, fmt.Errorf("list %q does not have homogenous types. found %s", variableName, reportTypes(listTypes))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list) > 0 {
|
|
||||||
return list[0].Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return TypeInvalid, fmt.Errorf("list %q does not have any elements so cannot determine type.", variableName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) {
|
func VariableMapValueTypesAreHomogenous(variableName string, vmap map[string]Variable) (Type, error) {
|
||||||
valueTypes := make(map[Type]struct{})
|
if len(vmap) == 0 {
|
||||||
|
return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
elemType := TypeUnknown
|
||||||
for _, v := range vmap {
|
for _, v := range vmap {
|
||||||
// Allow unknown
|
|
||||||
if v.Type == TypeUnknown {
|
if v.Type == TypeUnknown {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := valueTypes[v.Type]; ok {
|
if elemType == TypeUnknown {
|
||||||
|
elemType = v.Type
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
valueTypes[v.Type] = struct{}{}
|
if v.Type != elemType {
|
||||||
|
return TypeInvalid, fmt.Errorf(
|
||||||
|
"map %q does not have homogenous types. found %s and then %s",
|
||||||
|
variableName,
|
||||||
|
elemType, v.Type,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
elemType = v.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(valueTypes) != 1 && len(vmap) != 0 {
|
return elemType, nil
|
||||||
return TypeInvalid, fmt.Errorf("map %q does not have homogenous value types. found %s", variableName, reportTypes(valueTypes))
|
|
||||||
}
|
|
||||||
|
|
||||||
// For loop here is an easy way to get a single key, we return immediately.
|
|
||||||
for _, v := range vmap {
|
|
||||||
return v.Type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This means the map is empty
|
|
||||||
return TypeInvalid, fmt.Errorf("map %q does not have any elements so cannot determine type.", variableName)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,10 +98,6 @@ func (v *TypeCheck) visit(raw ast.Node) ast.Node {
|
||||||
pos.Column, pos.Line, err)
|
pos.Column, pos.Line, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.StackPeek() == ast.TypeUnknown {
|
|
||||||
v.err = errExitUnknown
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +112,14 @@ func (tc *typeCheckArithmetic) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
exprs[len(tc.n.Exprs)-1-i] = v.StackPop()
|
exprs[len(tc.n.Exprs)-1-i] = v.StackPop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If any operand is unknown then our result is automatically unknown
|
||||||
|
for _, ty := range exprs {
|
||||||
|
if ty == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch tc.n.Op {
|
switch tc.n.Op {
|
||||||
case ast.ArithmeticOpLogicalAnd, ast.ArithmeticOpLogicalOr:
|
case ast.ArithmeticOpLogicalAnd, ast.ArithmeticOpLogicalOr:
|
||||||
return tc.checkLogical(v, exprs)
|
return tc.checkLogical(v, exprs)
|
||||||
|
@ -333,6 +337,11 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if args[i] == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
if args[i] != expected {
|
if args[i] != expected {
|
||||||
cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
|
cn := v.ImplicitConversion(args[i], expected, tc.n.Args[i])
|
||||||
if cn != nil {
|
if cn != nil {
|
||||||
|
@ -350,6 +359,11 @@ func (tc *typeCheckCall) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
if function.Variadic && function.VariadicType != ast.TypeAny {
|
if function.Variadic && function.VariadicType != ast.TypeAny {
|
||||||
args = args[len(function.ArgTypes):]
|
args = args[len(function.ArgTypes):]
|
||||||
for i, t := range args {
|
for i, t := range args {
|
||||||
|
if t == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
if t != function.VariadicType {
|
if t != function.VariadicType {
|
||||||
realI := i + len(function.ArgTypes)
|
realI := i + len(function.ArgTypes)
|
||||||
cn := v.ImplicitConversion(
|
cn := v.ImplicitConversion(
|
||||||
|
@ -384,6 +398,11 @@ func (tc *typeCheckConditional) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
trueType := v.StackPop()
|
trueType := v.StackPop()
|
||||||
condType := v.StackPop()
|
condType := v.StackPop()
|
||||||
|
|
||||||
|
if condType == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
if condType != ast.TypeBool {
|
if condType != ast.TypeBool {
|
||||||
cn := v.ImplicitConversion(condType, ast.TypeBool, tc.n.CondExpr)
|
cn := v.ImplicitConversion(condType, ast.TypeBool, tc.n.CondExpr)
|
||||||
if cn == nil {
|
if cn == nil {
|
||||||
|
@ -457,6 +476,13 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
types[len(n.Exprs)-1-i] = v.StackPop()
|
types[len(n.Exprs)-1-i] = v.StackPop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, ty := range types {
|
||||||
|
if ty == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If there is only one argument and it is a list, we evaluate to a list
|
// If there is only one argument and it is a list, we evaluate to a list
|
||||||
if len(types) == 1 {
|
if len(types) == 1 {
|
||||||
switch t := types[0]; t {
|
switch t := types[0]; t {
|
||||||
|
@ -469,7 +495,14 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, all concat args must be strings, so validate that
|
// Otherwise, all concat args must be strings, so validate that
|
||||||
|
resultType := ast.TypeString
|
||||||
for i, t := range types {
|
for i, t := range types {
|
||||||
|
|
||||||
|
if t == ast.TypeUnknown {
|
||||||
|
resultType = ast.TypeUnknown
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if t != ast.TypeString {
|
if t != ast.TypeString {
|
||||||
cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
|
cn := v.ImplicitConversion(t, ast.TypeString, n.Exprs[i])
|
||||||
if cn != nil {
|
if cn != nil {
|
||||||
|
@ -482,8 +515,8 @@ func (tc *typeCheckOutput) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This always results in type string
|
// This always results in type string, unless there are unknowns
|
||||||
v.StackPush(ast.TypeString)
|
v.StackPush(resultType)
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
@ -509,13 +542,6 @@ func (tc *typeCheckVariableAccess) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
"unknown variable accessed: %s", tc.n.Name)
|
"unknown variable accessed: %s", tc.n.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the variable contains any unknown types. If so, then
|
|
||||||
// mark it as unknown.
|
|
||||||
if ast.IsUnknown(variable) {
|
|
||||||
v.StackPush(ast.TypeUnknown)
|
|
||||||
return tc.n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the type to the stack
|
// Add the type to the stack
|
||||||
v.StackPush(variable.Type)
|
v.StackPush(variable.Type)
|
||||||
|
|
||||||
|
@ -530,6 +556,11 @@ func (tc *typeCheckIndex) TypeCheck(v *TypeCheck) (ast.Node, error) {
|
||||||
keyType := v.StackPop()
|
keyType := v.StackPop()
|
||||||
targetType := v.StackPop()
|
targetType := v.StackPop()
|
||||||
|
|
||||||
|
if keyType == ast.TypeUnknown || targetType == ast.TypeUnknown {
|
||||||
|
v.StackPush(ast.TypeUnknown)
|
||||||
|
return tc.n, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure we have a VariableAccess as the target
|
// Ensure we have a VariableAccess as the target
|
||||||
varAccessNode, ok := tc.n.Target.(*ast.VariableAccess)
|
varAccessNode, ok := tc.n.Target.(*ast.VariableAccess)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -54,6 +54,14 @@ func Eval(root ast.Node, config *EvalConfig) (EvaluationResult, error) {
|
||||||
return InvalidResult, err
|
return InvalidResult, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the result contains any nested unknowns then the result as a whole
|
||||||
|
// is unknown, so that callers only have to deal with "entirely known"
|
||||||
|
// or "entirely unknown" as outcomes.
|
||||||
|
if ast.IsUnknown(ast.Variable{Type: outputType, Value: output}) {
|
||||||
|
outputType = ast.TypeUnknown
|
||||||
|
output = UnknownValue
|
||||||
|
}
|
||||||
|
|
||||||
switch outputType {
|
switch outputType {
|
||||||
case ast.TypeList:
|
case ast.TypeList:
|
||||||
val, err := VariableToInterface(ast.Variable{
|
val, err := VariableToInterface(ast.Variable{
|
||||||
|
@ -264,6 +272,10 @@ func (v *evalCall) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type, e
|
||||||
args := make([]interface{}, len(v.Args))
|
args := make([]interface{}, len(v.Args))
|
||||||
for i, _ := range v.Args {
|
for i, _ := range v.Args {
|
||||||
node := stack.Pop().(*ast.LiteralNode)
|
node := stack.Pop().(*ast.LiteralNode)
|
||||||
|
if node.IsUnknown() {
|
||||||
|
// If any arguments are unknown then the result is automatically unknown
|
||||||
|
return UnknownValue, ast.TypeUnknown, nil
|
||||||
|
}
|
||||||
args[len(v.Args)-1-i] = node.Value
|
args[len(v.Args)-1-i] = node.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,6 +298,11 @@ func (v *evalConditional) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.
|
||||||
trueLit := stack.Pop().(*ast.LiteralNode)
|
trueLit := stack.Pop().(*ast.LiteralNode)
|
||||||
condLit := stack.Pop().(*ast.LiteralNode)
|
condLit := stack.Pop().(*ast.LiteralNode)
|
||||||
|
|
||||||
|
if condLit.IsUnknown() {
|
||||||
|
// If our conditional is unknown then our result is also unknown
|
||||||
|
return UnknownValue, ast.TypeUnknown, nil
|
||||||
|
}
|
||||||
|
|
||||||
if condLit.Value.(bool) {
|
if condLit.Value.(bool) {
|
||||||
return trueLit.Value, trueLit.Typex, nil
|
return trueLit.Value, trueLit.Typex, nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -301,6 +318,17 @@ func (v *evalIndex) Eval(scope ast.Scope, stack *ast.Stack) (interface{}, ast.Ty
|
||||||
|
|
||||||
variableName := v.Index.Target.(*ast.VariableAccess).Name
|
variableName := v.Index.Target.(*ast.VariableAccess).Name
|
||||||
|
|
||||||
|
if key.IsUnknown() {
|
||||||
|
// If our key is unknown then our result is also unknown
|
||||||
|
return UnknownValue, ast.TypeUnknown, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// For target, we'll accept collections containing unknown values but
|
||||||
|
// we still need to catch when the collection itself is unknown, shallowly.
|
||||||
|
if target.Typex == ast.TypeUnknown {
|
||||||
|
return UnknownValue, ast.TypeUnknown, nil
|
||||||
|
}
|
||||||
|
|
||||||
switch target.Typex {
|
switch target.Typex {
|
||||||
case ast.TypeList:
|
case ast.TypeList:
|
||||||
return v.evalListIndex(variableName, target.Value, key.Value)
|
return v.evalListIndex(variableName, target.Value, key.Value)
|
||||||
|
@ -377,8 +405,22 @@ func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type,
|
||||||
// The expressions should all be on the stack in reverse
|
// The expressions should all be on the stack in reverse
|
||||||
// order. So pop them off, reverse their order, and concatenate.
|
// order. So pop them off, reverse their order, and concatenate.
|
||||||
nodes := make([]*ast.LiteralNode, 0, len(v.Exprs))
|
nodes := make([]*ast.LiteralNode, 0, len(v.Exprs))
|
||||||
|
haveUnknown := false
|
||||||
for range v.Exprs {
|
for range v.Exprs {
|
||||||
nodes = append(nodes, stack.Pop().(*ast.LiteralNode))
|
n := stack.Pop().(*ast.LiteralNode)
|
||||||
|
nodes = append(nodes, n)
|
||||||
|
|
||||||
|
// If we have any unknowns then the whole result is unknown
|
||||||
|
// (we must deal with this first, because the type checker can
|
||||||
|
// skip type conversions in the presence of unknowns, and thus
|
||||||
|
// any of our other nodes may be incorrectly typed.)
|
||||||
|
if n.IsUnknown() {
|
||||||
|
haveUnknown = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if haveUnknown {
|
||||||
|
return UnknownValue, ast.TypeUnknown, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case the single list and map
|
// Special case the single list and map
|
||||||
|
@ -396,6 +438,14 @@ func (v *evalOutput) Eval(s ast.Scope, stack *ast.Stack) (interface{}, ast.Type,
|
||||||
// Otherwise concatenate the strings
|
// Otherwise concatenate the strings
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
for i := len(nodes) - 1; i >= 0; i-- {
|
for i := len(nodes) - 1; i >= 0; i-- {
|
||||||
|
if nodes[i].Typex != ast.TypeString {
|
||||||
|
return nil, ast.TypeInvalid, fmt.Errorf(
|
||||||
|
"invalid output with %s value at index %d: %#v",
|
||||||
|
nodes[i].Typex,
|
||||||
|
i,
|
||||||
|
nodes[i].Value,
|
||||||
|
)
|
||||||
|
}
|
||||||
buf.WriteString(nodes[i].Value.(string))
|
buf.WriteString(nodes[i].Value.(string))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,11 +468,5 @@ func (v *evalVariableAccess) Eval(scope ast.Scope, _ *ast.Stack) (interface{}, a
|
||||||
"unknown variable accessed: %s", v.Name)
|
"unknown variable accessed: %s", v.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the variable contains any unknown types. If so, then
|
|
||||||
// mark it as unknown and return that type.
|
|
||||||
if ast.IsUnknown(variable) {
|
|
||||||
return nil, ast.TypeUnknown, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return variable.Value, variable.Type, nil
|
return variable.Value, variable.Type, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2138,28 +2138,28 @@
|
||||||
"revisionTime": "2017-05-04T19:02:34Z"
|
"revisionTime": "2017-05-04T19:02:34Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "2Nrl/YKrmowkRgCDLhA6UTFgYEY=",
|
"checksumSHA1": "zz3/f3YpHHBN78uLhnhLBW2aF8o=",
|
||||||
"path": "github.com/hashicorp/hil",
|
"path": "github.com/hashicorp/hil",
|
||||||
"revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2",
|
"revision": "747a6e1523d6808f91144df070435b16865cd333",
|
||||||
"revisionTime": "2016-12-21T19:20:42Z"
|
"revisionTime": "2017-05-01T20:07:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "oZ2a2x9qyHqvqJdv/Du3LGeaFdA=",
|
"checksumSHA1": "0S0KeBcfqVFYBPeZkuJ4fhQ5mCA=",
|
||||||
"path": "github.com/hashicorp/hil/ast",
|
"path": "github.com/hashicorp/hil/ast",
|
||||||
"revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2",
|
"revision": "747a6e1523d6808f91144df070435b16865cd333",
|
||||||
"revisionTime": "2016-12-21T19:20:42Z"
|
"revisionTime": "2017-05-01T20:07:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "P5PZ3k7SmqWmxgJ8Q0gLzeNpGhE=",
|
"checksumSHA1": "P5PZ3k7SmqWmxgJ8Q0gLzeNpGhE=",
|
||||||
"path": "github.com/hashicorp/hil/parser",
|
"path": "github.com/hashicorp/hil/parser",
|
||||||
"revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2",
|
"revision": "747a6e1523d6808f91144df070435b16865cd333",
|
||||||
"revisionTime": "2016-12-21T19:20:42Z"
|
"revisionTime": "2017-05-01T20:07:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "DC1k5kOua4oFqmo+JRt0YzfP44o=",
|
"checksumSHA1": "DC1k5kOua4oFqmo+JRt0YzfP44o=",
|
||||||
"path": "github.com/hashicorp/hil/scanner",
|
"path": "github.com/hashicorp/hil/scanner",
|
||||||
"revision": "5b8d13c8c5c2753e109fab25392a1dbfa2db93d2",
|
"revision": "747a6e1523d6808f91144df070435b16865cd333",
|
||||||
"revisionTime": "2016-12-21T19:20:42Z"
|
"revisionTime": "2017-05-01T20:07:50Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "vt+P9D2yWDO3gdvdgCzwqunlhxU=",
|
"checksumSHA1": "vt+P9D2yWDO3gdvdgCzwqunlhxU=",
|
||||||
|
|
Loading…
Reference in New Issue