Skip to content

Commit 21bf051

Browse files
committed
Make structFieldInfo type simpler
1 parent dec2a8c commit 21bf051

File tree

5 files changed

+47
-62
lines changed

5 files changed

+47
-62
lines changed

gopls/internal/golang/codeaction.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,16 @@ func quickFix(ctx context.Context, req *codeActionsRequest) error {
337337
msg := fmt.Sprintf("Declare missing method %s.%s", si.Receiver.Obj().Name(), si.MethodName)
338338
req.addApplyFixAction(msg, fixMissingCalledFunction, req.loc)
339339
} else {
340-
// Offer a "Declare missing field T.f" code action.
340+
// Offer a "Declare missing field T.f" code action AND "Declare missing method T.f" as specified above
341341
// See [stubMissingStructFieldFixer] for command implementation.
342342
fi := stubmethods.GetFieldStubInfo(req.pkg.FileSet(), info, path)
343343
if fi != nil {
344344
msg := fmt.Sprintf("Declare missing struct field %s.%s", fi.Named.Obj().Name(), fi.Expr.Sel.Name)
345345
req.addApplyFixAction(msg, fixMissingStructField, req.loc)
346+
347+
// undeclared field might be a method
348+
msg = fmt.Sprintf("Declare missing method %s.%s", fi.Named.Obj().Name(), fi.Expr.Sel.Name)
349+
req.addApplyFixAction(msg, fixMissingCalledFunction, req.loc)
346350
}
347351
}
348352

gopls/internal/golang/stub.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ func insertStructField(ctx context.Context, snapshot *cache.Snapshot, mp *metada
204204
var structType *ast.StructType
205205
ast.Inspect(declPGF.File, func(n ast.Node) bool {
206206
if typeSpec, ok := n.(*ast.TypeSpec); ok {
207-
if typeSpec.Name.Name == fieldInfo.Object.Name() {
207+
if typeSpec.Name.Name == fieldInfo.Named.Obj().Name() {
208208
if st, ok := typeSpec.Type.(*ast.StructType); ok {
209209
structType = st
210210
return false

gopls/internal/golang/stubmethods/stubcalledfunc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func GetCallStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node, pos
4747
// If recvExpr is a package name, compiler error would be
4848
// e.g., "undefined: http.bar", thus will not hit this code path.
4949
recvExpr := s.X
50-
recvType, pointer := concreteType(recvExpr, info)
50+
recvType, pointer := concreteType(info, recvExpr)
5151

5252
if recvType == nil || recvType.Obj().Pkg() == nil {
5353
return nil

gopls/internal/golang/stubmethods/stubmethods.go

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ func fromCallExpr(fset *token.FileSet, info *types.Info, pos token.Pos, call *as
218218
return nil
219219
}
220220

221-
concType, pointer := concreteType(arg, info)
221+
concType, pointer := concreteType(info, arg)
222222
if concType == nil || concType.Obj().Pkg() == nil {
223223
return nil
224224
}
@@ -272,7 +272,7 @@ func fromReturnStmt(fset *token.FileSet, info *types.Info, pos token.Pos, path [
272272
return nil, fmt.Errorf("pos %d not within return statement bounds: [%d-%d]", pos, ret.Pos(), ret.End())
273273
}
274274

275-
concType, pointer := concreteType(ret.Results[returnIdx], info)
275+
concType, pointer := concreteType(info, ret.Results[returnIdx])
276276
if concType == nil || concType.Obj().Pkg() == nil {
277277
return nil, nil // result is not a named or *named or alias thereof
278278
}
@@ -330,7 +330,7 @@ func fromValueSpec(fset *token.FileSet, info *types.Info, spec *ast.ValueSpec, p
330330
ifaceNode = call.Fun
331331
rhs = call.Args[0]
332332
}
333-
concType, pointer := concreteType(rhs, info)
333+
concType, pointer := concreteType(info, rhs)
334334
if concType == nil || concType.Obj().Pkg() == nil {
335335
return nil
336336
}
@@ -385,7 +385,7 @@ func fromAssignStmt(fset *token.FileSet, info *types.Info, assign *ast.AssignStm
385385
if ifaceObj == nil {
386386
return nil
387387
}
388-
concType, pointer := concreteType(rhs, info)
388+
concType, pointer := concreteType(info, rhs)
389389
if concType == nil || concType.Obj().Pkg() == nil {
390390
return nil
391391
}
@@ -434,7 +434,7 @@ func ifaceObjFromType(t types.Type) *types.TypeName {
434434
// method will return a nil *types.Named. The second return parameter
435435
// is a boolean that indicates whether the concreteType was defined as a
436436
// pointer or value.
437-
func concreteType(e ast.Expr, info *types.Info) (*types.Named, bool) {
437+
func concreteType(info *types.Info, e ast.Expr) (*types.Named, bool) {
438438
tv, ok := info.Types[e]
439439
if !ok {
440440
return nil, false
@@ -451,6 +451,7 @@ func concreteType(e ast.Expr, info *types.Info) (*types.Named, bool) {
451451
return named, isPtr
452452
}
453453

454+
// GetFieldStubInfo creates a StructFieldInfo instance to generate a struct field in a given SelectorExpr
454455
func GetFieldStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node) *StructFieldInfo {
455456
for _, node := range path {
456457
s, ok := node.(*ast.SelectorExpr)
@@ -460,43 +461,25 @@ func GetFieldStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node) *S
460461
// If recvExpr is a package name, compiler error would be
461462
// e.g., "undefined: http.bar", thus will not hit this code path.
462463
recvExpr := s.X
463-
recvType, _ := concreteType(recvExpr, info)
464+
recvNamed, _ := concreteType(info, recvExpr)
464465

465-
if recvType == nil || recvType.Obj().Pkg() == nil {
466+
if recvNamed == nil || recvNamed.Obj().Pkg() == nil {
466467
return nil
467468
}
468469

469-
// A method of a function-local type cannot be stubbed
470-
// since there's nowhere to put the methods.
471-
recv := recvType.Obj()
472-
if recv.Parent() != recv.Pkg().Scope() {
473-
return nil
474-
}
475-
476-
obj := types.Object(recv)
477-
tv, ok := info.Types[s.X]
478-
if !ok {
479-
break
480-
}
481-
482-
named, ok := tv.Type.(*types.Named)
483-
if !ok {
484-
break
485-
}
486-
487-
structType, ok := named.Underlying().(*types.Struct)
470+
structType, ok := recvNamed.Underlying().(*types.Struct)
488471
if !ok {
489472
break
490473
}
491474

475+
// Have: x.f where x has a named struct type.
492476
return &StructFieldInfo{
493-
Fset: fset,
494-
Expr: s,
495-
Struct: structType,
496-
Named: named,
497-
Info: info,
498-
Path: path,
499-
Object: obj,
477+
Fset: fset,
478+
Expr: s,
479+
Named: recvNamed,
480+
info: info,
481+
path: path,
482+
structType: structType,
500483
}
501484
}
502485

@@ -505,13 +488,18 @@ func GetFieldStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node) *S
505488

506489
// StructFieldInfo describes f field in x.f where x has a named struct type
507490
type StructFieldInfo struct {
508-
Fset *token.FileSet
509-
Expr *ast.SelectorExpr
510-
Struct *types.Struct
511-
Named *types.Named
512-
Info *types.Info
513-
Path []ast.Node
514-
Object types.Object
491+
// Fset is a file set to provide a file where the struct field is accessed
492+
Fset *token.FileSet
493+
// Expr is a selector expression
494+
Expr *ast.SelectorExpr
495+
// Named is a selected struct type
496+
Named *types.Named
497+
498+
info *types.Info
499+
// path is a node path to SelectorExpr
500+
path []ast.Node
501+
// structType is an underlying struct type, makes sure the receiver is a struct
502+
structType *types.Struct
515503
}
516504

517505
// Emit writes to out the missing field based on type info.
@@ -521,22 +509,18 @@ func (si *StructFieldInfo) Emit(out *bytes.Buffer, qual types.Qualifier) error {
521509
}
522510

523511
// Get types from context at the selector expression position
524-
typesFromContext := typesutil.TypesFromContext(si.Info, si.Path, si.Expr.Pos())
512+
typesFromContext := typesutil.TypesFromContext(si.info, si.path, si.Expr.Pos())
525513

526-
// Default to interface{} if we couldn't determine the type from context
514+
// Default to any if we couldn't determine the type from context
527515
var fieldType types.Type
528516
if len(typesFromContext) > 0 {
529517
fieldType = typesFromContext[0]
530518
} else {
531-
// Create a new interface{} type
532519
fieldType = types.Universe.Lookup("any").Type()
533520
}
534521

535-
out.Write([]byte{'\n', '\t'})
536-
out.WriteString(si.Expr.Sel.Name)
537-
out.WriteByte(' ')
538-
out.WriteString(types.TypeString(fieldType, qual))
539-
if si.Struct.NumFields() == 0 {
522+
fmt.Fprintf(out, "\n\t%s %s", si.Expr.Sel.Name, types.TypeString(fieldType, qual))
523+
if si.structType.NumFields() == 0 {
540524
out.WriteByte('\n')
541525
}
542526
return nil

gopls/internal/util/typesutil/typesutil.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,11 @@ func TypesFromContext(info *types.Info, path []ast.Node, pos token.Pos) []types.
6262

6363
switch parent := parent.(type) {
6464
case *ast.AssignStmt:
65-
right := false
66-
for _, rhs := range parent.Rhs {
67-
if astutil.NodeContains(rhs, pos) {
68-
right = true
69-
break
70-
}
71-
}
65+
right := pos > parent.TokPos
7266
if right {
73-
typs = append(typs, typeFromExprAssignExpr(parent.Rhs, parent.Lhs, info, path, pos, validType)...)
67+
typs = append(typs, typeFromExprAssignExpr(parent.Rhs, parent.Lhs, info, pos, validType)...)
7468
} else {
75-
typs = append(typs, typeFromExprAssignExpr(parent.Lhs, parent.Rhs, info, path, pos, validType)...)
69+
typs = append(typs, typeFromExprAssignExpr(parent.Lhs, parent.Rhs, info, pos, validType)...)
7670
}
7771
case *ast.ValueSpec:
7872
if len(parent.Values) == 1 {
@@ -234,7 +228,10 @@ func EnclosingSignature(path []ast.Node, info *types.Info) *types.Signature {
234228
return nil
235229
}
236230

237-
func typeFromExprAssignExpr(exprs, opposites []ast.Expr, info *types.Info, path []ast.Node, pos token.Pos, validType func(t types.Type) types.Type) []types.Type {
231+
// typeFromExprAssignExpr extracts a type from a given expression
232+
// f.x = v
233+
// where v - a value which type the function extracts
234+
func typeFromExprAssignExpr(exprs, opposites []ast.Expr, info *types.Info, pos token.Pos, validType func(t types.Type) types.Type) []types.Type {
238235
typs := make([]types.Type, 0)
239236
// Append all lhs's type
240237
if len(exprs) == 1 {
@@ -250,7 +247,7 @@ func typeFromExprAssignExpr(exprs, opposites []ast.Expr, info *types.Info, path
250247
}
251248
// Append corresponding index of lhs's type
252249
for i := range exprs {
253-
if exprs[i].Pos() <= pos && pos <= exprs[i].End() {
250+
if astutil.NodeContains(exprs[i], pos) {
254251
t := info.TypeOf(opposites[i])
255252
typs = append(typs, validType(t))
256253
break

0 commit comments

Comments
 (0)