Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / refactor / rename / spec.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/refactor/rename/spec.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/refactor/rename/spec.go
new file mode 100644 (file)
index 0000000..0c4526d
--- /dev/null
@@ -0,0 +1,593 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package rename
+
+// This file contains logic related to specifying a renaming: parsing of
+// the flags as a form of query, and finding the object(s) it denotes.
+// See Usage for flag details.
+
+import (
+       "bytes"
+       "fmt"
+       "go/ast"
+       "go/build"
+       "go/parser"
+       "go/token"
+       "go/types"
+       "log"
+       "os"
+       "path/filepath"
+       "regexp"
+       "strconv"
+       "strings"
+
+       "golang.org/x/tools/go/buildutil"
+       "golang.org/x/tools/go/loader"
+)
+
+// A spec specifies an entity to rename.
+//
+// It is populated from an -offset flag or -from query;
+// see Usage for the allowed -from query forms.
+//
+type spec struct {
+       // pkg is the package containing the position
+       // specified by the -from or -offset flag.
+       // If filename == "", our search for the 'from' entity
+       // is restricted to this package.
+       pkg string
+
+       // The original name of the entity being renamed.
+       // If the query had a ::from component, this is that;
+       // otherwise it's the last segment, e.g.
+       //   (encoding/json.Decoder).from
+       //   encoding/json.from
+       fromName string
+
+       // -- The remaining fields are private to this file.  All are optional. --
+
+       // The query's ::x suffix, if any.
+       searchFor string
+
+       // e.g. "Decoder" in "(encoding/json.Decoder).fieldOrMethod"
+       //                or "encoding/json.Decoder
+       pkgMember string
+
+       // e.g. fieldOrMethod in "(encoding/json.Decoder).fieldOrMethod"
+       typeMember string
+
+       // Restricts the query to this file.
+       // Implied by -from="file.go::x" and -offset flags.
+       filename string
+
+       // Byte offset of the 'from' identifier within the file named 'filename'.
+       // -offset mode only.
+       offset int
+}
+
+// parseFromFlag interprets the "-from" flag value as a renaming specification.
+// See Usage in rename.go for valid formats.
+func parseFromFlag(ctxt *build.Context, fromFlag string) (*spec, error) {
+       var spec spec
+       var main string // sans "::x" suffix
+       switch parts := strings.Split(fromFlag, "::"); len(parts) {
+       case 1:
+               main = parts[0]
+       case 2:
+               main = parts[0]
+               spec.searchFor = parts[1]
+               if parts[1] == "" {
+                       // error
+               }
+       default:
+               return nil, fmt.Errorf("-from %q: invalid identifier specification (see -help for formats)", fromFlag)
+       }
+
+       if strings.HasSuffix(main, ".go") {
+               // main is "filename.go"
+               if spec.searchFor == "" {
+                       return nil, fmt.Errorf("-from: filename %q must have a ::name suffix", main)
+               }
+               spec.filename = main
+               if !buildutil.FileExists(ctxt, spec.filename) {
+                       return nil, fmt.Errorf("no such file: %s", spec.filename)
+               }
+
+               bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
+               if err != nil {
+                       return nil, err
+               }
+               spec.pkg = bp.ImportPath
+
+       } else {
+               // main is one of:
+               //  "importpath"
+               //  "importpath".member
+               //  (*"importpath".type).fieldormethod           (parens and star optional)
+               if err := parseObjectSpec(&spec, main); err != nil {
+                       return nil, err
+               }
+       }
+
+       if spec.searchFor != "" {
+               spec.fromName = spec.searchFor
+       }
+
+       cwd, err := os.Getwd()
+       if err != nil {
+               return nil, err
+       }
+
+       // Sanitize the package.
+       bp, err := ctxt.Import(spec.pkg, cwd, build.FindOnly)
+       if err != nil {
+               return nil, fmt.Errorf("can't find package %q", spec.pkg)
+       }
+       spec.pkg = bp.ImportPath
+
+       if !isValidIdentifier(spec.fromName) {
+               return nil, fmt.Errorf("-from: invalid identifier %q", spec.fromName)
+       }
+
+       if Verbose {
+               log.Printf("-from spec: %+v", spec)
+       }
+
+       return &spec, nil
+}
+
+// parseObjectSpec parses main as one of the non-filename forms of
+// object specification.
+func parseObjectSpec(spec *spec, main string) error {
+       // Parse main as a Go expression, albeit a strange one.
+       e, _ := parser.ParseExpr(main)
+
+       if pkg := parseImportPath(e); pkg != "" {
+               // e.g. bytes or "encoding/json": a package
+               spec.pkg = pkg
+               if spec.searchFor == "" {
+                       return fmt.Errorf("-from %q: package import path %q must have a ::name suffix",
+                               main, main)
+               }
+               return nil
+       }
+
+       if e, ok := e.(*ast.SelectorExpr); ok {
+               x := unparen(e.X)
+
+               // Strip off star constructor, if any.
+               if star, ok := x.(*ast.StarExpr); ok {
+                       x = star.X
+               }
+
+               if pkg := parseImportPath(x); pkg != "" {
+                       // package member e.g. "encoding/json".HTMLEscape
+                       spec.pkg = pkg              // e.g. "encoding/json"
+                       spec.pkgMember = e.Sel.Name // e.g. "HTMLEscape"
+                       spec.fromName = e.Sel.Name
+                       return nil
+               }
+
+               if x, ok := x.(*ast.SelectorExpr); ok {
+                       // field/method of type e.g. ("encoding/json".Decoder).Decode
+                       y := unparen(x.X)
+                       if pkg := parseImportPath(y); pkg != "" {
+                               spec.pkg = pkg               // e.g. "encoding/json"
+                               spec.pkgMember = x.Sel.Name  // e.g. "Decoder"
+                               spec.typeMember = e.Sel.Name // e.g. "Decode"
+                               spec.fromName = e.Sel.Name
+                               return nil
+                       }
+               }
+       }
+
+       return fmt.Errorf("-from %q: invalid expression", main)
+}
+
+// parseImportPath returns the import path of the package denoted by e.
+// Any import path may be represented as a string literal;
+// single-segment import paths (e.g. "bytes") may also be represented as
+// ast.Ident.  parseImportPath returns "" for all other expressions.
+func parseImportPath(e ast.Expr) string {
+       switch e := e.(type) {
+       case *ast.Ident:
+               return e.Name // e.g. bytes
+
+       case *ast.BasicLit:
+               if e.Kind == token.STRING {
+                       pkgname, _ := strconv.Unquote(e.Value)
+                       return pkgname // e.g. "encoding/json"
+               }
+       }
+       return ""
+}
+
+// parseOffsetFlag interprets the "-offset" flag value as a renaming specification.
+func parseOffsetFlag(ctxt *build.Context, offsetFlag string) (*spec, error) {
+       var spec spec
+       // Validate -offset, e.g. file.go:#123
+       parts := strings.Split(offsetFlag, ":#")
+       if len(parts) != 2 {
+               return nil, fmt.Errorf("-offset %q: invalid offset specification", offsetFlag)
+       }
+
+       spec.filename = parts[0]
+       if !buildutil.FileExists(ctxt, spec.filename) {
+               return nil, fmt.Errorf("no such file: %s", spec.filename)
+       }
+
+       bp, err := buildutil.ContainingPackage(ctxt, wd, spec.filename)
+       if err != nil {
+               return nil, err
+       }
+       spec.pkg = bp.ImportPath
+
+       for _, r := range parts[1] {
+               if !isDigit(r) {
+                       return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
+               }
+       }
+       spec.offset, err = strconv.Atoi(parts[1])
+       if err != nil {
+               return nil, fmt.Errorf("-offset %q: non-numeric offset", offsetFlag)
+       }
+
+       // Parse the file and check there's an identifier at that offset.
+       fset := token.NewFileSet()
+       f, err := buildutil.ParseFile(fset, ctxt, nil, wd, spec.filename, parser.ParseComments)
+       if err != nil {
+               return nil, fmt.Errorf("-offset %q: cannot parse file: %s", offsetFlag, err)
+       }
+
+       id := identAtOffset(fset, f, spec.offset)
+       if id == nil {
+               return nil, fmt.Errorf("-offset %q: no identifier at this position", offsetFlag)
+       }
+
+       spec.fromName = id.Name
+
+       return &spec, nil
+}
+
+var wd = func() string {
+       wd, err := os.Getwd()
+       if err != nil {
+               panic("cannot get working directory: " + err.Error())
+       }
+       return wd
+}()
+
+// For source trees built with 'go build', the -from or -offset
+// spec identifies exactly one initial 'from' object to rename ,
+// but certain proprietary build systems allow a single file to
+// appear in multiple packages (e.g. the test package contains a
+// copy of its library), so there may be multiple objects for
+// the same source entity.
+
+func findFromObjects(iprog *loader.Program, spec *spec) ([]types.Object, error) {
+       if spec.filename != "" {
+               return findFromObjectsInFile(iprog, spec)
+       }
+
+       // Search for objects defined in specified package.
+
+       // TODO(adonovan): the iprog.ImportMap has an entry {"main": ...}
+       // for main packages, even though that's not an import path.
+       // Seems like a bug.
+       //
+       // pkg := iprog.ImportMap[spec.pkg]
+       // if pkg == nil {
+       //      return fmt.Errorf("cannot find package %s", spec.pkg) // can't happen?
+       // }
+       // info := iprog.AllPackages[pkg]
+
+       // Workaround: lookup by value.
+       var info *loader.PackageInfo
+       var pkg *types.Package
+       for pkg, info = range iprog.AllPackages {
+               if pkg.Path() == spec.pkg {
+                       break
+               }
+       }
+       if info == nil {
+               return nil, fmt.Errorf("package %q was not loaded", spec.pkg)
+       }
+
+       objects, err := findObjects(info, spec)
+       if err != nil {
+               return nil, err
+       }
+       if len(objects) > 1 {
+               // ambiguous "*" scope query
+               return nil, ambiguityError(iprog.Fset, objects)
+       }
+       return objects, nil
+}
+
+func findFromObjectsInFile(iprog *loader.Program, spec *spec) ([]types.Object, error) {
+       var fromObjects []types.Object
+       for _, info := range iprog.AllPackages {
+               // restrict to specified filename
+               // NB: under certain proprietary build systems, a given
+               // filename may appear in multiple packages.
+               for _, f := range info.Files {
+                       thisFile := iprog.Fset.File(f.Pos())
+                       if !sameFile(thisFile.Name(), spec.filename) {
+                               continue
+                       }
+                       // This package contains the query file.
+
+                       if spec.offset != 0 {
+                               // We cannot refactor generated files since position information is invalidated.
+                               if generated(f, thisFile) {
+                                       return nil, fmt.Errorf("cannot rename identifiers in generated file containing DO NOT EDIT marker: %s", thisFile.Name())
+                               }
+
+                               // Search for a specific ident by file/offset.
+                               id := identAtOffset(iprog.Fset, f, spec.offset)
+                               if id == nil {
+                                       // can't happen?
+                                       return nil, fmt.Errorf("identifier not found")
+                               }
+                               obj := info.Uses[id]
+                               if obj == nil {
+                                       obj = info.Defs[id]
+                                       if obj == nil {
+                                               // Ident without Object.
+
+                                               // Package clause?
+                                               pos := thisFile.Pos(spec.offset)
+                                               _, path, _ := iprog.PathEnclosingInterval(pos, pos)
+                                               if len(path) == 2 { // [Ident File]
+                                                       // TODO(adonovan): support this case.
+                                                       return nil, fmt.Errorf("cannot rename %q: renaming package clauses is not yet supported",
+                                                               path[1].(*ast.File).Name.Name)
+                                               }
+
+                                               // Implicit y in "switch y := x.(type) {"?
+                                               if obj := typeSwitchVar(&info.Info, path); obj != nil {
+                                                       return []types.Object{obj}, nil
+                                               }
+
+                                               // Probably a type error.
+                                               return nil, fmt.Errorf("cannot find object for %q", id.Name)
+                                       }
+                               }
+                               if obj.Pkg() == nil {
+                                       return nil, fmt.Errorf("cannot rename predeclared identifiers (%s)", obj)
+
+                               }
+
+                               fromObjects = append(fromObjects, obj)
+                       } else {
+                               // do a package-wide query
+                               objects, err := findObjects(info, spec)
+                               if err != nil {
+                                       return nil, err
+                               }
+
+                               // filter results: only objects defined in thisFile
+                               var filtered []types.Object
+                               for _, obj := range objects {
+                                       if iprog.Fset.File(obj.Pos()) == thisFile {
+                                               filtered = append(filtered, obj)
+                                       }
+                               }
+                               if len(filtered) == 0 {
+                                       return nil, fmt.Errorf("no object %q declared in file %s",
+                                               spec.fromName, spec.filename)
+                               } else if len(filtered) > 1 {
+                                       return nil, ambiguityError(iprog.Fset, filtered)
+                               }
+                               fromObjects = append(fromObjects, filtered[0])
+                       }
+                       break
+               }
+       }
+       if len(fromObjects) == 0 {
+               // can't happen?
+               return nil, fmt.Errorf("file %s was not part of the loaded program", spec.filename)
+       }
+       return fromObjects, nil
+}
+
+func typeSwitchVar(info *types.Info, path []ast.Node) types.Object {
+       if len(path) > 3 {
+               // [Ident AssignStmt TypeSwitchStmt...]
+               if sw, ok := path[2].(*ast.TypeSwitchStmt); ok {
+                       // choose the first case.
+                       if len(sw.Body.List) > 0 {
+                               obj := info.Implicits[sw.Body.List[0].(*ast.CaseClause)]
+                               if obj != nil {
+                                       return obj
+                               }
+                       }
+               }
+       }
+       return nil
+}
+
+// On success, findObjects returns the list of objects named
+// spec.fromName matching the spec.  On success, the result has exactly
+// one element unless spec.searchFor!="", in which case it has at least one
+// element.
+//
+func findObjects(info *loader.PackageInfo, spec *spec) ([]types.Object, error) {
+       if spec.pkgMember == "" {
+               if spec.searchFor == "" {
+                       panic(spec)
+               }
+               objects := searchDefs(&info.Info, spec.searchFor)
+               if objects == nil {
+                       return nil, fmt.Errorf("no object %q declared in package %q",
+                               spec.searchFor, info.Pkg.Path())
+               }
+               return objects, nil
+       }
+
+       pkgMember := info.Pkg.Scope().Lookup(spec.pkgMember)
+       if pkgMember == nil {
+               return nil, fmt.Errorf("package %q has no member %q",
+                       info.Pkg.Path(), spec.pkgMember)
+       }
+
+       var searchFunc *types.Func
+       if spec.typeMember == "" {
+               // package member
+               if spec.searchFor == "" {
+                       return []types.Object{pkgMember}, nil
+               }
+
+               // Search within pkgMember, which must be a function.
+               searchFunc, _ = pkgMember.(*types.Func)
+               if searchFunc == nil {
+                       return nil, fmt.Errorf("cannot search for %q within %s %q",
+                               spec.searchFor, objectKind(pkgMember), pkgMember)
+               }
+       } else {
+               // field/method of type
+               // e.g. (encoding/json.Decoder).Decode
+               // or ::x within it.
+
+               tName, _ := pkgMember.(*types.TypeName)
+               if tName == nil {
+                       return nil, fmt.Errorf("%s.%s is a %s, not a type",
+                               info.Pkg.Path(), pkgMember.Name(), objectKind(pkgMember))
+               }
+
+               // search within named type.
+               obj, _, _ := types.LookupFieldOrMethod(tName.Type(), true, info.Pkg, spec.typeMember)
+               if obj == nil {
+                       return nil, fmt.Errorf("cannot find field or method %q of %s %s.%s",
+                               spec.typeMember, typeKind(tName.Type()), info.Pkg.Path(), tName.Name())
+               }
+
+               if spec.searchFor == "" {
+                       // If it is an embedded field, return the type of the field.
+                       if v, ok := obj.(*types.Var); ok && v.Anonymous() {
+                               switch t := v.Type().(type) {
+                               case *types.Pointer:
+                                       return []types.Object{t.Elem().(*types.Named).Obj()}, nil
+                               case *types.Named:
+                                       return []types.Object{t.Obj()}, nil
+                               }
+                       }
+                       return []types.Object{obj}, nil
+               }
+
+               searchFunc, _ = obj.(*types.Func)
+               if searchFunc == nil {
+                       return nil, fmt.Errorf("cannot search for local name %q within %s (%s.%s).%s; need a function",
+                               spec.searchFor, objectKind(obj), info.Pkg.Path(), tName.Name(),
+                               obj.Name())
+               }
+               if isInterface(tName.Type()) {
+                       return nil, fmt.Errorf("cannot search for local name %q within abstract method (%s.%s).%s",
+                               spec.searchFor, info.Pkg.Path(), tName.Name(), searchFunc.Name())
+               }
+       }
+
+       // -- search within function or method --
+
+       decl := funcDecl(info, searchFunc)
+       if decl == nil {
+               return nil, fmt.Errorf("cannot find syntax for %s", searchFunc) // can't happen?
+       }
+
+       var objects []types.Object
+       for _, obj := range searchDefs(&info.Info, spec.searchFor) {
+               // We use positions, not scopes, to determine whether
+               // the obj is within searchFunc.  This is clumsy, but the
+               // alternative, using the types.Scope tree, doesn't
+               // account for non-lexical objects like fields and
+               // interface methods.
+               if decl.Pos() <= obj.Pos() && obj.Pos() < decl.End() && obj != searchFunc {
+                       objects = append(objects, obj)
+               }
+       }
+       if objects == nil {
+               return nil, fmt.Errorf("no local definition of %q within %s",
+                       spec.searchFor, searchFunc)
+       }
+       return objects, nil
+}
+
+func funcDecl(info *loader.PackageInfo, fn *types.Func) *ast.FuncDecl {
+       for _, f := range info.Files {
+               for _, d := range f.Decls {
+                       if d, ok := d.(*ast.FuncDecl); ok && info.Defs[d.Name] == fn {
+                               return d
+                       }
+               }
+       }
+       return nil
+}
+
+func searchDefs(info *types.Info, name string) []types.Object {
+       var objects []types.Object
+       for id, obj := range info.Defs {
+               if obj == nil {
+                       // e.g. blank ident.
+                       // TODO(adonovan): but also implicit y in
+                       //    switch y := x.(type)
+                       // Needs some thought.
+                       continue
+               }
+               if id.Name == name {
+                       objects = append(objects, obj)
+               }
+       }
+       return objects
+}
+
+func identAtOffset(fset *token.FileSet, f *ast.File, offset int) *ast.Ident {
+       var found *ast.Ident
+       ast.Inspect(f, func(n ast.Node) bool {
+               if id, ok := n.(*ast.Ident); ok {
+                       idpos := fset.Position(id.Pos()).Offset
+                       if idpos <= offset && offset < idpos+len(id.Name) {
+                               found = id
+                       }
+               }
+               return found == nil // keep traversing only until found
+       })
+       return found
+}
+
+// ambiguityError returns an error describing an ambiguous "*" scope query.
+func ambiguityError(fset *token.FileSet, objects []types.Object) error {
+       var buf bytes.Buffer
+       for i, obj := range objects {
+               if i > 0 {
+                       buf.WriteString(", ")
+               }
+               posn := fset.Position(obj.Pos())
+               fmt.Fprintf(&buf, "%s at %s:%d:%d",
+                       objectKind(obj), filepath.Base(posn.Filename), posn.Line, posn.Column)
+       }
+       return fmt.Errorf("ambiguous specifier %s matches %s",
+               objects[0].Name(), buf.String())
+}
+
+// Matches cgo generated comment as well as the proposed standard:
+//     https://golang.org/s/generatedcode
+var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
+
+// generated reports whether ast.File is a generated file.
+func generated(f *ast.File, tokenFile *token.File) bool {
+
+       // Iterate over the comments in the file
+       for _, commentGroup := range f.Comments {
+               for _, comment := range commentGroup.List {
+                       if matched := generatedRx.MatchString(comment.Text); matched {
+                               // Check if comment is at the beginning of the line in source
+                               if pos := tokenFile.Position(comment.Slash); pos.Column == 1 {
+                                       return true
+                               }
+                       }
+               }
+       }
+       return false
+}