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