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