Gofmt and Rewrite Rules
Aug 4, 2015One thing I absolutely love about Go is its tooling support. Whenever I use the numerous tools I always discover something new. In this short post I will be showing off gofmt’s -r flag, this flag allows you to apply a rewrite rule to your source before formatting.
A rewrite rule is a string in the following format:
pattern -> replacement
Both pattern and replacement must be valid Go expressions (more on this later), lets apply a simple rewrite to the following code:
// test1.go
package main
import (
"fmt"
)
func main() {
foo := "Hello World"
fmt.Println(foo)
}
The following rewrite rule changes the variable name from ‘foo’ to ‘bar’:
$ gofmt -r='foo -> bar' test1.go
Output:
// test1.go
package main
import (
"fmt"
)
func main() {
bar := "Hello World"
fmt.Println(bar)
}
We will now apply a more powerful rule to the below code:
// test2.go
package main
func main() {
vals := make([]int, 0)
vals = append(vals, 15)
vals = append(vals, 17)
vals = append(vals, 23)
slice := vals[1:len(vals)]
_ = slice
}
The line:
slice := vals[1:len(vals)]
Is not very idiomatic Go so lets change this:
$ gofmt -r='a[b:len(a)] -> a[b:]' test2.go
Output:
// test2.go
package main
func main() {
vals := make([]int, 0)
vals = append(vals, 15)
vals = append(vals, 17)
vals = append(vals, 23)
slice := vals[1:]
_ = slice
}
As you can see the code was correctly transformed. Notice how the rule used the characters ‘a’ and ‘b’. If your rule uses single-character lowercase identifiers, then these will serve as wild-cards matching arbitrary sub-expressions; these expressions will be substituted for the same identifiers in the replacement. So the rule:
-r='a[b:len(a)] -> a[b:]'
Would match:
x := vals[1:len(vals)] // vals[1:]
y := nums[5:len(nums)] // nums[5:]
Were on the first match:
‘a’ would be substituted for ‘val’
‘b’ would be substituted for ‘1’
And on the second match:
‘a’ would be substituted for ‘nums’
‘b’ would be substituted for ‘5’
An important thing to remember when using the -r flag is that the resulting transformation must be a syntactically valid declaration list, statement list, or expression. So the following rule:
-r='a[b:len(a)] -> a[const]'
Would be syntactically incorrect (const is a reserved keyword), and you would get the error:
parsing replacement a[const] at 1:4: expected operand, found 'const'
If you would like to learn more about rewrite rules then run:
$ godoc gofmt
Note: godoc gofmt reports the following at the end:
BUGS
The implementation of -r is a bit slow.
:)
Note: I am using Go version 1.4.2