// pkgName returns the package name implemented by the // go source filename. // func pkgName(filename string) string { prog, _ := parser.ParseFile(types.FileSet, filename, nil, parser.PackageClauseOnly, nil) if prog != nil { return prog.Name.Name } return "" }
// TestLineComments, using a simple test case, checks that consequtive line // comments are properly terminated with a newline even if the AST position // information is incorrect. // func TestLineComments(t *testing.T) { const src = `// comment 1 // comment 2 // comment 3 package main ` fset := token.NewFileSet() ast1, err1 := parser.ParseFile(fset, "", src, parser.ParseComments) if err1 != nil { panic(err1) } var buf bytes.Buffer fset = token.NewFileSet() // use the wrong file set Fprint(&buf, fset, ast1) nlines := 0 for _, ch := range buf.Bytes() { if ch == '\n' { nlines++ } } const expected = 3 if nlines < expected { t.Errorf("got %d, expected %d\n", nlines, expected) } }
// parseLocalPackage reads and parses all go files from the // current directory that implement the same package name // the principal source file, except the original source file // itself, which will already have been parsed. // func parseLocalPackage(filename string, src *ast.File, pkgScope *ast.Scope) (*ast.Package, error) { pkg := &ast.Package{src.Name.Name, pkgScope, nil, map[string]*ast.File{filename: src}} d, f := filepath.Split(filename) if d == "" { d = "./" } fd, err := os.Open(d) if err != nil { return nil, errNoPkgFiles } defer fd.Close() list, err := fd.Readdirnames(-1) if err != nil { return nil, errNoPkgFiles } for _, pf := range list { file := filepath.Join(d, pf) if !strings.HasSuffix(pf, ".go") || pf == f || pkgName(file) != pkg.Name { continue } src, err := parser.ParseFile(types.FileSet, file, nil, 0, pkg.Scope) if err == nil { pkg.Files[file] = src } } if len(pkg.Files) == 1 { return nil, errNoPkgFiles } return pkg, nil }
func TestOneFile(t *testing.T) { code, offsetMap := translateSymbols(testCode) //fmt.Printf("------------------- {%s}\n", code) f, err := parser.ParseFile(FileSet, "xx.go", code, 0, ast.NewScope(parser.Universe)) if err != nil { t.Fatalf("parse failed: %v", err) } v := make(identVisitor) go func() { ast.Walk(v, f) close(v) }() for e := range v { testExpr(t, FileSet, e, offsetMap) } }
func lookup(filepath string, offset int) (Definition, error) { def := Definition{} f, err := parser.ParseFile(fileset, filepath, nil, 0, getScope(filepath)) if err != nil { return def, err } containsOffset := func(node ast.Node) bool { from := fileset.Position(node.Pos()).Offset to := fileset.Position(node.End()).Offset return offset >= from && offset < to } // traverse the ast tree until we find a node at the given offset position var ident ast.Expr ast.Inspect(f, func(node ast.Node) bool { switch expr := node.(type) { case *ast.SelectorExpr: if containsOffset(expr) && containsOffset(expr.Sel) { ident = expr } case *ast.Ident: if containsOffset(expr) { ident = expr } } return ident == nil }) if ident == nil { return def, errors.New("no identifier found") } pos := getDefPosition(ident) if pos == nil { return def, errors.New("could not find definition of identifier") } obj, _ := types.ExprType(ident, types.DefaultImporter) def.Name = obj.Name def.Position = *pos return def, nil }
// cannot initialize in init because (printer) Fprint launches goroutines. func initialize() { const filename = "testdata/parser.go" src, err := ioutil.ReadFile(filename) if err != nil { log.Fatalf("%s", err) } file, err := parser.ParseFile(fset, filename, src, parser.ParseComments) if err != nil { log.Fatalf("%s", err) } var buf bytes.Buffer testprint(&buf, file) if !bytes.Equal(buf.Bytes(), src) { log.Fatalf("print error: %s not idempotent", filename) } testfile = file }
func main() { flag.Usage = func() { fmt.Fprintf(os.Stderr, "usage: godef [flags] [expr]\n") flag.PrintDefaults() } flag.Parse() if flag.NArg() > 1 { flag.Usage() os.Exit(2) } types.Debug = *debug *tflag = *tflag || *aflag || *Aflag searchpos := *offset filename := *fflag var afile *acmeFile var src []byte if *acmeFlag { var err error if afile, err = acmeCurrentFile(); err != nil { fail("%v", err) } filename, src, searchpos = afile.name, afile.body, afile.offset } else if *readStdin { src, _ = ioutil.ReadAll(os.Stdin) } else { // TODO if there's no filename, look in the current // directory and do something plausible. b, err := ioutil.ReadFile(filename) if err != nil { fail("cannot read %s: %v", filename, err) } src = b } pkgScope := ast.NewScope(parser.Universe) f, err := parser.ParseFile(types.FileSet, filename, src, 0, pkgScope) if f == nil { fail("cannot parse %s: %v", filename, err) } var o ast.Node switch { case flag.NArg() > 0: o = parseExpr(f.Scope, flag.Arg(0)) case searchpos >= 0: o = findIdentifier(f, searchpos) default: fmt.Fprintf(os.Stderr, "no expression or offset specified\n") flag.Usage() os.Exit(2) } // print old source location to facilitate backtracking if *acmeFlag { fmt.Printf("\t%s:#%d\n", afile.name, afile.runeOffset) } switch e := o.(type) { case *ast.ImportSpec: path := importPath(e) pkg, err := build.Default.Import(path, "", build.FindOnly) if err != nil { fail("error finding import path for %s: %s", path, err) } fmt.Println(pkg.Dir) case ast.Expr: if !*tflag { // try local declarations only if obj, typ := types.ExprType(e, types.DefaultImporter); obj != nil { done(obj, typ) } } // add declarations from other files in the local package and try again pkg, err := parseLocalPackage(filename, f, pkgScope) if pkg == nil && !*tflag { fmt.Printf("parseLocalPackage error: %v\n", err) } if obj, typ := types.ExprType(e, types.DefaultImporter); obj != nil { done(obj, typ) } fail("no declaration found for %v", pretty{e}) } }
func (def Definition) findReferences(searchpath string, recursive bool) (chan Reference, chan error) { refs := make(chan Reference) errs := make(chan error, 1000) // returns true on error and reports it failed := func(err error) bool { if err != nil { select { case errs <- err: default: } return true } return false } scanAST := func(f ast.Node) { check := func(expr ast.Expr) { pos := getDefPosition(expr) if pos != nil && *pos == def.Position { refs <- Reference{fileset.Position(expr.Pos())} } } ast.Inspect(f, func(node ast.Node) bool { switch e := node.(type) { case *ast.SelectorExpr: if e.Sel.Name == def.Name { check(e) } case *ast.Ident: if e.Name == def.Name { check(e) } } return true }) } scanFile := func(filepath string) { f, err := parser.ParseFile(fileset, filepath, nil, 0, getScope(filepath)) if failed(err) { return } scanAST(f) } var scanFolder func(dirpath string) scanFolder = func(dirpath string) { filter := func(fi os.FileInfo) bool { return path.Ext(fi.Name()) == ".go" } result, err := parser.ParseDir(fileset, dirpath, filter, 0) if failed(err) { return } for _, pkg := range result { scanAST(pkg) } if !recursive { return } dir, err := os.Open(dirpath) if failed(err) { return } infos, err := dir.Readdir(0) if failed(err) { return } for _, fi := range infos { if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") { scanFolder(path.Join(dirpath, fi.Name())) } } } go func() { defer close(refs) defer close(errs) fi, err := os.Lstat(searchpath) if err != nil { return } if fi.IsDir() { scanFolder(searchpath) } else { scanFile(searchpath) } }() return refs, errs }
func runcheck(t *testing.T, source, golden string, mode checkMode) { // parse source prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments) if err != nil { t.Error(err) return } // filter exports if necessary if mode&export != 0 { ast.FileExports(prog) // ignore result prog.Comments = nil // don't print comments that are not in AST } // determine printer configuration cfg := Config{Tabwidth: tabwidth} if mode&rawFormat != 0 { cfg.Mode |= RawFormat } // format source var buf bytes.Buffer if _, err := cfg.Fprint(&buf, fset, prog); err != nil { t.Error(err) } res := buf.Bytes() // update golden files if necessary if *update { if err := ioutil.WriteFile(golden, res, 0644); err != nil { t.Error(err) } return } // get golden gld, err := ioutil.ReadFile(golden) if err != nil { t.Error(err) return } // compare lengths if len(res) != len(gld) { t.Errorf("len = %d, expected %d (= len(%s))", len(res), len(gld), golden) } // compare contents for i, line, offs := 0, 1, 0; i < len(res) && i < len(gld); i++ { ch := res[i] if ch != gld[i] { t.Errorf("%s:%d:%d: %s", source, line, i-offs+1, lineString(res, offs)) t.Errorf("%s:%d:%d: %s", golden, line, i-offs+1, lineString(gld, offs)) t.Error() return } if ch == '\n' { line++ offs = i + 1 } } }