// describe describes the syntax node denoted by the query position, // including: // - its syntactic category // - the location of the definition of its referent (for identifiers) // - its type and method set (for an expression or type expression) // - its points-to set (for a pointer-like expression) // - its dynamic types (for an interface, reflect.Value, or // reflect.Type expression) and their points-to sets. // // All printed sets are sorted to ensure determinism. // func describe(o *Oracle, qpos *QueryPos) (queryResult, error) { if false { // debugging o.fprintf(os.Stderr, qpos.path[0], "you selected: %s %s", importer.NodeDescription(qpos.path[0]), pathToString2(qpos.path)) } path, action := findInterestingNode(qpos.info, qpos.path) switch action { case actionExpr: return describeValue(o, qpos, path) case actionType: return describeType(o, qpos, path) case actionPackage: return describePackage(o, qpos, path) case actionStmt: return describeStmt(o, qpos, path) case actionUnknown: return &describeUnknownResult{path[0]}, nil default: panic(action) // unreachable } }
// ParseQueryPos parses the source query position pos. // If needExact, it must identify a single AST subtree. // func ParseQueryPos(imp *importer.Importer, pos string, needExact bool) (*QueryPos, error) { start, end, err := parseQueryPos(imp.Fset, pos) if err != nil { return nil, err } info, path, exact := imp.PathEnclosingInterval(start, end) if path == nil { return nil, fmt.Errorf("no syntax here") } if needExact && !exact { return nil, fmt.Errorf("ambiguous selection within %s", importer.NodeDescription(path[0])) } return &QueryPos{start, end, info, path}, nil }
func describeStmt(o *Oracle, qpos *QueryPos, path []ast.Node) (*describeStmtResult, error) { var description string switch n := path[0].(type) { case *ast.Ident: if qpos.info.ObjectOf(n).Pos() == n.Pos() { description = "labelled statement" } else { description = "reference to labelled statement" } default: // Nothing much to say about statements. description = importer.NodeDescription(n) } return &describeStmtResult{o.prog.Fset, path[0], description}, nil }
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) { var value, objpos, ptaerr string if r.constVal != nil { value = r.constVal.String() } if r.obj != nil { objpos = fset.Position(r.obj.Pos()).String() } if r.ptaErr != nil { ptaerr = r.ptaErr.Error() } var pts []*serial.DescribePointer for _, ptr := range r.ptrs { var namePos string if nt, ok := deref(ptr.typ).(*types.Named); ok { namePos = fset.Position(nt.Obj().Pos()).String() } var labels []serial.DescribePTALabel for _, l := range ptr.labels { labels = append(labels, serial.DescribePTALabel{ Pos: fset.Position(l.Pos()).String(), Desc: l.String(), }) } pts = append(pts, &serial.DescribePointer{ Type: ptr.typ.String(), NamePos: namePos, Labels: labels, }) } res.Describe = &serial.Describe{ Desc: importer.NodeDescription(r.expr), Pos: fset.Position(r.expr.Pos()).String(), Detail: "value", Value: &serial.DescribeValue{ Type: r.typ.String(), Value: value, ObjPos: objpos, PTAErr: ptaerr, PTS: pts, }, } }
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) { res.Describe = &serial.Describe{ Desc: importer.NodeDescription(r.node), Pos: fset.Position(r.node.Pos()).String(), } }
func (r *describeUnknownResult) display(printf printfFunc) { // Nothing much to say about misc syntax. printf(r.node, "%s", importer.NodeDescription(r.node)) }
func (r *describeValueResult) display(printf printfFunc) { var prefix, suffix string if r.constVal != nil { suffix = fmt.Sprintf(" of constant value %s", r.constVal) } switch obj := r.obj.(type) { case *types.Func: if recv := obj.Type().(*types.Signature).Recv(); recv != nil { if _, ok := recv.Type().Underlying().(*types.Interface); ok { prefix = "interface method " } else { prefix = "method " } } case *types.Var: // TODO(adonovan): go/types should make it simple to // ask: IsStructField(*Var)? if false { prefix = "struct field " } } // Describe the expression. if r.obj != nil { if r.obj.Pos() == r.expr.Pos() { // defining ident printf(r.expr, "definition of %s%s%s", prefix, r.obj, suffix) } else { // referring ident printf(r.expr, "reference to %s%s%s", prefix, r.obj, suffix) if def := r.obj.Pos(); def != token.NoPos { printf(def, "defined here") } } } else { desc := importer.NodeDescription(r.expr) if suffix != "" { // constant expression printf(r.expr, "%s%s", desc, suffix) } else { // non-constant expression printf(r.expr, "%s of type %s", desc, r.typ) } } // pointer analysis could not be run if r.ptaErr != nil { printf(r.expr, "no points-to information: %s", r.ptaErr) return } if r.ptrs == nil { return // PTA was not invoked (not an error) } // Display the results of pointer analysis. if pointer.CanHaveDynamicTypes(r.typ) { // Show concrete types for interface, reflect.Type or // reflect.Value expression. if len(r.ptrs) > 0 { printf(r.qpos, "this %s may contain these dynamic types:", r.typ) for _, ptr := range r.ptrs { var obj types.Object if nt, ok := deref(ptr.typ).(*types.Named); ok { obj = nt.Obj() } if len(ptr.labels) > 0 { printf(obj, "\t%s, may point to:", ptr.typ) printLabels(printf, ptr.labels, "\t\t") } else { printf(obj, "\t%s", ptr.typ) } } } else { printf(r.qpos, "this %s cannot contain any dynamic types.", r.typ) } } else { // Show labels for other expressions. if ptr := r.ptrs[0]; len(ptr.labels) > 0 { printf(r.qpos, "value may point to these labels:") printLabels(printf, ptr.labels, "\t") } else { printf(r.qpos, "value cannot point to anything.") } } }