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