Static Code Analysis Using SSA

Static Code Analysis Using SSA

Presented at GopherCon 2015

6c76488dff9b5d9a872dff88f008f88e?s=128

benbjohnson

July 09, 2015
Tweet

Transcript

  1. Static Code Analysis Using SSA

  2. ‘go’ toolchain is great!

  3. go build go test go fmt go get go vet

    go tool cover go tool pprof go -race
  4. What’s the next step?

  5. Code generation

  6. go/ast go/doc go/parser go/printer go/scanner go/token

  7. Over 1,000 packages import go/ast

  8. The AST only shows the structure of code

  9. Static Single Assignment Intermediate Representation

  10. SSA IR

  11. SSA IR shows how code flows

  12. golang.org/x/tools/go/ssa

  13. golang.org/x/tools/go/ssa Primary Author: Alan Donovan

  14. None
  15. Only 5 projects import golang.org/x/tools/go/ssa

  16. github.com/DanielMorsing/stackcheck github.com/go-llvm/llgo github.com/motemen/go-typeswitch-gen github.com/stripe/safesql github.com/tardisgo/tardisgo

  17. Intermediate Representation Go IR Machine Code

  18. IR is high level

  19. IR is portable

  20. x := 1 y := 2 x = x +

    y Static Single Assignment
  21. x := 1 y := 2 x = x +

    y Static Single Assignment
  22. x1 := 1 y1 := 2 x2 = x1 +

    y1 Static Single Assignment
  23. SSA makes analysis easier

  24. Terminology

  25. Program github.com/benbjohnson/foo github.com/benbjohnson/bar fmt os io

  26. github.com/benbjohnson/foo type Bar type Baz func Bat() Package

  27. func hello(name string) { if name == “” { fmt.Print(“name?”)

    name = readString() } fmt.Printf(“hello %s”, name) } Function
  28. func hello(name string) { if name == “” { fmt.Print(“name?”)

    name = readString() } fmt.Printf(“hello %s”, name) } Basic Blocks
  29. fmt.Print(“name?”) name = readString() Control Flow Graph (CFG) fmt.Printf(“hello %s”,

    name) false true if name == “”
  30. func hello(name string) { if name == “” { fmt.Print(“name?”)

    name = readString() } fmt.Printf(“hello %s”, name) } Instructions
  31. func hello(name string) { if name == “” { fmt.Print(“name?”)

    name = readString() } fmt.Printf(“hello %s”, name) } Values
  32. SSA IR Primitives Alloc BinOp Builtin Call ChangeInterface ChangeType Const

    Convert DebugRef Defer Extract Field FieldAddr FreeVar Function Global Go If Index IndexAddr Jump Lookup MakeChan MakeClosure MakeInterface MakeMap MakeSlice MapUpdate NamedConst Next Panic Parameter Phi Range Return RunDefers Select Send Slice Store Type TypeAssert UnOp
  33. What does it look like?

  34. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  35. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  36. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  37. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) } true
  38. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) } false
  39. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  40. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  41. package main import ( "flag" "fmt" "os" ) func main()

    { // Parse CLI arguments. flag.Parse() if flag.NArg() == 0 { fmt.Println("usage: hello NAME") os.Exit(2) } // Extract name from args and say hello. name := flag.Arg(0) sayHello(name) } func sayHello(name string) { fmt.Printf("hello, %s!", name) }
  42. $ ssadump -build=CPF -importbin .

  43. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  44. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  45. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  46. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  47. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  48. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  49. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  50. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  51. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  52. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  53. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return true
  54. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return false
  55. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  56. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  57. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  58. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  59. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  60. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  61. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  62. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return ???
  63. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  64. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  65. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  66. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  67. func main(): 0: entry P:0 S:2 t0 = flag.Parse() ()

    t1 = flag.NArg() int t2 = t1 == 0:int bool if t2 goto 1 else 2 1: if.then P:1 S:1 t3 = new [1]interface{} (varargs) *[1]interface{} t4 = &t3[0:int] *interface{} t5 = make interface{} <- string ("usage: hello NAME":string) interface{} *t4 = t5 t6 = slice t3[:] []interface{} t7 = fmt.Println(t6...) (n int, err error) t8 = os.Exit(2:int) () jump 2 2: if.done P:2 S:0 t9 = flag.Arg(0:int) string t10 = sayHello(t9) () return
  68. Tools built on go/ssa

  69. safesql

  70. safesql • Written by Carl Jackson at Stripe. • Verifies

    that the query string arg is a compile time constant. • Helps to prevent SQL injection.
  71. safesql func main() { // Open database. db, err :=

    sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") }
  72. safesql func main() { // Open database. db, err :=

    sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") } User Input
  73. safesql func main() { // Open database. db, err :=

    sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Retrieve color from CLI args. color := flag.Arg(0) // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + color + "'") } Ah! SQL Injection!
  74. safesql $ safesql . Found 1 potentially unsafe SQL statements:

    - main.go:13:10 Please ensure that all SQL queries you use are compile-time constants. You should always use parameterized queries or prepared statements instead of building queries from strings.
  75. safesql const FavColor = “purple” func main() { // Open

    database. db, err := sql.Open("postgres", "dbname=db0") if err != nil { log.Fatal(err) } defer db.Close() // Execute query. db.Query("SELECT FROM widgets WHERE color = '" + FavColor + "'") }
  76. safesql $ safesql . You're safe from SQL injection! Yay

    \o/
  77. oracle

  78. oracle • Provides insights about your code. • Even works

    with interfaces! • Uses pointer analysis to scan your whole program
  79. oracle • What code calls a function • What code

    is called by a function • Trace possible paths to a function call from main() • Determine what implements an interface • Find who sends to & receives from a channel • Determine possible values of a pointer • Find other identifiers that refer to the same value
  80. oracle callees

  81. type I interface { Foo() } type T struct{} func

    (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} x.Foo() if condition { x = &V{} } x.Foo() }
  82. $ oracle -pos main.go:#168 callees main.go main.go:24:7: this dynamic method

    call dispatches to: main.go:7:11: (*main.T).Foo
  83. type I interface { Foo() } type T struct{} func

    (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} x.Foo() if condition { x = &V{} } x.Foo() }
  84. $ oracle -pos main.go:#210 callees main.go main.go:24:7: this dynamic method

    call dispatches to: main.go:7:11: (*main.T).Foo main.go:11:11: (*main.V).Foo
  85. oracle callers

  86. type I interface { Foo() } type T struct{} func

    (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }
  87. type I interface { Foo() } type T struct{} func

    (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }
  88. $ oracle -pos main.go:#61 callers main.go main.go:7:11: (*main.T).Foo is not

    reachable in this program.
  89. type I interface { Foo() } type T struct{} func

    (*T) Foo() {} type V struct{} func (*V) Foo() {} func doSomething(condition bool) { var x I = &T{} if condition { x = &V{} x.Foo() } }
  90. $ oracle -pos main.go:#98 callers main.go main.go:11:11: (*main.V).Foo is called

    from these 1 sites: main.go:22:8: dynamic method call from main.main
  91. oracle pointsto

  92. type T struct{} func (*T) Foo() {} type V struct{}

    func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }
  93. type T struct{} func (*T) Foo() {} type V struct{}

    func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }
  94. $ oracle -pos main.go:#168 pointsto main.go main.go:21:6: this I may

    contain these dynamic types: main.go:8:6: *T, may point to: main.go:21:14: complit
  95. type T struct{} func (*T) Foo() {} type V struct{}

    func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }
  96. $ oracle -pos main.go:#206 pointsto main.go main.go:24:3: this I may

    contain these dynamic types: main.go:12:6: *V, may point to: main.go:24:9: complit
  97. type T struct{} func (*T) Foo() {} type V struct{}

    func (*V) Foo() {} type I interface { Foo() } func doSomething(cond bool) { var x I = &T{} if cond { x = &V{} } fmt.Printf("x is %T\n", x) }
  98. $ oracle -pos main.go:#244 pointsto main.go main.go:27:26: this I may

    contain these dynamic types: main.go:8:6: *T, may point to: main.go:21:14: complit main.go:12:6: *V, may point to: main.go:24:9: complit
  99. gorename

  100. gorename • Safely renames identifiers • Renames the declaration as

    well as references. • Even works across packages in GOPATH • Just uses go/types but it’s still cool • Intelligently prevents ambiguity and shadowing conflicts.
  101. gorename func doSomething() { var x int if y :=

    rand.Intn(10); y == 0 { x = 1 } fmt.Printf("x is %d\n", x) } Shadowing Conflict
  102. gorename $ gorename -from mypkg.doSomething::x -to y Shadowing Conflict

  103. gorename mypkg.go:2:6: renaming this var "x" to "y" mypkg.go:4:3: would

    cause this reference to become shadowed mypkg.go:5:5: by this intervening var definition Shadowing Conflict
  104. gorename package mypkg type Widget struct { Name string }

    ... type Woojit struct { Widget Floojit string } Ambiguity Conflict
  105. gorename $ gorename -from mypkg.Widget -to Floojit Ambiguity Conflict

  106. gorename main.go:8:2: renaming this field "Widget" to "Floojit" main.go:9:2: would

    conflict with this field Ambiguity Conflict
  107. golang.org/x/tools

  108. It’s just the beginning! There is so much to build!

  109. Q&A @benbjohnson