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