diff --git a/src/cmd/cgo/ast.go b/src/cmd/cgo/ast.go
index 861479d..dac7151f 100644
--- a/src/cmd/cgo/ast.go
+++ b/src/cmd/cgo/ast.go
@@ -217,6 +217,8 @@
}
case *ast.CallExpr:
f.saveCall(x, context)
+ case *ast.CompositeLit:
+ f.saveLiteral(x, context)
}
}
@@ -277,6 +279,38 @@
f.Calls = append(f.Calls, c)
}
+// Save composite literals for later processing.
+func (f *File) saveLiteral(lit *ast.CompositeLit, context astContext) {
+ sel, ok := lit.Type.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+ if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" || len(lit.Elts) == 0 {
+ return
+ }
+ // if it's already in field:value form, no need to edit
+ for _, e := range lit.Elts {
+ if _, ok := e.(*ast.KeyValueExpr); ok {
+ return
+ }
+ }
+ c := &Lit{Lit: lit}
+ f.Lits = append(f.Lits, c)
+ f.LitMap[lit] = c
+}
+
+// doneLiteral marks composite literals in an AST (fragment) as done.
+// This is used when a call has been rewritten, which will also cause
+// the literal to be processed (and it has to be processed as part of the
+// call, otherwise it will cause an "overlapping rewrite" error).
+func (f *File) doneLiteral(x interface{}, context astContext) {
+ if lit, ok := x.(*ast.CompositeLit); ok {
+ if c := f.LitMap[lit]; c != nil {
+ c.Done = true
+ }
+ }
+}
+
// If a function should be exported add it to ExpFunc.
func (f *File) saveExport(x interface{}, context astContext) {
n, ok := x.(*ast.FuncDecl)
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 02c22ab..8d8e19d 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -224,10 +224,12 @@
}
}
p.prepareNames(f)
+ p.prewriteLits(f)
if p.rewriteCalls(f) {
// Add `import _cgo_unsafe "unsafe"` after the package statement.
f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"")
}
+ p.rewriteLits(f)
p.rewriteRef(f)
}
@@ -791,6 +793,71 @@
})
}
+// prewriteLits modifies the AST of the composite literal in case it is
+// emitted as part of a gofmt in call rewriting.
+func (p *Package) prewriteLits(f *File) {
+ for _, lit := range f.Lits {
+ p.prewriteLit(lit)
+ }
+}
+
+func typeFor(cType string) *Type {
+ ty := typedef["_Ctype_struct_"+cType]
+ if ty != nil {
+ return ty
+ }
+ ty = typedef["_Ctype_"+cType]
+ return ty
+}
+
+func (p *Package) prewriteLit(lit *Lit) {
+ sel := lit.Lit.Type.(*ast.SelectorExpr)
+ ty := typeFor(sel.Sel.Name)
+ fields := ty.Go.(*ast.StructType).Fields.List
+ j := 0
+ for _, fs := range fields {
+ for _, fieldName := range fs.Names {
+ if fieldName.Name == "_" {
+ continue
+ }
+ init := lit.Lit.Elts[j]
+ // prepend fieldName.Name+":"
+ lit.Lit.Elts[j] = &ast.KeyValueExpr{Key: ast.NewIdent(fieldName.Name), Colon: init.Pos(), Value: init}
+ j++
+ }
+ }
+}
+
+func (p *Package) rewriteLits(f *File) {
+ for _, lit := range f.Lits {
+ if lit.Done {
+ continue
+ }
+ p.rewriteLit(f, lit)
+ lit.Done = true
+ }
+}
+
+func (p *Package) rewriteLit(f *File, lit *Lit) {
+ sel := lit.Lit.Type.(*ast.SelectorExpr)
+ ty := typeFor(sel.Sel.Name)
+ fields := ty.Go.(*ast.StructType).Fields.List
+ j := 0
+ for _, fs := range fields {
+ for _, fieldName := range fs.Names {
+ if fieldName.Name == "_" {
+ continue
+ }
+
+ init := lit.Lit.Elts[j].(*ast.KeyValueExpr).Value // it got rewritten in place, in prewriteLit
+ j++
+ pos := f.offset(init.Pos())
+ // just splice in the tag.
+ f.Edit.Replace(pos, pos, fieldName.Name+":")
+ }
+ }
+}
+
// rewriteCalls rewrites all calls that pass pointers to check that
// they follow the rules for passing pointers between Go and C.
// This reports whether the package needs to import unsafe as _cgo_unsafe.
@@ -809,6 +876,7 @@
if nu {
needsUnsafe = true
}
+ f.walk(call.Call, ctxExpr, (*File).doneLiteral)
}
}
return needsUnsafe
diff --git a/src/cmd/cgo/internal/testerrors/ptr_test.go b/src/cmd/cgo/internal/testerrors/ptr_test.go
index 863dead..ce434ce 100644
--- a/src/cmd/cgo/internal/testerrors/ptr_test.go
+++ b/src/cmd/cgo/internal/testerrors/ptr_test.go
@@ -45,14 +45,14 @@
// Passing a pointer to a struct that contains a Go pointer.
name: "ptr1",
c: `typedef struct s1 { int *p; } s1; void f1(s1 *ps) {}`,
- body: `C.f1(&C.s1{p:new(C.int)})`,
+ body: `C.f1(&C.s1{new(C.int)})`,
fail: true,
},
{
// Passing a pointer to a struct that contains a Go pointer.
name: "ptr2",
c: `typedef struct s2 { int *p; } s2; void f2(s2 *ps) {}`,
- body: `p := &C.s2{p:new(C.int)}; C.f2(p)`,
+ body: `p := &C.s2{new(C.int)}; C.f2(p)`,
fail: true,
},
{
@@ -60,7 +60,7 @@
// that (irrelevantly) contains a Go pointer.
name: "ok1",
c: `struct s3 { int i; int *p; }; void f3(int *p) {}`,
- body: `p := &C.struct_s3{i: 0, p: new(C.int)}; C.f3(&p.i)`,
+ body: `p := &C.struct_s3{0, new(C.int)}; C.f3(&p.i)`,
fail: false,
},
{
@@ -231,7 +231,7 @@
struct s19b { struct s19a f; };
struct s19b *f19() { return malloc(sizeof(struct s19b)); }
void f19b(struct s19b *p) {}`,
- body: `p := C.f19(); p.f = C.struct_s19a{a:[32769]*C.char{new(C.char)}}; C.f19b(p)`,
+ body: `p := C.f19(); p.f = C.struct_s19a{[32769]*C.char{new(C.char)}}; C.f19b(p)`,
fail: true,
expensive: true,
},
@@ -245,7 +245,7 @@
void f20b(struct s20b *p) {}
void f20c(void *p) {}`,
imports: []string{"unsafe"},
- body: `p := C.f20(); n := &C.struct_s20a{a:[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
+ body: `p := C.f20(); n := &C.struct_s20a{[32769]*C.char{new(C.char)}}; p.f = *n; C.f20b(p); n.a[0] = nil; C.f20c(unsafe.Pointer(n))`,
fail: true,
expensive: true,
},
diff --git a/src/cmd/cgo/main.go b/src/cmd/cgo/main.go
index 939e282..3aad849 100644
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -64,17 +64,19 @@
// A File collects information about a single Go input file.
type File struct {
- AST *ast.File // parsed AST
- Comments []*ast.CommentGroup // comments from file
- Package string // Package name
- Preamble string // C preamble (doc comment on import "C")
- Ref []*Ref // all references to C.xxx in AST
- Calls []*Call // all calls to C.xxx in AST
- ExpFunc []*ExpFunc // exported functions for this file
- Name map[string]*Name // map from Go name to Name
- NamePos map[*Name]token.Pos // map from Name to position of the first reference
- NoCallbacks map[string]bool // C function names that with #cgo nocallback directive
- NoEscapes map[string]bool // C function names that with #cgo noescape directive
+ AST *ast.File // parsed AST
+ Comments []*ast.CommentGroup // comments from file
+ Package string // Package name
+ Preamble string // C preamble (doc comment on import "C")
+ Ref []*Ref // all references to C.xxx in AST
+ Calls []*Call // all calls to C.xxx in AST
+ Lits []*Lit // all C.xxx{...} literals in AST
+ LitMap map[*ast.CompositeLit]*Lit // composite literals can be rewritten in TWO WAYS, use this to find and mark done.
+ ExpFunc []*ExpFunc // exported functions for this file
+ Name map[string]*Name // map from Go name to Name
+ NamePos map[*Name]token.Pos // map from Name to position of the first reference
+ NoCallbacks map[string]bool // C function names that with #cgo nocallback directive
+ NoEscapes map[string]bool // C function names that with #cgo noescape directive
Edit *edit.Buffer
}
@@ -98,6 +100,12 @@
Done bool
}
+// A Lit refers to a composite literal of a C.xxx type in the AST.
+type Lit struct {
+ Lit *ast.CompositeLit
+ Done bool
+}
+
// A Ref refers to an expression of the form C.xxx in the AST.
type Ref struct {
Name *Name
@@ -430,6 +438,7 @@
f := new(File)
f.Edit = edit.NewBuffer(b)
+ f.LitMap = make(map[*ast.CompositeLit]*Lit)
f.ParseGo(input, b)
f.ProcessCgoDirectives()
gccIsClang := f.loadDefines(p.GccOptions)