diff --git a/lang/funcs/collection.go b/lang/funcs/collection.go index cca7423c0..0b5922cb1 100644 --- a/lang/funcs/collection.go +++ b/lang/funcs/collection.go @@ -220,50 +220,6 @@ func flattener(flattenList cty.Value) ([]cty.Value, bool) { return out, true } -// ListFunc constructs a function that takes an arbitrary number of arguments -// and returns a list containing those values in the same order. -// -// This function is deprecated in Terraform v0.12 -var ListFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) == 0 { - return cty.NilType, errors.New("at least one argument is required") - } - - argTypes := make([]cty.Type, len(args)) - - for i, arg := range args { - argTypes[i] = arg.Type() - } - - retType, _ := convert.UnifyUnsafe(argTypes) - if retType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.List(retType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - newList := make([]cty.Value, 0, len(args)) - - for _, arg := range args { - // We already know this will succeed because of the checks in our Type func above - arg, _ = convert.Convert(arg, retType.ElementType()) - newList = append(newList, arg) - } - - return cty.ListVal(newList), nil - }, -}) - // LookupFunc constructs a function that performs dynamic lookups of map types. var LookupFunc = function.New(&function.Spec{ Params: []function.Parameter{ @@ -354,81 +310,6 @@ var LookupFunc = function.New(&function.Spec{ }, }) -// MapFunc constructs a function that takes an even number of arguments and -// returns a map whose elements are constructed from consecutive pairs of arguments. -// -// This function is deprecated in Terraform v0.12 -var MapFunc = function.New(&function.Spec{ - Params: []function.Parameter{}, - VarParam: &function.Parameter{ - Name: "vals", - Type: cty.DynamicPseudoType, - AllowUnknown: true, - AllowDynamicType: true, - AllowNull: true, - }, - Type: func(args []cty.Value) (ret cty.Type, err error) { - if len(args) < 2 || len(args)%2 != 0 { - return cty.NilType, fmt.Errorf("map requires an even number of two or more arguments, got %d", len(args)) - } - - argTypes := make([]cty.Type, len(args)/2) - index := 0 - - for i := 0; i < len(args); i += 2 { - argTypes[index] = args[i+1].Type() - index++ - } - - valType, _ := convert.UnifyUnsafe(argTypes) - if valType == cty.NilType { - return cty.NilType, errors.New("all arguments must have the same type") - } - - return cty.Map(valType), nil - }, - Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { - for _, arg := range args { - if !arg.IsWhollyKnown() { - return cty.UnknownVal(retType), nil - } - } - - outputMap := make(map[string]cty.Value) - - for i := 0; i < len(args); i += 2 { - - keyVal, err := convert.Convert(args[i], cty.String) - if err != nil { - return cty.NilVal, err - } - if keyVal.IsNull() { - return cty.NilVal, fmt.Errorf("argument %d is a null key", i+1) - } - key := keyVal.AsString() - - val := args[i+1] - - var variable cty.Value - err = gocty.FromCtyValue(val, &variable) - if err != nil { - return cty.NilVal, err - } - - // We already know this will succeed because of the checks in our Type func above - variable, _ = convert.Convert(variable, retType.ElementType()) - - // Check for duplicate keys - if _, ok := outputMap[key]; ok { - return cty.NilVal, fmt.Errorf("argument %d is a duplicate key: %q", i+1, key) - } - outputMap[key] = variable - } - - return cty.MapVal(outputMap), nil - }, -}) - // MatchkeysFunc constructs a function that constructs a new list by taking a // subset of elements from one list whose indexes match the corresponding // indexes of values in another list. @@ -614,6 +495,48 @@ var TransposeFunc = function.New(&function.Spec{ }, }) +// ListFunc constructs a function that takes an arbitrary number of arguments +// and returns a list containing those values in the same order. +// +// This function is deprecated in Terraform v0.12 +var ListFunc = function.New(&function.Spec{ + Params: []function.Parameter{}, + VarParam: &function.Parameter{ + Name: "vals", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowDynamicType: true, + AllowNull: true, + }, + Type: func(args []cty.Value) (ret cty.Type, err error) { + return cty.DynamicPseudoType, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + return cty.DynamicVal, fmt.Errorf("the \"list\" function was deprecated in Terraform v0.12 and is no longer available; use tolist([ ... ]) syntax to write a literal list") + }, +}) + +// MapFunc constructs a function that takes an even number of arguments and +// returns a map whose elements are constructed from consecutive pairs of arguments. +// +// This function is deprecated in Terraform v0.12 +var MapFunc = function.New(&function.Spec{ + Params: []function.Parameter{}, + VarParam: &function.Parameter{ + Name: "vals", + Type: cty.DynamicPseudoType, + AllowUnknown: true, + AllowDynamicType: true, + AllowNull: true, + }, + Type: func(args []cty.Value) (ret cty.Type, err error) { + return cty.DynamicPseudoType, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") + }, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + return cty.DynamicVal, fmt.Errorf("the \"map\" function was deprecated in Terraform v0.12 and is no longer available; use tomap({ ... }) syntax to write a literal map") + }, +}) + // helper function to add an element to a list, if it does not already exist func appendIfMissing(slice []cty.Value, element cty.Value) ([]cty.Value, error) { for _, ele := range slice { diff --git a/lang/funcs/collection_test.go b/lang/funcs/collection_test.go index f74b3aafc..89cb2a15f 100644 --- a/lang/funcs/collection_test.go +++ b/lang/funcs/collection_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/function" ) func TestLength(t *testing.T) { @@ -472,83 +471,6 @@ func TestIndex(t *testing.T) { } } -func TestList(t *testing.T) { - tests := []struct { - Values []cty.Value - Want cty.Value - Err bool - }{ - { - []cty.Value{ - cty.NilVal, - }, - cty.NilVal, - true, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("World"), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("World"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.NumberIntVal(42), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.StringVal("42"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("Hello"), - cty.UnknownVal(cty.String), - }, - cty.ListVal([]cty.Value{ - cty.StringVal("Hello"), - cty.UnknownVal(cty.String), - }), - false, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("list(%#v)", test.Values), func(t *testing.T) { - got, err := List(test.Values...) - - if test.Err { - if err == nil { - t.Fatal("succeeded; want error") - } - return - } else if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - if !got.RawEquals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - func TestLookup(t *testing.T) { simpleMap := cty.MapVal(map[string]cty.Value{ "foo": cty.StringVal("bar"), @@ -802,169 +724,6 @@ func TestLookup(t *testing.T) { } } -func TestMap(t *testing.T) { - tests := []struct { - Values []cty.Value - Want cty.Value - Err bool - }{ - { - []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("hello"), - cty.UnknownVal(cty.String), - }, - cty.UnknownVal(cty.Map(cty.String)), - false, - }, - { - []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - cty.StringVal("what's"), - cty.StringVal("up"), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - "what's": cty.StringVal("up"), - }), - false, - }, - { - []cty.Value{ - cty.StringVal("hello"), - cty.NumberIntVal(1), - cty.StringVal("goodbye"), - cty.NumberIntVal(42), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.NumberIntVal(1), - "goodbye": cty.NumberIntVal(42), - }), - false, - }, - { // convert numbers to strings - []cty.Value{ - cty.StringVal("hello"), - cty.NumberIntVal(1), - cty.StringVal("goodbye"), - cty.StringVal("42"), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("1"), - "goodbye": cty.StringVal("42"), - }), - false, - }, - { // convert number keys to strings - []cty.Value{ - cty.NumberIntVal(1), - cty.StringVal("hello"), - cty.NumberIntVal(2), - cty.StringVal("goodbye"), - }, - cty.MapVal(map[string]cty.Value{ - "1": cty.StringVal("hello"), - "2": cty.StringVal("goodbye"), - }), - false, - }, - { // map of lists is okay - []cty.Value{ - cty.StringVal("hello"), - cty.ListVal([]cty.Value{ - cty.StringVal("world"), - }), - cty.StringVal("what's"), - cty.ListVal([]cty.Value{ - cty.StringVal("up"), - }), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.ListVal([]cty.Value{cty.StringVal("world")}), - "what's": cty.ListVal([]cty.Value{cty.StringVal("up")}), - }), - false, - }, - { // map of maps is okay - []cty.Value{ - cty.StringVal("hello"), - cty.MapVal(map[string]cty.Value{ - "there": cty.StringVal("world"), - }), - cty.StringVal("what's"), - cty.MapVal(map[string]cty.Value{ - "really": cty.StringVal("up"), - }), - }, - cty.MapVal(map[string]cty.Value{ - "hello": cty.MapVal(map[string]cty.Value{ - "there": cty.StringVal("world"), - }), - "what's": cty.MapVal(map[string]cty.Value{ - "really": cty.StringVal("up"), - }), - }), - false, - }, - { // single argument returns an error - []cty.Value{ - cty.StringVal("hello"), - }, - cty.NilVal, - true, - }, - { // duplicate keys returns an error - []cty.Value{ - cty.StringVal("hello"), - cty.StringVal("world"), - cty.StringVal("hello"), - cty.StringVal("universe"), - }, - cty.NilVal, - true, - }, - { // null key returns an error - []cty.Value{ - cty.NullVal(cty.DynamicPseudoType), - cty.NumberIntVal(5), - }, - cty.NilVal, - true, - }, - } - - for _, test := range tests { - t.Run(fmt.Sprintf("map(%#v)", test.Values), func(t *testing.T) { - got, err := Map(test.Values...) - if test.Err { - if err == nil { - t.Fatal("succeeded; want error") - } - if _, ok := err.(function.PanicError); ok { - t.Fatalf("unexpected panic: %s", err) - } - return - } else if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - if !got.RawEquals(test.Want) { - t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) - } - }) - } -} - func TestMatchkeys(t *testing.T) { tests := []struct { Keys cty.Value diff --git a/lang/functions_test.go b/lang/functions_test.go index 4690a4a05..46be8649f 100644 --- a/lang/functions_test.go +++ b/lang/functions_test.go @@ -224,7 +224,7 @@ func TestFunctions(t *testing.T) { "coalescelist": { { - `coalescelist(list("a", "b"), list("c", "d"))`, + `coalescelist(tolist(["a", "b"]), tolist(["c", "d"]))`, cty.ListVal([]cty.Value{ cty.StringVal("a"), cty.StringVal("b"), @@ -512,12 +512,8 @@ func TestFunctions(t *testing.T) { }, "list": { - { - `list("hello")`, - cty.ListVal([]cty.Value{ - cty.StringVal("hello"), - }), - }, + // There are intentionally no test cases for "list" because + // it is a stub that always returns an error. }, "log": { @@ -542,12 +538,8 @@ func TestFunctions(t *testing.T) { }, "map": { - { - `map("hello", "world")`, - cty.MapVal(map[string]cty.Value{ - "hello": cty.StringVal("world"), - }), - }, + // There are intentionally no test cases for "map" because + // it is a stub that always returns an error. }, "matchkeys": { @@ -759,7 +751,7 @@ func TestFunctions(t *testing.T) { "slice": { { // force a list type here for testing - `slice(list("a", "b", "c", "d"), 1, 3)`, + `slice(tolist(["a", "b", "c", "d"]), 1, 3)`, cty.ListVal([]cty.Value{ cty.StringVal("b"), cty.StringVal("c"), }), diff --git a/terraform/testdata/apply-resource-scale-in/main.tf b/terraform/testdata/apply-resource-scale-in/main.tf index 0363d89b7..8cb38473e 100644 --- a/terraform/testdata/apply-resource-scale-in/main.tf +++ b/terraform/testdata/apply-resource-scale-in/main.tf @@ -5,7 +5,7 @@ resource "aws_instance" "one" { } locals { - one_id = element(concat(aws_instance.one.*.id, list("")), 0) + one_id = element(concat(aws_instance.one.*.id, [""]), 0) } resource "aws_instance" "two" { diff --git a/terraform/testdata/plan-for-each/main.tf b/terraform/testdata/plan-for-each/main.tf index bffb079cb..94572e20a 100644 --- a/terraform/testdata/plan-for-each/main.tf +++ b/terraform/testdata/plan-for-each/main.tf @@ -13,7 +13,7 @@ resource "aws_instance" "bar" { for_each = toset([]) } resource "aws_instance" "bar2" { - for_each = toset(list("z", "y", "x")) + for_each = toset(["z", "y", "x"]) } # an empty map should generate no resource diff --git a/website/docs/configuration/functions/list.html.md b/website/docs/configuration/functions/list.html.md index 0313bac13..cb6eabb9c 100644 --- a/website/docs/configuration/functions/list.html.md +++ b/website/docs/configuration/functions/list.html.md @@ -12,37 +12,22 @@ description: |- earlier, see [0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). -~> **This function is deprecated.** From Terraform v0.12, the Terraform -language has built-in syntax for creating lists using the `[` and `]` -delimiters. Use the built-in syntax instead. The `list` function will be -removed in a future version of Terraform. +The `list` function is no longer available. Prior to Terraform v0.12 it was +the only available syntax for writing a literal list inside an expression, +but Terraform v0.12 introduced a new first-class syntax. -`list` takes an arbitrary number of arguments and returns a list containing -those values in the same order. - -## Examples +To update an expression like `list(a, b, c)`, write the following instead: ``` -> list("a", "b", "c") -[ - "a", - "b", - "c", -] +tolist([a, b, c]) ``` -Do not use the above form in Terraform v0.12 or above. Instead, use the -built-in list construction syntax, which achieves the same result: - -``` -> ["a", "b", "c"] -[ - "a", - "b", - "c", -] -``` +The `[ ... ]` brackets construct a tuple value, and then the `tolist` function +then converts it to a list. For more information on the value types in the +Terraform language, see [Type Constraints](../types.html). ## Related Functions -* [`tolist`](./tolist.html) converts a set value to a list. +* [`concat`](./concat.html) produces a new list by concatenating together the + elements from other lists. +* [`tolist`](./tolist.html) converts a set or tuple value to a list. diff --git a/website/docs/configuration/functions/map.html.md b/website/docs/configuration/functions/map.html.md index 4735b8788..58c3e0909 100644 --- a/website/docs/configuration/functions/map.html.md +++ b/website/docs/configuration/functions/map.html.md @@ -12,35 +12,25 @@ description: |- earlier, see [0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html). -~> **This function is deprecated.** From Terraform v0.12, the Terraform -language has built-in syntax for creating maps using the `{` and `}` -delimiters. Use the built-in syntax instead. The `map` function will be -removed in a future version of Terraform. +The `map` function is no longer available. Prior to Terraform v0.12 it was +the only available syntax for writing a literal map inside an expression, +but Terraform v0.12 introduced a new first-class syntax. -`map` takes an even number of arguments and returns a map whose elements -are constructed from consecutive pairs of arguments. - -## Examples +To update an expression like `map("a", "b", "c", "d")`, write the following instead: ``` -> map("a", "b", "c", "d") -{ - "a" = "b" - "c" = "d" -} +tomap({ + a = "b" + c = "d" +}) ``` -Do not use the above form in Terraform v0.12 or above. Instead, use the -built-in map construction syntax, which achieves the same result: - -``` -> {"a" = "b", "c" = "d"} -{ - "a" = "b" - "c" = "d" -} -``` +The `{ ... }` braces construct an object value, and then the `tomap` function +then converts it to a map. For more information on the value types in the +Terraform language, see [Type Constraints](../types.html). ## Related Functions -* [`tomap`](./tomap.html) performs a type conversion to a map type. +* [`tomap`](./tomap.html) converts an object value to a map. +* [`zipmap`](./zipmap.html) constructs a map dynamically, by taking keys from + one list and values from another list.