Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/cgo/ast.go

Documentation: cmd/cgo

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

View as plain text