github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/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  		f.ExpFunc = append(f.ExpFunc, &ExpFunc{
   239  			Func:    n,
   240  			ExpName: name,
   241  		})
   242  		break
   243  	}
   244  }
   245  
   246  // Make f.ExpFunc[i] point at the Func from this AST instead of the other one.
   247  func (f *File) saveExport2(x interface{}, context string) {
   248  	n, ok := x.(*ast.FuncDecl)
   249  	if !ok {
   250  		return
   251  	}
   252  
   253  	for _, exp := range f.ExpFunc {
   254  		if exp.Func.Name.Name == n.Name.Name {
   255  			exp.Func = n
   256  			break
   257  		}
   258  	}
   259  }
   260  
   261  // walk walks the AST x, calling visit(f, x, context) for each node.
   262  func (f *File) walk(x interface{}, context string, visit func(*File, interface{}, string)) {
   263  	visit(f, x, context)
   264  	switch n := x.(type) {
   265  	case *ast.Expr:
   266  		f.walk(*n, context, visit)
   267  
   268  	// everything else just recurs
   269  	default:
   270  		error_(token.NoPos, "unexpected type %T in walk", x, visit)
   271  		panic("unexpected type")
   272  
   273  	case nil:
   274  
   275  	// These are ordered and grouped to match ../../go/ast/ast.go
   276  	case *ast.Field:
   277  		if len(n.Names) == 0 && context == "field" {
   278  			f.walk(&n.Type, "embed-type", visit)
   279  		} else {
   280  			f.walk(&n.Type, "type", visit)
   281  		}
   282  	case *ast.FieldList:
   283  		for _, field := range n.List {
   284  			f.walk(field, context, visit)
   285  		}
   286  	case *ast.BadExpr:
   287  	case *ast.Ident:
   288  	case *ast.Ellipsis:
   289  	case *ast.BasicLit:
   290  	case *ast.FuncLit:
   291  		f.walk(n.Type, "type", visit)
   292  		f.walk(n.Body, "stmt", visit)
   293  	case *ast.CompositeLit:
   294  		f.walk(&n.Type, "type", visit)
   295  		f.walk(n.Elts, "expr", visit)
   296  	case *ast.ParenExpr:
   297  		f.walk(&n.X, context, visit)
   298  	case *ast.SelectorExpr:
   299  		f.walk(&n.X, "selector", visit)
   300  	case *ast.IndexExpr:
   301  		f.walk(&n.X, "expr", visit)
   302  		f.walk(&n.Index, "expr", visit)
   303  	case *ast.SliceExpr:
   304  		f.walk(&n.X, "expr", visit)
   305  		if n.Low != nil {
   306  			f.walk(&n.Low, "expr", visit)
   307  		}
   308  		if n.High != nil {
   309  			f.walk(&n.High, "expr", visit)
   310  		}
   311  		if n.Max != nil {
   312  			f.walk(&n.Max, "expr", visit)
   313  		}
   314  	case *ast.TypeAssertExpr:
   315  		f.walk(&n.X, "expr", visit)
   316  		f.walk(&n.Type, "type", visit)
   317  	case *ast.CallExpr:
   318  		if context == "as2" {
   319  			f.walk(&n.Fun, "call2", visit)
   320  		} else {
   321  			f.walk(&n.Fun, "call", visit)
   322  		}
   323  		f.walk(n.Args, "expr", visit)
   324  	case *ast.StarExpr:
   325  		f.walk(&n.X, context, visit)
   326  	case *ast.UnaryExpr:
   327  		f.walk(&n.X, "expr", visit)
   328  	case *ast.BinaryExpr:
   329  		f.walk(&n.X, "expr", visit)
   330  		f.walk(&n.Y, "expr", visit)
   331  	case *ast.KeyValueExpr:
   332  		f.walk(&n.Key, "expr", visit)
   333  		f.walk(&n.Value, "expr", visit)
   334  
   335  	case *ast.ArrayType:
   336  		f.walk(&n.Len, "expr", visit)
   337  		f.walk(&n.Elt, "type", visit)
   338  	case *ast.StructType:
   339  		f.walk(n.Fields, "field", visit)
   340  	case *ast.FuncType:
   341  		f.walk(n.Params, "param", visit)
   342  		if n.Results != nil {
   343  			f.walk(n.Results, "param", visit)
   344  		}
   345  	case *ast.InterfaceType:
   346  		f.walk(n.Methods, "field", visit)
   347  	case *ast.MapType:
   348  		f.walk(&n.Key, "type", visit)
   349  		f.walk(&n.Value, "type", visit)
   350  	case *ast.ChanType:
   351  		f.walk(&n.Value, "type", visit)
   352  
   353  	case *ast.BadStmt:
   354  	case *ast.DeclStmt:
   355  		f.walk(n.Decl, "decl", visit)
   356  	case *ast.EmptyStmt:
   357  	case *ast.LabeledStmt:
   358  		f.walk(n.Stmt, "stmt", visit)
   359  	case *ast.ExprStmt:
   360  		f.walk(&n.X, "expr", visit)
   361  	case *ast.SendStmt:
   362  		f.walk(&n.Chan, "expr", visit)
   363  		f.walk(&n.Value, "expr", visit)
   364  	case *ast.IncDecStmt:
   365  		f.walk(&n.X, "expr", visit)
   366  	case *ast.AssignStmt:
   367  		f.walk(n.Lhs, "expr", visit)
   368  		if len(n.Lhs) == 2 && len(n.Rhs) == 1 {
   369  			f.walk(n.Rhs, "as2", visit)
   370  		} else {
   371  			f.walk(n.Rhs, "expr", visit)
   372  		}
   373  	case *ast.GoStmt:
   374  		f.walk(n.Call, "expr", visit)
   375  	case *ast.DeferStmt:
   376  		f.walk(n.Call, "expr", visit)
   377  	case *ast.ReturnStmt:
   378  		f.walk(n.Results, "expr", visit)
   379  	case *ast.BranchStmt:
   380  	case *ast.BlockStmt:
   381  		f.walk(n.List, context, visit)
   382  	case *ast.IfStmt:
   383  		f.walk(n.Init, "stmt", visit)
   384  		f.walk(&n.Cond, "expr", visit)
   385  		f.walk(n.Body, "stmt", visit)
   386  		f.walk(n.Else, "stmt", visit)
   387  	case *ast.CaseClause:
   388  		if context == "typeswitch" {
   389  			context = "type"
   390  		} else {
   391  			context = "expr"
   392  		}
   393  		f.walk(n.List, context, visit)
   394  		f.walk(n.Body, "stmt", visit)
   395  	case *ast.SwitchStmt:
   396  		f.walk(n.Init, "stmt", visit)
   397  		f.walk(&n.Tag, "expr", visit)
   398  		f.walk(n.Body, "switch", visit)
   399  	case *ast.TypeSwitchStmt:
   400  		f.walk(n.Init, "stmt", visit)
   401  		f.walk(n.Assign, "stmt", visit)
   402  		f.walk(n.Body, "typeswitch", visit)
   403  	case *ast.CommClause:
   404  		f.walk(n.Comm, "stmt", visit)
   405  		f.walk(n.Body, "stmt", visit)
   406  	case *ast.SelectStmt:
   407  		f.walk(n.Body, "stmt", visit)
   408  	case *ast.ForStmt:
   409  		f.walk(n.Init, "stmt", visit)
   410  		f.walk(&n.Cond, "expr", visit)
   411  		f.walk(n.Post, "stmt", visit)
   412  		f.walk(n.Body, "stmt", visit)
   413  	case *ast.RangeStmt:
   414  		f.walk(&n.Key, "expr", visit)
   415  		f.walk(&n.Value, "expr", visit)
   416  		f.walk(&n.X, "expr", visit)
   417  		f.walk(n.Body, "stmt", visit)
   418  
   419  	case *ast.ImportSpec:
   420  	case *ast.ValueSpec:
   421  		f.walk(&n.Type, "type", visit)
   422  		f.walk(n.Values, "expr", visit)
   423  	case *ast.TypeSpec:
   424  		f.walk(&n.Type, "type", visit)
   425  
   426  	case *ast.BadDecl:
   427  	case *ast.GenDecl:
   428  		f.walk(n.Specs, "spec", visit)
   429  	case *ast.FuncDecl:
   430  		if n.Recv != nil {
   431  			f.walk(n.Recv, "param", visit)
   432  		}
   433  		f.walk(n.Type, "type", visit)
   434  		if n.Body != nil {
   435  			f.walk(n.Body, "stmt", visit)
   436  		}
   437  
   438  	case *ast.File:
   439  		f.walk(n.Decls, "decl", visit)
   440  
   441  	case *ast.Package:
   442  		for _, file := range n.Files {
   443  			f.walk(file, "file", visit)
   444  		}
   445  
   446  	case []ast.Decl:
   447  		for _, d := range n {
   448  			f.walk(d, context, visit)
   449  		}
   450  	case []ast.Expr:
   451  		for i := range n {
   452  			f.walk(&n[i], context, visit)
   453  		}
   454  	case []ast.Stmt:
   455  		for _, s := range n {
   456  			f.walk(s, context, visit)
   457  		}
   458  	case []ast.Spec:
   459  		for _, s := range n {
   460  			f.walk(s, context, visit)
   461  		}
   462  	}
   463  }