// processAnnotations takes an *ssa.Package and a // *importer.PackageInfo, and processes all of the // llgo source annotations attached to each top-level // function and global variable. func (c *compiler) processAnnotations(u *unit, pkginfo *goimporter.PackageInfo) { members := make(map[types.Object]*LLVMValue, len(u.globals)) for k, v := range u.globals { members[k.(ssa.Member).Object()] = v } applyAttributes := func(attrs []Attribute, idents ...*ast.Ident) { if len(attrs) == 0 { return } for _, ident := range idents { if v := members[pkginfo.ObjectOf(ident)]; v != nil { for _, attr := range attrs { attr.Apply(v) } } } } for _, f := range pkginfo.Files { for _, decl := range f.Decls { switch decl := decl.(type) { case *ast.FuncDecl: attrs := parseAttributes(decl.Doc) applyAttributes(attrs, decl.Name) case *ast.GenDecl: if decl.Tok != token.VAR { continue } for _, spec := range decl.Specs { varspec := spec.(*ast.ValueSpec) attrs := parseAttributes(decl.Doc) applyAttributes(attrs, varspec.Names...) } } } } }
// findInterestingNode classifies the syntax node denoted by path as one of: // - an expression, part of an expression or a reference to a constant // or variable; // - a type, part of a type, or a reference to a named type; // - a statement, part of a statement, or a label referring to a statement; // - part of a package declaration or import spec. // - none of the above. // and returns the most "interesting" associated node, which may be // the same node, an ancestor or a descendent. // func findInterestingNode(pkginfo *importer.PackageInfo, path []ast.Node) ([]ast.Node, action) { // TODO(adonovan): integrate with go/types/stdlib_test.go and // apply this to every AST node we can find to make sure it // doesn't crash. // TODO(adonovan): audit for ParenExpr safety, esp. since we // traverse up and down. // TODO(adonovan): if the users selects the "." in // "fmt.Fprintf()", they'll get an ambiguous selection error; // we won't even reach here. Can we do better? // TODO(adonovan): describing a field within 'type T struct {...}' // describes the (anonymous) struct type and concludes "no methods". // We should ascend to the enclosing type decl, if any. for len(path) > 0 { switch n := path[0].(type) { case *ast.GenDecl: if len(n.Specs) == 1 { // Descend to sole {Import,Type,Value}Spec child. path = append([]ast.Node{n.Specs[0]}, path...) continue } return path, actionUnknown // uninteresting case *ast.FuncDecl: // Descend to function name. path = append([]ast.Node{n.Name}, path...) continue case *ast.ImportSpec: return path, actionPackage case *ast.ValueSpec: if len(n.Names) == 1 { // Descend to sole Ident child. path = append([]ast.Node{n.Names[0]}, path...) continue } return path, actionUnknown // uninteresting case *ast.TypeSpec: // Descend to type name. path = append([]ast.Node{n.Name}, path...) continue case ast.Stmt: return path, actionStmt case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: return path, actionType case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause: return path, actionUnknown // uninteresting case *ast.Ellipsis: // Continue to enclosing node. // e.g. [...]T in ArrayType // f(x...) in CallExpr // f(x...T) in FuncType case *ast.Field: // TODO(adonovan): this needs more thought, // since fields can be so many things. if len(n.Names) == 1 { // Descend to sole Ident child. path = append([]ast.Node{n.Names[0]}, path...) continue } // Zero names (e.g. anon field in struct) // or multiple field or param names: // continue to enclosing field list. case *ast.FieldList: // Continue to enclosing node: // {Struct,Func,Interface}Type or FuncDecl. case *ast.BasicLit: if _, ok := path[1].(*ast.ImportSpec); ok { return path[1:], actionPackage } return path, actionExpr case *ast.SelectorExpr: if pkginfo.ObjectOf(n.Sel) == nil { // Is this reachable? return path, actionUnknown } // Descend to .Sel child. path = append([]ast.Node{n.Sel}, path...) continue case *ast.Ident: switch obj := pkginfo.ObjectOf(n).(type) { case *types.PkgName: return path, actionPackage case *types.Const: return path, actionExpr case *types.Label: return path, actionStmt case *types.TypeName: return path, actionType case *types.Var: // For x in 'struct {x T}', return struct type, for now. if _, ok := path[1].(*ast.Field); ok { _ = path[2].(*ast.FieldList) // assertion if _, ok := path[3].(*ast.StructType); ok { return path[3:], actionType } } return path, actionExpr case *types.Func: // For f in 'interface {f()}', return the interface type, for now. if _, ok := path[1].(*ast.Field); ok { _ = path[2].(*ast.FieldList) // assertion if _, ok := path[3].(*ast.InterfaceType); ok { return path[3:], actionType } } // For reference to built-in function, return enclosing call. if _, ok := obj.Type().(*types.Builtin); ok { // Ascend to enclosing function call. path = path[1:] continue } return path, actionExpr } // No object. switch path[1].(type) { case *ast.SelectorExpr: // Return enclosing selector expression. return path[1:], actionExpr case *ast.Field: // TODO(adonovan): test this. // e.g. all f in: // struct { f, g int } // interface { f() } // func (f T) method(f, g int) (f, g bool) // // switch path[3].(type) { // case *ast.FuncDecl: // case *ast.StructType: // case *ast.InterfaceType: // } // // return path[1:], actionExpr // // Unclear what to do with these. // Struct.Fields -- field // Interface.Methods -- field // FuncType.{Params.Results} -- actionExpr // FuncDecl.Recv -- actionExpr case *ast.File: // 'package foo' return path, actionPackage case *ast.ImportSpec: // TODO(adonovan): fix: why no package object? go/types bug? return path[1:], actionPackage default: // e.g. blank identifier (go/types bug?) // or y in "switch y := x.(type)" (go/types bug?) fmt.Printf("unknown reference %s in %T\n", n, path[1]) return path, actionUnknown } case *ast.StarExpr: if pkginfo.IsType(n) { return path, actionType } return path, actionExpr case ast.Expr: // All Expr but {BasicLit,Ident,StarExpr} are // "true" expressions that evaluate to a value. return path, actionExpr } // Ascend to parent. path = path[1:] } return nil, actionUnknown // unreachable }