]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/cgo/ast.go
cmd/cgo: walk {FuncType,TypeSpec}.TypeParams fields
[gostls13.git] / src / cmd / cgo / ast.go
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.
4
5 // Parse input AST and prepare Prog structure.
6
7 package main
8
9 import (
10         "fmt"
11         "go/ast"
12         "go/parser"
13         "go/scanner"
14         "go/token"
15         "os"
16         "strings"
17 )
18
19 func parse(name string, src []byte, flags parser.Mode) *ast.File {
20         ast1, err := parser.ParseFile(fset, name, src, flags)
21         if err != nil {
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)
29                         }
30                         os.Exit(2)
31                 }
32                 fatalf("parsing %s: %s", name, err)
33         }
34         return ast1
35 }
36
37 func sourceLine(n ast.Node) int {
38         return fset.Position(n.Pos()).Line
39 }
40
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
45 // printed.
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
52         // and reprinting.
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)
57
58         f.Package = ast1.Name.Name
59         f.Name = make(map[string]*Name)
60         f.NamePos = make(map[*Name]token.Pos)
61
62         // In ast1, find the import "C" line and get any extra C preamble.
63         sawC := false
64         for _, decl := range ast1.Decls {
65                 d, ok := decl.(*ast.GenDecl)
66                 if !ok {
67                         continue
68                 }
69                 for _, spec := range d.Specs {
70                         s, ok := spec.(*ast.ImportSpec)
71                         if !ok || s.Path.Value != `"C"` {
72                                 continue
73                         }
74                         sawC = true
75                         if s.Name != nil {
76                                 error_(s.Path.Pos(), `cannot rename import "C"`)
77                         }
78                         cg := s.Doc
79                         if cg == nil && len(d.Specs) == 1 {
80                                 cg = d.Doc
81                         }
82                         if cg != nil {
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"
86                         }
87                 }
88         }
89         if !sawC {
90                 error_(ast1.Package, `cannot find import "C"`)
91         }
92
93         // In ast2, strip the import "C" line.
94         if *godefs {
95                 w := 0
96                 for _, decl := range ast2.Decls {
97                         d, ok := decl.(*ast.GenDecl)
98                         if !ok {
99                                 ast2.Decls[w] = decl
100                                 w++
101                                 continue
102                         }
103                         ws := 0
104                         for _, spec := range d.Specs {
105                                 s, ok := spec.(*ast.ImportSpec)
106                                 if !ok || s.Path.Value != `"C"` {
107                                         d.Specs[ws] = spec
108                                         ws++
109                                 }
110                         }
111                         if ws == 0 {
112                                 continue
113                         }
114                         d.Specs = d.Specs[0:ws]
115                         ast2.Decls[w] = d
116                         w++
117                 }
118                 ast2.Decls = ast2.Decls[0:w]
119         } else {
120                 for _, decl := range ast2.Decls {
121                         d, ok := decl.(*ast.GenDecl)
122                         if !ok {
123                                 continue
124                         }
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"`)
131                                 }
132                         }
133                 }
134         }
135
136         // Accumulate pointers to uses of C.x.
137         if f.Ref == nil {
138                 f.Ref = make([]*Ref, 0, 8)
139         }
140         f.walk(ast2, ctxProg, (*File).validateIdents)
141         f.walk(ast2, ctxProg, (*File).saveExprs)
142
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)
151
152         f.Comments = ast1.Comments
153         f.AST = ast2
154 }
155
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 {
159         var pieces []string
160         for _, com := range g.List {
161                 c := com.Text
162                 // Remove comment markers.
163                 // The parser has given us exactly the comment text.
164                 switch c[1] {
165                 case '/':
166                         //-style comment (no newline at the end)
167                         c = c[2:] + "\n"
168                 case '*':
169                         /*-style comment */
170                         c = c[2 : len(c)-2]
171                 }
172                 pieces = append(pieces, c)
173         }
174         return strings.Join(pieces, "")
175 }
176
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)
181                 }
182         }
183 }
184
185 // Save various references we are going to need later.
186 func (f *File) saveExprs(x interface{}, context astContext) {
187         switch x := x.(type) {
188         case *ast.Expr:
189                 switch (*x).(type) {
190                 case *ast.SelectorExpr:
191                         f.saveRef(x, context)
192                 }
193         case *ast.CallExpr:
194                 f.saveCall(x, context)
195         }
196 }
197
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
205         // local C.
206         if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
207                 return
208         }
209         if context == ctxAssign2 {
210                 context = ctxExpr
211         }
212         if context == ctxEmbedType {
213                 error_(sel.Pos(), "cannot embed C type")
214         }
215         goname := sel.Sel.Name
216         if goname == "errno" {
217                 error_(sel.Pos(), "cannot refer to errno directly; see documentation")
218                 return
219         }
220         if goname == "_CMalloc" {
221                 error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
222                 return
223         }
224         if goname == "malloc" {
225                 goname = "_CMalloc"
226         }
227         name := f.Name[goname]
228         if name == nil {
229                 name = &Name{
230                         Go: goname,
231                 }
232                 f.Name[goname] = name
233                 f.NamePos[name] = sel.Pos()
234         }
235         f.Ref = append(f.Ref, &Ref{
236                 Name:    name,
237                 Expr:    n,
238                 Context: context,
239         })
240 }
241
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)
245         if !ok {
246                 return
247         }
248         if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
249                 return
250         }
251         c := &Call{Call: call, Deferred: context == ctxDefer}
252         f.Calls = append(f.Calls, c)
253 }
254
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)
258         if !ok {
259                 return
260         }
261
262         if n.Doc == nil {
263                 return
264         }
265         for _, c := range n.Doc.List {
266                 if !strings.HasPrefix(c.Text, "//export ") {
267                         continue
268                 }
269
270                 name := strings.TrimSpace(c.Text[9:])
271                 if name == "" {
272                         error_(c.Pos(), "export missing name")
273                 }
274
275                 if name != n.Name.Name {
276                         error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
277                 }
278
279                 doc := ""
280                 for _, c1 := range n.Doc.List {
281                         if c1 != c {
282                                 doc += c1.Text + "\n"
283                         }
284                 }
285
286                 f.ExpFunc = append(f.ExpFunc, &ExpFunc{
287                         Func:    n,
288                         ExpName: name,
289                         Doc:     doc,
290                 })
291                 break
292         }
293 }
294
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)
298         if !ok {
299                 return
300         }
301
302         for _, exp := range f.ExpFunc {
303                 if exp.Func.Name.Name == n.Name.Name {
304                         exp.Func = n
305                         break
306                 }
307         }
308 }
309
310 type astContext int
311
312 const (
313         ctxProg astContext = iota
314         ctxEmbedType
315         ctxType
316         ctxStmt
317         ctxExpr
318         ctxField
319         ctxParam
320         ctxAssign2 // assignment of a single expression to two variables
321         ctxSwitch
322         ctxTypeSwitch
323         ctxFile
324         ctxDecl
325         ctxSpec
326         ctxDefer
327         ctxCall  // any function call other than ctxCall2
328         ctxCall2 // function call whose result is assigned to two variables
329         ctxSelector
330 )
331
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)) {
334         visit(f, x, context)
335         switch n := x.(type) {
336         case *ast.Expr:
337                 f.walk(*n, context, visit)
338
339         // everything else just recurs
340         default:
341                 f.walkUnexpected(x, context, visit)
342
343         case nil:
344
345         // These are ordered and grouped to match ../../go/ast/ast.go
346         case *ast.Field:
347                 if len(n.Names) == 0 && context == ctxField {
348                         f.walk(&n.Type, ctxEmbedType, visit)
349                 } else {
350                         f.walk(&n.Type, ctxType, visit)
351                 }
352         case *ast.FieldList:
353                 for _, field := range n.List {
354                         f.walk(field, context, visit)
355                 }
356         case *ast.BadExpr:
357         case *ast.Ident:
358         case *ast.Ellipsis:
359                 f.walk(&n.Elt, ctxType, visit)
360         case *ast.BasicLit:
361         case *ast.FuncLit:
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)
367         case *ast.ParenExpr:
368                 f.walk(&n.X, context, visit)
369         case *ast.SelectorExpr:
370                 f.walk(&n.X, ctxSelector, visit)
371         case *ast.IndexExpr:
372                 f.walk(&n.X, ctxExpr, visit)
373                 f.walk(&n.Index, ctxExpr, visit)
374         case *ast.SliceExpr:
375                 f.walk(&n.X, ctxExpr, visit)
376                 if n.Low != nil {
377                         f.walk(&n.Low, ctxExpr, visit)
378                 }
379                 if n.High != nil {
380                         f.walk(&n.High, ctxExpr, visit)
381                 }
382                 if n.Max != nil {
383                         f.walk(&n.Max, ctxExpr, visit)
384                 }
385         case *ast.TypeAssertExpr:
386                 f.walk(&n.X, ctxExpr, visit)
387                 f.walk(&n.Type, ctxType, visit)
388         case *ast.CallExpr:
389                 if context == ctxAssign2 {
390                         f.walk(&n.Fun, ctxCall2, visit)
391                 } else {
392                         f.walk(&n.Fun, ctxCall, visit)
393                 }
394                 f.walk(n.Args, ctxExpr, visit)
395         case *ast.StarExpr:
396                 f.walk(&n.X, context, visit)
397         case *ast.UnaryExpr:
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)
405
406         case *ast.ArrayType:
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)
411         case *ast.FuncType:
412                 if tparams := funcTypeTypeParams(n); tparams != nil {
413                         f.walk(tparams, ctxParam, visit)
414                 }
415                 f.walk(n.Params, ctxParam, visit)
416                 if n.Results != nil {
417                         f.walk(n.Results, ctxParam, visit)
418                 }
419         case *ast.InterfaceType:
420                 f.walk(n.Methods, ctxField, visit)
421         case *ast.MapType:
422                 f.walk(&n.Key, ctxType, visit)
423                 f.walk(&n.Value, ctxType, visit)
424         case *ast.ChanType:
425                 f.walk(&n.Value, ctxType, visit)
426
427         case *ast.BadStmt:
428         case *ast.DeclStmt:
429                 f.walk(n.Decl, ctxDecl, visit)
430         case *ast.EmptyStmt:
431         case *ast.LabeledStmt:
432                 f.walk(n.Stmt, ctxStmt, visit)
433         case *ast.ExprStmt:
434                 f.walk(&n.X, ctxExpr, visit)
435         case *ast.SendStmt:
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)
444                 } else {
445                         f.walk(n.Rhs, ctxExpr, visit)
446                 }
447         case *ast.GoStmt:
448                 f.walk(n.Call, ctxExpr, visit)
449         case *ast.DeferStmt:
450                 f.walk(n.Call, ctxDefer, visit)
451         case *ast.ReturnStmt:
452                 f.walk(n.Results, ctxExpr, visit)
453         case *ast.BranchStmt:
454         case *ast.BlockStmt:
455                 f.walk(n.List, context, visit)
456         case *ast.IfStmt:
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 {
463                         context = ctxType
464                 } else {
465                         context = ctxExpr
466                 }
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)
482         case *ast.ForStmt:
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)
487         case *ast.RangeStmt:
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)
492
493         case *ast.ImportSpec:
494         case *ast.ValueSpec:
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)
498                 } else {
499                         f.walk(n.Values, ctxExpr, visit)
500                 }
501         case *ast.TypeSpec:
502                 if tparams := typeSpecTypeParams(n); tparams != nil {
503                         f.walk(tparams, ctxParam, visit)
504                 }
505                 f.walk(&n.Type, ctxType, visit)
506
507         case *ast.BadDecl:
508         case *ast.GenDecl:
509                 f.walk(n.Specs, ctxSpec, visit)
510         case *ast.FuncDecl:
511                 if n.Recv != nil {
512                         f.walk(n.Recv, ctxParam, visit)
513                 }
514                 f.walk(n.Type, ctxType, visit)
515                 if n.Body != nil {
516                         f.walk(n.Body, ctxStmt, visit)
517                 }
518
519         case *ast.File:
520                 f.walk(n.Decls, ctxDecl, visit)
521
522         case *ast.Package:
523                 for _, file := range n.Files {
524                         f.walk(file, ctxFile, visit)
525                 }
526
527         case []ast.Decl:
528                 for _, d := range n {
529                         f.walk(d, context, visit)
530                 }
531         case []ast.Expr:
532                 for i := range n {
533                         f.walk(&n[i], context, visit)
534                 }
535         case []ast.Stmt:
536                 for _, s := range n {
537                         f.walk(s, context, visit)
538                 }
539         case []ast.Spec:
540                 for _, s := range n {
541                         f.walk(s, context, visit)
542                 }
543         }
544 }