Source file
src/cmd/cgo/ast.go
1
2
3
4
5
6
7 package main
8
9 import (
10 "fmt"
11 "go/ast"
12 "go/format"
13 "go/parser"
14 "go/scanner"
15 "go/token"
16 "os"
17 "strings"
18 )
19
20 func parse(name string, src []byte, flags parser.Mode) *ast.File {
21 ast1, err := parser.ParseFile(fset, name, src, flags)
22 if err != nil {
23 if list, ok := err.(scanner.ErrorList); ok {
24
25
26
27
28 for _, e := range list {
29 fmt.Fprintln(os.Stderr, e)
30 }
31 os.Exit(2)
32 }
33 fatalf("parsing %s: %s", name, err)
34 }
35 return ast1
36 }
37
38 func sourceLine(n ast.Node) int {
39 return fset.Position(n.Pos()).Line
40 }
41
42
43
44
45
46
47 func (f *File) ParseGo(abspath string, src []byte) {
48
49
50
51
52
53
54
55
56 ast1 := parse(abspath, src, parser.SkipObjectResolution|parser.ParseComments)
57 ast2 := parse(abspath, src, parser.SkipObjectResolution)
58
59 f.Package = ast1.Name.Name
60 f.Name = make(map[string]*Name)
61 f.NamePos = make(map[*Name]token.Pos)
62
63
64 sawC := false
65 for _, decl := range ast1.Decls {
66 switch decl := decl.(type) {
67 case *ast.GenDecl:
68 for _, spec := range decl.Specs {
69 s, ok := spec.(*ast.ImportSpec)
70 if !ok || s.Path.Value != `"C"` {
71 continue
72 }
73 sawC = true
74 if s.Name != nil {
75 error_(s.Path.Pos(), `cannot rename import "C"`)
76 }
77 cg := s.Doc
78 if cg == nil && len(decl.Specs) == 1 {
79 cg = decl.Doc
80 }
81 if cg != nil {
82 if strings.ContainsAny(abspath, "\r\n") {
83
84
85 fatalf("internal error: ParseGo: abspath contains unexpected newline character: %q", abspath)
86 }
87 f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), abspath)
88 f.Preamble += commentText(cg) + "\n"
89 f.Preamble += "#line 1 \"cgo-generated-wrapper\"\n"
90 }
91 }
92
93 case *ast.FuncDecl:
94
95
96
97 if decl.Recv != nil && len(decl.Recv.List) > 0 {
98 recvType := decl.Recv.List[0].Type
99 if recvType != nil {
100 t := recvType
101 if star, ok := unparen(t).(*ast.StarExpr); ok {
102 t = star.X
103 }
104 if sel, ok := unparen(t).(*ast.SelectorExpr); ok {
105 var buf strings.Builder
106 format.Node(&buf, fset, recvType)
107 error_(sel.Pos(), `cannot define new methods on non-local type %s`, &buf)
108 }
109 }
110 }
111 }
112
113 }
114 if !sawC {
115 error_(ast1.Package, `cannot find import "C"`)
116 }
117
118
119 if *godefs {
120 w := 0
121 for _, decl := range ast2.Decls {
122 d, ok := decl.(*ast.GenDecl)
123 if !ok {
124 ast2.Decls[w] = decl
125 w++
126 continue
127 }
128 ws := 0
129 for _, spec := range d.Specs {
130 s, ok := spec.(*ast.ImportSpec)
131 if !ok || s.Path.Value != `"C"` {
132 d.Specs[ws] = spec
133 ws++
134 }
135 }
136 if ws == 0 {
137 continue
138 }
139 d.Specs = d.Specs[0:ws]
140 ast2.Decls[w] = d
141 w++
142 }
143 ast2.Decls = ast2.Decls[0:w]
144 } else {
145 for _, decl := range ast2.Decls {
146 d, ok := decl.(*ast.GenDecl)
147 if !ok {
148 continue
149 }
150 for _, spec := range d.Specs {
151 if s, ok := spec.(*ast.ImportSpec); ok && s.Path.Value == `"C"` {
152
153
154
155 f.Edit.Replace(f.offset(s.Path.Pos()), f.offset(s.Path.End()), `_ "unsafe"`)
156 }
157 }
158 }
159 }
160
161
162 if f.Ref == nil {
163 f.Ref = make([]*Ref, 0, 8)
164 }
165 f.walk(ast2, ctxProg, (*File).validateIdents)
166 f.walk(ast2, ctxProg, (*File).saveExprs)
167
168
169
170
171
172
173
174 f.walk(ast1, ctxProg, (*File).saveExport)
175 f.walk(ast2, ctxProg, (*File).saveExport2)
176
177 f.Comments = ast1.Comments
178 f.AST = ast2
179 }
180
181
182
183 func commentText(g *ast.CommentGroup) string {
184 pieces := make([]string, 0, len(g.List))
185 for _, com := range g.List {
186 c := com.Text
187
188
189 switch c[1] {
190 case '/':
191
192 c = c[2:] + "\n"
193 case '*':
194
195 c = c[2 : len(c)-2]
196 }
197 pieces = append(pieces, c)
198 }
199 return strings.Join(pieces, "")
200 }
201
202 func (f *File) validateIdents(x any, context astContext) {
203 if x, ok := x.(*ast.Ident); ok {
204 if f.isMangledName(x.Name) {
205 error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name)
206 }
207 }
208 }
209
210
211 func (f *File) saveExprs(x any, context astContext) {
212 switch x := x.(type) {
213 case *ast.Expr:
214 switch (*x).(type) {
215 case *ast.SelectorExpr:
216 f.saveRef(x, context)
217 }
218 case *ast.CallExpr:
219 f.saveCall(x, context)
220 }
221 }
222
223
224 func (f *File) saveRef(n *ast.Expr, context astContext) {
225 sel := (*n).(*ast.SelectorExpr)
226
227
228
229
230
231 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
232 return
233 }
234 if context == ctxAssign2 {
235 context = ctxExpr
236 }
237 if context == ctxEmbedType {
238 error_(sel.Pos(), "cannot embed C type")
239 }
240 goname := sel.Sel.Name
241 if goname == "errno" {
242 error_(sel.Pos(), "cannot refer to errno directly; see documentation")
243 return
244 }
245 if goname == "_CMalloc" {
246 error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
247 return
248 }
249 if goname == "malloc" {
250 goname = "_CMalloc"
251 }
252 name := f.Name[goname]
253 if name == nil {
254 name = &Name{
255 Go: goname,
256 }
257 f.Name[goname] = name
258 f.NamePos[name] = sel.Pos()
259 }
260 f.Ref = append(f.Ref, &Ref{
261 Name: name,
262 Expr: n,
263 Context: context,
264 })
265 }
266
267
268 func (f *File) saveCall(call *ast.CallExpr, context astContext) {
269 sel, ok := call.Fun.(*ast.SelectorExpr)
270 if !ok {
271 return
272 }
273 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
274 return
275 }
276 c := &Call{Call: call, Deferred: context == ctxDefer}
277 f.Calls = append(f.Calls, c)
278 }
279
280
281 func (f *File) saveExport(x any, context astContext) {
282 n, ok := x.(*ast.FuncDecl)
283 if !ok {
284 return
285 }
286
287 if n.Doc == nil {
288 return
289 }
290 for _, c := range n.Doc.List {
291 if !strings.HasPrefix(c.Text, "//export ") {
292 continue
293 }
294
295 name := strings.TrimSpace(c.Text[9:])
296 if name == "" {
297 error_(c.Pos(), "export missing name")
298 }
299
300 if name != n.Name.Name {
301 error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
302 }
303
304 f.ExpFunc = append(f.ExpFunc, &ExpFunc{
305 Func: n,
306 ExpName: name,
307
308
309
310 })
311 break
312 }
313 }
314
315
316 func (f *File) saveExport2(x any, context astContext) {
317 n, ok := x.(*ast.FuncDecl)
318 if !ok {
319 return
320 }
321
322 for _, exp := range f.ExpFunc {
323 if exp.Func.Name.Name == n.Name.Name {
324 exp.Func = n
325 break
326 }
327 }
328 }
329
330 type astContext int
331
332 const (
333 ctxProg astContext = iota
334 ctxEmbedType
335 ctxType
336 ctxStmt
337 ctxExpr
338 ctxField
339 ctxParam
340 ctxAssign2
341 ctxSwitch
342 ctxTypeSwitch
343 ctxFile
344 ctxDecl
345 ctxSpec
346 ctxDefer
347 ctxCall
348 ctxCall2
349 ctxSelector
350 )
351
352
353 func (f *File) walk(x any, context astContext, visit func(*File, any, astContext)) {
354 visit(f, x, context)
355 switch n := x.(type) {
356 case *ast.Expr:
357 f.walk(*n, context, visit)
358
359
360 default:
361 error_(token.NoPos, "unexpected type %T in walk", x)
362 panic("unexpected type")
363
364 case nil:
365
366
367 case *ast.Field:
368 if len(n.Names) == 0 && context == ctxField {
369 f.walk(&n.Type, ctxEmbedType, visit)
370 } else {
371 f.walk(&n.Type, ctxType, visit)
372 }
373 case *ast.FieldList:
374 for _, field := range n.List {
375 f.walk(field, context, visit)
376 }
377 case *ast.BadExpr:
378 case *ast.Ident:
379 case *ast.Ellipsis:
380 f.walk(&n.Elt, ctxType, visit)
381 case *ast.BasicLit:
382 case *ast.FuncLit:
383 f.walk(n.Type, ctxType, visit)
384 f.walk(n.Body, ctxStmt, visit)
385 case *ast.CompositeLit:
386 f.walk(&n.Type, ctxType, visit)
387 f.walk(n.Elts, ctxExpr, visit)
388 case *ast.ParenExpr:
389 f.walk(&n.X, context, visit)
390 case *ast.SelectorExpr:
391 f.walk(&n.X, ctxSelector, visit)
392 case *ast.IndexExpr:
393 f.walk(&n.X, ctxExpr, visit)
394 f.walk(&n.Index, ctxExpr, visit)
395 case *ast.IndexListExpr:
396 f.walk(&n.X, ctxExpr, visit)
397 f.walk(n.Indices, ctxExpr, visit)
398 case *ast.SliceExpr:
399 f.walk(&n.X, ctxExpr, visit)
400 if n.Low != nil {
401 f.walk(&n.Low, ctxExpr, visit)
402 }
403 if n.High != nil {
404 f.walk(&n.High, ctxExpr, visit)
405 }
406 if n.Max != nil {
407 f.walk(&n.Max, ctxExpr, visit)
408 }
409 case *ast.TypeAssertExpr:
410 f.walk(&n.X, ctxExpr, visit)
411 f.walk(&n.Type, ctxType, visit)
412 case *ast.CallExpr:
413 if context == ctxAssign2 {
414 f.walk(&n.Fun, ctxCall2, visit)
415 } else {
416 f.walk(&n.Fun, ctxCall, visit)
417 }
418 f.walk(n.Args, ctxExpr, visit)
419 case *ast.StarExpr:
420 f.walk(&n.X, context, visit)
421 case *ast.UnaryExpr:
422 f.walk(&n.X, ctxExpr, visit)
423 case *ast.BinaryExpr:
424 f.walk(&n.X, ctxExpr, visit)
425 f.walk(&n.Y, ctxExpr, visit)
426 case *ast.KeyValueExpr:
427 f.walk(&n.Key, ctxExpr, visit)
428 f.walk(&n.Value, ctxExpr, visit)
429
430 case *ast.ArrayType:
431 f.walk(&n.Len, ctxExpr, visit)
432 f.walk(&n.Elt, ctxType, visit)
433 case *ast.StructType:
434 f.walk(n.Fields, ctxField, visit)
435 case *ast.FuncType:
436 if n.TypeParams != nil {
437 f.walk(n.TypeParams, ctxParam, visit)
438 }
439 f.walk(n.Params, ctxParam, visit)
440 if n.Results != nil {
441 f.walk(n.Results, ctxParam, visit)
442 }
443 case *ast.InterfaceType:
444 f.walk(n.Methods, ctxField, visit)
445 case *ast.MapType:
446 f.walk(&n.Key, ctxType, visit)
447 f.walk(&n.Value, ctxType, visit)
448 case *ast.ChanType:
449 f.walk(&n.Value, ctxType, visit)
450
451 case *ast.BadStmt:
452 case *ast.DeclStmt:
453 f.walk(n.Decl, ctxDecl, visit)
454 case *ast.EmptyStmt:
455 case *ast.LabeledStmt:
456 f.walk(n.Stmt, ctxStmt, visit)
457 case *ast.ExprStmt:
458 f.walk(&n.X, ctxExpr, visit)
459 case *ast.SendStmt:
460 f.walk(&n.Chan, ctxExpr, visit)
461 f.walk(&n.Value, ctxExpr, visit)
462 case *ast.IncDecStmt:
463 f.walk(&n.X, ctxExpr, visit)
464 case *ast.AssignStmt:
465 f.walk(n.Lhs, ctxExpr, visit)
466 if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
467 f.walk(n.Rhs, ctxAssign2, visit)
468 } else {
469 f.walk(n.Rhs, ctxExpr, visit)
470 }
471 case *ast.GoStmt:
472 f.walk(n.Call, ctxExpr, visit)
473 case *ast.DeferStmt:
474 f.walk(n.Call, ctxDefer, visit)
475 case *ast.ReturnStmt:
476 f.walk(n.Results, ctxExpr, visit)
477 case *ast.BranchStmt:
478 case *ast.BlockStmt:
479 f.walk(n.List, context, visit)
480 case *ast.IfStmt:
481 f.walk(n.Init, ctxStmt, visit)
482 f.walk(&n.Cond, ctxExpr, visit)
483 f.walk(n.Body, ctxStmt, visit)
484 f.walk(n.Else, ctxStmt, visit)
485 case *ast.CaseClause:
486 if context == ctxTypeSwitch {
487 context = ctxType
488 } else {
489 context = ctxExpr
490 }
491 f.walk(n.List, context, visit)
492 f.walk(n.Body, ctxStmt, visit)
493 case *ast.SwitchStmt:
494 f.walk(n.Init, ctxStmt, visit)
495 f.walk(&n.Tag, ctxExpr, visit)
496 f.walk(n.Body, ctxSwitch, visit)
497 case *ast.TypeSwitchStmt:
498 f.walk(n.Init, ctxStmt, visit)
499 f.walk(n.Assign, ctxStmt, visit)
500 f.walk(n.Body, ctxTypeSwitch, visit)
501 case *ast.CommClause:
502 f.walk(n.Comm, ctxStmt, visit)
503 f.walk(n.Body, ctxStmt, visit)
504 case *ast.SelectStmt:
505 f.walk(n.Body, ctxStmt, visit)
506 case *ast.ForStmt:
507 f.walk(n.Init, ctxStmt, visit)
508 f.walk(&n.Cond, ctxExpr, visit)
509 f.walk(n.Post, ctxStmt, visit)
510 f.walk(n.Body, ctxStmt, visit)
511 case *ast.RangeStmt:
512 f.walk(&n.Key, ctxExpr, visit)
513 f.walk(&n.Value, ctxExpr, visit)
514 f.walk(&n.X, ctxExpr, visit)
515 f.walk(n.Body, ctxStmt, visit)
516
517 case *ast.ImportSpec:
518 case *ast.ValueSpec:
519 f.walk(&n.Type, ctxType, visit)
520 if len(n.Names) == 2 && len(n.Values) == 1 {
521 f.walk(&n.Values[0], ctxAssign2, visit)
522 } else {
523 f.walk(n.Values, ctxExpr, visit)
524 }
525 case *ast.TypeSpec:
526 if n.TypeParams != nil {
527 f.walk(n.TypeParams, ctxParam, visit)
528 }
529 f.walk(&n.Type, ctxType, visit)
530
531 case *ast.BadDecl:
532 case *ast.GenDecl:
533 f.walk(n.Specs, ctxSpec, visit)
534 case *ast.FuncDecl:
535 if n.Recv != nil {
536 f.walk(n.Recv, ctxParam, visit)
537 }
538 f.walk(n.Type, ctxType, visit)
539 if n.Body != nil {
540 f.walk(n.Body, ctxStmt, visit)
541 }
542
543 case *ast.File:
544 f.walk(n.Decls, ctxDecl, visit)
545
546 case *ast.Package:
547 for _, file := range n.Files {
548 f.walk(file, ctxFile, visit)
549 }
550
551 case []ast.Decl:
552 for _, d := range n {
553 f.walk(d, context, visit)
554 }
555 case []ast.Expr:
556 for i := range n {
557 f.walk(&n[i], context, visit)
558 }
559 case []ast.Stmt:
560 for _, s := range n {
561 f.walk(s, context, visit)
562 }
563 case []ast.Spec:
564 for _, s := range n {
565 f.walk(s, context, visit)
566 }
567 }
568 }
569
570
571 func unparen(x ast.Expr) ast.Expr {
572 if p, isParen := x.(*ast.ParenExpr); isParen {
573 x = unparen(p.X)
574 }
575 return x
576 }
577
View as plain text