Commit 0f7b4e72 authored by Daniel Martí's avatar Daniel Martí

text/template: don't evaluate '.' as a float64

When using a '.' constant literal as a reflect.Value variadic argument,
idealConstant would incorrectly result in a float64. This is because
rune literals can be represented as a float64, and contain a period,
which tricked the logic into thinking the literal must have been a
floating point number.

This also happened with other characters that can be part of a floating
point number, such as 'e' or 'P'.

To fix these edge cases, exit the case sooner if the literal was a rune,
since that should always go to the int case instead.

Finally, add test cases that verify that they behave properly. These
would error before, since eq would receive a mix of int and float64,
which aren't comparable.

Fixes #34483.

Change-Id: Icfcb7803bfa0cf317a1d1adacacad3d69a57eb42
Reviewed-on: https://go-review.googlesource.com/c/go/+/196808
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarTom Payne <tom@airmap.com>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent 95cbcc5c
...@@ -496,20 +496,29 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value { ...@@ -496,20 +496,29 @@ func (s *state) idealConstant(constant *parse.NumberNode) reflect.Value {
switch { switch {
case constant.IsComplex: case constant.IsComplex:
return reflect.ValueOf(constant.Complex128) // incontrovertible. return reflect.ValueOf(constant.Complex128) // incontrovertible.
case constant.IsFloat && !isHexInt(constant.Text) && strings.ContainsAny(constant.Text, ".eEpP"):
case constant.IsFloat &&
!isHexInt(constant.Text) && !isRuneInt(constant.Text) &&
strings.ContainsAny(constant.Text, ".eEpP"):
return reflect.ValueOf(constant.Float64) return reflect.ValueOf(constant.Float64)
case constant.IsInt: case constant.IsInt:
n := int(constant.Int64) n := int(constant.Int64)
if int64(n) != constant.Int64 { if int64(n) != constant.Int64 {
s.errorf("%s overflows int", constant.Text) s.errorf("%s overflows int", constant.Text)
} }
return reflect.ValueOf(n) return reflect.ValueOf(n)
case constant.IsUint: case constant.IsUint:
s.errorf("%s overflows int", constant.Text) s.errorf("%s overflows int", constant.Text)
} }
return zero return zero
} }
func isRuneInt(s string) bool {
return len(s) > 0 && s[0] == '\''
}
func isHexInt(s string) bool { func isHexInt(s string) bool {
return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && !strings.ContainsAny(s, "pP") return len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && !strings.ContainsAny(s, "pP")
} }
......
...@@ -663,6 +663,12 @@ var execTests = []execTest{ ...@@ -663,6 +663,12 @@ var execTests = []execTest{
{"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true}, {"bug17c", "{{len .NonEmptyInterfacePtS}}", "2", tVal, true},
{"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true}, {"bug17d", "{{index .NonEmptyInterfacePtS 0}}", "a", tVal, true},
{"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true}, {"bug17e", "{{range .NonEmptyInterfacePtS}}-{{.}}-{{end}}", "-a--b-", tVal, true},
// More variadic function corner cases. Some runes would get evaluated
// as constant floats instead of ints. Issue 34483.
{"bug18a", "{{eq . '.'}}", "true", '.', true},
{"bug18b", "{{eq . 'e'}}", "true", 'e', true},
{"bug18c", "{{eq . 'P'}}", "true", 'P', true},
} }
func zeroArgs() string { func zeroArgs() string {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment