Source file
src/cmd/cgo/ast.go
Documentation: cmd/cgo
1
2
3
4
5
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
24
25
26
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
42
43
44
45
46 func (f *File) ParseGo(abspath string, src []byte) {
47
48
49
50
51
52
53
54
55 ast1 := parse(abspath, src, parser.ParseComments)
56 ast2 := parse(abspath, src, 0)
57
58 f.Package = ast1.Name.Name
59 f.Name = make(map[string]*Name)
60 f.NamePos = make(map[*Name]token.Pos)
61
62
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
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
128
129
130 f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
131 }
132 }
133 }
134 }
135
136
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
144
145
146
147
148
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
157
158 func commentText(g *ast.CommentGroup) string {
159 var pieces []string
160 for _, com := range g.List {
161 c := com.Text
162
163
164 switch c[1] {
165 case '/':
166
167 c = c[2:] + "\n"
168 case '*':
169
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
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
199 func (f *File) saveRef(n *ast.Expr, context astContext) {
200 sel := (*n).(*ast.SelectorExpr)
201
202
203
204
205
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
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
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
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
321 ctxSwitch
322 ctxTypeSwitch
323 ctxFile
324 ctxDecl
325 ctxSpec
326 ctxDefer
327 ctxCall
328 ctxCall2
329 ctxSelector
330 )
331
332
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
340 default:
341 error_(token.NoPos, "unexpected type %T in walk", x)
342 panic("unexpected type")
343
344 case nil:
345
346
347 case *ast.Field:
348 if len(n.Names) == 0 && context == ctxField {
349 f.walk(&n.Type, ctxEmbedType, visit)
350 } else {
351 f.walk(&n.Type, ctxType, visit)
352 }
353 case *ast.FieldList:
354 for _, field := range n.List {
355 f.walk(field, context, visit)
356 }
357 case *ast.BadExpr:
358 case *ast.Ident:
359 case *ast.Ellipsis:
360 f.walk(&n.Elt, ctxType, visit)
361 case *ast.BasicLit:
362 case *ast.FuncLit:
363 f.walk(n.Type, ctxType, visit)
364 f.walk(n.Body, ctxStmt, visit)
365 case *ast.CompositeLit:
366 f.walk(&n.Type, ctxType, visit)
367 f.walk(n.Elts, ctxExpr, visit)
368 case *ast.ParenExpr:
369 f.walk(&n.X, context, visit)
370 case *ast.SelectorExpr:
371 f.walk(&n.X, ctxSelector, visit)
372 case *ast.IndexExpr:
373 f.walk(&n.X, ctxExpr, visit)
374 f.walk(&n.Index, ctxExpr, visit)
375 case *ast.SliceExpr:
376 f.walk(&n.X, ctxExpr, visit)
377 if n.Low != nil {
378 f.walk(&n.Low, ctxExpr, visit)
379 }
380 if n.High != nil {
381 f.walk(&n.High, ctxExpr, visit)
382 }
383 if n.Max != nil {
384 f.walk(&n.Max, ctxExpr, visit)
385 }
386 case *ast.TypeAssertExpr:
387 f.walk(&n.X, ctxExpr, visit)
388 f.walk(&n.Type, ctxType, visit)
389 case *ast.CallExpr:
390 if context == ctxAssign2 {
391 f.walk(&n.Fun, ctxCall2, visit)
392 } else {
393 f.walk(&n.Fun, ctxCall, visit)
394 }
395 f.walk(n.Args, ctxExpr, visit)
396 case *ast.StarExpr:
397 f.walk(&n.X, context, visit)
398 case *ast.UnaryExpr:
399 f.walk(&n.X, ctxExpr, visit)
400 case *ast.BinaryExpr:
401 f.walk(&n.X, ctxExpr, visit)
402 f.walk(&n.Y, ctxExpr, visit)
403 case *ast.KeyValueExpr:
404 f.walk(&n.Key, ctxExpr, visit)
405 f.walk(&n.Value, ctxExpr, visit)
406
407 case *ast.ArrayType:
408 f.walk(&n.Len, ctxExpr, visit)
409 f.walk(&n.Elt, ctxType, visit)
410 case *ast.StructType:
411 f.walk(n.Fields, ctxField, visit)
412 case *ast.FuncType:
413 f.walk(n.Params, ctxParam, visit)
414 if n.Results != nil {
415 f.walk(n.Results, ctxParam, visit)
416 }
417 case *ast.InterfaceType:
418 f.walk(n.Methods, ctxField, visit)
419 case *ast.MapType:
420 f.walk(&n.Key, ctxType, visit)
421 f.walk(&n.Value, ctxType, visit)
422 case *ast.ChanType:
423 f.walk(&n.Value, ctxType, visit)
424
425 case *ast.BadStmt:
426 case *ast.DeclStmt:
427 f.walk(n.Decl, ctxDecl, visit)
428 case *ast.EmptyStmt:
429 case *ast.LabeledStmt:
430 f.walk(n.Stmt, ctxStmt, visit)
431 case *ast.ExprStmt:
432 f.walk(&n.X, ctxExpr, visit)
433 case *ast.SendStmt:
434 f.walk(&n.Chan, ctxExpr, visit)
435 f.walk(&n.Value, ctxExpr, visit)
436 case *ast.IncDecStmt:
437 f.walk(&n.X, ctxExpr, visit)
438 case *ast.AssignStmt:
439 f.walk(n.Lhs, ctxExpr, visit)
440 if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
441 f.walk(n.Rhs, ctxAssign2, visit)
442 } else {
443 f.walk(n.Rhs, ctxExpr, visit)
444 }
445 case *ast.GoStmt:
446 f.walk(n.Call, ctxExpr, visit)
447 case *ast.DeferStmt:
448 f.walk(n.Call, ctxDefer, visit)
449 case *ast.ReturnStmt:
450 f.walk(n.Results, ctxExpr, visit)
451 case *ast.BranchStmt:
452 case *ast.BlockStmt:
453 f.walk(n.List, context, visit)
454 case *ast.IfStmt:
455 f.walk(n.Init, ctxStmt, visit)
456 f.walk(&n.Cond, ctxExpr, visit)
457 f.walk(n.Body, ctxStmt, visit)
458 f.walk(n.Else, ctxStmt, visit)
459 case *ast.CaseClause:
460 if context == ctxTypeSwitch {
461 context = ctxType
462 } else {
463 context = ctxExpr
464 }
465 f.walk(n.List, context, visit)
466 f.walk(n.Body, ctxStmt, visit)
467 case *ast.SwitchStmt:
468 f.walk(n.Init, ctxStmt, visit)
469 f.walk(&n.Tag, ctxExpr, visit)
470 f.walk(n.Body, ctxSwitch, visit)
471 case *ast.TypeSwitchStmt:
472 f.walk(n.Init, ctxStmt, visit)
473 f.walk(n.Assign, ctxStmt, visit)
474 f.walk(n.Body, ctxTypeSwitch, visit)
475 case *ast.CommClause:
476 f.walk(n.Comm, ctxStmt, visit)
477 f.walk(n.Body, ctxStmt, visit)
478 case *ast.SelectStmt:
479 f.walk(n.Body, ctxStmt, visit)
480 case *ast.ForStmt:
481 f.walk(n.Init, ctxStmt, visit)
482 f.walk(&n.Cond, ctxExpr, visit)
483 f.walk(n.Post, ctxStmt, visit)
484 f.walk(n.Body, ctxStmt, visit)
485 case *ast.RangeStmt:
486 f.walk(&n.Key, ctxExpr, visit)
487 f.walk(&n.Value, ctxExpr, visit)
488 f.walk(&n.X, ctxExpr, visit)
489 f.walk(n.Body, ctxStmt, visit)
490
491 case *ast.ImportSpec:
492 case *ast.ValueSpec:
493 f.walk(&n.Type, ctxType, visit)
494 if len(n.Names) == 2 && len(n.Values) == 1 {
495 f.walk(&n.Values[0], ctxAssign2, visit)
496 } else {
497 f.walk(n.Values, ctxExpr, visit)
498 }
499 case *ast.TypeSpec:
500 f.walk(&n.Type, ctxType, visit)
501
502 case *ast.BadDecl:
503 case *ast.GenDecl:
504 f.walk(n.Specs, ctxSpec, visit)
505 case *ast.FuncDecl:
506 if n.Recv != nil {
507 f.walk(n.Recv, ctxParam, visit)
508 }
509 f.walk(n.Type, ctxType, visit)
510 if n.Body != nil {
511 f.walk(n.Body, ctxStmt, visit)
512 }
513
514 case *ast.File:
515 f.walk(n.Decls, ctxDecl, visit)
516
517 case *ast.Package:
518 for _, file := range n.Files {
519 f.walk(file, ctxFile, visit)
520 }
521
522 case []ast.Decl:
523 for _, d := range n {
524 f.walk(d, context, visit)
525 }
526 case []ast.Expr:
527 for i := range n {
528 f.walk(&n[i], context, visit)
529 }
530 case []ast.Stmt:
531 for _, s := range n {
532 f.walk(s, context, visit)
533 }
534 case []ast.Spec:
535 for _, s := range n {
536 f.walk(s, context, visit)
537 }
538 }
539 }
540
View as plain text