gofactor
Advanced utility for golang refactor based on DSL transformations provided by bblfsh/sdk
Usage example
Imagine you have a piece of code
package main
import "fmt"
func main() {
var (
i int
X int
j int
)
if i%2 == 0 {
i = 5
}
if X%2 == 0 {
X = 5
}
fmt.Println(i)
if i%2 == 0 {
i = 5
}
if j%2 == 0 {
j = 5
}
}
func a(i, X int) {
if i%2 == 0 {
i = 5
}
if X%2 == 0 {
X = 5
}
fmt.Println(i)
if i%2 == 0 {
i = 5
}
if X%2 == 0 {
X = 5
}
}
And you want to replace all code patterns like
if i%2 == 0 {
i = 5
}
if X%2 == 0 {
X = 5
}
to
if i%2 == 1 {
i = 1
} else {
X = 1
}
Here's where refactor library comes for help.
- Init refactor object
refactor, err := gofactor.NewRefactor(beforeSnippet, afterSnippet)
if err != nil {
log.Error(err)
os.Exit(1)
}
- call
Prepare
function to generate transformations mappings
if err := refactor.Prepare(); err != nil {
log.Error(err)
os.Exit(1)
}
- Apply generated transformations to the desired code
code, err := refactor.Apply(desiredCode)
if err != nil {
log.Error(err)
os.Exit(1)
}
Result
package main
import "fmt"
func main() {
var (
i int
X int
j int
)
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 1 {
i = 1
} else {
j = 1
}
}
func a(i, X int) {
if i%2 == 1 {
i = 1
} else {
X = 1
}
fmt.Println(i)
if i%2 == 1 {
i = 1
} else {
X = 1
}
}
Supported cases
See fixtures
Under the hood
- both input and output patterns are converted to go
AST
nodes
- both input and output nodes converted to
bblfsh
uast.Node
s
- define mapping of transformation operations from input to output node
- apply transformation mapping to the desired code: traverse over the
uast.Node
s tree and transform matching nodes
- convert transformed tree back to golang
AST
- convert golang
AST
to string
Roadmap
- currently library cannot be build because of
bblfsh/go-driver
dependency issue, fix this part
- support functions refactor
- handle cases with cascade
if
s, switch
es and tail recursions
- during the transformations we are forced to drop nodes positions, need to investigate the possibilities of preserving/reconstructing them(probably using DST nodes could help, related issue https://github.com/dave/dst/issues/38)