Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename Symbol - support rename variable name and function name at gop #119

Open
wants to merge 1 commit into
base: goplus
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions gopls/internal/lsp/source/rename_check_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,50 @@ func gopForEachLexicalRef(pkg Package, obj types.Object, fn func(id *ast.Ident,
return ok
}

// isLocal reports whether obj is local to some function.
// Precondition: not a struct field or interface method.
func isGopLocal(obj types.Object) bool {
var depth int
for scope := obj.Parent(); scope != nil; scope = scope.Parent() {
depth++
}
return depth >= 3
}

func isGopPackageLevel(obj types.Object) bool {
if obj == nil {
return false
}
// goxls: fast version
return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}

// check performs safety checks of the renaming of the 'from' object to r.to.
func (r *renamer) gopCheck(from types.Object) {
if r.objsToUpdate[from] {
return
}
r.objsToUpdate[from] = true

// NB: order of conditions is important.
if from_, ok := from.(*types.PkgName); ok {
r.checkInFileBlock(from_)
} else if from_, ok := from.(*types.Label); ok {
r.checkLabel(from_)
} else if isGopPackageLevel(from) {
r.checkInPackageBlock(from)
} else if v, ok := from.(*types.Var); ok && v.IsField() {
r.checkStructField(v)
} else if f, ok := from.(*types.Func); ok && recv(f) != nil {
r.checkMethod(f)
} else if isGopLocal(from) {
r.checkInLexicalScope(from)
} else {
r.errorf(from.Pos(), "unexpected %s object %q (please report a bug)\n",
objectKind(from), from)
}
}

func (r *renamer) gopCheckExport(id *ast.Ident, pkg *types.Package, from types.Object) bool {
// Reject cross-package references if r.to is unexported.
// (Such references may be qualified identifiers or field/method
Expand Down
62 changes: 61 additions & 1 deletion gopls/internal/lsp/source/rename_gox.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import (

"github.com/goplus/gop/ast"
"github.com/goplus/gop/token"
xlog "github.com/qiniu/x/log"
"golang.org/x/tools/go/types/objectpath"
"golang.org/x/tools/gop/ast/astutil"
"golang.org/x/tools/gopls/internal/bug"
"golang.org/x/tools/gopls/internal/goxls"
"golang.org/x/tools/gopls/internal/goxls/parserutil"
"golang.org/x/tools/gopls/internal/lsp/protocol"
"golang.org/x/tools/gopls/internal/lsp/safetoken"
Expand Down Expand Up @@ -306,7 +308,7 @@ func gopRenameOrdinary(ctx context.Context, snapshot Snapshot, f FileHandle, pp
for obj := range targets {
objects = append(objects, obj)
}
editMap, _, err := renameObjects(ctx, snapshot, newName, pkg, objects...)
editMap, _, err := gopRenameObjects(ctx, snapshot, newName, pkg, objects...)
return editMap, err
}

Expand Down Expand Up @@ -367,6 +369,64 @@ func gopRenameOrdinary(ctx context.Context, snapshot Snapshot, f FileHandle, pp
return renameExported(ctx, snapshot, pkgs, declPkgPath, declObjPath, newName)
}

// renameObjects computes the edits to the type-checked syntax package pkg
// required to rename a set of target objects to newName.
//
// It also returns the set of objects that were found (due to
// corresponding methods and embedded fields) to require renaming as a
// consequence of the requested renamings.
//
// It returns an error if the renaming would cause a conflict.
func gopRenameObjects(ctx context.Context, snapshot Snapshot, newName string, pkg Package, targets ...types.Object) (map[span.URI][]diff.Edit, map[types.Object]bool, error) {
r := renamer{
pkg: pkg,
objsToUpdate: make(map[types.Object]bool),
from: targets[0].Name(),
to: newName,
}

// A renaming initiated at an interface method indicates the
// intention to rename abstract and concrete methods as needed
// to preserve assignability.
// TODO(adonovan): pull this into the caller.
for _, obj := range targets {
if obj, ok := obj.(*types.Func); ok {
recv := obj.Type().(*types.Signature).Recv()
if recv != nil && types.IsInterface(recv.Type().Underlying()) {
r.changeMethods = true
break
}
}
}

// Check that the renaming of the identifier is ok.
for _, obj := range targets {
if goxls.DbgRename {
xlog.Printf("renameObjects: %T, %s => %s\n", obj, r.from, r.to)
}
r.gopCheck(obj)
if len(r.conflicts) > 0 {
if goxls.DbgRename {
xlog.Println("renameObjects:", r.conflicts)
xlog.SingleStack()
}
// Stop at first error.
return nil, nil, fmt.Errorf("%s", strings.Join(r.conflicts, "\n"))
}
}

editMap, err := r.update()
if err != nil {
return nil, nil, err
}

// Remove initial targets so that only 'consequences' remain.
for _, obj := range targets {
delete(r.objsToUpdate, obj)
}
return editMap, r.objsToUpdate, nil
}

// gopRenamePackageName renames package declarations, imports, and go.mod files.
func gopRenamePackageName(ctx context.Context, s Snapshot, f FileHandle, newName PackageName) (map[span.URI][]diff.Edit, error) {
log.Panicln("todo: Go+ files")
Expand Down
Loading