github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/cmd/cgo/ast.go (about)

     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  
     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, flags parser.Mode) *ast.File {
    20  	ast1, err := parser.ParseFile(fset, name, nil, flags)
    21  	if err != nil {
    22  		if list, ok := err.(scanner.ErrorList); ok {
    23  			// If err is a scanner.ErrorList, its String will print just
    24  			// the first error and then (+n more errors).
    25  			// Instead, turn it into a new Error that will return
    26  			// details for all the errors.
    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  // ReadGo populates f with information learned from reading the
    42  // Go source file with the given file name.  It gathers the C preamble
    43  // attached to the import "C" comment, a list of references to C.xxx,
    44  // a list of exported functions, and the actual AST, to be rewritten and
    45  // printed.
    46  func (f *File) ReadGo(name string) {
    47  	// Two different parses: once with comments, once without.
    48  	// The printer is not good enough at printing comments in the
    49  	// right place when we start editing the AST behind its back,
    50  	// so we use ast1 to look for the doc comments on import "C"
    51  	// and on exported functions, and we use ast2 for translating
    52  	// and reprinting.
    53  	ast1 := parse(name, parser.ParseComments)
    54  	ast2 := parse(name, 0)
    55  
    56  	f.Package = ast1.Name.Name
    57  	f.Name = make(map[string]*Name)
    58  
    59  	// In ast1, find the import "C" line and get any extra C preamble.
    60  	sawC := false
    61  	for _, decl := range ast1.Decls {
    62  		d, ok := decl.(*ast.GenDecl)
    63  		if !ok {
    64  			continue
    65  		}
    66  		for _, spec := range d.Specs {
    67  			s, ok := spec.(*ast.ImportSpec)
    68  			if !ok || string(s.Path.Value) != `"C"` {
    69  				continue
    70  			}
    71  			sawC = true
    72  			if s.Name != nil {
    73  				error_(s.Path.Pos(), `cannot rename import "C"`)
    74  			}
    75  			cg := s.Doc
    76  			if cg == nil && len(d.Specs) == 1 {
    77  				cg = d.Doc
    78  			}
    79  			if cg != nil {
    80  				f.Preamble += fmt.Sprintf("#line %d %q\n", sourceLine(cg), name)
    81  				f.Preamble += commentText(cg) + "\n"
    82  			}
    83  		}
    84  	}
    85  	if !sawC {
    86  		error_(token.NoPos, `cannot find import "C"`)
    87  	}
    88  
    89  	// In ast2, strip the import "C" line.
    90  	w := 0
    91  	for _, decl := range ast2.Decls {
    92  		d, ok := decl.(*ast.GenDecl)
    93  		if !ok {
    94  			ast2.Decls[w] = decl
    95  			w++
    96  			continue
    97  		}
    98  		ws := 0
    99  		for _, spec := range d.Specs {
   100  			s, ok := spec.(*ast.ImportSpec)
   101  			if !ok || string(s.Path.Value) != `"C"` {
   102  				d.Specs[ws] = spec
   103  				ws++
   104  			}
   105  		}
   106  		if ws == 0 {
   107  			continue
   108  		}
   109  		d.Specs = d.Specs[0:ws]
   110  		ast2.Decls[w] = d
   111  		w++
   112  	}
   113  	ast2.Decls = ast2.Decls[0:w]
   114  
   115  	// Accumulate pointers to uses of C.x.
   116  	if f.Ref == nil {
   117  		f.Ref = make([]*Ref, 0, 8)
   118  	}
   119  	f.walk(ast2, "prog", (*File).saveRef)
   120  
   121  	// Accumulate exported functions.
   122  	// The comments are only on ast1 but we need to
   123  	// save the function bodies from ast2.
   124  	// The first walk fills in ExpFunc, and the
   125  	// second walk changes the entries to
   126  	// refer to ast2 instead.
   127  	f.walk(ast1, "prog", (*File).saveExport)
   128  	f.walk(ast2, "prog", (*File).saveExport2)
   129  
   130  	f.Comments = ast1.Comments
   131  	f.AST = ast2
   132  }
   133  
   134  // Like ast.CommentGroup's Text method but preserves
   135  // leading blank lines, so that line numbers line up.
   136  func commentText(g *ast.CommentGroup) string {
   137  	if g == nil {
   138  		return ""
   139  	}
   140  	var pieces []string
   141  	for _, com := range g.List {
   142  		c := string(com.Text)
   143  		// Remove comment markers.
   144  		// The parser has given us exactly the comment text.
   145  		switch c[1] {
   146  		case '/':
   147  			//-style comment (no newline at the end)
   148  			c = c[2:] + "\n"
   149  		case '*':
   150  			/*-style comment */
   151  			c = c[2 : len(c)-2]
   152  		}
   153  		pieces = append(pieces, c)
   154  	}
   155  	return strings.Join(pieces, "")
   156  }
   157  
   158  // Save references to C.xxx for later processing.
   159  func (f *File) saveRef(x interface{}, context string) {
   160  	n, ok := x.(*ast.Expr)
   161  	if !ok {
   162  		return
   163  	}
   164  	if sel, ok := (*n).(*ast.SelectorExpr); ok {
   165  		// For now, assume that the only instance of capital C is
   166  		// when used as the imported package identifier.
   167  		// The parser should take care of scoping in the future,
   168  		// so that we will be able to distinguish a "top-level C"
   169  		// from a local C.
   170  		if l, ok := sel.X.(*ast.Ident); ok && l.Name == "C" {
   171  			if context == "as2" {
   172  				context = "expr"
   173  			}
   174  			if context == "embed-type" {
   175  				error_(sel.Pos(), "cannot embed C type")
   176  			}
   177  			goname := sel.Sel.Name
   178  			if goname == "errno" {
   179  				error_(sel.Pos(), "cannot refer to errno directly; see documentation")
   180  				return
   181  			}
   182  			name := f.Name[goname]
   183  			if name == nil {
   184  				name = &Name{
   185  					Go: goname,
   186  				}
   187  				f.Name[goname] = name
   188  			}
   189  			f.Ref = append(f.Ref, &Ref{
   190  				Name:    name,
   191  				Expr:    n,
   192  				Context: context,
   193  			})
   194  			return
   195  		}
   196  	}
   197  }
   198  
   199  // If a function should be exported add it to ExpFunc.
   200  func (f *File) saveExport(x interface{}, context string) {
   201  	n, ok := x.(*ast.FuncDecl)
   202  	if !ok {
   203  		return
   204  	}
   205  
   206  	if n.Doc == nil {
   207  		return
   208  	}
   209  	for _, c := range n.Doc.List {
   210  		if !strings.HasPrefix(string(c.Text), "//export ") {
   211  			continue
   212  		}
   213  
   214  		name := strings.TrimSpace(string(c.Text[9:]))
   215  		if name == "" {
   216  			error_(c.Pos(), "export missing name")
   217  		}
   218  
   219  		if name != n.Name.Name {
   220  			error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
   221  		}
   222  
   223  		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
   224  			Func:    n,
   225  			ExpName: name,
   226  		})
   227  		break
   228  	}
   229  }
   230  
   231  // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
   232  func (f *File) saveExport2(x interface{}, context string) {
   233  	n, ok := x.(*ast.FuncDecl)
   234  	if !ok {
   235  		return
   236  	}
   237  
   238  	for _, exp := range f.ExpFunc {
   239  		if exp.Func.Name.Name == n.Name.Name {
   240  			exp.Func = n
   241  			break
   242  		}
   243  	}
   244  }
   245  
   246  // walk walks the AST x, calling visit(f, x, context) for each node.
   247  func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
   248  	visit(f, x, context)
   249  	switch n := x.(type) {
   250  	case *ast.Expr:
   251  		f.walk(*n, context, visit)
   252  
   253  	// everything else just recurs
   254  	default:
   255  		error_(token.NoPos, "unexpected type %T in walk", x, visit)
   256  		panic("unexpected type")
   257  
   258  	case nil:
   259  
   260  	// These are ordered and grouped to match ../../pkg/go/ast/ast.go
   261  	case *ast.Field:
   262  		if len(n.Names) == 0 && context == "field" {
   263  			f.walk(&n.Type, "embed-type", visit)
   264  		} else {
   265  			f.walk(&n.Type, "type", visit)
   266  		}
   267  	case *ast.FieldList:
   268  		for _, field := range n.List {
   269  			f.walk(field, context, visit)
   270  		}
   271  	case *ast.BadExpr:
   272  	case *ast.Ident:
   273  	case *ast.Ellipsis:
   274  	case *ast.BasicLit:
   275  	case *ast.FuncLit:
   276  		f.walk(n.Type, "type", visit)
   277  		f.walk(n.Body, "stmt", visit)
   278  	case *ast.CompositeLit:
   279  		f.walk(&n.Type, "type", visit)
   280  		f.walk(n.Elts, "expr", visit)
   281  	case *ast.ParenExpr:
   282  		f.walk(&n.X, context, visit)
   283  	case *ast.SelectorExpr:
   284  		f.walk(&n.X, "selector", visit)
   285  	case *ast.IndexExpr:
   286  		f.walk(&n.X, "expr", visit)
   287  		f.walk(&n.Index, "expr", visit)
   288  	case *ast.SliceExpr:
   289  		f.walk(&n.X, "expr", visit)
   290  		if n.Low != nil {
   291  			f.walk(&n.Low, "expr", visit)
   292  		}
   293  		if n.High != nil {
   294  			f.walk(&n.High, "expr", visit)
   295  		}
   296  	case *ast.TypeAssertExpr:
   297  		f.walk(&n.X, "expr", visit)
   298  		f.walk(&n.Type, "type", visit)
   299  	case *ast.CallExpr:
   300  		if context == "as2" {
   301  			f.walk(&n.Fun, "call2", visit)
   302  		} else {
   303  			f.walk(&n.Fun, "call", visit)
   304  		}
   305  		f.walk(n.Args, "expr", visit)
   306  	case *ast.StarExpr:
   307  		f.walk(&n.X, context, visit)
   308  	case *ast.UnaryExpr:
   309  		f.walk(&n.X, "expr", visit)
   310  	case *ast.BinaryExpr:
   311  		f.walk(&n.X, "expr", visit)
   312  		f.walk(&n.Y, "expr", visit)
   313  	case *ast.KeyValueExpr:
   314  		f.walk(&n.Key, "expr", visit)
   315  		f.walk(&n.Value, "expr", visit)
   316  
   317  	case *ast.ArrayType:
   318  		f.walk(&n.Len, "expr", visit)
   319  		f.walk(&n.Elt, "type", visit)
   320  	case *ast.StructType:
   321  		f.walk(n.Fields, "field", visit)
   322  	case *ast.FuncType:
   323  		f.walk(n.Params, "param", visit)
   324  		if n.Results != nil {
   325  			f.walk(n.Results, "param", visit)
   326  		}
   327  	case *ast.InterfaceType:
   328  		f.walk(n.Methods, "field", visit)
   329  	case *ast.MapType:
   330  		f.walk(&n.Key, "type", visit)
   331  		f.walk(&n.Value, "type", visit)
   332  	case *ast.ChanType:
   333  		f.walk(&n.Value, "type", visit)
   334  
   335  	case *ast.BadStmt:
   336  	case *ast.DeclStmt:
   337  		f.walk(n.Decl, "decl", visit)
   338  	case *ast.EmptyStmt:
   339  	case *ast.LabeledStmt:
   340  		f.walk(n.Stmt, "stmt", visit)
   341  	case *ast.ExprStmt:
   342  		f.walk(&n.X, "expr", visit)
   343  	case *ast.SendStmt:
   344  		f.walk(&n.Chan, "expr", visit)
   345  		f.walk(&n.Value, "expr", visit)
   346  	case *ast.IncDecStmt:
   347  		f.walk(&n.X, "expr", visit)
   348  	case *ast.AssignStmt:
   349  		f.walk(n.Lhs, "expr", visit)
   350  		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
   351  			f.walk(n.Rhs, "as2", visit)
   352  		} else {
   353  			f.walk(n.Rhs, "expr", visit)
   354  		}
   355  	case *ast.GoStmt:
   356  		f.walk(n.Call, "expr", visit)
   357  	case *ast.DeferStmt:
   358  		f.walk(n.Call, "expr", visit)
   359  	case *ast.ReturnStmt:
   360  		f.walk(n.Results, "expr", visit)
   361  	case *ast.BranchStmt:
   362  	case *ast.BlockStmt:
   363  		f.walk(n.List, context, visit)
   364  	case *ast.IfStmt:
   365  		f.walk(n.Init, "stmt", visit)
   366  		f.walk(&n.Cond, "expr", visit)
   367  		f.walk(n.Body, "stmt", visit)
   368  		f.walk(n.Else, "stmt", visit)
   369  	case *ast.CaseClause:
   370  		if context == "typeswitch" {
   371  			context = "type"
   372  		} else {
   373  			context = "expr"
   374  		}
   375  		f.walk(n.List, context, visit)
   376  		f.walk(n.Body, "stmt", visit)
   377  	case *ast.SwitchStmt:
   378  		f.walk(n.Init, "stmt", visit)
   379  		f.walk(&n.Tag, "expr", visit)
   380  		f.walk(n.Body, "switch", visit)
   381  	case *ast.TypeSwitchStmt:
   382  		f.walk(n.Init, "stmt", visit)
   383  		f.walk(n.Assign, "stmt", visit)
   384  		f.walk(n.Body, "typeswitch", visit)
   385  	case *ast.CommClause:
   386  		f.walk(n.Comm, "stmt", visit)
   387  		f.walk(n.Body, "stmt", visit)
   388  	case *ast.SelectStmt:
   389  		f.walk(n.Body, "stmt", visit)
   390  	case *ast.ForStmt:
   391  		f.walk(n.Init, "stmt", visit)
   392  		f.walk(&n.Cond, "expr", visit)
   393  		f.walk(n.Post, "stmt", visit)
   394  		f.walk(n.Body, "stmt", visit)
   395  	case *ast.RangeStmt:
   396  		f.walk(&n.Key, "expr", visit)
   397  		f.walk(&n.Value, "expr", visit)
   398  		f.walk(&n.X, "expr", visit)
   399  		f.walk(n.Body, "stmt", visit)
   400  
   401  	case *ast.ImportSpec:
   402  	case *ast.ValueSpec:
   403  		f.walk(&n.Type, "type", visit)
   404  		f.walk(n.Values, "expr", visit)
   405  	case *ast.TypeSpec:
   406  		f.walk(&n.Type, "type", visit)
   407  
   408  	case *ast.BadDecl:
   409  	case *ast.GenDecl:
   410  		f.walk(n.Specs, "spec", visit)
   411  	case *ast.FuncDecl:
   412  		if n.Recv != nil {
   413  			f.walk(n.Recv, "param", visit)
   414  		}
   415  		f.walk(n.Type, "type", visit)
   416  		if n.Body != nil {
   417  			f.walk(n.Body, "stmt", visit)
   418  		}
   419  
   420  	case *ast.File:
   421  		f.walk(n.Decls, "decl", visit)
   422  
   423  	case *ast.Package:
   424  		for _, file := range n.Files {
   425  			f.walk(file, "file", visit)
   426  		}
   427  
   428  	case []ast.Decl:
   429  		for _, d := range n {
   430  			f.walk(d, context, visit)
   431  		}
   432  	case []ast.Expr:
   433  		for i := range n {
   434  			f.walk(&n[i], context, visit)
   435  		}
   436  	case []ast.Stmt:
   437  		for _, s := range n {
   438  			f.walk(s, context, visit)
   439  		}
   440  	case []ast.Spec:
   441  		for _, s := range n {
   442  			f.walk(s, context, visit)
   443  		}
   444  	}
   445  }