ast.go 9.77 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Parse input AST and prepare Prog structure.

package main

import (
10 11 12 13 14
	"fmt"
	"go/ast"
	"go/doc"
	"go/parser"
	"go/scanner"
15
	"go/token"
16
	"os"
Ian Lance Taylor's avatar
Ian Lance Taylor committed
17
	"strings"
18 19
)

Russ Cox's avatar
Russ Cox committed
20
func parse(name string, flags uint) *ast.File {
21
	ast1, err := parser.ParseFile(fset, name, nil, flags)
22 23 24 25 26 27 28
	if err != nil {
		if list, ok := err.(scanner.ErrorList); ok {
			// If err is a scanner.ErrorList, its String will print just
			// the first error and then (+n more errors).
			// Instead, turn it into a new Error that will return
			// details for all the errors.
			for _, e := range list {
29
				fmt.Fprintln(os.Stderr, e)
30
			}
31
			os.Exit(2)
32
		}
33
		fatalf("parsing %s: %s", name, err)
34
	}
Russ Cox's avatar
Russ Cox committed
35 36 37
	return ast1
}

38 39 40 41
func sourceLine(n ast.Node) int {
	return fset.Position(n.Pos()).Line
}

Russ Cox's avatar
Russ Cox committed
42 43 44 45 46 47 48 49 50 51 52 53 54 55
// ReadGo populates f with information learned from reading the
// Go source file with the given file name.  It gathers the C preamble
// attached to the import "C" comment, a list of references to C.xxx,
// a list of exported functions, and the actual AST, to be rewritten and
// printed.
func (f *File) ReadGo(name string) {
	// Two different parses: once with comments, once without.
	// The printer is not good enough at printing comments in the
	// right place when we start editing the AST behind its back,
	// so we use ast1 to look for the doc comments on import "C"
	// and on exported functions, and we use ast2 for translating
	// and reprinting.
	ast1 := parse(name, parser.ParseComments)
	ast2 := parse(name, 0)
56

57
	f.Package = ast1.Name.Name
Russ Cox's avatar
Russ Cox committed
58 59 60
	f.Name = make(map[string]*Name)

	// In ast1, find the import "C" line and get any extra C preamble.
61
	sawC := false
Russ Cox's avatar
Russ Cox committed
62
	for _, decl := range ast1.Decls {
63
		d, ok := decl.(*ast.GenDecl)
64
		if !ok {
65
			continue
66
		}
Russ Cox's avatar
Russ Cox committed
67
		for _, spec := range d.Specs {
68
			s, ok := spec.(*ast.ImportSpec)
69
			if !ok || string(s.Path.Value) != `"C"` {
70
				continue
71
			}
72
			sawC = true
73
			if s.Name != nil {
74
				error_(s.Path.Pos(), `cannot rename import "C"`)
75
			}
76 77 78 79 80 81 82
			cg := s.Doc
			if cg == nil && len(d.Specs) == 1 {
				cg = d.Doc
			}
			if cg != nil {
				f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
				f.Preamble += doc.CommentText(cg) + "\n"
Russ Cox's avatar
Russ Cox committed
83 84 85 86
			}
		}
	}
	if !sawC {
87
		error_(token.NoPos, `cannot find import "C"`)
Russ Cox's avatar
Russ Cox committed
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
	}

	// In ast2, strip the import "C" line.
	w := 0
	for _, decl := range ast2.Decls {
		d, ok := decl.(*ast.GenDecl)
		if !ok {
			ast2.Decls[w] = decl
			w++
			continue
		}
		ws := 0
		for _, spec := range d.Specs {
			s, ok := spec.(*ast.ImportSpec)
			if !ok || string(s.Path.Value) != `"C"` {
				d.Specs[ws] = spec
				ws++
105
			}
Russ Cox's avatar
Russ Cox committed
106 107
		}
		if ws == 0 {
108
			continue
109
		}
110
		d.Specs = d.Specs[0:ws]
Russ Cox's avatar
Russ Cox committed
111
		ast2.Decls[w] = d
112
		w++
113
	}
Russ Cox's avatar
Russ Cox committed
114
	ast2.Decls = ast2.Decls[0:w]
115 116

	// Accumulate pointers to uses of C.x.
Russ Cox's avatar
Russ Cox committed
117 118
	if f.Ref == nil {
		f.Ref = make([]*Ref, 0, 8)
Devon H. O'Dell's avatar
Devon H. O'Dell committed
119
	}
Russ Cox's avatar
Russ Cox committed
120 121 122 123 124 125 126 127 128 129 130 131
	f.walk(ast2, "prog", (*File).saveRef)

	// Accumulate exported functions.
	// The comments are only on ast1 but we need to
	// save the function bodies from ast2.
	// The first walk fills in ExpFunc, and the
	// second walk changes the entries to
	// refer to ast2 instead.
	f.walk(ast1, "prog", (*File).saveExport)
	f.walk(ast2, "prog", (*File).saveExport2)

	f.AST = ast2
132 133
}

Russ Cox's avatar
Russ Cox committed
134 135 136 137 138 139 140 141 142 143 144 145
// Save references to C.xxx for later processing.
func (f *File) saveRef(x interface{}, context string) {
	n, ok := x.(*ast.Expr)
	if !ok {
		return
	}
	if sel, ok := (*n).(*ast.SelectorExpr); ok {
		// For now, assume that the only instance of capital C is
		// when used as the imported package identifier.
		// The parser should take care of scoping in the future,
		// so that we will be able to distinguish a "top-level C"
		// from a local C.
146
		if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" {
Russ Cox's avatar
Russ Cox committed
147 148 149
			if context == "as2" {
				context = "expr"
			}
150
			goname := sel.Sel.Name
Russ Cox's avatar
Russ Cox committed
151
			if goname == "errno" {
152
				error_(sel.Pos(), "cannot refer to errno directly; see documentation")
Russ Cox's avatar
Russ Cox committed
153 154
				return
			}
Russ Cox's avatar
Russ Cox committed
155 156 157 158
			name := f.Name[goname]
			if name == nil {
				name = &Name{
					Go: goname,
159
				}
Russ Cox's avatar
Russ Cox committed
160 161
				f.Name[goname] = name
			}
Russ Cox's avatar
Russ Cox committed
162
			f.Ref = append(f.Ref, &Ref{
Russ Cox's avatar
Russ Cox committed
163 164 165
				Name:    name,
				Expr:    n,
				Context: context,
Russ Cox's avatar
Russ Cox committed
166
			})
Russ Cox's avatar
Russ Cox committed
167
			return
168
		}
Russ Cox's avatar
Russ Cox committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182
	}
}

// If a function should be exported add it to ExpFunc.
func (f *File) saveExport(x interface{}, context string) {
	n, ok := x.(*ast.FuncDecl)
	if !ok {
		return
	}

	if n.Doc == nil {
		return
	}
	for _, c := range n.Doc.List {
183
		if !strings.HasPrefix(string(c.Text), "//export ") {
Russ Cox's avatar
Russ Cox committed
184 185 186 187 188
			continue
		}

		name := strings.TrimSpace(string(c.Text[9:]))
		if name == "" {
189
			error_(c.Pos(), "export missing name")
Russ Cox's avatar
Russ Cox committed
190 191
		}

Russ Cox's avatar
Russ Cox committed
192
		if name != n.Name.Name {
193
			error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
Russ Cox's avatar
Russ Cox committed
194 195
		}

Russ Cox's avatar
Russ Cox committed
196
		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
Russ Cox's avatar
Russ Cox committed
197 198
			Func:    n,
			ExpName: name,
Russ Cox's avatar
Russ Cox committed
199
		})
Russ Cox's avatar
Russ Cox committed
200 201 202 203 204 205 206 207 208 209 210 211
		break
	}
}

// Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
func (f *File) saveExport2(x interface{}, context string) {
	n, ok := x.(*ast.FuncDecl)
	if !ok {
		return
	}

	for _, exp := range f.ExpFunc {
212
		if exp.Func.Name.Name == n.Name.Name {
Russ Cox's avatar
Russ Cox committed
213 214 215 216 217 218 219 220 221 222 223 224
			exp.Func = n
			break
		}
	}
}

// walk walks the AST x, calling visit(f, x, context) for each node.
func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
	visit(f, x, context)
	switch n := x.(type) {
	case *ast.Expr:
		f.walk(*n, context, visit)
225 226 227

	// everything else just recurs
	default:
228
		error_(token.NoPos, "unexpected type %T in walk", x, visit)
Russ Cox's avatar
Russ Cox committed
229
		panic("unexpected type")
230 231 232 233 234

	case nil:

	// These are ordered and grouped to match ../../pkg/go/ast/ast.go
	case *ast.Field:
Russ Cox's avatar
Russ Cox committed
235
		f.walk(&n.Type, "type", visit)
236
	case *ast.FieldList:
Russ Cox's avatar
Russ Cox committed
237 238
		for _, field := range n.List {
			f.walk(field, context, visit)
239
		}
240 241 242 243 244
	case *ast.BadExpr:
	case *ast.Ident:
	case *ast.Ellipsis:
	case *ast.BasicLit:
	case *ast.FuncLit:
Russ Cox's avatar
Russ Cox committed
245 246
		f.walk(n.Type, "type", visit)
		f.walk(n.Body, "stmt", visit)
247
	case *ast.CompositeLit:
Russ Cox's avatar
Russ Cox committed
248 249
		f.walk(&n.Type, "type", visit)
		f.walk(n.Elts, "expr", visit)
250
	case *ast.ParenExpr:
Russ Cox's avatar
Russ Cox committed
251
		f.walk(&n.X, context, visit)
252
	case *ast.SelectorExpr:
Russ Cox's avatar
Russ Cox committed
253
		f.walk(&n.X, "selector", visit)
254
	case *ast.IndexExpr:
Russ Cox's avatar
Russ Cox committed
255 256
		f.walk(&n.X, "expr", visit)
		f.walk(&n.Index, "expr", visit)
257
	case *ast.SliceExpr:
Russ Cox's avatar
Russ Cox committed
258
		f.walk(&n.X, "expr", visit)
259 260 261 262 263
		if n.Low != nil {
			f.walk(&n.Low, "expr", visit)
		}
		if n.High != nil {
			f.walk(&n.High, "expr", visit)
264 265
		}
	case *ast.TypeAssertExpr:
Russ Cox's avatar
Russ Cox committed
266 267
		f.walk(&n.X, "expr", visit)
		f.walk(&n.Type, "type", visit)
268
	case *ast.CallExpr:
Russ Cox's avatar
Russ Cox committed
269 270 271 272 273 274
		if context == "as2" {
			f.walk(&n.Fun, "call2", visit)
		} else {
			f.walk(&n.Fun, "call", visit)
		}
		f.walk(n.Args, "expr", visit)
275
	case *ast.StarExpr:
Russ Cox's avatar
Russ Cox committed
276
		f.walk(&n.X, context, visit)
277
	case *ast.UnaryExpr:
Russ Cox's avatar
Russ Cox committed
278
		f.walk(&n.X, "expr", visit)
279
	case *ast.BinaryExpr:
Russ Cox's avatar
Russ Cox committed
280 281
		f.walk(&n.X, "expr", visit)
		f.walk(&n.Y, "expr", visit)
282
	case *ast.KeyValueExpr:
Russ Cox's avatar
Russ Cox committed
283 284
		f.walk(&n.Key, "expr", visit)
		f.walk(&n.Value, "expr", visit)
285 286

	case *ast.ArrayType:
Russ Cox's avatar
Russ Cox committed
287 288
		f.walk(&n.Len, "expr", visit)
		f.walk(&n.Elt, "type", visit)
289
	case *ast.StructType:
Russ Cox's avatar
Russ Cox committed
290
		f.walk(n.Fields, "field", visit)
291
	case *ast.FuncType:
Russ Cox's avatar
Russ Cox committed
292
		f.walk(n.Params, "field", visit)
293
		if n.Results != nil {
Russ Cox's avatar
Russ Cox committed
294
			f.walk(n.Results, "field", visit)
295
		}
296
	case *ast.InterfaceType:
Russ Cox's avatar
Russ Cox committed
297
		f.walk(n.Methods, "field", visit)
298
	case *ast.MapType:
Russ Cox's avatar
Russ Cox committed
299 300
		f.walk(&n.Key, "type", visit)
		f.walk(&n.Value, "type", visit)
301
	case *ast.ChanType:
Russ Cox's avatar
Russ Cox committed
302
		f.walk(&n.Value, "type", visit)
303 304 305

	case *ast.BadStmt:
	case *ast.DeclStmt:
Russ Cox's avatar
Russ Cox committed
306
		f.walk(n.Decl, "decl", visit)
307 308
	case *ast.EmptyStmt:
	case *ast.LabeledStmt:
Russ Cox's avatar
Russ Cox committed
309
		f.walk(n.Stmt, "stmt", visit)
310
	case *ast.ExprStmt:
Russ Cox's avatar
Russ Cox committed
311
		f.walk(&n.X, "expr", visit)
312 313 314
	case *ast.SendStmt:
		f.walk(&n.Chan, "expr", visit)
		f.walk(&n.Value, "expr", visit)
315
	case *ast.IncDecStmt:
Russ Cox's avatar
Russ Cox committed
316
		f.walk(&n.X, "expr", visit)
317
	case *ast.AssignStmt:
Russ Cox's avatar
Russ Cox committed
318
		f.walk(n.Lhs, "expr", visit)
Russ Cox's avatar
Russ Cox committed
319
		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
Russ Cox's avatar
Russ Cox committed
320 321 322 323
			f.walk(n.Rhs, "as2", visit)
		} else {
			f.walk(n.Rhs, "expr", visit)
		}
324
	case *ast.GoStmt:
Russ Cox's avatar
Russ Cox committed
325
		f.walk(n.Call, "expr", visit)
326
	case *ast.DeferStmt:
Russ Cox's avatar
Russ Cox committed
327
		f.walk(n.Call, "expr", visit)
328
	case *ast.ReturnStmt:
Russ Cox's avatar
Russ Cox committed
329
		f.walk(n.Results, "expr", visit)
330 331
	case *ast.BranchStmt:
	case *ast.BlockStmt:
332
		f.walk(n.List, context, visit)
333
	case *ast.IfStmt:
Russ Cox's avatar
Russ Cox committed
334 335 336 337
		f.walk(n.Init, "stmt", visit)
		f.walk(&n.Cond, "expr", visit)
		f.walk(n.Body, "stmt", visit)
		f.walk(n.Else, "stmt", visit)
338
	case *ast.CaseClause:
339 340 341 342 343 344
		if context == "typeswitch" {
			context = "type"
		} else {
			context = "expr"
		}
		f.walk(n.List, context, visit)
Russ Cox's avatar
Russ Cox committed
345
		f.walk(n.Body, "stmt", visit)
346
	case *ast.SwitchStmt:
Russ Cox's avatar
Russ Cox committed
347 348
		f.walk(n.Init, "stmt", visit)
		f.walk(&n.Tag, "expr", visit)
349
		f.walk(n.Body, "switch", visit)
350
	case *ast.TypeSwitchStmt:
Russ Cox's avatar
Russ Cox committed
351 352
		f.walk(n.Init, "stmt", visit)
		f.walk(n.Assign, "stmt", visit)
353
		f.walk(n.Body, "typeswitch", visit)
354
	case *ast.CommClause:
355
		f.walk(n.Comm, "stmt", visit)
Russ Cox's avatar
Russ Cox committed
356
		f.walk(n.Body, "stmt", visit)
357
	case *ast.SelectStmt:
Russ Cox's avatar
Russ Cox committed
358
		f.walk(n.Body, "stmt", visit)
359
	case *ast.ForStmt:
Russ Cox's avatar
Russ Cox committed
360 361 362 363
		f.walk(n.Init, "stmt", visit)
		f.walk(&n.Cond, "expr", visit)
		f.walk(n.Post, "stmt", visit)
		f.walk(n.Body, "stmt", visit)
364
	case *ast.RangeStmt:
Russ Cox's avatar
Russ Cox committed
365 366 367 368
		f.walk(&n.Key, "expr", visit)
		f.walk(&n.Value, "expr", visit)
		f.walk(&n.X, "expr", visit)
		f.walk(n.Body, "stmt", visit)
369 370 371

	case *ast.ImportSpec:
	case *ast.ValueSpec:
Russ Cox's avatar
Russ Cox committed
372 373
		f.walk(&n.Type, "type", visit)
		f.walk(n.Values, "expr", visit)
374
	case *ast.TypeSpec:
Russ Cox's avatar
Russ Cox committed
375
		f.walk(&n.Type, "type", visit)
376 377 378

	case *ast.BadDecl:
	case *ast.GenDecl:
Russ Cox's avatar
Russ Cox committed
379
		f.walk(n.Specs, "spec", visit)
380 381
	case *ast.FuncDecl:
		if n.Recv != nil {
Russ Cox's avatar
Russ Cox committed
382
			f.walk(n.Recv, "field", visit)
383
		}
Russ Cox's avatar
Russ Cox committed
384
		f.walk(n.Type, "type", visit)
Russ Cox's avatar
Russ Cox committed
385
		if n.Body != nil {
Russ Cox's avatar
Russ Cox committed
386
			f.walk(n.Body, "stmt", visit)
Russ Cox's avatar
Russ Cox committed
387
		}
388 389

	case *ast.File:
Russ Cox's avatar
Russ Cox committed
390
		f.walk(n.Decls, "decl", visit)
391 392

	case *ast.Package:
Russ Cox's avatar
Russ Cox committed
393 394
		for _, file := range n.Files {
			f.walk(file, "file", visit)
395 396 397 398
		}

	case []ast.Decl:
		for _, d := range n {
Russ Cox's avatar
Russ Cox committed
399
			f.walk(d, context, visit)
400 401 402
		}
	case []ast.Expr:
		for i := range n {
Russ Cox's avatar
Russ Cox committed
403
			f.walk(&n[i], context, visit)
404 405 406
		}
	case []ast.Stmt:
		for _, s := range n {
Russ Cox's avatar
Russ Cox committed
407
			f.walk(s, context, visit)
408 409 410
		}
	case []ast.Spec:
		for _, s := range n {
Russ Cox's avatar
Russ Cox committed
411
			f.walk(s, context, visit)
Ian Lance Taylor's avatar
Ian Lance Taylor committed
412 413 414
		}
	}
}