Example #1
0
File: guru.go Project: tsandall/opa
// ParseQueryPos parses the source query position pos and returns the
// AST node of the loaded program lprog that it identifies.
// If needExact, it must identify a single AST subtree;
// this is appropriate for queries that allow fairly arbitrary syntax,
// e.g. "describe".
//
func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
	filename, startOffset, endOffset, err := parsePos(pos)
	if err != nil {
		return nil, err
	}

	// Find the named file among those in the loaded program.
	var file *token.File
	lprog.Fset.Iterate(func(f *token.File) bool {
		if sameFile(filename, f.Name()) {
			file = f
			return false // done
		}
		return true // continue
	})
	if file == nil {
		return nil, fmt.Errorf("file %s not found in loaded program", filename)
	}

	start, end, err := fileOffsetToPos(file, startOffset, endOffset)
	if err != nil {
		return nil, err
	}
	info, path, exact := lprog.PathEnclosingInterval(start, end)
	if path == nil {
		return nil, fmt.Errorf("no syntax here")
	}
	if needExact && !exact {
		return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
	}
	return &queryPos{lprog.Fset, start, end, path, exact, info}, nil
}
Example #2
0
func printProgram(prog *loader.Program) {
	// Created packages are the initial packages specified by a call
	// to CreateFromFilenames or CreateFromFiles.
	var names []string
	for _, info := range prog.Created {
		names = append(names, info.Pkg.Path())
	}
	fmt.Printf("created: %s\n", names)

	// Imported packages are the initial packages specified by a
	// call to Import or ImportWithTests.
	names = nil
	for _, info := range prog.Imported {
		names = append(names, info.Pkg.Path())
	}
	sort.Strings(names)
	fmt.Printf("imported: %s\n", names)

	// InitialPackages contains the union of created and imported.
	names = nil
	for _, info := range prog.InitialPackages() {
		names = append(names, info.Pkg.Path())
	}
	sort.Strings(names)
	fmt.Printf("initial: %s\n", names)

	// AllPackages contains all initial packages and their dependencies.
	names = nil
	for pkg := range prog.AllPackages {
		names = append(names, pkg.Path())
	}
	sort.Strings(names)
	fmt.Printf("all: %s\n", names)
}
Example #3
0
// Create a pointer.Config whose scope is the initial packages of lprog
// and their dependencies.
func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
	// TODO(adonovan): the body of this function is essentially
	// duplicated in all go/pointer clients.  Refactor.

	// For each initial package (specified on the command line),
	// if it has a main function, analyze that,
	// otherwise analyze its tests, if any.
	var testPkgs, mains []*ssa.Package
	for _, info := range lprog.InitialPackages() {
		initialPkg := prog.Package(info.Pkg)

		// Add package to the pointer analysis scope.
		if initialPkg.Func("main") != nil {
			mains = append(mains, initialPkg)
		} else {
			testPkgs = append(testPkgs, initialPkg)
		}
	}
	if testPkgs != nil {
		if p := prog.CreateTestMainPackage(testPkgs...); p != nil {
			mains = append(mains, p)
		}
	}
	if mains == nil {
		return nil, fmt.Errorf("analysis scope has no main and no tests")
	}
	return &pointer.Config{
		Log:        ptaLog,
		Reflection: reflection,
		Mains:      mains,
	}, nil
}
Example #4
0
func getDeclareStructOrInterface(prog *loader.Program, v *types.Var) string {
	// From x/tools/refactor/rename/check.go(checkStructField)#L288
	// go/types offers no easy way to get from a field (or interface
	// method) to its declaring struct (or interface), so we must
	// ascend the AST.
	_, path, _ := prog.PathEnclosingInterval(v.Pos(), v.Pos())
	// path matches this pattern:
	// [Ident SelectorExpr? StarExpr? Field FieldList StructType ParenExpr* ... File]

	// Ascend to FieldList.
	var i int
	for {
		if _, ok := path[i].(*ast.FieldList); ok {
			break
		}
		i++
	}
	i++
	_ = path[i].(*ast.StructType)
	i++
	for {
		if _, ok := path[i].(*ast.ParenExpr); !ok {
			break
		}
		i++
	}
	if spec, ok := path[i].(*ast.TypeSpec); ok {
		return spec.Name.String()
	}
	return ""
}
Example #5
0
// progType returns the go/types type for the given reflect.Type,
// which must represent a named non-predeclared Go type.
func progType(prog *loader.Program, t reflect.Type) (*types.TypeName, error) {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	typeName := t.Name()
	if typeName == "" {
		return nil, errgo.Newf("type %s is not named", t)
	}
	pkgPath := t.PkgPath()
	if pkgPath == "" {
		// TODO could return types.Basic type here if we needed to.
		return nil, errgo.Newf("type %s not declared in package", t)
	}
	pkgInfo := prog.Package(pkgPath)
	if pkgInfo == nil {
		return nil, errgo.Newf("cannot find %q in imported code", pkgPath)
	}
	pkg := pkgInfo.Pkg
	obj := pkg.Scope().Lookup(typeName)
	if obj == nil {
		return nil, errgo.Newf("type %s not found in %s", typeName, pkgPath)
	}
	objTypeName, ok := obj.(*types.TypeName)
	if !ok {
		return nil, errgo.Newf("%s is not a type", typeName)
	}
	return objTypeName, nil
}
Example #6
0
// FindMains returns the set of all packages loaded into the given
// loader.Program which contain main functions
func FindMains(p *loader.Program, s *ssa.Program) []*ssa.Package {
	ips := p.InitialPackages()
	mains := make([]*ssa.Package, 0, len(ips))
	for _, info := range ips {
		ssaPkg := s.Package(info.Pkg)
		if ssaPkg.Func("main") != nil {
			mains = append(mains, ssaPkg)
		}
	}
	return mains
}
Example #7
0
func findDotImports(prog *loader.Program, pi *loader.PackageInfo) {
	for _, file := range pi.Files {
		for _, importSpec := range file.Imports {
			if importSpec.Name != nil && importSpec.Name.Name == "." {
				dotImportImportPath := strings.Trim(importSpec.Path.Value, `"`)
				dotImportPi := prog.Package(dotImportImportPath)
				dotImports = append(dotImports, dotImportPi)
				findDotImports(prog, dotImportPi)
			}
		}
	}
}
Example #8
0
func newOracle(iprog *loader.Program, ptalog io.Writer, needs int, reflection bool) (*Oracle, error) {
	o := &Oracle{fset: iprog.Fset}

	// Retain type info for all ASTs in the program.
	if needs&needRetainTypeInfo != 0 {
		o.typeInfo = iprog.AllPackages
	}

	// Create SSA package for the initial packages and their dependencies.
	if needs&needSSA != 0 {
		var mode ssa.BuilderMode
		if needs&needSSADebug != 0 {
			mode |= ssa.GlobalDebug
		}
		prog := ssa.Create(iprog, mode)

		// For each initial package (specified on the command line),
		// if it has a main function, analyze that,
		// otherwise analyze its tests, if any.
		var testPkgs, mains []*ssa.Package
		for _, info := range iprog.InitialPackages() {
			initialPkg := prog.Package(info.Pkg)

			// Add package to the pointer analysis scope.
			if initialPkg.Func("main") != nil {
				mains = append(mains, initialPkg)
			} else {
				testPkgs = append(testPkgs, initialPkg)
			}
		}
		if testPkgs != nil {
			if p := prog.CreateTestMainPackage(testPkgs...); p != nil {
				mains = append(mains, p)
			}
		}
		if mains == nil {
			return nil, fmt.Errorf("analysis scope has no main and no tests")
		}
		o.ptaConfig.Log = ptalog
		o.ptaConfig.Reflection = reflection
		o.ptaConfig.Mains = mains

		o.prog = prog
	}

	return o, nil
}
Example #9
0
// ParseQueryPos parses the source query position pos.
// If needExact, it must identify a single AST subtree;
// this is appropriate for queries that allow fairly arbitrary syntax,
// e.g. "describe".
//
func ParseQueryPos(iprog *loader.Program, posFlag string, needExact bool) (*QueryPos, error) {
	filename, startOffset, endOffset, err := parsePosFlag(posFlag)
	if err != nil {
		return nil, err
	}
	start, end, err := findQueryPos(iprog.Fset, filename, startOffset, endOffset)
	if err != nil {
		return nil, err
	}
	info, path, exact := iprog.PathEnclosingInterval(start, end)
	if path == nil {
		return nil, fmt.Errorf("no syntax here")
	}
	if needExact && !exact {
		return nil, fmt.Errorf("ambiguous selection within %s", astutil.NodeDescription(path[0]))
	}
	return &QueryPos{iprog.Fset, start, end, path, exact, info}, nil
}
Example #10
0
// DocForPos attempts to get the documentation for an item given a filename and byte offset.
func DocForPos(ctxt *build.Context, lprog *loader.Program, filename string, offset int64) (*Doc, error) {
	tokFile := FileFromProgram(lprog, filename)
	if tokFile == nil {
		return nil, fmt.Errorf("gogetdoc: couldn't find %s in program", filename)
	}
	offPos := tokFile.Pos(int(offset))

	pkgInfo, nodes, _ := lprog.PathEnclosingInterval(offPos, offPos)
	for _, node := range nodes {
		switch i := node.(type) {
		case *ast.ImportSpec:
			abs, err := filepath.Abs(filename)
			if err != nil {
				return nil, err
			}
			return PackageDoc(ctxt, lprog.Fset, filepath.Dir(abs), ImportPath(i))
		case *ast.Ident:
			// if we can't find the object denoted by the identifier, keep searching)
			if obj := pkgInfo.ObjectOf(i); obj == nil {
				continue
			}
			return IdentDoc(ctxt, i, pkgInfo, lprog)
		case *ast.File:
			if i.Doc != nil {
				return &Doc{
					Doc: i.Doc.Text(),
				}, nil
			}
			for _, f := range pkgInfo.Files {
				if f.Doc != nil {
					return &Doc{
						Doc: f.Doc.Text(),
					}, nil
				}
			}
		}
	}
	return nil, errors.New("gogetdoc: no documentation found")
}
Example #11
0
File: guru.go Project: tsandall/opa
// Create a pointer.Config whose scope is the initial packages of lprog
// and their dependencies.
func setupPTA(prog *ssa.Program, lprog *loader.Program, ptaLog io.Writer, reflection bool) (*pointer.Config, error) {
	// For each initial package (specified on the command line),
	// if it has a main function, analyze that,
	// otherwise analyze its tests, if any.
	var mains []*ssa.Package
	for _, info := range lprog.InitialPackages() {
		p := prog.Package(info.Pkg)

		// Add package to the pointer analysis scope.
		if p.Pkg.Name() == "main" && p.Func("main") != nil {
			mains = append(mains, p)
		} else if main := prog.CreateTestMainPackage(p); main != nil {
			mains = append(mains, main)
		}
	}
	if mains == nil {
		return nil, fmt.Errorf("analysis scope has no main and no tests")
	}
	return &pointer.Config{
		Log:        ptaLog,
		Reflection: reflection,
		Mains:      mains,
	}, nil
}
Example #12
0
func findFromObjectsInFile(iprog *loader.Program, spec *spec) ([]types.Object, error) {
	var fromObjects []types.Object
	for _, info := range iprog.AllPackages {
		// restrict to specified filename
		// NB: under certain proprietary build systems, a given
		// filename may appear in multiple packages.
		for _, f := range info.Files {
			thisFile := iprog.Fset.File(f.Pos())
			if !sameFile(thisFile.Name(), spec.filename) {
				continue
			}
			// This package contains the query file.

			if spec.offset != 0 {
				// Search for a specific ident by file/offset.
				id := identAtOffset(iprog.Fset, f, spec.offset)
				if id == nil {
					// can't happen?
					return nil, fmt.Errorf("identifier not found")
				}
				obj := info.Uses[id]
				if obj == nil {
					obj = info.Defs[id]
					if obj == nil {
						// Ident without Object.

						// Package clause?
						pos := thisFile.Pos(spec.offset)
						_, path, _ := iprog.PathEnclosingInterval(pos, pos)
						if len(path) == 2 { // [Ident File]
							// TODO(adonovan): support this case.
							return nil, fmt.Errorf("cannot rename %q: renaming package clauses is not yet supported",
								path[1].(*ast.File).Name.Name)
						}

						// Implicit y in "switch y := x.(type) {"?
						if obj := typeSwitchVar(&info.Info, path); obj != nil {
							return []types.Object{obj}, nil
						}

						// Probably a type error.
						return nil, fmt.Errorf("cannot find object for %q", id.Name)
					}
				}
				if obj.Pkg() == nil {
					return nil, fmt.Errorf("cannot rename predeclared identifiers (%s)", obj)

				}

				fromObjects = append(fromObjects, obj)
			} else {
				// do a package-wide query
				objects, err := findObjects(info, spec)
				if err != nil {
					return nil, err
				}

				// filter results: only objects defined in thisFile
				var filtered []types.Object
				for _, obj := range objects {
					if iprog.Fset.File(obj.Pos()) == thisFile {
						filtered = append(filtered, obj)
					}
				}
				if len(filtered) == 0 {
					return nil, fmt.Errorf("no object %q declared in file %s",
						spec.fromName, spec.filename)
				} else if len(filtered) > 1 {
					return nil, ambiguityError(iprog.Fset, filtered)
				}
				fromObjects = append(fromObjects, filtered[0])
			}
			break
		}
	}
	if len(fromObjects) == 0 {
		// can't happen?
		return nil, fmt.Errorf("file %s was not part of the loaded program", spec.filename)
	}
	return fromObjects, nil
}
Example #13
0
// IdentDoc attempts to get the documentation for a *ast.Ident.
func IdentDoc(ctxt *build.Context, id *ast.Ident, info *loader.PackageInfo, prog *loader.Program) (*Doc, error) {
	// get definition of identifier
	obj := info.ObjectOf(id)
	var pos string
	if p := obj.Pos(); p.IsValid() {
		pos = prog.Fset.Position(p).String()
	}

	pkgPath := ""
	if obj.Pkg() != nil {
		pkgPath = obj.Pkg().Path()
	}

	// handle packages imported under a different name
	if p, ok := obj.(*types.PkgName); ok {
		return PackageDoc(ctxt, prog.Fset, "", p.Imported().Path()) // SRCDIR TODO TODO
	}

	_, nodes, _ := prog.PathEnclosingInterval(obj.Pos(), obj.Pos())
	if len(nodes) == 0 {
		// special case - builtins
		doc, decl := findInBuiltin(obj.Name(), obj, prog)
		if doc != "" {
			return &Doc{
				Import: "builtin",
				Name:   obj.Name(),
				Doc:    doc,
				Decl:   decl,
				Pos:    pos,
			}, nil
		}
		return nil, fmt.Errorf("No documentation found for %s", obj.Name())
	}
	var doc *Doc
	for _, node := range nodes {
		switch node.(type) {
		case *ast.FuncDecl, *ast.GenDecl, *ast.Field:
		default:
			continue
		}
		doc = &Doc{
			Import: stripVendorFromImportPath(pkgPath),
			Name:   obj.Name(),
			Decl:   formatNode(node, obj, prog),
			Pos:    pos,
		}
		break
	}
	if doc == nil {
		// This shouldn't happen
		return nil, fmt.Errorf("No documentation found for %s", obj.Name())
	}

	for i, node := range nodes {
		//fmt.Printf("for %s: found %T\n%#v\n", id.Name, node, node)
		switch n := node.(type) {
		case *ast.FuncDecl:
			doc.Doc = n.Doc.Text()
			return doc, nil
		case *ast.GenDecl:
			constValue := ""
			if c, ok := obj.(*types.Const); ok {
				constValue = c.Val().ExactString()
			}

			if len(n.Specs) > 0 {
				switch n.Specs[0].(type) {
				case *ast.TypeSpec:
					spec := findTypeSpec(n, nodes[i-1].Pos())
					if spec.Doc != nil {
						doc.Doc = spec.Doc.Text()
					} else if spec.Comment != nil {
						doc.Doc = spec.Comment.Text()
					}
				case *ast.ValueSpec:
					spec := findVarSpec(n, nodes[i-1].Pos())
					if spec.Doc != nil {
						doc.Doc = spec.Doc.Text()
					} else if spec.Comment != nil {
						doc.Doc = spec.Comment.Text()
					}
				}
			}

			// if we didn't find doc for the spec, check the overall GenDecl
			if doc.Doc == "" && n.Doc != nil {
				doc.Doc = n.Doc.Text()
			}
			if constValue != "" {
				doc.Doc += fmt.Sprintf("\nConstant Value: %s", constValue)
			}

			return doc, nil
		case *ast.Field:
			// check the doc first, if not present, then look for a comment
			if n.Doc != nil {
				doc.Doc = n.Doc.Text()
				return doc, nil
			} else if n.Comment != nil {
				doc.Doc = n.Comment.Text()
				return doc, nil
			}
		}
	}
	return doc, nil
}
Example #14
0
// Returns true if unused
func checkObj(expr *ast.Ident, object types.Object, prog *loader.Program, ssaprog *ssa.Program, fset *token.FileSet) (unused bool) {
	if _, ok := object.(*types.Var); !ok {
		if debug {
			fmt.Println("Skipping object", object)
		}
		return false
	}
	pkg, node, _ := prog.PathEnclosingInterval(expr.Pos(), expr.End())
	spkg := ssaprog.Package(pkg.Pkg)
	f := ssa.EnclosingFunction(spkg, node)
	if f == nil {
		if debug {
			fmt.Printf("Unknown function %v %v %v %v\n", fset.Position(expr.Pos()), object, pkg, prog)
		}
		return false
	}
	value, _ := f.ValueForExpr(expr)
	// Unwrap unops and grab the value inside
	if v, ok := value.(*ssa.UnOp); ok {
		if debug {
			fmt.Println("Unwrapping unop")
		}
		value = v.X
	}
	if debug {
		fmt.Printf("%v %v: %v      %#v\n", fset.Position(expr.Pos()), expr, object, value)
	}
	if _, ok := value.(*ssa.Global); ok {
		if debug {
			fmt.Printf("     is global\n")
		}
		return false
	}
	if value == nil {
		if debug {
			fmt.Println("Value is nil", object)
		}
		return false
	}
	refs := value.Referrers()
	if refs == nil {
		if debug {
			fmt.Println("Referrers is nil", object)
		}
		return false
	}
	if debug {
		fmt.Printf("   (refs) %v\n", refs)
	}
	hasRef := false
	for _, r := range *refs {
		_, ok := r.(*ssa.DebugRef)
		hasRef = hasRef || !ok
		if debug && !ok {
			fmt.Printf("%v %v: %v      %v\n", fset.Position(expr.Pos()), expr, object, r)
		}
	}
	if !hasRef {
		unused = true
	}
	return unused
}