blob: c0d59aee01d747ec8ed02a587c72da0dd55f016b [file] [log] [blame]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"go/token"
"os"
"path/filepath"
"strings"
)
// godefs returns the output for -godefs mode.
func (p *Package) godefs(f *File) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n")
fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
fmt.Fprintf(&buf, "\n")
override := make(map[string]string)
// Allow source file to specify override mappings.
// For example, the socket data structures refer
// to in_addr and in_addr6 structs but we want to be
// able to treat them as byte arrays, so the godefs
// inputs in package syscall say
//
// // +godefs map struct_in_addr [4]byte
// // +godefs map struct_in_addr6 [16]byte
//
for _, g := range f.Comments {
for _, c := range g.List {
i := strings.Index(c.Text, "+godefs map")
if i < 0 {
continue
}
s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
i = strings.Index(s, " ")
if i < 0 {
fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
continue
}
override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
}
}
for _, n := range f.Name {
if s := override[n.Go]; s != "" {
override[n.Mangle] = s
}
}
// Otherwise, if the source file says type T C.whatever,
// use "T" as the mangling of C.whatever,
// except in the definition (handled at end of function).
refName := make(map[*ast.Expr]*Name)
for _, r := range f.Ref {
refName[r.Expr] = r.Name
}
for _, d := range f.AST.Decls {
d, ok := d.(*ast.GenDecl)
if !ok || d.Tok != token.TYPE {
continue
}
for _, s := range d.Specs {
s := s.(*ast.TypeSpec)
n := refName[&s.Type]
if n != nil && n.Mangle != "" {
override[n.Mangle] = s.Name.Name
}
}
}
// Extend overrides using typedefs:
// If we know that C.xxx should format as T
// and xxx is a typedef for yyy, make C.yyy format as T.
for typ, def := range typedef {
if new := override[typ]; new != "" {
if id, ok := def.Go.(*ast.Ident); ok {
override[id.Name] = new
}
}
}
// Apply overrides.
for old, new := range override {
if id := goIdent[old]; id != nil {
id.Name = new
}
}
// Any names still using the _C syntax are not going to compile,
// although in general we don't know whether they all made it
// into the file, so we can't warn here.
//
// The most common case is union types, which begin with
// _Ctype_union and for which typedef[name] is a Go byte
// array of the appropriate size (such as [4]byte).
// Substitute those union types with byte arrays.
for name, id := range goIdent {
if id.Name == name && strings.Contains(name, "_Ctype_union") {
if def := typedef[name]; def != nil {
id.Name = gofmt(def)
}
}
}
conf.Fprint(&buf, fset, f.AST)
return buf.String()
}
var gofmtBuf bytes.Buffer
// gofmt returns the gofmt-formatted string for an AST node.
func gofmt(n interface{}) string {
gofmtBuf.Reset()
err := printer.Fprint(&gofmtBuf, fset, n)
if err != nil {
return "<" + err.Error() + ">"
}
return gofmtBuf.String()
}
// gofmtLineReplacer is used to put a gofmt-formatted string for an
// AST expression onto a single line. The lexer normally inserts a
// semicolon at each newline, so we can replace newline with semicolon.
// However, we can't do that in cases where the lexer would not insert
// a semicolon. We only have to worry about cases that can occur in an
// expression passed through gofmt, which means composite literals and
// (due to the printer possibly inserting newlines because of position
// information) operators.
var gofmtLineReplacer = strings.NewReplacer(
// Want to replace \n without ; after everything from
// https://golang.org/ref/spec#Operators_and_punctuation
// EXCEPT ++ -- ) ] }
"++\n", "++;",
"--\n", "--;",
"+\n", "+ ",
"-\n", "- ",
"*\n", "* ",
"/\n", "/ ",
"%\n", "% ",
"&\n", "& ",
"|\n", "| ",
"^\n", "^ ",
"<\n", "< ",
">\n", "> ",
"=\n", "= ",
"!\n", "! ", // not possible in gofmt today
"(\n", "(",
"[\n", "[", // not possible in gofmt today
"{\n", "{",
",\n", ",",
".\n", ". ",
":\n", ": ", // not possible in gofmt today
"\n", ";",
)
// gofmtLine returns the gofmt-formatted string for an AST node,
// ensuring that it is on a single line.
func gofmtLine(n interface{}) string {
return gofmtLineReplacer.Replace(gofmt(n))
}