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
7package main
8
9import (
10	"fmt"
11	"go/ast"
12	"go/parser"
13	"go/scanner"
14	"go/token"
15	"os"
16	"path/filepath"
17	"strings"
18)
19
20func 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)
30			}
31			os.Exit(2)
32		}
33		fatalf("parsing %s: %s", name, err)
34	}
35	return ast1
36}
37
38func sourceLine(n ast.Node) int {
39	return fset.Position(n.Pos()).Line
40}
41
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.
47func (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
53	}
54
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)
65
66	f.Package = ast1.Name.Name
67	f.Name = make(map[string]*Name)
68	f.NamePos = make(map[*Name]token.Pos)
69
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
76		}
77		for _, spec := range d.Specs {
78			s, ok := spec.(*ast.ImportSpec)
79			if !ok || s.Path.Value != `"C"` {
80				continue
81			}
82			sawC = true
83			if s.Name != nil {
84				error_(s.Path.Pos(), `cannot rename import "C"`)
85			}
86			cg := s.Doc
87			if cg == nil && len(d.Specs) == 1 {
88				cg = d.Doc
89			}
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"
94			}
95		}
96	}
97	if !sawC {
98		error_(ast1.Package, `cannot find import "C"`)
99	}
100
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
108				w++
109				continue
110			}
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++
117				}
118			}
119			if ws == 0 {
120				continue
121			}
122			d.Specs = d.Specs[0:ws]
123			ast2.Decls[w] = d
124			w++
125		}
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
132			}
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"`)
139				}
140			}
141		}
142	}
143
144	// Accumulate pointers to uses of C.x.
145	if f.Ref == nil {
146		f.Ref = make([]*Ref, 0, 8)
147	}
148	f.walk(ast2, ctxProg, (*File).validateIdents)
149	f.walk(ast2, ctxProg, (*File).saveExprs)
150
151	// Accumulate exported functions.
152	// The comments are only on ast1 but we need to
153	// save the function bodies from ast2.
154	// The first walk fills in ExpFunc, and the
155	// second walk changes the entries to
156	// refer to ast2 instead.
157	f.walk(ast1, ctxProg, (*File).saveExport)
158	f.walk(ast2, ctxProg, (*File).saveExport2)
159
160	f.Comments = ast1.Comments
161	f.AST = ast2
162}
163
164// Like ast.CommentGroup's Text method but preserves
165// leading blank lines, so that line numbers line up.
166func commentText(g *ast.CommentGroup) string {
167	var pieces []string
168	for _, com := range g.List {
169		c := com.Text
170		// Remove comment markers.
171		// The parser has given us exactly the comment text.
172		switch c[1] {
173		case '/':
174			//-style comment (no newline at the end)
175			c = c[2:] + "\n"
176		case '*':
177			/*-style comment */
178			c = c[2 : len(c)-2]
179		}
180		pieces = append(pieces, c)
181	}
182	return strings.Join(pieces, "")
183}
184
185func (f *File) validateIdents(x interface{}, context astContext) {
186	if x, ok := x.(*ast.Ident); ok {
187		if f.isMangledName(x.Name) {
188			error_(x.Pos(), "identifier %q may conflict with identifiers generated by cgo", x.Name)
189		}
190	}
191}
192
193// Save various references we are going to need later.
194func (f *File) saveExprs(x interface{}, context astContext) {
195	switch x := x.(type) {
196	case *ast.Expr:
197		switch (*x).(type) {
198		case *ast.SelectorExpr:
199			f.saveRef(x, context)
200		}
201	case *ast.CallExpr:
202		f.saveCall(x, context)
203	}
204}
205
206// Save references to C.xxx for later processing.
207func (f *File) saveRef(n *ast.Expr, context astContext) {
208	sel := (*n).(*ast.SelectorExpr)
209	// For now, assume that the only instance of capital C is when
210	// used as the imported package identifier.
211	// The parser should take care of scoping in the future, so
212	// that we will be able to distinguish a "top-level C" from a
213	// local C.
214	if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
215		return
216	}
217	if context == ctxAssign2 {
218		context = ctxExpr
219	}
220	if context == ctxEmbedType {
221		error_(sel.Pos(), "cannot embed C type")
222	}
223	goname := sel.Sel.Name
224	if goname == "errno" {
225		error_(sel.Pos(), "cannot refer to errno directly; see documentation")
226		return
227	}
228	if goname == "_CMalloc" {
229		error_(sel.Pos(), "cannot refer to C._CMalloc; use C.malloc")
230		return
231	}
232	if goname == "malloc" {
233		goname = "_CMalloc"
234	}
235	name := f.Name[goname]
236	if name == nil {
237		name = &Name{
238			Go: goname,
239		}
240		f.Name[goname] = name
241		f.NamePos[name] = sel.Pos()
242	}
243	f.Ref = append(f.Ref, &Ref{
244		Name:    name,
245		Expr:    n,
246		Context: context,
247	})
248}
249
250// Save calls to C.xxx for later processing.
251func (f *File) saveCall(call *ast.CallExpr, context astContext) {
252	sel, ok := call.Fun.(*ast.SelectorExpr)
253	if !ok {
254		return
255	}
256	if l, ok := sel.X.(*ast.Ident); !ok || l.Name != "C" {
257		return
258	}
259	c := &Call{Call: call, Deferred: context == ctxDefer}
260	f.Calls = append(f.Calls, c)
261}
262
263// If a function should be exported add it to ExpFunc.
264func (f *File) saveExport(x interface{}, context astContext) {
265	n, ok := x.(*ast.FuncDecl)
266	if !ok {
267		return
268	}
269
270	if n.Doc == nil {
271		return
272	}
273	for _, c := range n.Doc.List {
274		if !strings.HasPrefix(c.Text, "//export ") {
275			continue
276		}
277
278		name := strings.TrimSpace(c.Text[9:])
279		if name == "" {
280			error_(c.Pos(), "export missing name")
281		}
282
283		if name != n.Name.Name {
284			error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
285		}
286
287		doc := ""
288		for _, c1 := range n.Doc.List {
289			if c1 != c {
290				doc += c1.Text + "\n"
291			}
292		}
293
294		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
295			Func:    n,
296			ExpName: name,
297			Doc:     doc,
298		})
299		break
300	}
301}
302
303// Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
304func (f *File) saveExport2(x interface{}, context astContext) {
305	n, ok := x.(*ast.FuncDecl)
306	if !ok {
307		return
308	}
309
310	for _, exp := range f.ExpFunc {
311		if exp.Func.Name.Name == n.Name.Name {
312			exp.Func = n
313			break
314		}
315	}
316}
317
318type astContext int
319
320const (
321	ctxProg astContext = iota
322	ctxEmbedType
323	ctxType
324	ctxStmt
325	ctxExpr
326	ctxField
327	ctxParam
328	ctxAssign2 // assignment of a single expression to two variables
329	ctxSwitch
330	ctxTypeSwitch
331	ctxFile
332	ctxDecl
333	ctxSpec
334	ctxDefer
335	ctxCall  // any function call other than ctxCall2
336	ctxCall2 // function call whose result is assigned to two variables
337	ctxSelector
338)
339
340// walk walks the AST x, calling visit(f, x, context) for each node.
341func (f *File) walk(x interface{}, context astContext, visit func(*File, interface{}, astContext)) {
342	visit(f, x, context)
343	switch n := x.(type) {
344	case *ast.Expr:
345		f.walk(*n, context, visit)
346
347	// everything else just recurs
348	default:
349		error_(token.NoPos, "unexpected type %T in walk", x)
350		panic("unexpected type")
351
352	case nil:
353
354	// These are ordered and grouped to match ../../go/ast/ast.go
355	case *ast.Field:
356		if len(n.Names) == 0 && context == ctxField {
357			f.walk(&n.Type, ctxEmbedType, visit)
358		} else {
359			f.walk(&n.Type, ctxType, visit)
360		}
361	case *ast.FieldList:
362		for _, field := range n.List {
363			f.walk(field, context, visit)
364		}
365	case *ast.BadExpr:
366	case *ast.Ident:
367	case *ast.Ellipsis:
368		f.walk(&n.Elt, ctxType, visit)
369	case *ast.BasicLit:
370	case *ast.FuncLit:
371		f.walk(n.Type, ctxType, visit)
372		f.walk(n.Body, ctxStmt, visit)
373	case *ast.CompositeLit:
374		f.walk(&n.Type, ctxType, visit)
375		f.walk(n.Elts, ctxExpr, visit)
376	case *ast.ParenExpr:
377		f.walk(&n.X, context, visit)
378	case *ast.SelectorExpr:
379		f.walk(&n.X, ctxSelector, visit)
380	case *ast.IndexExpr:
381		f.walk(&n.X, ctxExpr, visit)
382		f.walk(&n.Index, ctxExpr, visit)
383	case *ast.SliceExpr:
384		f.walk(&n.X, ctxExpr, visit)
385		if n.Low != nil {
386			f.walk(&n.Low, ctxExpr, visit)
387		}
388		if n.High != nil {
389			f.walk(&n.High, ctxExpr, visit)
390		}
391		if n.Max != nil {
392			f.walk(&n.Max, ctxExpr, visit)
393		}
394	case *ast.TypeAssertExpr:
395		f.walk(&n.X, ctxExpr, visit)
396		f.walk(&n.Type, ctxType, visit)
397	case *ast.CallExpr:
398		if context == ctxAssign2 {
399			f.walk(&n.Fun, ctxCall2, visit)
400		} else {
401			f.walk(&n.Fun, ctxCall, visit)
402		}
403		f.walk(n.Args, ctxExpr, visit)
404	case *ast.StarExpr:
405		f.walk(&n.X, context, visit)
406	case *ast.UnaryExpr:
407		f.walk(&n.X, ctxExpr, visit)
408	case *ast.BinaryExpr:
409		f.walk(&n.X, ctxExpr, visit)
410		f.walk(&n.Y, ctxExpr, visit)
411	case *ast.KeyValueExpr:
412		f.walk(&n.Key, ctxExpr, visit)
413		f.walk(&n.Value, ctxExpr, visit)
414
415	case *ast.ArrayType:
416		f.walk(&n.Len, ctxExpr, visit)
417		f.walk(&n.Elt, ctxType, visit)
418	case *ast.StructType:
419		f.walk(n.Fields, ctxField, visit)
420	case *ast.FuncType:
421		f.walk(n.Params, ctxParam, visit)
422		if n.Results != nil {
423			f.walk(n.Results, ctxParam, visit)
424		}
425	case *ast.InterfaceType:
426		f.walk(n.Methods, ctxField, visit)
427	case *ast.MapType:
428		f.walk(&n.Key, ctxType, visit)
429		f.walk(&n.Value, ctxType, visit)
430	case *ast.ChanType:
431		f.walk(&n.Value, ctxType, visit)
432
433	case *ast.BadStmt:
434	case *ast.DeclStmt:
435		f.walk(n.Decl, ctxDecl, visit)
436	case *ast.EmptyStmt:
437	case *ast.LabeledStmt:
438		f.walk(n.Stmt, ctxStmt, visit)
439	case *ast.ExprStmt:
440		f.walk(&n.X, ctxExpr, visit)
441	case *ast.SendStmt:
442		f.walk(&n.Chan, ctxExpr, visit)
443		f.walk(&n.Value, ctxExpr, visit)
444	case *ast.IncDecStmt:
445		f.walk(&n.X, ctxExpr, visit)
446	case *ast.AssignStmt:
447		f.walk(n.Lhs, ctxExpr, visit)
448		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
449			f.walk(n.Rhs, ctxAssign2, visit)
450		} else {
451			f.walk(n.Rhs, ctxExpr, visit)
452		}
453	case *ast.GoStmt:
454		f.walk(n.Call, ctxExpr, visit)
455	case *ast.DeferStmt:
456		f.walk(n.Call, ctxDefer, visit)
457	case *ast.ReturnStmt:
458		f.walk(n.Results, ctxExpr, visit)
459	case *ast.BranchStmt:
460	case *ast.BlockStmt:
461		f.walk(n.List, context, visit)
462	case *ast.IfStmt:
463		f.walk(n.Init, ctxStmt, visit)
464		f.walk(&n.Cond, ctxExpr, visit)
465		f.walk(n.Body, ctxStmt, visit)
466		f.walk(n.Else, ctxStmt, visit)
467	case *ast.CaseClause:
468		if context == ctxTypeSwitch {
469			context = ctxType
470		} else {
471			context = ctxExpr
472		}
473		f.walk(n.List, context, visit)
474		f.walk(n.Body, ctxStmt, visit)
475	case *ast.SwitchStmt:
476		f.walk(n.Init, ctxStmt, visit)
477		f.walk(&n.Tag, ctxExpr, visit)
478		f.walk(n.Body, ctxSwitch, visit)
479	case *ast.TypeSwitchStmt:
480		f.walk(n.Init, ctxStmt, visit)
481		f.walk(n.Assign, ctxStmt, visit)
482		f.walk(n.Body, ctxTypeSwitch, visit)
483	case *ast.CommClause:
484		f.walk(n.Comm, ctxStmt, visit)
485		f.walk(n.Body, ctxStmt, visit)
486	case *ast.SelectStmt:
487		f.walk(n.Body, ctxStmt, visit)
488	case *ast.ForStmt:
489		f.walk(n.Init, ctxStmt, visit)
490		f.walk(&n.Cond, ctxExpr, visit)
491		f.walk(n.Post, ctxStmt, visit)
492		f.walk(n.Body, ctxStmt, visit)
493	case *ast.RangeStmt:
494		f.walk(&n.Key, ctxExpr, visit)
495		f.walk(&n.Value, ctxExpr, visit)
496		f.walk(&n.X, ctxExpr, visit)
497		f.walk(n.Body, ctxStmt, visit)
498
499	case *ast.ImportSpec:
500	case *ast.ValueSpec:
501		f.walk(&n.Type, ctxType, visit)
502		if len(n.Names) == 2 && len(n.Values) == 1 {
503			f.walk(&n.Values[0], ctxAssign2, visit)
504		} else {
505			f.walk(n.Values, ctxExpr, visit)
506		}
507	case *ast.TypeSpec:
508		f.walk(&n.Type, ctxType, visit)
509
510	case *ast.BadDecl:
511	case *ast.GenDecl:
512		f.walk(n.Specs, ctxSpec, visit)
513	case *ast.FuncDecl:
514		if n.Recv != nil {
515			f.walk(n.Recv, ctxParam, visit)
516		}
517		f.walk(n.Type, ctxType, visit)
518		if n.Body != nil {
519			f.walk(n.Body, ctxStmt, visit)
520		}
521
522	case *ast.File:
523		f.walk(n.Decls, ctxDecl, visit)
524
525	case *ast.Package:
526		for _, file := range n.Files {
527			f.walk(file, ctxFile, visit)
528		}
529
530	case []ast.Decl:
531		for _, d := range n {
532			f.walk(d, context, visit)
533		}
534	case []ast.Expr:
535		for i := range n {
536			f.walk(&n[i], context, visit)
537		}
538	case []ast.Stmt:
539		for _, s := range n {
540			f.walk(s, context, visit)
541		}
542	case []ast.Spec:
543		for _, s := range n {
544			f.walk(s, context, visit)
545		}
546	}
547}
548