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 "" }
// VarValue returns the SSA Value that corresponds to a specific // identifier denoting the source-level named variable obj. // // VarValue returns nil if a local variable was not found, perhaps // because its package was not built, the debug information was not // requested during SSA construction, or the value was optimized away. // // ref is the path to an ast.Ident (e.g. from PathEnclosingInterval), // and that ident must resolve to obj. // // pkg is the package enclosing the reference. (A reference to a var // always occurs within a function, so we need to know where to find it.) // // If the identifier is a field selector and its base expression is // non-addressable, then VarValue returns the value of that field. // For example: // func f() struct {x int} // f().x // VarValue(x) returns a *Field instruction of type int // // All other identifiers denote addressable locations (variables). // For them, VarValue may return either the variable's address or its // value, even when the expression is evaluated only for its value; the // situation is reported by isAddr, the second component of the result. // // If !isAddr, the returned value is the one associated with the // specific identifier. For example, // var x int // VarValue(x) returns Const 0 here // x = 1 // VarValue(x) returns Const 1 here // // It is not specified whether the value or the address is returned in // any particular case, as it may depend upon optimizations performed // during SSA code generation, such as registerization, constant // folding, avoidance of materialization of subexpressions, etc. // func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) (value Value, isAddr bool) { // All references to a var are local to some function, possibly init. fn := EnclosingFunction(pkg, ref) if fn == nil { return // e.g. def of struct field; SSA not built? } id := ref[0].(*ast.Ident) // Defining ident of a parameter? if id.Pos() == obj.Pos() { for _, param := range fn.Params { if param.Object() == obj { return param, false } } } // Other ident? for _, b := range fn.Blocks { for _, instr := range b.Instrs { if dr, ok := instr.(*DebugRef); ok { if dr.Pos() == id.Pos() { return dr.X, dr.IsAddr } } } } // Defining ident of package-level var? if v := prog.packageLevelValue(obj); v != nil { return v.(*Global), true } return // e.g. debug info not requested, or var optimized away }
// checkStructField checks that the field renaming will not cause // conflicts at its declaration, or ambiguity or changes to any selection. func (r *Unexporter) checkStructField(objsToUpdate map[types.Object]string, from *types.Var, to string) { // Check that the struct declaration is free of field conflicts, // and field/method conflicts. // 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. info, path, _ := r.iprog.PathEnclosingInterval(from.Pos(), from.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++ tStruct := path[i].(*ast.StructType) i++ // Ascend past parens (unlikely). for { _, ok := path[i].(*ast.ParenExpr) if !ok { break } i++ } if spec, ok := path[i].(*ast.TypeSpec); ok { // This struct is also a named type. // We must check for direct (non-promoted) field/field // and method/field conflicts. named := info.Defs[spec.Name].Type() prev, indices, _ := types.LookupFieldOrMethod(named, true, info.Pkg, to) if len(indices) == 1 { r.warn(from, r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), to), r.errorf(prev.Pos(), "\twould conflict with this %s", objectKind(prev))) return // skip checkSelections to avoid redundant errors } } else { // This struct is not a named type. // We need only check for direct (non-promoted) field/field conflicts. t := info.Types[tStruct].Type.Underlying().(*types.Struct) for i := 0; i < t.NumFields(); i++ { if prev := t.Field(i); prev.Name() == to { r.warn(from, r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), to), r.errorf(prev.Pos(), "\twould conflict with this field")) return // skip checkSelections to avoid redundant errors } } } // Renaming an anonymous field requires renaming the type too. e.g. // print(s.T) // if we rename T to U, // type T int // this and // var s struct {T} // this must change too. if from.Anonymous() { if named, ok := from.Type().(*types.Named); ok { r.check(objsToUpdate, named.Obj(), to) } else if named, ok := deref(from.Type()).(*types.Named); ok { r.check(objsToUpdate, named.Obj(), to) } } // Check integrity of existing (field and method) selections. r.checkSelections(objsToUpdate, from, to) }