// 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 "" }
// 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 }
// 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) } }
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) } }
// 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 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 } } }