...

Source file src/cmd/cgo/ast.go

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

View as plain text