// emitFieldSelection emits to f code to select the index'th field of v. // // If wantAddr, the input must be a pointer-to-struct and the result // will be the field's address; otherwise the result will be the // field's value. // Ident id is used for position and debug info. // func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value { fld := deref(v.Type()).Underlying().(*types.Struct).Field(index) if isPointer(v.Type()) { instr := &FieldAddr{ X: v, Field: index, } instr.setPos(id.Pos()) instr.setType(types.NewPointer(fld.Type())) v = f.emit(instr) // Load the field's value iff we don't want its address. if !wantAddr { v = emitLoad(f, v) } } else { instr := &Field{ X: v, Field: index, } instr.setPos(id.Pos()) instr.setType(fld.Type()) v = f.emit(instr) } emitDebugRef(f, id, v, wantAddr) return v }
func (v *visitor) Visit(node ast.Node) bool { descend := true var ident *ast.Ident var kind string switch t := node.(type) { case *ast.FuncDecl: kind = "func" ident = t.Name descend = false case *ast.TypeSpec: kind = "type" ident = t.Name descend = false } if ident != nil && strings.Contains(strings.ToLower(ident.Name), v.query) { f := v.fset.File(ident.Pos()) v.syms = append(v.syms, symbol{ Package: v.pkg.Name, Path: f.Name(), Name: ident.Name, Kind: kind, Line: f.Line(ident.Pos()) - 1, }) } return descend }
func createDef(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext, isType bool) *Definition { fullName := getFullName(obj, ctx, isType) if def, ok := ctx.defs[fullName]; ok { return def } def := new(Definition) def.Name = fullName def.Pkg = obj.Pkg() def.IsExported = obj.Exported() def.TypeOf = reflect.TypeOf(obj) def.SimpleName = obj.Name() def.Usages = make([]*Usage, 0) def.InterfacesDefs = make([]*Definition, 0) if ident != nil { position := ctx.fset.Position(ident.Pos()) def.File = position.Filename def.Line = position.Line def.Offset = position.Offset def.Col = position.Column } if !types.IsInterface(obj.Type()) { fillInterfaces(def, obj, ctx) } ctx.defs[def.Name] = def logDefinition(def, obj, ident, ctx) return def }
func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id == nil { return } name := x.intern(id.Name) switch kind { case TypeDecl, FuncDecl, ConstDecl, VarDecl: x.curPkgExports[name] = kind } lists, found := x.words[name] if !found { lists = new(IndexResult) x.words[name] = lists } if kind == Use || x.decl == nil { if x.c.IndexGoCode { // not a declaration or no snippet required info := makeSpotInfo(kind, x.current.Line(id.Pos()), false) lists.Others = append(lists.Others, Spot{x.file, info}) } } else { // a declaration with snippet index := x.addSnippet(NewSnippet(x.fset, x.decl, id)) info := makeSpotInfo(kind, index, true) lists.Decls = append(lists.Decls, Spot{x.file, info}) } x.stats.Spots++ }
// collectMethods collects the method declarations from an AST File and // returns a mapping from receiver types to their method FuncDecl's. func (c *checker) collectMethods(file *ast.File) { for _, decl := range file.Decls { if funcdecl, ok := decl.(*ast.FuncDecl); ok && funcdecl.Recv != nil { recvField := funcdecl.Recv.List[0] var recv *ast.Ident switch typ := recvField.Type.(type) { case *ast.StarExpr: recv = typ.X.(*ast.Ident) case *ast.Ident: recv = typ case *ast.BadExpr: return } if recv.Obj == nil { // error reported elsewhere. return } if recv.Obj.Kind != ast.Typ { c.errorf(recv.Pos(), "%s is not a type", recv.Name) return } // The Obj field of the funcdecl wll be nil, so we'll have to // create a new one. funcdecl.Name.Obj = ast.NewObj(ast.Fun, funcdecl.Name.String()) funcdecl.Name.Obj.Decl = funcdecl c.methods[recv.Obj] = append(c.methods[recv.Obj], funcdecl.Name.Obj) } } }
// findField returns the object with the given name if visible in the type's scope. // If no such object is found, an error is reported and a bad object is returned instead. func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) { // TODO(gri) This is simplistic at the moment and ignores anonymous fields. obj = typ.Scope.Lookup(name.Name) if obj == nil { tc.Errorf(name.Pos(), "%s not declared", name.Name) obj = ast.NewObj(ast.Bad, name.Name) } return }
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. // It returns the newly allocated object. If an object with the same name already exists in scope, an error // is reported and the object is not inserted. // (Objects with _ name are always inserted into a scope without errors, but they cannot be found.) func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object { obj := ast.NewObj(kind, name.Name) obj.Decl = decl obj.N = n name.Obj = obj if alt := scope.Insert(obj); alt != obj { tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt)) } return obj }
// find returns the object with the given name if visible in the current scope hierarchy. // If no such object is found, an error is reported and a bad object is returned instead. func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) { for s := tc.topScope; s != nil && obj == nil; s = s.Outer { obj = s.Lookup(name.Name) } if obj == nil { tc.Errorf(name.Pos(), "%s not declared", name.Name) obj = ast.NewObj(ast.Bad, name.Name) } name.Obj = obj return }
func newSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) *Snippet { // TODO instead of pretty-printing the node, should use the original source instead var buf1 bytes.Buffer writeNode(&buf1, fset, decl) // wrap text with <pre> tag var buf2 bytes.Buffer buf2.WriteString("<pre>") FormatText(&buf2, buf1.Bytes(), -1, true, id.Name, nil) buf2.WriteString("</pre>") return &Snippet{fset.Position(id.Pos()).Line, buf2.String()} }
// declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields. // It returns the newly allocated object. If an object with the same name already exists in scope, an error // is reported and the object is not inserted. func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object { obj := ast.NewObj(kind, name.Name) obj.Decl = decl //obj.N = n name.Obj = obj if name.Name != "_" { if alt := scope.Insert(obj); alt != nil { tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String()) } } return obj }
func (p *parser) declIdent(scope *ast.Scope, id *ast.Ident) { decl := scope.Declare(id.Obj) if p.checkDecl && decl != id.Obj { if decl.Kind == ast.Err { // declared object is a forward declaration - update it *decl = *id.Obj id.Obj = decl return } p.Error(id.Pos(), "'"+id.Name()+"' declared already at "+decl.Pos.String()) } }
func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { assert(check.lookup(ident) == nil) // identifier already declared or resolved check.register(ident, obj) if ident.Name != "_" { if alt := scope.Insert(obj); alt != nil { prevDecl := "" if pos := alt.GetPos(); pos.IsValid() { prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) } check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) } } }
// declarePkgObj declares obj in the package scope, records its ident -> obj mapping, // and updates check.objMap. The object must not be a function or method. func (check *checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) { assert(ident.Name == obj.Name()) // spec: "A package-scope or file-scope identifier with name init // may only be declared to be a function with this (func()) signature." if ident.Name == "init" { check.errorf(ident.Pos(), "cannot declare init - must be func") return } check.declare(check.pkg.scope, ident, obj) check.objMap[obj] = d }
func (r *renamer) checkExport(id *ast.Ident, pkg *types.Package, from types.Object) bool { // Reject cross-package references if r.to is unexported. // (Such references may be qualified identifiers or field/method // selections.) if !ast.IsExported(r.to) && pkg != from.Pkg() { r.errorf(from.Pos(), "renaming this %s %q to %q would make it unexported", objectKind(from), from.Name(), r.to) r.errorf(id.Pos(), "\tbreaking references from packages such as %q", pkg.Path()) return false } return true }
func (a *typeCompiler) compileIdent(x *ast.Ident, allowRec bool) Type { _, _, def := a.block.Lookup(x.Name) if def == nil { a.diagAt(x.Pos(), "%s: undefined", x.Name) return nil } switch def := def.(type) { case *Constant: a.diagAt(x.Pos(), "constant %v used as type", x.Name) return nil case *Variable: a.diagAt(x.Pos(), "variable %v used as type", x.Name) return nil case *NamedType: if !allowRec && def.incomplete { a.diagAt(x.Pos(), "illegal recursive type") return nil } if !def.incomplete && def.Def == nil { // Placeholder type from an earlier error return nil } return def case Type: return def } log.Panicf("name %s has unknown type %T", x.Name, def) return nil }
func saveRecent(ident *ast.Ident, obj types.Object) { if *cacheFile == "" { return } if obj == nil || obj.Pos() == token.NoPos { return } if fset.Position(ident.Pos()).Filename == fset.Position(obj.Pos()).Filename { lg("same file") return } k := posPrinter{ident}.String() sha := fileSHA(fset.Position(obj.Pos()).Filename) if sha == "" { return } now := time.Now() ent := &objectEntry{ FromSHA1: fileSHA1, ToPos: posPrinter{obj}.String(), ToSHA1: sha, Created: now.UnixNano(), } lg("new recent entry %s => %+v", k, ent) if recents == nil { recents = &recentObjects{entries: make(map[string]*objectEntry)} } recents.entries[k] = ent expire := now.Add(-24 * time.Hour).UnixNano() for k, ent := range recents.entries { if ent.bad || ent.Created < expire { delete(recents.entries, k) } } b, err := json.MarshalIndent(recents.entries, "", " ") if err != nil { lg("marshal recents err=%v", err) return } if err = ioutil.WriteFile(*cacheFile, b, 0600); err != nil { lg("write recents err=%v", err) } }
// declare declares an object of the given kind and name (ident) in scope; // decl is the corresponding declaration in the AST. An error is reported // if the object was declared before. // // TODO(gri) This is very similar to the declare function in go/parser; it // is only used to associate methods with their respective receiver base types. // In a future version, it might be simpler and cleaner to do all the resolution // in the type-checking phase. It would simplify the parser, AST, and also // reduce some amount of code duplication. // func (check *checker) declare(scope *ast.Scope, kind ast.ObjKind, ident *ast.Ident, decl ast.Decl) { assert(ident.Obj == nil) // identifier already declared or resolved obj := ast.NewObj(kind, ident.Name) obj.Decl = decl ident.Obj = obj if ident.Name != "_" { if alt := scope.Insert(obj); alt != nil { prevDecl := "" if pos := alt.Pos(); pos.IsValid() { prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) } check.errorf(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl)) } } }
func (f *file) checkPkgName(pkg *ast.Ident) { //ref "http://golang.org/doc/effective_go.html#package-names" pkgName := pkg.Name var desc string if strings.Contains(pkgName, "_") { suggestName := strings.Replace(pkgName, "_", "/", -1) desc = "don't use an underscore in package name, " + pkgName + " should be " + suggestName } else if strings.ToLower(pkgName) != pkgName { desc = "don't use capital letters in package name: " + pkgName } if desc != "" { start := f.fset.Position(pkg.Pos()) problem := Problem{Description: desc, Position: &start, Type: PackageName} f.problems = append(f.problems, problem) } }
// NewSnippet creates a text snippet from a declaration decl containing an // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. // func NewSnippet(fset *token.FileSet, decl ast.Decl, id *ast.Ident) (s *Snippet) { switch d := decl.(type) { case *ast.GenDecl: s = genSnippet(fset, d, id) case *ast.FuncDecl: s = funcSnippet(fset, d, id) } // handle failure gracefully if s == nil { var buf bytes.Buffer fmt.Fprintf(&buf, `<span class="alert">could not generate a snippet for <span class="highlight">%s</span></span>`, id.Name) s = &Snippet{fset.Position(id.Pos()).Line, buf.String()} } return }
// NewSnippet creates a text snippet from a declaration decl containing an // identifier id. Parts of the declaration not containing the identifier // may be removed for a more compact snippet. // func NewSnippet(decl ast.Decl, id *ast.Ident) (s *Snippet) { switch d := decl.(type) { case *ast.GenDecl: s = genSnippet(d, id) case *ast.FuncDecl: s = funcSnippet(d, id) } // handle failure gracefully if s == nil { s = &Snippet{ id.Pos().Line, fmt.Sprintf(`could not generate a snippet for <span class="highlight">%s</span>`, id.Name()), } } return }
// scopeOf returns the tightest scope encompassing id. func (p *pkg) scopeOf(id *ast.Ident) *types.Scope { var scope *types.Scope if obj := p.typesInfo.ObjectOf(id); obj != nil { scope = obj.Parent() } if scope == p.typesPkg.Scope() { // We were given a top-level identifier. // Use the file-level scope instead of the package-level scope. pos := id.Pos() for _, f := range p.files { if f.f.Pos() <= pos && pos < f.f.End() { scope = p.typesInfo.Scopes[f.f] break } } } return scope }
func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) { if !*vetMethods && !*vetAll { return } // Expected input/output. expect, ok := canonicalMethods[id.Name] if !ok { return } // Actual input/output args := typeFlatten(t.Params.List) var results []ast.Expr if t.Results != nil { results = typeFlatten(t.Results.List) } // Do the =s (if any) all match? if !f.matchParams(expect.args, args, "=") || !f.matchParams(expect.results, results, "=") { return } // Everything must match. if !f.matchParams(expect.args, args, "") || !f.matchParams(expect.results, results, "") { expectFmt := id.Name + "(" + argjoin(expect.args) + ")" if len(expect.results) == 1 { expectFmt += " " + argjoin(expect.results) } else if len(expect.results) > 1 { expectFmt += " (" + argjoin(expect.results) + ")" } f.b.Reset() if err := printer.Fprint(&f.b, f.fset, t); err != nil { fmt.Fprintf(&f.b, "<%s>", err) } actual := f.b.String() if strings.HasPrefix(actual, "func(") { actual = actual[4:] } actual = id.Name + actual f.Warnf(id.Pos(), "method %s should have signature %s", actual, expectFmt) } }
// growSpan expands the span for the object to contain the instance represented // by the identifier. func (pkg *Package) growSpan(ident *ast.Ident, obj types.Object) { if *strictShadowing { return // No need } pos := ident.Pos() end := ident.End() span, ok := pkg.spans[obj] if ok { if span.min > pos { span.min = pos } if span.max < end { span.max = end } } else { span = Span{pos, end} } pkg.spans[obj] = span }
func (r *resolver) define(b *Block, id *ast.Ident) { obj := r.info.Defs[id] if obj == nil { logf("%s: internal error: not a defining ident: %s\n", r.fset.Position(id.Pos()), id.Name) panic(id) } r.defineObject(b, id.Name, obj) // Objects (other than PkgName) defined at file scope // are also defined in the enclosing package scope. if _, ok := b.syntax.(*ast.File); ok { switch obj.(type) { default: r.defineObject(b.parent.block, id.Name, obj) case nil, *types.PkgName: } } }
func (r *resolver) use(id *ast.Ident, env Environment) { if id.Name == "_" { return // an error } obj, _ := env.Lookup(id.Name) if obj == nil { logf("%s: lookup of %s failed\n", r.fset.Position(id.Pos()), id.Name) } else if want := r.info.Uses[id]; obj != want { // sanity check against go/types resolver logf("%s: internal error: lookup of %s yielded wrong object: got %v (%s), want %v\n", r.fset.Position(id.Pos()), id.Name, types.ObjectString(obj, r.qualifier), r.fset.Position(obj.Pos()), want) } if trace { logf("use %s = %v in %s\n", id.Name, types.ObjectString(obj, r.qualifier), env) } r.result.Refs[obj] = append(r.result.Refs[obj], Reference{id, env}) }
func (a *stmtCompiler) defineVar(ident *ast.Ident, t Type) *Variable { v, prev := a.block.DefineVar(ident.Name(), ident.Pos(), t) if prev != nil { // TODO(austin) It's silly that we have to capture // Pos() in a variable. pos := prev.Pos() if pos.IsValid() { a.diagAt(ident, "variable %s redeclared in this block\n\tprevious declaration at %s", ident.Name(), &pos) } else { a.diagAt(ident, "variable %s redeclared in this block", ident.Name()) } return nil } // Initialize the variable index := v.Index if v.Index >= 0 { a.push(func(v *Thread) { v.f.Vars[index] = t.Zero() }) } return v }
func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { lists, found := x.words[id.Value] if !found { lists = new(IndexResult) x.words[id.Value] = lists } if kind == Use || x.decl == nil { // not a declaration or no snippet required info := makeSpotInfo(kind, id.Pos().Line, false) lists.Others.Push(Spot{x.file, info}) } else { // a declaration with snippet index := x.addSnippet(NewSnippet(x.decl, id)) info := makeSpotInfo(kind, index, true) lists.Decls.Push(Spot{x.file, info}) } x.nspots++ } }
func (x *Indexer) visitIdent(kind SpotKind, id *ast.Ident) { if id != nil { lists, found := x.words[id.Name] if !found { lists = new(IndexResult) x.words[id.Name] = lists } if kind == Use || x.decl == nil { // not a declaration or no snippet required info := makeSpotInfo(kind, x.current.Line(id.Pos()), false) lists.Others = append(lists.Others, Spot{x.file, info}) } else { // a declaration with snippet index := x.addSnippet(NewSnippet(x.fset, x.decl, id)) info := makeSpotInfo(kind, index, true) lists.Decls = append(lists.Decls, Spot{x.file, info}) } x.stats.Spots++ } }
func (d *mDeclarations) decl(fset *token.FileSet, id *ast.Ident, name, kind string) *mDeclarationsDecl { if name == "" { name = id.Name } if name == "_" { return nil } tp := fset.Position(id.Pos()) if !tp.IsValid() { return nil } return &mDeclarationsDecl{ Name: name, Kind: d.kind(id, kind), Fn: tp.Filename, Row: tp.Line - 1, Col: tp.Column - 1, } }
func isValidObject(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext) bool { if obj == nil { return false } position := ctx.fset.Position(ident.Pos()) typeOf := reflect.TypeOf(obj) fullName := getFullName(obj, ctx, false) if !position.IsValid() { util.Warn("position object is invalid for %s", ident.Name) return false } if len(fullName) == 0 { util.Warn("warning: cann't get full name for %s: %v", ident.Name, typeOf) return false } if typeOf == nil { return false } if ctx.defs[fullName] != nil { return false } return true }