functions: Improve marks support for length

Similar to cty's implementation, we only need to preserve marks from the
value itself, not any nested values it may contain. This means that
taking the length of an umarked list with marked elements results in an
unmarked number.
This commit is contained in:
Alisdair McDiarmid 2021-05-07 11:57:37 -04:00
parent 8d4d333efe
commit e0c6b3fcda
2 changed files with 53 additions and 3 deletions

View File

@ -20,6 +20,7 @@ var LengthFunc = function.New(&function.Spec{
Type: cty.DynamicPseudoType,
AllowDynamicType: true,
AllowUnknown: true,
AllowMarked: true,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
@ -34,15 +35,16 @@ var LengthFunc = function.New(&function.Spec{
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) {
coll := args[0]
collTy := args[0].Type()
marks := coll.Marks()
switch {
case collTy == cty.DynamicPseudoType:
return cty.UnknownVal(cty.Number), nil
return cty.UnknownVal(cty.Number).WithMarks(marks), nil
case collTy.IsTupleType():
l := len(collTy.TupleElementTypes())
return cty.NumberIntVal(int64(l)), nil
return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
case collTy.IsObjectType():
l := len(collTy.AttributeTypes())
return cty.NumberIntVal(int64(l)), nil
return cty.NumberIntVal(int64(l)).WithMarks(marks), nil
case collTy == cty.String:
// We'll delegate to the cty stdlib strlen function here, because
// it deals with all of the complexities of tokenizing unicode

View File

@ -122,6 +122,54 @@ func TestLength(t *testing.T) {
cty.DynamicVal,
cty.UnknownVal(cty.Number),
},
{ // Marked collections return a marked length
cty.ListVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}).Mark("secret"),
cty.NumberIntVal(2).Mark("secret"),
},
{ // Marks on values in unmarked collections do not propagate
cty.ListVal([]cty.Value{
cty.StringVal("hello").Mark("a"),
cty.StringVal("world").Mark("b"),
}),
cty.NumberIntVal(2),
},
{ // Marked strings return a marked length
cty.StringVal("hello world").Mark("secret"),
cty.NumberIntVal(11).Mark("secret"),
},
{ // Marked tuples return a marked length
cty.TupleVal([]cty.Value{
cty.StringVal("hello"),
cty.StringVal("world"),
}).Mark("secret"),
cty.NumberIntVal(2).Mark("secret"),
},
{ // Marks on values in unmarked tuples do not propagate
cty.TupleVal([]cty.Value{
cty.StringVal("hello").Mark("a"),
cty.StringVal("world").Mark("b"),
}),
cty.NumberIntVal(2),
},
{ // Marked objects return a marked length
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("hello"),
"b": cty.StringVal("world"),
"c": cty.StringVal("nice to meet you"),
}).Mark("secret"),
cty.NumberIntVal(3).Mark("secret"),
},
{ // Marks on object attribute values do not propagate
cty.ObjectVal(map[string]cty.Value{
"a": cty.StringVal("hello").Mark("a"),
"b": cty.StringVal("world").Mark("b"),
"c": cty.StringVal("nice to meet you").Mark("c"),
}),
cty.NumberIntVal(3),
},
}
for _, test := range tests {