]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gofmt: normalize integer imaginary literals starting with 0
authorRobert Griesemer <gri@golang.org>
Thu, 14 Feb 2019 00:38:01 +0000 (16:38 -0800)
committerRobert Griesemer <gri@golang.org>
Tue, 19 Feb 2019 20:38:06 +0000 (20:38 +0000)
An 'i' suffix on an integer literal marks the integer literal as
a decimal integer imaginary value, even if the literal without the
suffix starts with a 0 and thus looks like an octal value:

0123i == 123i // != 0123 * 1i

This is at best confusing, and at worst a potential source of bugs.
It is always safe to rewrite such literals into the equivalent
literal without the leading 0.

This CL implements this normalization.

Change-Id: Ib77ad535f98b5be912ecbdec20ca1b472c1b4973
Reviewed-on: https://go-review.googlesource.com/c/162538
Run-TryBot: Robert Griesemer <gri@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/cmd/gofmt/gofmt.go
src/cmd/gofmt/testdata/go2numbers.golden
src/cmd/gofmt/testdata/go2numbers.input

index ce4613fb60a604bfc623880d8e19959fa13b374f..4bba44489d3eb74420e14b5ed8a17262fc80bf11 100644 (file)
@@ -330,7 +330,9 @@ func backupFile(filename string, data []byte, perm os.FileMode) (string, error)
 }
 
 // normalizeNumbers rewrites base prefixes and exponents to
-// use lower-case letters. It leaves hexadecimal digits alone.
+// use lower-case letters, and removes leading 0's from
+// integer imaginary literals. It leaves hexadecimal digits
+// alone.
 func normalizeNumbers(n ast.Node) bool {
        lit, _ := n.(*ast.BasicLit)
        if lit == nil {
@@ -339,37 +341,60 @@ func normalizeNumbers(n ast.Node) bool {
        if len(lit.Value) < 2 {
                return false // only one digit - nothing to do
        }
-       // lit.Value >= 2
+       // len(lit.Value) >= 2
 
+       x := lit.Value
        switch lit.Kind {
        case token.INT:
-               switch lit.Value[:2] {
+               switch x[:2] {
                case "0X":
-                       lit.Value = "0x" + lit.Value[2:]
+                       lit.Value = "0x" + x[2:]
                case "0O":
-                       lit.Value = "0o" + lit.Value[2:]
+                       lit.Value = "0o" + x[2:]
                case "0B":
-                       lit.Value = "0b" + lit.Value[2:]
+                       lit.Value = "0b" + x[2:]
                }
 
        case token.FLOAT:
                switch lit.Value[:2] {
                default:
-                       if i := strings.LastIndexByte(lit.Value, 'E'); i >= 0 {
-                               lit.Value = lit.Value[:i] + "e" + lit.Value[i+1:]
+                       if i := strings.LastIndexByte(x, 'E'); i >= 0 {
+                               lit.Value = x[:i] + "e" + x[i+1:]
                        }
                case "0x":
-                       if i := strings.LastIndexByte(lit.Value, 'P'); i >= 0 {
-                               lit.Value = lit.Value[:i] + "p" + lit.Value[i+1:]
+                       if i := strings.LastIndexByte(x, 'P'); i >= 0 {
+                               lit.Value = x[:i] + "p" + x[i+1:]
                        }
                case "0X":
-                       if i := strings.LastIndexByte(lit.Value, 'P'); i >= 0 {
-                               lit.Value = "0x" + lit.Value[2:i] + "p" + lit.Value[i+1:]
+                       if i := strings.LastIndexByte(x, 'P'); i >= 0 {
+                               lit.Value = "0x" + x[2:i] + "p" + x[i+1:]
                        } else {
-                               lit.Value = "0x" + lit.Value[2:]
+                               lit.Value = "0x" + x[2:]
+                       }
+               }
+
+       case token.IMAG:
+               // Note that integer imaginary literals may contain
+               // any decimal digit even if they start with zero.
+               // Imaginary literals should always end in 'i' but be
+               // conservative and check anyway before proceeding.
+               if x[0] == '0' && x[len(x)-1] == 'i' && isDecimals(x[1:len(x)-1]) {
+                       x = strings.TrimLeft(x, "0_")
+                       if x == "i" {
+                               x = "0i"
                        }
+                       lit.Value = x
                }
        }
 
        return false
 }
+
+// isDecimals reports whether x consists entirely of decimal digits and underscores.
+func isDecimals(x string) bool {
+       i := 0
+       for i < len(x) && ('0' <= x[i] && x[i] <= '9' || x[i] == '_') {
+               i++
+       }
+       return i == len(x)
+}
index 2fab834bcd2db65a9c034aefd4bc81cc91fa7ab8..abefcb6c586d4d0f469b5d69fdbb600c358ba28d 100644 (file)
@@ -141,10 +141,22 @@ const (
 
        // imaginaries
        _ = 0i
-       _ = 00i
+       _ = 0i
+       _ = 8i
+       _ = 0i
+       _ = 123i
+       _ = 123i
+       _ = 56789i
        _ = 1234i
        _ = 1234567i
 
+       _ = 0i
+       _ = 0i
+       _ = 8i
+       _ = 0i
+       _ = 123i
+       _ = 123i
+       _ = 56_789i
        _ = 1_234i
        _ = 1_234_567i
 
index 7b3fd391da60888b0eb4d03169beb08ded112264..51a9f8eaf6603f803ae6514eb84413b86a8ea9a1 100644 (file)
@@ -142,9 +142,21 @@ const (
        // imaginaries
        _ = 0i
        _ = 00i
+       _ = 08i
+       _ = 0000000000i
+       _ = 0123i
+       _ = 0000000123i
+       _ = 0000056789i
        _ = 1234i
        _ = 1234567i
 
+       _ = 0i
+       _ = 0_0i
+       _ = 0_8i
+       _ = 0_000_000_000i
+       _ = 0_123i
+       _ = 0_000_000_123i
+       _ = 0_000_056_789i
        _ = 1_234i
        _ = 1_234_567i