@@ -44,11 +44,17 @@ var stringArrayTypeInfo = &typeInfo{Type: reflect.ArrayOf(2, stringType), Proper
44
44
var boolSliceTypeInfo = & typeInfo {Type : reflect .SliceOf (boolType ), Properties : propertyAddressable }
45
45
var boolArrayTypeInfo = & typeInfo {Type : reflect .ArrayOf (2 , boolType ), Properties : propertyAddressable }
46
46
var interfaceSliceTypeInfo = & typeInfo {Type : reflect .SliceOf (emptyInterfaceType ), Properties : propertyAddressable }
47
+ var stringToAnyMapTypeInfo = & typeInfo {Type : reflect .MapOf (stringType , emptyInterfaceType ), Properties : propertyAddressable }
47
48
48
49
var stringToIntMapTypeInfo = & typeInfo {Type : reflect .MapOf (stringType , intType ), Properties : propertyAddressable }
49
50
var intToStringMapTypeInfo = & typeInfo {Type : reflect .MapOf (intType , stringType ), Properties : propertyAddressable }
51
+ var intToAnyMapTypeInfo = & typeInfo {Type : reflect .MapOf (intType , emptyInterfaceType ), Properties : propertyAddressable }
52
+ var anyToIntMapTypeInfo = & typeInfo {Type : reflect .MapOf (emptyInterfaceType , intType ), Properties : propertyAddressable }
53
+ var definedStringToStringMapTypeInfo = & typeInfo {Type : reflect .MapOf (definedStringTypeInfo .Type , stringType ), Properties : propertyAddressable }
50
54
var definedIntToStringMapTypeInfo = & typeInfo {Type : reflect .MapOf (definedIntTypeInfo .Type , stringType ), Properties : propertyAddressable }
51
55
56
+ var stringToStringToIntMapTypeInfo = & typeInfo {Type : reflect .MapOf (stringType , reflect .MapOf (stringType , intType )), Properties : propertyAddressable }
57
+
52
58
var definedIntTypeInfo = & typeInfo {Type : reflect .TypeOf (definedInt (0 )), Properties : propertyAddressable }
53
59
var definedIntSliceTypeInfo = & typeInfo {Type : reflect .SliceOf (definedIntTypeInfo .Type ), Properties : propertyAddressable }
54
60
@@ -70,6 +76,12 @@ func tiMarkdown() *typeInfo {
70
76
return & typeInfo {Type : formatTypes [ast .FormatMarkdown ]}
71
77
}
72
78
79
+ type TF struct {
80
+ F int
81
+ }
82
+
83
+ var stringToTFMapTypeInfo = & typeInfo {Type : reflect .MapOf (stringType , reflect .TypeOf (TF {})), Properties : propertyAddressable }
84
+
73
85
var checkerTemplateExprs = []struct {
74
86
src string
75
87
ti * typeInfo
@@ -156,6 +168,16 @@ var checkerTemplateExprs = []struct {
156
168
// conversion from markdown to html
157
169
{`html(a)` , tiHTMLConst ("<h1>title</h1>" ), map [string ]* typeInfo {"a" : tiMarkdownConst ("# title" )}},
158
170
{`html(a)` , tiHTML (), map [string ]* typeInfo {"a" : tiMarkdown ()}},
171
+
172
+ // key selector
173
+ {`m.x` , tiInt (), map [string ]* typeInfo {"m" : stringToIntMapTypeInfo }},
174
+ {`m.x` , tiString (), map [string ]* typeInfo {"m" : definedStringToStringMapTypeInfo }},
175
+ {`m.x` , tiInt (), map [string ]* typeInfo {"m" : anyToIntMapTypeInfo }},
176
+ {`m.x.y` , tiInt (), map [string ]* typeInfo {"m" : stringToStringToIntMapTypeInfo }},
177
+ {`m.x.y` , tiInterface (), map [string ]* typeInfo {"m" : stringToAnyMapTypeInfo }},
178
+ {`m.x.y.z` , tiInterface (), map [string ]* typeInfo {"m" : stringToAnyMapTypeInfo }},
179
+ {`m.x.nil` , tiInterface (), map [string ]* typeInfo {"m" : stringToAnyMapTypeInfo }},
180
+ {`m.x.F` , tiInt (), map [string ]* typeInfo {"m" : stringToTFMapTypeInfo }},
159
181
}
160
182
161
183
func TestCheckerTemplateExpressions (t * testing.T ) {
@@ -236,6 +258,14 @@ var checkerTemplateExprErrors = []struct {
236
258
// slicing of a format type
237
259
{`a[1:2]` , tierr (1 , 5 , `invalid operation a[1:2] (slice of compiler.html)` ), map [string ]* typeInfo {"a" : tiHTMLConst ("<b>a</b>" )}},
238
260
{`a[1:2]` , tierr (1 , 5 , `invalid operation a[1:2] (slice of compiler.html)` ), map [string ]* typeInfo {"a" : tiHTML ()}},
261
+
262
+ // key selector
263
+ {`m.x` , tierr (1 , 5 , `invalid operation: cannot select m.x (type map[int]string does not support key selection)` ), map [string ]* typeInfo {"m" : intToStringMapTypeInfo }},
264
+ {`m.x` , tierr (1 , 5 , `invalid operation: cannot select m.x (type map[int]interface {} does not support key selection)` ), map [string ]* typeInfo {"m" : intToAnyMapTypeInfo }},
265
+ {`m.x.y` , tierr (1 , 7 , `m.x.y undefined (type int has no field or method y)` ), map [string ]* typeInfo {"m" : stringToIntMapTypeInfo }},
266
+ {`m.x.y.z` , tierr (1 , 7 , `m.x.y undefined (type int has no field or method y)` ), map [string ]* typeInfo {"m" : stringToIntMapTypeInfo }},
267
+ {`nil.x.y.z` , tierr (1 , 4 , `use of untyped nil` ), nil },
268
+ {`m._` , tierr (1 , 5 , `cannot refer to blank field or method` ), map [string ]* typeInfo {"m" : stringToAnyMapTypeInfo }},
239
269
}
240
270
241
271
func TestCheckerTemplateExpressionErrors (t * testing.T ) {
@@ -613,6 +643,23 @@ var checkerTemplateStmts = []struct {
613
643
{src : `{% L: select %}{% default %}{% break L %}{% end %}` , expected : ok },
614
644
{src : `{% _ = func() { L: goto L } %}` , expected : ok },
615
645
{src : `{% L: for %}{% _ = func() { goto L } %}{% end %}` , expected : `label L not defined` },
646
+
647
+ // Key selector.
648
+ {src : `{% m := map[string]int{} %}{% m.x = 5 %}{% m.x += 1 %}{% m.x++ %}{{ m.x + 2 }}` , expected : ok },
649
+ {src : `{% m := map[DS]int{} %}{% m.x = 5 %}{% m.x += 1 %}{% m.x++ %}{{ m.x + 2 }}` , expected : ok },
650
+ {src : `{% m := map[string]bool{} %}{% m.nil = true %}{{ m.nil && false }}` , expected : ok },
651
+ {src : `{% m := map[interface{}]string{} %}{% m.x = "a" %}{{ m.a + "b" }}` , expected : ok },
652
+ {src : `{% m := map[string]interface{}{} %}{% m.x = 6.89 %}{{ m.x.(float64) - 1.4 }}` , expected : ok },
653
+ {src : `{% m := map[string]interface{}{} %}{% m.x = map[string]interface{}{} %}{% m.x.y = true %}` , expected : `cannot index m.x (map index expression of type interface{})` },
654
+ {src : `{% m := map[interface{}]interface{}{} %}{% m.x = map[string]interface{}{} %}{% m.x.y = 'v' %}` , expected : `cannot index m.x (map index expression of type interface{})` },
655
+ {src : `{% m := map[string]map[string]int{} %}{% m.x = map[string]int{} %}{% m.x.y = 3 %}{% m.x.y += 1 %}{% m.x.y++ %}{{ m.x.y * 2 }}` , expected : ok },
656
+ {src : `{% m := map[string]map[string]interface{}{} %}{% m.x = map[string]interface{}{} %}{% m.x.y = "a" %}{{ m.x.y.(string) + "b" }}` , expected : ok },
657
+ {src : `{% m := map[string]int{} %}{% m.x = "a" %}` , expected : `cannot use "a" (type untyped string) as type int in assignment` },
658
+ {src : `{% m := map[interface{}]int{} %}{% m.x = "a" %}` , expected : `cannot use "a" (type untyped string) as type int in assignment` },
659
+ {src : `{% m := map[string]int{} %}{% m.x := "a" %}` , expected : `non-name m.x on left side of :=` },
660
+ {src : `{% m := map[interface{}]int{} %}{% m.x := "a" %}` , expected : `non-name m.x on left side of :=` },
661
+ {src : `{% m := map[string]int{} %}{{ m.x + "a" }}` , expected : `invalid operation: m.x + "a" (cannot convert "a" (type untyped string) to type int)` },
662
+ {src : `{% m := map[interface{}]int{} %}{{ m.x + "a" }}` , expected : `invalid operation: m.x + "a" (cannot convert "a" (type untyped string) to type int)` },
616
663
}
617
664
618
665
func TestCheckerTemplatesStatements (t * testing.T ) {
@@ -633,6 +680,7 @@ func TestCheckerTemplatesStatements(t *testing.T) {
633
680
"Ui" : native .UntypedNumericConst ("5" ),
634
681
"Uf" : native .UntypedNumericConst ("5.0" ),
635
682
"R" : 'r' ,
683
+ "DS" : reflect .TypeOf (definedString ("" )),
636
684
},
637
685
}
638
686
for _ , cas := range checkerTemplateStmts {
0 commit comments