Beispiel #1
0
// 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
}
Beispiel #2
0
// 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 ""
}
Beispiel #3
0
// 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, nil)
	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)
	}
}
Beispiel #4
0
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)
	}
}
Beispiel #5
0
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
}
Beispiel #6
0
// 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, nil)
	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
}
Beispiel #7
0
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})
	}
}
Beispiel #8
0
func runcheck(t *testing.T, source, golden string, mode checkMode) {
	// parse source
	prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments, nil)
	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
		}
	}
}
Beispiel #9
0
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) {
		defer func() {
			if e := recover(); e != nil {
				return
			}
		}()
		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"
		}
		defer func() {
			if e := recover(); e != nil {
				return
			}
		}()
		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
}