1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Parse input AST and prepare Prog structure.
19 func parse(name string, src []byte, flags parser.Mode) *ast.File {
20 ast1, err := parser.ParseFile(fset, name, src, flags)
22 if list, ok := err.(scanner.ErrorList); ok {
23 // If err is a scanner.ErrorList, its String will print just
24 // the first error and then (+n more errors).
25 // Instead, turn it into a new Error that will return
26 // details for all the errors.
27 for _, e := range list {
28 fmt.Fprintln(os.Stderr, e)
32 fatalf("parsing %s: %s", name, err)
37 func sourceLine(n ast.Node) int {
38 return fset.Position(n.Pos()).Line
41 // ParseGo populates f with information learned from the Go source code
42 // which was read from the named file. It gathers the C preamble
43 // attached to the import "C" comment, a list of references to C.xxx,
44 // a list of exported functions, and the actual AST, to be rewritten and
46 func (f *File) ParseGo(abspath string, src []byte) {
47 // Two different parses: once with comments, once without.
48 // The printer is not good enough at printing comments in the
49 // right place when we start editing the AST behind its back,
50 // so we use ast1 to look for the doc comments on import "C"
51 // and on exported functions, and we use ast2 for translating
53 // In cgo mode, we ignore ast2 and just apply edits directly
54 // the text behind ast1. In godefs mode we modify and print ast2.
55 ast1 := parse(abspath, src, parser.SkipObjectResolution|parser.ParseComments)
56 ast2 := parse(abspath, src, parser.SkipObjectResolution)
58 f.Package = ast1.Name.Name
59 f.Name = make(map[string]*Name)
60 f.NamePos = make(map[*Name]token.Pos)
62 // In ast1, find the import "C" line and get any extra C preamble.
64 for _, decl := range ast1.Decls {
65 d, ok := decl.(*ast.GenDecl)
69 for _, spec := range d.Specs {
70 s, ok := spec.(*ast.ImportSpec)
71 if !ok || s.Path.Value != `"C"` {
76 error_(s.Path.Pos(), `cannot rename import "C"`)
79 if cg == nil && len(d.Specs) == 1 {
83 f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
84 f.Preamble += commentText(cg) + "\n"
85 f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
90 error_(ast1.Package, `cannot find import "C"`)
93 // In ast2, strip the import "C" line.
96 for _, decl := range ast2.Decls {
97 d, ok := decl.(*ast.GenDecl)
104 for _, spec := range d.Specs {
105 s, ok := spec.(*ast.ImportSpec)
106 if !ok || s.Path.Value != `"C"` {
114 d.Specs = d.Specs[0:ws]
118 ast2.Decls = ast2.Decls[0:w]
120 for _, decl := range ast2.Decls {
121 d, ok := decl.(*ast.GenDecl)
125 for _, spec := range d.Specs {
126 if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` {
127 // Replace "C" with _ "unsafe", to keep program valid.
128 // (Deleting import statement or clause is not safe if it is followed
129 // in the source by an explicit semicolon.)
130 f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
136 // Accumulate pointers to uses of C.x.
138 f.Ref = make([]*Ref, 0, 8)
140 f.walk(ast2, ctxProg, (*File).validateIdents)
141 f.walk(ast2, ctxProg, (*File).saveExprs)
143 // Accumulate exported functions.
144 // The comments are only on ast1 but we need to
145 // save the function bodies from ast2.
146 // The first walk fills in ExpFunc, and the
147 // second walk changes the entries to
148 // refer to ast2 instead.
149 f.walk(ast1, ctxProg, (*File).saveExport)
150 f.walk(ast2, ctxProg, (*File).saveExport2)
152 f.Comments = ast1.Comments
156 // Like ast.CommentGroup's Text method but preserves
157 // leading blank lines, so that line numbers line up.
158 func commentText(g *ast.CommentGroup) string {
160 for _, com := range g.List {
162 // Remove comment markers.
163 // The parser has given us exactly the comment text.
166 //-style comment (no newline at the end)
172 pieces = append(pieces, c)
174 return strings.Join(pieces, "")
177 func (f *File) validateIdents(x interface{}, context astContext) {
178 if x, ok := x.(*ast.Ident); ok {
179 if f.isMangledName(x.Name) {
180 error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name)
185 // Save various references we are going to need later.
186 func (f *File) saveExprs(x interface{}, context astContext) {
187 switch x := x.(type) {
190 case *ast.SelectorExpr:
191 f.saveRef(x, context)
194 f.saveCall(x, context)
198 // Save references to C.xxx for later processing.
199 func (f *File) saveRef(n *ast.Expr, context astContext) {
200 sel := (*n).(*ast.SelectorExpr)
201 // For now, assume that the only instance of capital C is when
202 // used as the imported package identifier.
203 // The parser should take care of scoping in the future, so
204 // that we will be able to distinguish a "top-level C" from a
206 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
209 if context == ctxAssign2 {
212 if context == ctxEmbedType {
213 error_(sel.Pos(), "cannot embed C type")
215 goname := sel.Sel.Name
216 if goname == "errno" {
217 error_(sel.Pos(), "cannot refer to errno directly; see documentation")
220 if goname == "_CMalloc" {
221 error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
224 if goname == "malloc" {
227 name := f.Name[goname]
232 f.Name[goname] = name
233 f.NamePos[name] = sel.Pos()
235 f.Ref = append(f.Ref, &Ref{
242 // Save calls to C.xxx for later processing.
243 func (f *File) saveCall(call *ast.CallExpr, context astContext) {
244 sel, ok := call.Fun.(*ast.SelectorExpr)
248 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
251 c := &Call{Call: call, Deferred: context == ctxDefer}
252 f.Calls = append(f.Calls, c)
255 // If a function should be exported add it to ExpFunc.
256 func (f *File) saveExport(x interface{}, context astContext) {
257 n, ok := x.(*ast.FuncDecl)
265 for _, c := range n.Doc.List {
266 if !strings.HasPrefix(c.Text, "//export ") {
270 name := strings.TrimSpace(c.Text[9:])
272 error_(c.Pos(), "export missing name")
275 if name != n.Name.Name {
276 error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
280 for _, c1 := range n.Doc.List {
282 doc += c1.Text + "\n"
286 f.ExpFunc = append(f.ExpFunc, &ExpFunc{
295 // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
296 func (f *File) saveExport2(x interface{}, context astContext) {
297 n, ok := x.(*ast.FuncDecl)
302 for _, exp := range f.ExpFunc {
303 if exp.Func.Name.Name == n.Name.Name {
313 ctxProg astContext = iota
320 ctxAssign2 // assignment of a single expression to two variables
327 ctxCall // any function call other than ctxCall2
328 ctxCall2 // function call whose result is assigned to two variables
332 // walk walks the AST x, calling visit(f, x, context) for each node.
333 func (f *File) walk(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
335 switch n := x.(type) {
337 f.walk(*n, context, visit)
339 // everything else just recurs
341 f.walkUnexpected(x, context, visit)
345 // These are ordered and grouped to match ../../go/ast/ast.go
347 if len(n.Names) == 0 && context == ctxField {
348 f.walk(&n.Type, ctxEmbedType, visit)
350 f.walk(&n.Type, ctxType, visit)
353 for _, field := range n.List {
354 f.walk(field, context, visit)
359 f.walk(&n.Elt, ctxType, visit)
362 f.walk(n.Type, ctxType, visit)
363 f.walk(n.Body, ctxStmt, visit)
364 case *ast.CompositeLit:
365 f.walk(&n.Type, ctxType, visit)
366 f.walk(n.Elts, ctxExpr, visit)
368 f.walk(&n.X, context, visit)
369 case *ast.SelectorExpr:
370 f.walk(&n.X, ctxSelector, visit)
372 f.walk(&n.X, ctxExpr, visit)
373 f.walk(&n.Index, ctxExpr, visit)
375 f.walk(&n.X, ctxExpr, visit)
377 f.walk(&n.Low, ctxExpr, visit)
380 f.walk(&n.High, ctxExpr, visit)
383 f.walk(&n.Max, ctxExpr, visit)
385 case *ast.TypeAssertExpr:
386 f.walk(&n.X, ctxExpr, visit)
387 f.walk(&n.Type, ctxType, visit)
389 if context == ctxAssign2 {
390 f.walk(&n.Fun, ctxCall2, visit)
392 f.walk(&n.Fun, ctxCall, visit)
394 f.walk(n.Args, ctxExpr, visit)
396 f.walk(&n.X, context, visit)
398 f.walk(&n.X, ctxExpr, visit)
399 case *ast.BinaryExpr:
400 f.walk(&n.X, ctxExpr, visit)
401 f.walk(&n.Y, ctxExpr, visit)
402 case *ast.KeyValueExpr:
403 f.walk(&n.Key, ctxExpr, visit)
404 f.walk(&n.Value, ctxExpr, visit)
407 f.walk(&n.Len, ctxExpr, visit)
408 f.walk(&n.Elt, ctxType, visit)
409 case *ast.StructType:
410 f.walk(n.Fields, ctxField, visit)
412 if tparams := funcTypeTypeParams(n); tparams != nil {
413 f.walk(tparams, ctxParam, visit)
415 f.walk(n.Params, ctxParam, visit)
416 if n.Results != nil {
417 f.walk(n.Results, ctxParam, visit)
419 case *ast.InterfaceType:
420 f.walk(n.Methods, ctxField, visit)
422 f.walk(&n.Key, ctxType, visit)
423 f.walk(&n.Value, ctxType, visit)
425 f.walk(&n.Value, ctxType, visit)
429 f.walk(n.Decl, ctxDecl, visit)
431 case *ast.LabeledStmt:
432 f.walk(n.Stmt, ctxStmt, visit)
434 f.walk(&n.X, ctxExpr, visit)
436 f.walk(&n.Chan, ctxExpr, visit)
437 f.walk(&n.Value, ctxExpr, visit)
438 case *ast.IncDecStmt:
439 f.walk(&n.X, ctxExpr, visit)
440 case *ast.AssignStmt:
441 f.walk(n.Lhs, ctxExpr, visit)
442 if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
443 f.walk(n.Rhs, ctxAssign2, visit)
445 f.walk(n.Rhs, ctxExpr, visit)
448 f.walk(n.Call, ctxExpr, visit)
450 f.walk(n.Call, ctxDefer, visit)
451 case *ast.ReturnStmt:
452 f.walk(n.Results, ctxExpr, visit)
453 case *ast.BranchStmt:
455 f.walk(n.List, context, visit)
457 f.walk(n.Init, ctxStmt, visit)
458 f.walk(&n.Cond, ctxExpr, visit)
459 f.walk(n.Body, ctxStmt, visit)
460 f.walk(n.Else, ctxStmt, visit)
461 case *ast.CaseClause:
462 if context == ctxTypeSwitch {
467 f.walk(n.List, context, visit)
468 f.walk(n.Body, ctxStmt, visit)
469 case *ast.SwitchStmt:
470 f.walk(n.Init, ctxStmt, visit)
471 f.walk(&n.Tag, ctxExpr, visit)
472 f.walk(n.Body, ctxSwitch, visit)
473 case *ast.TypeSwitchStmt:
474 f.walk(n.Init, ctxStmt, visit)
475 f.walk(n.Assign, ctxStmt, visit)
476 f.walk(n.Body, ctxTypeSwitch, visit)
477 case *ast.CommClause:
478 f.walk(n.Comm, ctxStmt, visit)
479 f.walk(n.Body, ctxStmt, visit)
480 case *ast.SelectStmt:
481 f.walk(n.Body, ctxStmt, visit)
483 f.walk(n.Init, ctxStmt, visit)
484 f.walk(&n.Cond, ctxExpr, visit)
485 f.walk(n.Post, ctxStmt, visit)
486 f.walk(n.Body, ctxStmt, visit)
488 f.walk(&n.Key, ctxExpr, visit)
489 f.walk(&n.Value, ctxExpr, visit)
490 f.walk(&n.X, ctxExpr, visit)
491 f.walk(n.Body, ctxStmt, visit)
493 case *ast.ImportSpec:
495 f.walk(&n.Type, ctxType, visit)
496 if len(n.Names) == 2 && len(n.Values) == 1 {
497 f.walk(&n.Values[0], ctxAssign2, visit)
499 f.walk(n.Values, ctxExpr, visit)
502 if tparams := typeSpecTypeParams(n); tparams != nil {
503 f.walk(tparams, ctxParam, visit)
505 f.walk(&n.Type, ctxType, visit)
509 f.walk(n.Specs, ctxSpec, visit)
512 f.walk(n.Recv, ctxParam, visit)
514 f.walk(n.Type, ctxType, visit)
516 f.walk(n.Body, ctxStmt, visit)
520 f.walk(n.Decls, ctxDecl, visit)
523 for _, file := range n.Files {
524 f.walk(file, ctxFile, visit)
528 for _, d := range n {
529 f.walk(d, context, visit)
533 f.walk(&n[i], context, visit)
536 for _, s := range n {
537 f.walk(s, context, visit)
540 for _, s := range n {
541 f.walk(s, context, visit)