cmd/go: check for another GCC error message
[official-gcc.git] / libgo / go / cmd / cgo / ast.go
blob58e0ee78cb74cc8c77f27b172e943f81d523ec05
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.
7 package main
9 import (
10 "fmt"
11 "go/ast"
12 "go/parser"
13 "go/scanner"
14 "go/token"
15 "os"
16 "path/filepath"
17 "strings"
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 // 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)
31 os.Exit(2)
33 fatalf("parsing %s: %s", name, err)
35 return ast1
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
46 // printed.
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 {
52 name = aname
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
60 // and reprinting.
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.
71 sawC := false
72 for _, decl := range ast1.Decls {
73 d, ok := decl.(*ast.GenDecl)
74 if !ok {
75 continue
77 for _, spec := range d.Specs {
78 s, ok := spec.(*ast.ImportSpec)
79 if !ok || s.Path.Value != `"C"` {
80 continue
82 sawC = true
83 if s.Name != nil {
84 error_(s.Path.Pos(), `cannot rename import "C"`)
86 cg := s.Doc
87 if cg == nil && len(d.Specs) == 1 {
88 cg = d.Doc
90 if cg != nil {
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"
97 if !sawC {
98 error_(token.NoPos, `cannot find import "C"`)
101 // In ast2, strip the import "C" line.
102 if *godefs {
103 w := 0
104 for _, decl := range ast2.Decls {
105 d, ok := decl.(*ast.GenDecl)
106 if !ok {
107 ast2.Decls[w] = decl
109 continue
111 ws := 0
112 for _, spec := range d.Specs {
113 s, ok := spec.(*ast.ImportSpec)
114 if !ok || s.Path.Value != `"C"` {
115 d.Specs[ws] = spec
116 ws++
119 if ws == 0 {
120 continue
122 d.Specs = d.Specs[0:ws]
123 ast2.Decls[w] = d
126 ast2.Decls = ast2.Decls[0:w]
127 } else {
128 for _, decl := range ast2.Decls {
129 d, ok := decl.(*ast.GenDecl)
130 if !ok {
131 continue
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.
145 if f.Ref == nil {
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
160 f.AST = ast2
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 {
166 var pieces []string
167 for _, com := range g.List {
168 c := com.Text
169 // Remove comment markers.
170 // The parser has given us exactly the comment text.
171 switch c[1] {
172 case '/':
173 //-style comment (no newline at the end)
174 c = c[2:] + "\n"
175 case '*':
176 /*-style comment */
177 c = c[2 : len(c)-2]
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) {
187 case *ast.Expr:
188 switch (*x).(type) {
189 case *ast.SelectorExpr:
190 f.saveRef(x, context)
192 case *ast.CallExpr:
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
204 // local C.
205 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
206 return
208 if context == ctxAssign2 {
209 context = ctxExpr
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")
217 return
219 if goname == "_CMalloc" {
220 error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
221 return
223 if goname == "malloc" {
224 goname = "_CMalloc"
226 name := f.Name[goname]
227 if name == nil {
228 name = &Name{
229 Go: goname,
231 f.Name[goname] = name
232 f.NamePos[name] = sel.Pos()
234 f.Ref = append(f.Ref, &Ref{
235 Name: name,
236 Expr: n,
237 Context: context,
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)
244 if !ok {
245 return
247 if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
248 return
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)
257 if !ok {
258 return
261 if n.Doc == nil {
262 return
264 for _, c := range n.Doc.List {
265 if !strings.HasPrefix(c.Text, "//export ") {
266 continue
269 name := strings.TrimSpace(c.Text[9:])
270 if name == "" {
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)
278 doc := ""
279 for _, c1 := range n.Doc.List {
280 if c1 != c {
281 doc += c1.Text + "\n"
285 f.ExpFunc = append(f.ExpFunc, &ExpFunc{
286 Func: n,
287 ExpName: name,
288 Doc: doc,
290 break
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)
297 if !ok {
298 return
301 for _, exp := range f.ExpFunc {
302 if exp.Func.Name.Name == n.Name.Name {
303 exp.Func = n
304 break
309 type astContext int
311 const (
312 ctxProg astContext = iota
313 ctxEmbedType
314 ctxType
315 ctxStmt
316 ctxExpr
317 ctxField
318 ctxParam
319 ctxAssign2 // assignment of a single expression to two variables
320 ctxSwitch
321 ctxTypeSwitch
322 ctxFile
323 ctxDecl
324 ctxSpec
325 ctxDefer
326 ctxCall // any function call other than ctxCall2
327 ctxCall2 // function call whose result is assigned to two variables
328 ctxSelector
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)) {
333 visit(f, x, context)
334 switch n := x.(type) {
335 case *ast.Expr:
336 f.walk(*n, context, visit)
338 // everything else just recurs
339 default:
340 error_(token.NoPos, "unexpected type %T in walk", x)
341 panic("unexpected type")
343 case nil:
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)
352 case *ast.FieldList:
353 for _, field := range n.List {
354 f.walk(field, context, visit)
356 case *ast.BadExpr:
357 case *ast.Ident:
358 case *ast.Ellipsis:
359 case *ast.BasicLit:
360 case *ast.FuncLit:
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)
366 case *ast.ParenExpr:
367 f.walk(&n.X, context, visit)
368 case *ast.SelectorExpr:
369 f.walk(&n.X, ctxSelector, visit)
370 case *ast.IndexExpr:
371 f.walk(&n.X, ctxExpr, visit)
372 f.walk(&n.Index, ctxExpr, visit)
373 case *ast.SliceExpr:
374 f.walk(&n.X, ctxExpr, visit)
375 if n.Low != nil {
376 f.walk(&n.Low, ctxExpr, visit)
378 if n.High != nil {
379 f.walk(&n.High, ctxExpr, visit)
381 if n.Max != nil {
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)
387 case *ast.CallExpr:
388 if context == ctxAssign2 {
389 f.walk(&n.Fun, ctxCall2, visit)
390 } else {
391 f.walk(&n.Fun, ctxCall, visit)
393 f.walk(n.Args, ctxExpr, visit)
394 case *ast.StarExpr:
395 f.walk(&n.X, context, visit)
396 case *ast.UnaryExpr:
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)
405 case *ast.ArrayType:
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)
410 case *ast.FuncType:
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)
417 case *ast.MapType:
418 f.walk(&n.Key, ctxType, visit)
419 f.walk(&n.Value, ctxType, visit)
420 case *ast.ChanType:
421 f.walk(&n.Value, ctxType, visit)
423 case *ast.BadStmt:
424 case *ast.DeclStmt:
425 f.walk(n.Decl, ctxDecl, visit)
426 case *ast.EmptyStmt:
427 case *ast.LabeledStmt:
428 f.walk(n.Stmt, ctxStmt, visit)
429 case *ast.ExprStmt:
430 f.walk(&n.X, ctxExpr, visit)
431 case *ast.SendStmt:
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)
440 } else {
441 f.walk(n.Rhs, ctxExpr, visit)
443 case *ast.GoStmt:
444 f.walk(n.Call, ctxExpr, visit)
445 case *ast.DeferStmt:
446 f.walk(n.Call, ctxDefer, visit)
447 case *ast.ReturnStmt:
448 f.walk(n.Results, ctxExpr, visit)
449 case *ast.BranchStmt:
450 case *ast.BlockStmt:
451 f.walk(n.List, context, visit)
452 case *ast.IfStmt:
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 {
459 context = ctxType
460 } else {
461 context = ctxExpr
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)
478 case *ast.ForStmt:
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)
483 case *ast.RangeStmt:
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:
490 case *ast.ValueSpec:
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)
494 } else {
495 f.walk(n.Values, ctxExpr, visit)
497 case *ast.TypeSpec:
498 f.walk(&n.Type, ctxType, visit)
500 case *ast.BadDecl:
501 case *ast.GenDecl:
502 f.walk(n.Specs, ctxSpec, visit)
503 case *ast.FuncDecl:
504 if n.Recv != nil {
505 f.walk(n.Recv, ctxParam, visit)
507 f.walk(n.Type, ctxType, visit)
508 if n.Body != nil {
509 f.walk(n.Body, ctxStmt, visit)
512 case *ast.File:
513 f.walk(n.Decls, ctxDecl, visit)
515 case *ast.Package:
516 for _, file := range n.Files {
517 f.walk(file, ctxFile, visit)
520 case []ast.Decl:
521 for _, d := range n {
522 f.walk(d, context, visit)
524 case []ast.Expr:
525 for i := range n {
526 f.walk(&n[i], context, visit)
528 case []ast.Stmt:
529 for _, s := range n {
530 f.walk(s, context, visit)
532 case []ast.Spec:
533 for _, s := range n {
534 f.walk(s, context, visit)