Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Static Code Analysis Using SSA

Static Code Analysis Using SSA

Presented at GopherCon 2015

benbjohnson

July 09, 2015
Tweet

More Decks by benbjohnson

Other Decks in Technology

Transcript

  1. go build go test go fmt go get go vet

    go tool cover go tool pprof go -race
  2. x := 1 y := 2 x = x +

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

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

    y1 Static Single Assignment
  5. func hello(name string) { if name == “” { fmt.Print(“name?”)

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

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

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

    name = readString() } fmt.Printf(“hello %s”, name) } Values
  9. 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
  10. 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) }
  11. 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) }
  12. 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. 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
  14. 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
  15. 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) }
  16. 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) }
  17. 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. 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
  19. 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
  20. 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. 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. 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. 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. 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. 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. 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. 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. 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
  29. 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
  30. 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
  31. 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
  32. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. safesql • Written by Carl Jackson at Stripe. • Verifies

    that the query string arg is a compile time constant. • Helps to prevent SQL injection.
  44. 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 + "'") }
  45. 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
  46. 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!
  47. 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.
  48. 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 + "'") }
  49. oracle • Provides insights about your code. • Even works

    with interfaces! • Uses pointer analysis to scan your whole program
  50. 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
  51. 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() }
  52. 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() }
  53. $ 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
  54. 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() } }
  55. 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() } }
  56. 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() } }
  57. $ 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
  58. 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) }
  59. 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) }
  60. $ 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
  61. 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) }
  62. $ 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
  63. 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. $ 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
  65. 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.
  66. gorename func doSomething() { var x int if y :=

    rand.Intn(10); y == 0 { x = 1 } fmt.Printf("x is %d\n", x) } Shadowing Conflict
  67. 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
  68. gorename package mypkg type Widget struct { Name string }

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