Skip to content

Commit

Permalink
Move GetFieldStubInfo to stubmethods package
Browse files Browse the repository at this point in the history
  • Loading branch information
dennypenta committed Jan 29, 2025
1 parent 1675eb9 commit 13999c2
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 81 deletions.
73 changes: 1 addition & 72 deletions gopls/internal/golang/codeaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package golang

import (
"bytes"
"context"
"encoding/json"
"fmt"
Expand Down Expand Up @@ -42,7 +41,6 @@ import (
//
// See ../protocol/codeactionkind.go for some code action theory.
func CodeActions(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, rng protocol.Range, diagnostics []protocol.Diagnostic, enabled func(protocol.CodeActionKind) bool, trigger protocol.CodeActionTriggerKind) (actions []protocol.CodeAction, _ error) {

loc := protocol.Location{URI: fh.URI(), Range: rng}

pgf, err := snapshot.ParseGo(ctx, fh, parsego.Full)
Expand Down Expand Up @@ -341,7 +339,7 @@ func quickFix(ctx context.Context, req *codeActionsRequest) error {
} else {
// Offer a "Declare missing field T.f" code action.
// See [stubMissingStructFieldFixer] for command implementation.
fi := GetFieldStubInfo(req.pkg.FileSet(), info, path)
fi := stubmethods.GetFieldStubInfo(req.pkg.FileSet(), info, path)
if fi != nil {
msg := fmt.Sprintf("Declare missing struct field %s.%s", fi.Named.Obj().Name(), fi.Expr.Sel.Name)
req.addApplyFixAction(msg, fixMissingStructField, req.loc)
Expand All @@ -364,75 +362,6 @@ func quickFix(ctx context.Context, req *codeActionsRequest) error {
return nil
}

func GetFieldStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node) *StructFieldInfo {
for _, node := range path {
n, ok := node.(*ast.SelectorExpr)
if !ok {
continue
}
tv, ok := info.Types[n.X]
if !ok {
break
}

named, ok := tv.Type.(*types.Named)
if !ok {
break
}

structType, ok := named.Underlying().(*types.Struct)
if !ok {
break
}

return &StructFieldInfo{
Fset: fset,
Expr: n,
Struct: structType,
Named: named,
Info: info,
Path: path,
}
}

return nil
}

type StructFieldInfo struct {
Fset *token.FileSet
Expr *ast.SelectorExpr
Struct *types.Struct
Named *types.Named
Info *types.Info
Path []ast.Node
}

// Emit writes to out the missing field based on type info.
func (si *StructFieldInfo) Emit(out *bytes.Buffer, qual types.Qualifier) error {
if si.Expr == nil || si.Expr.Sel == nil {
return fmt.Errorf("invalid selector expression")
}

// Get types from context at the selector expression position
typesFromContext := typesutil.TypesFromContext(si.Info, si.Path, si.Expr.Pos())

// Default to interface{} if we couldn't determine the type from context
var fieldType types.Type
if len(typesFromContext) > 0 && typesFromContext[0] != nil {
fieldType = typesFromContext[0]
} else {
// Create a new interface{} type
fieldType = types.NewInterfaceType(nil, nil)
}

tpl := "\n\t%s %s"
if si.Struct.NumFields() == 0 {
tpl += "\n"
}
fmt.Fprintf(out, tpl, si.Expr.Sel.Name, types.TypeString(fieldType, qual))
return nil
}

// allImportsFixesResult is the result of a lazy call to allImportsFixes.
// It implements the codeActionsRequest lazyInit interface.
type allImportsFixesResult struct {
Expand Down
12 changes: 5 additions & 7 deletions gopls/internal/golang/stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func stubMissingCalledFunctionFixer(ctx context.Context, snapshot *cache.Snapsho
// at the cursor position.
func stubMissingStructFieldFixer(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Package, pgf *parsego.File, start, end token.Pos) (*token.FileSet, *analysis.SuggestedFix, error) {
nodes, _ := astutil.PathEnclosingInterval(pgf.File, start, end)
fi := GetFieldStubInfo(pkg.FileSet(), pkg.TypesInfo(), nodes)
fi := stubmethods.GetFieldStubInfo(pkg.FileSet(), pkg.TypesInfo(), nodes)
if fi == nil {
return nil, nil, fmt.Errorf("invalid type request")
}
Expand Down Expand Up @@ -252,7 +252,7 @@ func trimVersionSuffix(path string) string {
return path
}

func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *metadata.Package, fieldInfo *StructFieldInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *metadata.Package, fieldInfo *stubmethods.StructFieldInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
if fieldInfo == nil {
return nil, nil, fmt.Errorf("no field info provided")
}
Expand Down Expand Up @@ -304,13 +304,11 @@ func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *meta
textEdit := analysis.TextEdit{
Pos: insertPos,
End: insertPos,
NewText: []byte(buf.String()),
NewText: buf.Bytes(),
}

fix := &analysis.SuggestedFix{
return fieldInfo.Fset, &analysis.SuggestedFix{
Message: fmt.Sprintf("Add field %s to struct %s", fieldInfo.Expr.Sel.Name, fieldInfo.Named.Obj().Name()),
TextEdits: []analysis.TextEdit{textEdit},
}

return fieldInfo.Fset, fix, nil
}, nil
}
72 changes: 72 additions & 0 deletions gopls/internal/golang/stubmethods/stubmethods.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,75 @@ func concreteType(e ast.Expr, info *types.Info) (*types.Named, bool) {
}
return named, isPtr
}

func GetFieldStubInfo(fset *token.FileSet, info *types.Info, path []ast.Node) *StructFieldInfo {
for _, node := range path {
n, ok := node.(*ast.SelectorExpr)
if !ok {
continue
}
tv, ok := info.Types[n.X]
if !ok {
break
}

named, ok := tv.Type.(*types.Named)
if !ok {
break
}

structType, ok := named.Underlying().(*types.Struct)
if !ok {
break
}

return &StructFieldInfo{
Fset: fset,
Expr: n,
Struct: structType,
Named: named,
Info: info,
Path: path,
}
}

return nil
}

// StructFieldInfo describes f field in x.f where x has a named struct type
type StructFieldInfo struct {
Fset *token.FileSet
Expr *ast.SelectorExpr
Struct *types.Struct
Named *types.Named
Info *types.Info
Path []ast.Node
}

// Emit writes to out the missing field based on type info.
func (si *StructFieldInfo) Emit(out *bytes.Buffer, qual types.Qualifier) error {
if si.Expr == nil || si.Expr.Sel == nil {
return fmt.Errorf("invalid selector expression")
}

// Get types from context at the selector expression position
typesFromContext := typesutil.TypesFromContext(si.Info, si.Path, si.Expr.Pos())

// Default to interface{} if we couldn't determine the type from context
var fieldType types.Type
if len(typesFromContext) > 0 {
fieldType = typesFromContext[0]
} else {
// Create a new interface{} type
fieldType = types.Universe.Lookup("any").Type()
}

out.Write([]byte{'\n', '\t'})
out.WriteString(si.Expr.Sel.Name)
out.WriteByte(' ')
out.WriteString(types.TypeString(fieldType, qual))
if si.Struct.NumFields() == 0 {
out.WriteByte('\n')
}
return nil
}
4 changes: 2 additions & 2 deletions gopls/internal/util/typesutil/typesutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ func TypesFromContext(info *types.Info, path []ast.Node, pos token.Pos) []types.
}
case *ast.SelectorExpr:
for _, n := range path {
assignExpr, ok := n.(*ast.AssignStmt)
assign, ok := n.(*ast.AssignStmt)
if ok {
for _, rh := range assignExpr.Rhs {
for _, rh := range assign.Rhs {
// basic types
basicLit, ok := rh.(*ast.BasicLit)
if ok {
Expand Down

0 comments on commit 13999c2

Please sign in to comment.