Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / cmd / guru / pos.go
1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package main
6
7 // This file defines utilities for working with file positions.
8
9 import (
10         "fmt"
11         "go/build"
12         "go/parser"
13         "go/token"
14         "os"
15         "path/filepath"
16         "strconv"
17         "strings"
18
19         "golang.org/x/tools/go/ast/astutil"
20         "golang.org/x/tools/go/buildutil"
21 )
22
23 // parseOctothorpDecimal returns the numeric value if s matches "#%d",
24 // otherwise -1.
25 func parseOctothorpDecimal(s string) int {
26         if s != "" && s[0] == '#' {
27                 if s, err := strconv.ParseInt(s[1:], 10, 32); err == nil {
28                         return int(s)
29                 }
30         }
31         return -1
32 }
33
34 // parsePos parses a string of the form "file:pos" or
35 // file:start,end" where pos, start, end match #%d and represent byte
36 // offsets, and returns its components.
37 //
38 // (Numbers without a '#' prefix are reserved for future use,
39 // e.g. to indicate line/column positions.)
40 //
41 func parsePos(pos string) (filename string, startOffset, endOffset int, err error) {
42         if pos == "" {
43                 err = fmt.Errorf("no source position specified")
44                 return
45         }
46
47         colon := strings.LastIndex(pos, ":")
48         if colon < 0 {
49                 err = fmt.Errorf("bad position syntax %q", pos)
50                 return
51         }
52         filename, offset := pos[:colon], pos[colon+1:]
53         startOffset = -1
54         endOffset = -1
55         if comma := strings.Index(offset, ","); comma < 0 {
56                 // e.g. "foo.go:#123"
57                 startOffset = parseOctothorpDecimal(offset)
58                 endOffset = startOffset
59         } else {
60                 // e.g. "foo.go:#123,#456"
61                 startOffset = parseOctothorpDecimal(offset[:comma])
62                 endOffset = parseOctothorpDecimal(offset[comma+1:])
63         }
64         if startOffset < 0 || endOffset < 0 {
65                 err = fmt.Errorf("invalid offset %q in query position", offset)
66                 return
67         }
68         return
69 }
70
71 // fileOffsetToPos translates the specified file-relative byte offsets
72 // into token.Pos form.  It returns an error if the file was not found
73 // or the offsets were out of bounds.
74 //
75 func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
76         // Range check [start..end], inclusive of both end-points.
77
78         if 0 <= startOffset && startOffset <= file.Size() {
79                 start = file.Pos(int(startOffset))
80         } else {
81                 err = fmt.Errorf("start position is beyond end of file")
82                 return
83         }
84
85         if 0 <= endOffset && endOffset <= file.Size() {
86                 end = file.Pos(int(endOffset))
87         } else {
88                 err = fmt.Errorf("end position is beyond end of file")
89                 return
90         }
91
92         return
93 }
94
95 // sameFile returns true if x and y have the same basename and denote
96 // the same file.
97 //
98 func sameFile(x, y string) bool {
99         if filepath.Base(x) == filepath.Base(y) { // (optimisation)
100                 if xi, err := os.Stat(x); err == nil {
101                         if yi, err := os.Stat(y); err == nil {
102                                 return os.SameFile(xi, yi)
103                         }
104                 }
105         }
106         return false
107 }
108
109 // fastQueryPos parses the position string and returns a queryPos.
110 // It parses only a single file and does not run the type checker.
111 func fastQueryPos(ctxt *build.Context, pos string) (*queryPos, error) {
112         filename, startOffset, endOffset, err := parsePos(pos)
113         if err != nil {
114                 return nil, err
115         }
116
117         // Parse the file, opening it the file via the build.Context
118         // so that we observe the effects of the -modified flag.
119         fset := token.NewFileSet()
120         cwd, _ := os.Getwd()
121         f, err := buildutil.ParseFile(fset, ctxt, nil, cwd, filename, parser.Mode(0))
122         // ParseFile usually returns a partial file along with an error.
123         // Only fail if there is no file.
124         if f == nil {
125                 return nil, err
126         }
127         if !f.Pos().IsValid() {
128                 return nil, fmt.Errorf("%s is not a Go source file", filename)
129         }
130
131         start, end, err := fileOffsetToPos(fset.File(f.Pos()), startOffset, endOffset)
132         if err != nil {
133                 return nil, err
134         }
135
136         path, exact := astutil.PathEnclosingInterval(f, start, end)
137         if path == nil {
138                 return nil, fmt.Errorf("no syntax here")
139         }
140
141         return &queryPos{fset, start, end, path, exact, nil}, nil
142 }