Example #1
0
// 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
	}
}
Example #2
0
// 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
}
Example #3
0
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
}
Example #4
0
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,
		},
	}
}
Example #5
0
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(),
	}
}
Example #6
0
func (r *describeUnknownResult) display(printf printfFunc) {
	// Nothing much to say about misc syntax.
	printf(r.node, "%s", importer.NodeDescription(r.node))
}
Example #7
0
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.")
		}
	}
}