hcl: All number parsing should use cty.ParseNumberVal

When dealing with numbers that have no finite representation in base 2, it
is important that all parsers agree on the expected maximum precision.
Previously we had agreement by convention, but for robustness here we'll
centralize the handling of number parsing to cty.ParseNumberVal, which
uses the same settings as we were previously using in the JSON parser and,
for the native syntax parser, is just a shorthand to the same parsing
we were previously doing with the cty/convert package.

This should cause no behavior change since all of these callers were
previously in agreement with the cty "standard", but this factoring helps
establish that there _is_ a standard here.
This commit is contained in:
Martin Atkins 2019-01-24 15:06:28 -08:00
parent 96efb87de3
commit a9ca194bcd
2 changed files with 13 additions and 10 deletions

View File

@ -9,7 +9,6 @@ import (
"github.com/apparentlymart/go-textseg/textseg"
"github.com/hashicorp/hcl2/hcl"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
)
type parser struct {
@ -1048,11 +1047,10 @@ func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
}
func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) {
// We'll lean on the cty converter to do the conversion, to ensure that
// the behavior is the same as what would happen if converting a
// non-literal string to a number.
numStrVal := cty.StringVal(string(tok.Bytes))
numVal, err := convert.Convert(numStrVal, cty.Number)
// The cty.ParseNumberVal is always the same behavior as converting a
// string to a number, ensuring we always interpret decimal numbers in
// the same way.
numVal, err := cty.ParseNumberVal(string(tok.Bytes))
if err != nil {
ret := cty.UnknownVal(cty.Number)
return ret, hcl.Diagnostics{

View File

@ -3,9 +3,9 @@ package json
import (
"encoding/json"
"fmt"
"math/big"
"github.com/hashicorp/hcl2/hcl"
"github.com/zclconf/go-cty/cty"
)
func parseFileContent(buf []byte, filename string) (node, hcl.Diagnostics) {
@ -370,10 +370,15 @@ func parseNumber(p *peeker) (node, hcl.Diagnostics) {
}
}
f, _, err := big.ParseFloat(string(num), 10, 512, big.ToNearestEven)
// We want to guarantee that we parse numbers the same way as cty (and thus
// native syntax HCL) would here, so we'll use the cty parser even though
// in most other cases we don't actually introduce cty concepts until
// decoding time. We'll unwrap the parsed float immediately afterwards, so
// the cty value is just a temporary helper.
nv, err := cty.ParseNumberVal(string(num))
if err != nil {
// Should never happen if above passed, since JSON numbers are a subset
// of what big.Float can parse...
// of what cty can parse...
return nil, hcl.Diagnostics{
{
Severity: hcl.DiagError,
@ -385,7 +390,7 @@ func parseNumber(p *peeker) (node, hcl.Diagnostics) {
}
return &numberVal{
Value: f,
Value: nv.AsBigFloat(),
SrcRange: tok.Range,
}, nil
}