// so we use ast1 to look for the doc comments on import "C"
// and on exported functions, and we use ast2 for translating
// and reprinting.
+ // In cgo mode, we ignore ast2 and just apply edits directly
+ // the text behind ast1. In godefs mode we modify and print ast2.
ast1 := parse(name, src, parser.ParseComments)
ast2 := parse(name, src, 0)
}
// In ast2, strip the import "C" line.
- w := 0
- for _, decl := range ast2.Decls {
- d, ok := decl.(*ast.GenDecl)
- if !ok {
- ast2.Decls[w] = decl
+ if *godefs {
+ w := 0
+ for _, decl := range ast2.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok {
+ ast2.Decls[w] = decl
+ w++
+ continue
+ }
+ ws := 0
+ for _, spec := range d.Specs {
+ s, ok := spec.(*ast.ImportSpec)
+ if !ok || s.Path.Value != `"C"` {
+ d.Specs[ws] = spec
+ ws++
+ }
+ }
+ if ws == 0 {
+ continue
+ }
+ d.Specs = d.Specs[0:ws]
+ ast2.Decls[w] = d
w++
- continue
}
- ws := 0
- for _, spec := range d.Specs {
- s, ok := spec.(*ast.ImportSpec)
- if !ok || s.Path.Value != `"C"` {
- d.Specs[ws] = spec
- ws++
+ ast2.Decls = ast2.Decls[0:w]
+ } else {
+ for _, decl := range ast2.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok {
+ continue
+ }
+ for _, spec := range d.Specs {
+ if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` {
+ // Replace "C" with _ "unsafe", to keep program valid.
+ // (Deleting import statement or clause is not safe if it is followed
+ // in the source by an explicit semicolon.)
+ f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
+ }
}
}
- if ws == 0 {
- continue
- }
- d.Specs = d.Specs[0:ws]
- ast2.Decls[w] = d
- w++
}
- ast2.Decls = ast2.Decls[0:w]
// Accumulate pointers to uses of C.x.
if f.Ref == nil {
p.loadDWARF(f, needType)
}
if p.rewriteCalls(f) {
- // Add `import _cgo_unsafe "unsafe"` as the first decl
- // after the package statement.
- imp := &ast.GenDecl{
- Tok: token.IMPORT,
- Specs: []ast.Spec{
- &ast.ImportSpec{
- Name: ast.NewIdent("_cgo_unsafe"),
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: `"unsafe"`,
- },
- },
- },
- }
- f.AST.Decls = append([]ast.Decl{imp}, f.AST.Decls...)
+ // Add `import _cgo_unsafe "unsafe"` after the package statement.
+ f.Edit.Insert(f.offset(f.AST.Name.End()), "; import _cgo_unsafe \"unsafe\"")
}
p.rewriteRef(f)
}
stmts = append(stmts, stmt)
}
+ const cgoMarker = "__cgo__###__marker__"
fcall := &ast.CallExpr{
- Fun: call.Call.Fun,
+ Fun: ast.NewIdent(cgoMarker),
Args: nargs,
}
ftype := &ast.FuncType{
}
}
- // There is a Ref pointing to the old call.Call.Fun.
+ // If this call expects two results, we have to
+ // adjust the results of the function we generated.
for _, ref := range f.Ref {
- if ref.Expr == &call.Call.Fun {
- ref.Expr = &fcall.Fun
-
- // If this call expects two results, we have to
- // adjust the results of the function we generated.
- if ref.Context == ctxCall2 {
- if ftype.Results == nil {
- // An explicit void argument
- // looks odd but it seems to
- // be how cgo has worked historically.
- ftype.Results = &ast.FieldList{
- List: []*ast.Field{
- &ast.Field{
- Type: ast.NewIdent("_Ctype_void"),
- },
+ if ref.Expr == &call.Call.Fun && ref.Context == ctxCall2 {
+ if ftype.Results == nil {
+ // An explicit void argument
+ // looks odd but it seems to
+ // be how cgo has worked historically.
+ ftype.Results = &ast.FieldList{
+ List: []*ast.Field{
+ &ast.Field{
+ Type: ast.NewIdent("_Ctype_void"),
},
- }
+ },
}
- ftype.Results.List = append(ftype.Results.List,
- &ast.Field{
- Type: ast.NewIdent("error"),
- })
}
+ ftype.Results.List = append(ftype.Results.List,
+ &ast.Field{
+ Type: ast.NewIdent("error"),
+ })
}
}
Results: []ast.Expr{fcall},
}
}
- call.Call.Fun = &ast.FuncLit{
+ lit := &ast.FuncLit{
Type: ftype,
Body: &ast.BlockStmt{
List: append(stmts, fbody),
},
}
- call.Call.Lparen = token.NoPos
- call.Call.Rparen = token.NoPos
+ text := strings.Replace(gofmt(lit), "\n", ";", -1)
+ repl := strings.Split(text, cgoMarker)
+ f.Edit.Insert(f.offset(call.Call.Fun.Pos()), repl[0])
+ f.Edit.Insert(f.offset(call.Call.Fun.End()), repl[1])
return needsUnsafe
}
error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
}
}
+
if *godefs {
// Substitute definition for mangled type name.
if id, ok := expr.(*ast.Ident); ok {
expr = &ast.Ident{NamePos: pos, Name: x.Name}
}
+ // Change AST, because some later processing depends on it,
+ // and also because -godefs mode still prints the AST.
+ old := *r.Expr
*r.Expr = expr
+
+ // Record source-level edit for cgo output.
+ repl := gofmt(expr)
+ if r.Name.Kind != "type" {
+ repl = "(" + repl + ")"
+ }
+ f.Edit.Replace(f.offset(old.Pos()), f.offset(old.End()), repl)
}
// Remove functions only used as expressions, so their respective