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.
20 func parse(name
string, src
[]byte, flags parser
.Mode
) *ast
.File
{
21 ast1
, err
:= parser
.ParseFile(fset
, name
, src
, flags
)
23 if list
, ok
:= err
.(scanner
.ErrorList
); ok
{
24 // If err is a scanner.ErrorList, its String will print just
25 // the first error and then (+n more errors).
26 // Instead, turn it into a new Error that will return
27 // details for all the errors.
28 for _
, e
:= range list
{
29 fmt
.Fprintln(os
.Stderr
, e
)
33 fatalf("parsing %s: %s", name
, err
)
38 func sourceLine(n ast
.Node
) int {
39 return fset
.Position(n
.Pos()).Line
42 // ParseGo populates f with information learned from the Go source code
43 // which was read from the named file. It gathers the C preamble
44 // attached to the import "C" comment, a list of references to C.xxx,
45 // a list of exported functions, and the actual AST, to be rewritten and
47 func (f
*File
) ParseGo(name
string, src
[]byte) {
48 // Create absolute path for file, so that it will be used in error
49 // messages and recorded in debug line number information.
50 // This matches the rest of the toolchain. See golang.org/issue/5122.
51 if aname
, err
:= filepath
.Abs(name
); err
== nil {
55 // Two different parses: once with comments, once without.
56 // The printer is not good enough at printing comments in the
57 // right place when we start editing the AST behind its back,
58 // so we use ast1 to look for the doc comments on import "C"
59 // and on exported functions, and we use ast2 for translating
61 // In cgo mode, we ignore ast2 and just apply edits directly
62 // the text behind ast1. In godefs mode we modify and print ast2.
63 ast1
:= parse(name
, src
, parser
.ParseComments
)
64 ast2
:= parse(name
, src
, 0)
66 f
.Package
= ast1
.Name
.Name
67 f
.Name
= make(map[string]*Name
)
68 f
.NamePos
= make(map[*Name
]token
.Pos
)
70 // In ast1, find the import "C" line and get any extra C preamble.
72 for _
, decl
:= range ast1
.Decls
{
73 d
, ok
:= decl
.(*ast
.GenDecl
)
77 for _
, spec
:= range d
.Specs
{
78 s
, ok
:= spec
.(*ast
.ImportSpec
)
79 if !ok || s
.Path
.Value
!= `"C"` {
84 error_(s
.Path
.Pos(), `cannot rename import "C"`)
87 if cg
== nil && len(d
.Specs
) == 1 {
91 f
.Preamble
+= fmt
.Sprintf("#line %d %q\n", sourceLine(cg
), name
)
92 f
.Preamble
+= commentText(cg
) + "\n"
93 f
.Preamble
+= "#line 1 \"cgo-generated-wrapper\"\n"
98 error_(token
.NoPos
, `cannot find import "C"`)
101 // In ast2, strip the import "C" line.
104 for _
, decl
:= range ast2
.Decls
{
105 d
, ok
:= decl
.(*ast
.GenDecl
)
112 for _
, spec
:= range d
.Specs
{
113 s
, ok
:= spec
.(*ast
.ImportSpec
)
114 if !ok || s
.Path
.Value
!= `"C"` {
122 d
.Specs
= d
.Specs
[0:ws
]
126 ast2
.Decls
= ast2
.Decls
[0:w
]
128 for _
, decl
:= range ast2
.Decls
{
129 d
, ok
:= decl
.(*ast
.GenDecl
)
133 for _
, spec
:= range d
.Specs
{
134 if s
, ok
:= spec
.(*ast
.ImportSpec
); ok
&& s
.Path
.Value
== `"C"` {
135 // Replace "C" with _ "unsafe", to keep program valid.
136 // (Deleting import statement or clause is not safe if it is followed
137 // in the source by an explicit semicolon.)
138 f
.Edit
.Replace(f
.offset(s
.Path
.Pos()), f
.offset(s
.Path
.End()), `_ "unsafe"`)
144 // Accumulate pointers to uses of C.x.
146 f
.Ref
= make([]*Ref
, 0, 8)
148 f
.walk(ast2
, ctxProg
, (*File
).saveExprs
)
150 // Accumulate exported functions.
151 // The comments are only on ast1 but we need to
152 // save the function bodies from ast2.
153 // The first walk fills in ExpFunc, and the
154 // second walk changes the entries to
155 // refer to ast2 instead.
156 f
.walk(ast1
, ctxProg
, (*File
).saveExport
)
157 f
.walk(ast2
, ctxProg
, (*File
).saveExport2
)
159 f
.Comments
= ast1
.Comments
163 // Like ast.CommentGroup's Text method but preserves
164 // leading blank lines, so that line numbers line up.
165 func commentText(g
*ast
.CommentGroup
) string {
167 for _
, com
:= range g
.List
{
169 // Remove comment markers.
170 // The parser has given us exactly the comment text.
173 //-style comment (no newline at the end)
179 pieces
= append(pieces
, c
)
181 return strings
.Join(pieces
, "")
184 // Save various references we are going to need later.
185 func (f
*File
) saveExprs(x
interface{}, context astContext
) {
186 switch x
:= x
.(type) {
189 case *ast
.SelectorExpr
:
190 f
.saveRef(x
, context
)
193 f
.saveCall(x
, context
)
197 // Save references to C.xxx for later processing.
198 func (f
*File
) saveRef(n
*ast
.Expr
, context astContext
) {
199 sel
:= (*n
).(*ast
.SelectorExpr
)
200 // For now, assume that the only instance of capital C is when
201 // used as the imported package identifier.
202 // The parser should take care of scoping in the future, so
203 // that we will be able to distinguish a "top-level C" from a
205 if l
, ok
:= sel
.X
.(*ast
.Ident
); !ok || l
.Name
!= "C" {
208 if context
== ctxAssign2
{
211 if context
== ctxEmbedType
{
212 error_(sel
.Pos(), "cannot embed C type")
214 goname
:= sel
.Sel
.Name
215 if goname
== "errno" {
216 error_(sel
.Pos(), "cannot refer to errno directly; see documentation")
219 if goname
== "_CMalloc" {
220 error_(sel
.Pos(), "cannot refer to C._CMalloc; use C.malloc")
223 if goname
== "malloc" {
226 name
:= f
.Name
[goname
]
231 f
.Name
[goname
] = name
232 f
.NamePos
[name
] = sel
.Pos()
234 f
.Ref
= append(f
.Ref
, &Ref
{
241 // Save calls to C.xxx for later processing.
242 func (f
*File
) saveCall(call
*ast
.CallExpr
, context astContext
) {
243 sel
, ok
:= call
.Fun
.(*ast
.SelectorExpr
)
247 if l
, ok
:= sel
.X
.(*ast
.Ident
); !ok || l
.Name
!= "C" {
250 c
:= &Call
{Call
: call
, Deferred
: context
== ctxDefer
}
251 f
.Calls
= append(f
.Calls
, c
)
254 // If a function should be exported add it to ExpFunc.
255 func (f
*File
) saveExport(x
interface{}, context astContext
) {
256 n
, ok
:= x
.(*ast
.FuncDecl
)
264 for _
, c
:= range n
.Doc
.List
{
265 if !strings
.HasPrefix(c
.Text
, "//export ") {
269 name
:= strings
.TrimSpace(c
.Text
[9:])
271 error_(c
.Pos(), "export missing name")
274 if name
!= n
.Name
.Name
{
275 error_(c
.Pos(), "export comment has wrong name %q, want %q", name
, n
.Name
.Name
)
279 for _
, c1
:= range n
.Doc
.List
{
281 doc
+= c1
.Text
+ "\n"
285 f
.ExpFunc
= append(f
.ExpFunc
, &ExpFunc
{
294 // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
295 func (f
*File
) saveExport2(x
interface{}, context astContext
) {
296 n
, ok
:= x
.(*ast
.FuncDecl
)
301 for _
, exp
:= range f
.ExpFunc
{
302 if exp
.Func
.Name
.Name
== n
.Name
.Name
{
312 ctxProg astContext
= iota
319 ctxAssign2
// assignment of a single expression to two variables
326 ctxCall
// any function call other than ctxCall2
327 ctxCall2
// function call whose result is assigned to two variables
331 // walk walks the AST x, calling visit(f, x, context) for each node.
332 func (f
*File
) walk(x
interface{}, context astContext
, visit
func(*File
, interface{}, astContext
)) {
334 switch n
:= x
.(type) {
336 f
.walk(*n
, context
, visit
)
338 // everything else just recurs
340 error_(token
.NoPos
, "unexpected type %T in walk", x
)
341 panic("unexpected type")
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
)
361 f
.walk(n
.Type
, ctxType
, visit
)
362 f
.walk(n
.Body
, ctxStmt
, visit
)
363 case *ast
.CompositeLit
:
364 f
.walk(&n
.Type
, ctxType
, visit
)
365 f
.walk(n
.Elts
, ctxExpr
, visit
)
367 f
.walk(&n
.X
, context
, visit
)
368 case *ast
.SelectorExpr
:
369 f
.walk(&n
.X
, ctxSelector
, visit
)
371 f
.walk(&n
.X
, ctxExpr
, visit
)
372 f
.walk(&n
.Index
, ctxExpr
, visit
)
374 f
.walk(&n
.X
, ctxExpr
, visit
)
376 f
.walk(&n
.Low
, ctxExpr
, visit
)
379 f
.walk(&n
.High
, ctxExpr
, visit
)
382 f
.walk(&n
.Max
, ctxExpr
, visit
)
384 case *ast
.TypeAssertExpr
:
385 f
.walk(&n
.X
, ctxExpr
, visit
)
386 f
.walk(&n
.Type
, ctxType
, visit
)
388 if context
== ctxAssign2
{
389 f
.walk(&n
.Fun
, ctxCall2
, visit
)
391 f
.walk(&n
.Fun
, ctxCall
, visit
)
393 f
.walk(n
.Args
, ctxExpr
, visit
)
395 f
.walk(&n
.X
, context
, visit
)
397 f
.walk(&n
.X
, ctxExpr
, visit
)
398 case *ast
.BinaryExpr
:
399 f
.walk(&n
.X
, ctxExpr
, visit
)
400 f
.walk(&n
.Y
, ctxExpr
, visit
)
401 case *ast
.KeyValueExpr
:
402 f
.walk(&n
.Key
, ctxExpr
, visit
)
403 f
.walk(&n
.Value
, ctxExpr
, visit
)
406 f
.walk(&n
.Len
, ctxExpr
, visit
)
407 f
.walk(&n
.Elt
, ctxType
, visit
)
408 case *ast
.StructType
:
409 f
.walk(n
.Fields
, ctxField
, visit
)
411 f
.walk(n
.Params
, ctxParam
, visit
)
412 if n
.Results
!= nil {
413 f
.walk(n
.Results
, ctxParam
, visit
)
415 case *ast
.InterfaceType
:
416 f
.walk(n
.Methods
, ctxField
, visit
)
418 f
.walk(&n
.Key
, ctxType
, visit
)
419 f
.walk(&n
.Value
, ctxType
, visit
)
421 f
.walk(&n
.Value
, ctxType
, visit
)
425 f
.walk(n
.Decl
, ctxDecl
, visit
)
427 case *ast
.LabeledStmt
:
428 f
.walk(n
.Stmt
, ctxStmt
, visit
)
430 f
.walk(&n
.X
, ctxExpr
, visit
)
432 f
.walk(&n
.Chan
, ctxExpr
, visit
)
433 f
.walk(&n
.Value
, ctxExpr
, visit
)
434 case *ast
.IncDecStmt
:
435 f
.walk(&n
.X
, ctxExpr
, visit
)
436 case *ast
.AssignStmt
:
437 f
.walk(n
.Lhs
, ctxExpr
, visit
)
438 if len(n
.Lhs
) == 2 && len(n
.Rhs
) == 1 {
439 f
.walk(n
.Rhs
, ctxAssign2
, visit
)
441 f
.walk(n
.Rhs
, ctxExpr
, visit
)
444 f
.walk(n
.Call
, ctxExpr
, visit
)
446 f
.walk(n
.Call
, ctxDefer
, visit
)
447 case *ast
.ReturnStmt
:
448 f
.walk(n
.Results
, ctxExpr
, visit
)
449 case *ast
.BranchStmt
:
451 f
.walk(n
.List
, context
, visit
)
453 f
.walk(n
.Init
, ctxStmt
, visit
)
454 f
.walk(&n
.Cond
, ctxExpr
, visit
)
455 f
.walk(n
.Body
, ctxStmt
, visit
)
456 f
.walk(n
.Else
, ctxStmt
, visit
)
457 case *ast
.CaseClause
:
458 if context
== ctxTypeSwitch
{
463 f
.walk(n
.List
, context
, visit
)
464 f
.walk(n
.Body
, ctxStmt
, visit
)
465 case *ast
.SwitchStmt
:
466 f
.walk(n
.Init
, ctxStmt
, visit
)
467 f
.walk(&n
.Tag
, ctxExpr
, visit
)
468 f
.walk(n
.Body
, ctxSwitch
, visit
)
469 case *ast
.TypeSwitchStmt
:
470 f
.walk(n
.Init
, ctxStmt
, visit
)
471 f
.walk(n
.Assign
, ctxStmt
, visit
)
472 f
.walk(n
.Body
, ctxTypeSwitch
, visit
)
473 case *ast
.CommClause
:
474 f
.walk(n
.Comm
, ctxStmt
, visit
)
475 f
.walk(n
.Body
, ctxStmt
, visit
)
476 case *ast
.SelectStmt
:
477 f
.walk(n
.Body
, ctxStmt
, visit
)
479 f
.walk(n
.Init
, ctxStmt
, visit
)
480 f
.walk(&n
.Cond
, ctxExpr
, visit
)
481 f
.walk(n
.Post
, ctxStmt
, visit
)
482 f
.walk(n
.Body
, ctxStmt
, visit
)
484 f
.walk(&n
.Key
, ctxExpr
, visit
)
485 f
.walk(&n
.Value
, ctxExpr
, visit
)
486 f
.walk(&n
.X
, ctxExpr
, visit
)
487 f
.walk(n
.Body
, ctxStmt
, visit
)
489 case *ast
.ImportSpec
:
491 f
.walk(&n
.Type
, ctxType
, visit
)
492 if len(n
.Names
) == 2 && len(n
.Values
) == 1 {
493 f
.walk(&n
.Values
[0], ctxAssign2
, visit
)
495 f
.walk(n
.Values
, ctxExpr
, visit
)
498 f
.walk(&n
.Type
, ctxType
, visit
)
502 f
.walk(n
.Specs
, ctxSpec
, visit
)
505 f
.walk(n
.Recv
, ctxParam
, visit
)
507 f
.walk(n
.Type
, ctxType
, visit
)
509 f
.walk(n
.Body
, ctxStmt
, visit
)
513 f
.walk(n
.Decls
, ctxDecl
, visit
)
516 for _
, file
:= range n
.Files
{
517 f
.walk(file
, ctxFile
, visit
)
521 for _
, d
:= range n
{
522 f
.walk(d
, context
, visit
)
526 f
.walk(&n
[i
], context
, visit
)
529 for _
, s
:= range n
{
530 f
.walk(s
, context
, visit
)
533 for _
, s
:= range n
{
534 f
.walk(s
, context
, visit
)