diff --git a/go/loader/source_test.go b/go/loader/source_test.go index 1b38fce413..919237d806 100644 --- a/go/loader/source_test.go +++ b/go/loader/source_test.go @@ -60,26 +60,26 @@ func TestEnclosingFunction(t *testing.T) { // Function literal: {`package main func f() { println(func() { print(300) }) }`, - "300", "f$1"}, + "300", "main.f$1"}, // Doubly nested {`package main func f() { println(func() { print(func() { print(350) })})}`, - "350", "f$1$1"}, + "350", "main.f$1$1"}, // Implicit init for package-level var initializer. {"package main; var a = 400", "400", "main.init"}, // No code for constants: {"package main; const a = 500", "500", "(none)"}, // Explicit init() - {"package main; func init() { println(600) }", "600", "main.init$1"}, + {"package main; func init() { println(600) }", "600", "main.init#1"}, // Multiple explicit init functions: {`package main func init() { println("foo") } func init() { println(800) }`, - "800", "main.init$2"}, + "800", "main.init#2"}, // init() containing FuncLit. {`package main func init() { println(func(){print(900)}) }`, - "900", "init$1$1"}, + "900", "main.init#1$1"}, } for _, test := range tests { conf := loader.Config{Fset: token.NewFileSet()} diff --git a/go/pointer/testdata/channels.go b/go/pointer/testdata/channels.go index 454ff0f5b8..76eb5f8c10 100644 --- a/go/pointer/testdata/channels.go +++ b/go/pointer/testdata/channels.go @@ -19,7 +19,7 @@ func chan1() { print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c1makeB:13 - print(<-chB) // @pointsto main.decr | chan1$1 + print(<-chB) // @pointsto main.decr | main.chan1$1 } func chan2() { @@ -40,10 +40,10 @@ func chan2() { print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c2makeB:13 - print(<-chB) // @pointsto main.decr | chan2$1 + print(<-chB) // @pointsto main.decr | main.chan2$1 print(chAB) // @pointsto makechan@c2makeA:13 | makechan@c2makeB:13 - print(<-chAB) // @pointsto main.incr | main.decr | chan2$1 + print(<-chAB) // @pointsto main.incr | main.decr | main.chan2$1 (<-chA)(3) } @@ -59,7 +59,7 @@ func chan3() { print(chA) // @pointsto makechan@c3makeA:13 print(<-chA) // @pointsto main.incr print(chB) // @pointsto makechan@c3makeB:13 - print(<-chB) // @pointsto main.decr | chan3$1 + print(<-chB) // @pointsto main.decr | main.chan3$1 (<-chA)(3) } diff --git a/go/pointer/testdata/func.go b/go/pointer/testdata/func.go index 25f67dba0f..2155f8ef71 100644 --- a/go/pointer/testdata/func.go +++ b/go/pointer/testdata/func.go @@ -28,9 +28,9 @@ func func1() { print(&a) // @pointsto main.a } -// @calls main.func1 -> func1$2 -// @calls main.func1 -> func1$1 -// @calls func1$2 -> func1$1 +// @calls main.func1 -> main.func1$2 +// @calls main.func1 -> main.func1$1 +// @calls main.func1$2 -> main.func1$1 func func2() { var x, y *int @@ -124,7 +124,7 @@ func func6() { print(f()) // @pointsto main.a } -// @calls main.func6 -> func6$1 +// @calls main.func6 -> main.func6$1 type I interface { f() diff --git a/go/ssa/builder.go b/go/ssa/builder.go index f1b6d171c1..bcd5f096e5 100644 --- a/go/ssa/builder.go +++ b/go/ssa/builder.go @@ -2110,7 +2110,7 @@ func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) { if decl.Recv == nil && id.Name == "init" { pkg.ninit++ fn = &Function{ - name: fmt.Sprintf("init$%d", pkg.ninit), + name: fmt.Sprintf("init#%d", pkg.ninit), Signature: new(types.Signature), pos: decl.Name.NamePos, Pkg: pkg, diff --git a/go/ssa/func.go b/go/ssa/func.go index 8136b16d9d..3c9b5559f1 100644 --- a/go/ssa/func.go +++ b/go/ssa/func.go @@ -452,22 +452,33 @@ func (f *Function) emit(instr Instruction) Value { // The specific formatting rules are not guaranteed and may change. // // Examples: -// "math.IsNaN" // a package-level function -// "IsNaN" // intra-package reference to same -// "(*bytes.Buffer).Bytes" // a declared method or a wrapper -// "(*Buffer).Bytes" // intra-package reference to same -// "(*Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0) -// "(*Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure) -// "main$1" // an anonymous function -// "init$1" // a declared init function -// "init" // the synthesized package initializer +// "math.IsNaN" // a package-level function +// "(*bytes.Buffer).Bytes" // a declared method or a wrapper +// "(*bytes.Buffer).Bytes$thunk" // thunk (func wrapping method; receiver is param 0) +// "(*bytes.Buffer).Bytes$bound" // bound (func wrapping method; receiver supplied by closure) +// "main.main$1" // an anonymous function in main +// "main.init#1" // a declared init function +// "main.init" // the synthesized package initializer // -// If from==f.Pkg, suppress package qualification. +// When these functions are referred to from within the same package +// (i.e. from == f.Pkg.Object), they are rendered without the package path. +// For example: "IsNaN", "(*Buffer).Bytes", etc. +// +// Invariant: all non-synthetic functions have distinct package-qualified names. // func (f *Function) RelString(from *types.Package) string { // Anonymous? if f.parent != nil { - return f.name + // An anonymous function's Name() looks like "parentName$1", + // but its String() should include the type/package/etc. + parent := f.parent.RelString(from) + for i, anon := range f.parent.AnonFuncs { + if anon == f { + return fmt.Sprintf("%s$%d", parent, 1+i) + } + } + + return f.name // should never happen } // Method (declared or wrapper)? diff --git a/go/ssa/stdlib_test.go b/go/ssa/stdlib_test.go index d08f3449f3..3ee1b9b086 100644 --- a/go/ssa/stdlib_test.go +++ b/go/ssa/stdlib_test.go @@ -93,8 +93,25 @@ func TestStdlib(t *testing.T) { t.Errorf("Loaded only %d packages, want at least %d", numPkgs, want) } - // Dump some statistics. allFuncs := ssautil.AllFunctions(prog) + + // Check that all non-synthetic functions have distinct names. + byName := make(map[string]*ssa.Function) + for fn := range allFuncs { + if fn.Synthetic == "" { + str := fn.String() + prev := byName[str] + byName[str] = fn + if prev != nil { + t.Errorf("%s: duplicate function named %s", + prog.Fset.Position(fn.Pos()), str) + t.Errorf("%s: (previously defined here)", + prog.Fset.Position(prev.Pos())) + } + } + } + + // Dump some statistics. var numInstrs int for fn := range allFuncs { for _, b := range fn.Blocks { diff --git a/oracle/testdata/src/main/callgraph-json.golden b/oracle/testdata/src/main/callgraph-json.golden index cb6b6bf90c..7d99870748 100644 --- a/oracle/testdata/src/main/callgraph-json.golden +++ b/oracle/testdata/src/main/callgraph-json.golden @@ -44,7 +44,7 @@ "pos": "testdata/src/main/callgraph-json.go:9:6" }, { - "name": "main$1", + "name": "main.main$1", "pos": "testdata/src/main/callgraph-json.go:31:8" } ] diff --git a/oracle/testdata/src/main/callgraph.golden b/oracle/testdata/src/main/callgraph.golden index 654aa25697..697c857545 100644 --- a/oracle/testdata/src/main/callgraph.golden +++ b/oracle/testdata/src/main/callgraph.golden @@ -31,7 +31,7 @@ Non-numbered nodes indicate back- or cross-edges to the node whose 6 main.A 7 main.B 8 main.call2 -9 main$1 +9 main.main$1 main.main (3) 10 main.nop diff --git a/oracle/testdata/src/main/calls-json.golden b/oracle/testdata/src/main/calls-json.golden index f063bc0b81..435db7ebcb 100644 --- a/oracle/testdata/src/main/calls-json.golden +++ b/oracle/testdata/src/main/calls-json.golden @@ -6,7 +6,7 @@ "desc": "dynamic function call", "callees": [ { - "name": "main$1", + "name": "main.main$1", "pos": "testdata/src/main/calls-json.go:12:7" } ] @@ -16,7 +16,7 @@ "mode": "callstack", "callstack": { "pos": "testdata/src/main/calls-json.go:12:7", - "target": "main$1", + "target": "main.main$1", "callers": [ { "pos": "testdata/src/main/calls-json.go:8:3", diff --git a/oracle/testdata/src/main/calls.golden b/oracle/testdata/src/main/calls.golden index 3cbeb85f25..c06f0e85a5 100644 --- a/oracle/testdata/src/main/calls.golden +++ b/oracle/testdata/src/main/calls.golden @@ -35,11 +35,11 @@ main.store is called from these 2 sites: -------- @pointsto pointsto-result-f -------- this func() *int may point to these objects: - main$1 + main.main$1 -------- @callees callees-main.call-f -------- this dynamic function call dispatches to: - main$1 + main.main$1 -------- @callers callers-main.call -------- main.call is called from these 2 sites: @@ -103,7 +103,7 @@ main.init is called from these 1 sites: the root of the call graph -------- @callstack callstack-init -------- -Found a call path from root to main.init$1 -main.init$1 +Found a call path from root to main.init#1 +main.init#1 static function call from main.init diff --git a/oracle/testdata/src/main/pointsto.golden b/oracle/testdata/src/main/pointsto.golden index 98c14413bb..7b12b2aff7 100644 --- a/oracle/testdata/src/main/pointsto.golden +++ b/oracle/testdata/src/main/pointsto.golden @@ -29,7 +29,7 @@ this func() may point to these objects: -------- @pointsto ref-anon -------- this func() may point to these objects: - main$1 + pointsto.main$1 -------- @pointsto ref-global -------- this *string may point to these objects: