1 of 30

Using go/types for

Code Comprehension and

Refactoring Tools

Alan Donovan

Google NYC

GothamGo [YouTube]

October 2, 2015

2 of 30

$ go list -f '{{.ImportPath}} {{join .GoFiles " "}}' std |

while read dir files; do

echo $(cd $dir && cat $files | wc -l) $dir

done | sort -rn | head

30197 runtime

10464 go/types ←

9049 net

8100 net/http

7741 unicode

6734 math/big

6658 syscall

6283 crypto/tls

4959 math

4952 reflect

3 of 30

$ go list std |

while read pkg; do

echo $(go doc $pkg |

grep -E '^(type|func|var|const)' | wc -l) $pkg

done | sort -rn | head

262 syscall

91 go/ast

91 debug/elf

88 go/types ←

82 net

70 net/http

69 os

65 math

50 bytes

47 strings

4 of 30

Analysis Libraries and Tools

gofmt

SSA IR builder�x/tools/go/ssa

Type checker

go/types

Parser�go/{parser,ast,scanner,token}

Pointer analysis�x/tools/go/pointer

gorename

vet

oracle

godoc

-analysis

llgo

eg

gomvpkg

golint

5 of 30

gofmt

Type checker

go/types

Parser�go/{parser,ast,scanner,token}

gorename

vet

Grok

Kythe

eg

gomvpkg

Analysis Libraries and Tools

and Services

SourceGraph

6 of 30

Demo

$ godoc -analysis=type -http :8000

7 of 30

Demo

oracle definition

referrers

freevars

implements

8 of 30

Demo

gorename

9 of 30

fmt

go/types

fmt

fmt

a.go

mypkg

parsed files

of mypkg

dependency

packages

type-checked

package

expression

information

!

compile errors

10 of 30

name

resolution

var x int

fmt.Println(x)

“What does the name x refer to?”

11 of 30

type

deduction

name

resolution

z := x.f()[0] << y

“What is the type of z?”

12 of 30

type

deduction

name

resolution

constant

evaluation

const KiB = 1 << (10 * (1 + iota))

“What is the value of KiB?”

13 of 30

type

deduction

name

resolution

constant

evaluation

*bytes.Buffer

unsafe.Sizeof(x)

14 of 30

type

deduction

name

resolution

constant

evaluation

*bytes.Buffer

unsafe.Sizeof(x)

T{K: 0}

[C]int

15 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

16 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

Func main

Var i

PkgName fmt imports “fmt”

Const format

Objects

fmt

Func Printf

17 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

Definitions

Func main

Var i

PkgName fmt imports “fmt”

Const format

fmt

Func Printf

18 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

Uses

PkgName fmt imports “fmt”

Const format

Var i

fmt

Func Printf

19 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

Renaming format to foo

Const format

20 of 30

package main

import "fmt"

const foo = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(foo, i)

}

}

Successfully renamed

21 of 30

package main

import "fmt"

const main = "%d green bottles\n"

func main() { // error: main already defined at package level

for i := 10; i >= 0; i-- {

fmt.Printf(main, i)

}

}

Same-level conflict

x

22 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

Universe

Package mypkg

File a.go

fmt

Printf

Lexical Blocks

a.k.a. “Scopes”

23 of 30

Func Println

Const true

Universe

Func main

Var i

PkgName fmt

Const format

Func Printf

Package fmt

Package mypkg

Const true

File a.go

Func main

for

{}

Tree of Scopes

24 of 30

package main

import "fmt"

const i = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(i, i) // error: i is not a string

}

}

Down-level conflict

x

25 of 30

Func Println

Const true

Universe

Func main

Var i

PkgName fmt

Const format

Func Printf

Package fmt

Package mypkg

Const true

File a.go

Func main

for

{}

Tree of Scopes

26 of 30

renaming “format” to “i”

would cause this reference

to become shadowed by this intervening declaration

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i-- {

fmt.Printf(format, i)

}

}

27 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for format := 10; format >= 0; format-- {

fmt.Printf(format, format) // error

}

}

Up-level conflict

x

28 of 30

package main

import "fmt"

const format = "%d green bottles\n"

func main() {

for i := 10; i >= 0; i {

fmt.Printf(format, i)

}

}

renaming “i” to “format”

would shadow this reference

to this enclosing declaration

29 of 30

import “go/types”

30 of 30

Thank you. Questions?

Alan Donovan

Google NYC

adonovan@google.com

Available October 30

gopl.io