// 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 // may result in code, so we need to know where to find that code.) // // The Value of a defining (as opposed to referring) identifier is the // value assigned to it in its definition. // // In many cases where the identifier appears in an lvalue context, // the resulting Value is the var's address, not its value. // For example, x in all these examples: // x.y = 0 // x[0] = 0 // _ = x[:] // x = X{} // _ = &x // x.method() (iff method is on &x) // and all package-level vars. (This situation can be detected by // comparing the types of the Var and Value.) // func (prog *Program) VarValue(obj *types.Var, pkg *Package, ref []ast.Node) Value { id := ref[0].(*ast.Ident) // Package-level variable? if v := prog.packageLevelValue(obj); v != nil { return v.(*Global) } // Must be a function-local variable. // (e.g. local, parameter, or field selection e.f) fn := EnclosingFunction(pkg, ref) if fn == nil { return nil // e.g. SSA not built } // Defining ident of a parameter? if id.Pos() == obj.Pos() { for _, param := range fn.Params { if param.Object() == obj { return param } } } // Other ident? for _, b := range fn.Blocks { for _, instr := range b.Instrs { if ref, ok := instr.(*DebugRef); ok { if ref.Pos() == id.Pos() { return ref.X } } } } return nil // e.g. debug info not requested, or var optimized away }
// 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.) // // The Value of a defining (as opposed to referring) identifier is the // value assigned to it in its definition. Similarly, the Value of an // identifier that is the LHS of an assignment is the value assigned // to it in that statement. In all these examples, VarValue(x) returns // the value of x and isAddr==false. // // var x X // var x = X{} // x := X{} // x = X{} // // When an identifier appears in an lvalue context other than as the // LHS of an assignment, the resulting Value is the var's address, not // its value. This situation is reported by isAddr, the second // component of the result. In these examples, VarValue(x) returns // the address of x and isAddr==true. // // x.y = 0 // x[0] = 0 // _ = x[:] (where x is an array) // _ = &x // x.method() (iff method is on &x) // 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 *renamer) checkStructField(from *types.Var) { // 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 is [Ident Field FieldList StructType ... File]. Can't fail. // Ascend past parens (unlikely). i := 4 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, r.to) if len(indices) == 1 { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.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[path[3].(*ast.StructType)].Type.Underlying().(*types.Struct) for i := 0; i < T.NumFields(); i++ { if prev := T.Field(i); prev.Name() == r.to { r.errorf(from.Pos(), "renaming this field %q to %q", from.Name(), r.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(named.Obj()) } else if named, ok := deref(from.Type()).(*types.Named); ok { r.check(named.Obj()) } } // Check integrity of existing (field and method) selections. r.checkSelections(from) }