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