Static Code Analysis Using SSA

Static Code Analysis Using SSA

Presented at GopherCon 2015

6c76488dff9b5d9a872dff88f008f88e?s=128

benbjohnson

July 09, 2015
Tweet

Transcript

  1. 3.

    go build go test go fmt go get go vet

    go tool cover go tool pprof go -race
  2. 10.
  3. 14.
  4. 20.

    x := 1 y := 2 x = x +

    y Static Single Assignment
  5. 21.

    x := 1 y := 2 x = x +

    y Static Single Assignment
  6. 22.

    x1 := 1 y1 := 2 x2 = x1 +

    y1 Static Single Assignment
  7. 27.

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

    name = readString() } fmt.Printf(“hello %s”, name) } Function
  8. 28.

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

    name = readString() } fmt.Printf(“hello %s”, name) } Basic Blocks
  9. 30.

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

    name = readString() } fmt.Printf(“hello %s”, name) } Instructions
  10. 31.

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

    name = readString() } fmt.Printf(“hello %s”, name) } Values
  11. 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
  12. 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) }
  13. 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) }
  14. 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) }
  15. 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
  16. 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
  17. 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) }
  18. 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) }
  19. 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) }
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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
  33. 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
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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
  39. 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 ???
  40. 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
  41. 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
  42. 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
  43. 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
  44. 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
  45. 69.
  46. 70.

    safesql • Written by Carl Jackson at Stripe. • Verifies

    that the query string arg is a compile time constant. • Helps to prevent SQL injection.
  47. 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 + "'") }
  48. 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
  49. 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!
  50. 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.
  51. 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 + "'") }
  52. 77.
  53. 78.

    oracle • Provides insights about your code. • Even works

    with interfaces! • Uses pointer analysis to scan your whole program
  54. 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
  55. 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() }
  56. 82.
  57. 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() }
  58. 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
  59. 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() } }
  60. 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() } }
  61. 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() } }
  62. 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
  63. 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) }
  64. 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) }
  65. 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
  66. 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) }
  67. 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
  68. 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) }
  69. 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
  70. 99.
  71. 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.
  72. 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
  73. 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
  74. 104.

    gorename package mypkg type Widget struct { Name string }

    ... type Woojit struct { Widget Floojit string } Ambiguity Conflict