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 / go / buildutil / util.go
1 // Copyright 2014 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 buildutil
6
7 import (
8         "fmt"
9         "go/ast"
10         "go/build"
11         "go/parser"
12         "go/token"
13         "io"
14         "io/ioutil"
15         "os"
16         "path"
17         "path/filepath"
18         "strings"
19 )
20
21 // ParseFile behaves like parser.ParseFile,
22 // but uses the build context's file system interface, if any.
23 //
24 // If file is not absolute (as defined by IsAbsPath), the (dir, file)
25 // components are joined using JoinPath; dir must be absolute.
26 //
27 // The displayPath function, if provided, is used to transform the
28 // filename that will be attached to the ASTs.
29 //
30 // TODO(adonovan): call this from go/loader.parseFiles when the tree thaws.
31 //
32 func ParseFile(fset *token.FileSet, ctxt *build.Context, displayPath func(string) string, dir string, file string, mode parser.Mode) (*ast.File, error) {
33         if !IsAbsPath(ctxt, file) {
34                 file = JoinPath(ctxt, dir, file)
35         }
36         rd, err := OpenFile(ctxt, file)
37         if err != nil {
38                 return nil, err
39         }
40         defer rd.Close() // ignore error
41         if displayPath != nil {
42                 file = displayPath(file)
43         }
44         return parser.ParseFile(fset, file, rd, mode)
45 }
46
47 // ContainingPackage returns the package containing filename.
48 //
49 // If filename is not absolute, it is interpreted relative to working directory dir.
50 // All I/O is via the build context's file system interface, if any.
51 //
52 // The '...Files []string' fields of the resulting build.Package are not
53 // populated (build.FindOnly mode).
54 //
55 func ContainingPackage(ctxt *build.Context, dir, filename string) (*build.Package, error) {
56         if !IsAbsPath(ctxt, filename) {
57                 filename = JoinPath(ctxt, dir, filename)
58         }
59
60         // We must not assume the file tree uses
61         // "/" always,
62         // `\` always,
63         // or os.PathSeparator (which varies by platform),
64         // but to make any progress, we are forced to assume that
65         // paths will not use `\` unless the PathSeparator
66         // is also `\`, thus we can rely on filepath.ToSlash for some sanity.
67
68         dirSlash := path.Dir(filepath.ToSlash(filename)) + "/"
69
70         // We assume that no source root (GOPATH[i] or GOROOT) contains any other.
71         for _, srcdir := range ctxt.SrcDirs() {
72                 srcdirSlash := filepath.ToSlash(srcdir) + "/"
73                 if importPath, ok := HasSubdir(ctxt, srcdirSlash, dirSlash); ok {
74                         return ctxt.Import(importPath, dir, build.FindOnly)
75                 }
76         }
77
78         return nil, fmt.Errorf("can't find package containing %s", filename)
79 }
80
81 // -- Effective methods of file system interface -------------------------
82
83 // (go/build.Context defines these as methods, but does not export them.)
84
85 // hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
86 // the local file system to answer the question.
87 func HasSubdir(ctxt *build.Context, root, dir string) (rel string, ok bool) {
88         if f := ctxt.HasSubdir; f != nil {
89                 return f(root, dir)
90         }
91
92         // Try using paths we received.
93         if rel, ok = hasSubdir(root, dir); ok {
94                 return
95         }
96
97         // Try expanding symlinks and comparing
98         // expanded against unexpanded and
99         // expanded against expanded.
100         rootSym, _ := filepath.EvalSymlinks(root)
101         dirSym, _ := filepath.EvalSymlinks(dir)
102
103         if rel, ok = hasSubdir(rootSym, dir); ok {
104                 return
105         }
106         if rel, ok = hasSubdir(root, dirSym); ok {
107                 return
108         }
109         return hasSubdir(rootSym, dirSym)
110 }
111
112 func hasSubdir(root, dir string) (rel string, ok bool) {
113         const sep = string(filepath.Separator)
114         root = filepath.Clean(root)
115         if !strings.HasSuffix(root, sep) {
116                 root += sep
117         }
118
119         dir = filepath.Clean(dir)
120         if !strings.HasPrefix(dir, root) {
121                 return "", false
122         }
123
124         return filepath.ToSlash(dir[len(root):]), true
125 }
126
127 // FileExists returns true if the specified file exists,
128 // using the build context's file system interface.
129 func FileExists(ctxt *build.Context, path string) bool {
130         if ctxt.OpenFile != nil {
131                 r, err := ctxt.OpenFile(path)
132                 if err != nil {
133                         return false
134                 }
135                 r.Close() // ignore error
136                 return true
137         }
138         _, err := os.Stat(path)
139         return err == nil
140 }
141
142 // OpenFile behaves like os.Open,
143 // but uses the build context's file system interface, if any.
144 func OpenFile(ctxt *build.Context, path string) (io.ReadCloser, error) {
145         if ctxt.OpenFile != nil {
146                 return ctxt.OpenFile(path)
147         }
148         return os.Open(path)
149 }
150
151 // IsAbsPath behaves like filepath.IsAbs,
152 // but uses the build context's file system interface, if any.
153 func IsAbsPath(ctxt *build.Context, path string) bool {
154         if ctxt.IsAbsPath != nil {
155                 return ctxt.IsAbsPath(path)
156         }
157         return filepath.IsAbs(path)
158 }
159
160 // JoinPath behaves like filepath.Join,
161 // but uses the build context's file system interface, if any.
162 func JoinPath(ctxt *build.Context, path ...string) string {
163         if ctxt.JoinPath != nil {
164                 return ctxt.JoinPath(path...)
165         }
166         return filepath.Join(path...)
167 }
168
169 // IsDir behaves like os.Stat plus IsDir,
170 // but uses the build context's file system interface, if any.
171 func IsDir(ctxt *build.Context, path string) bool {
172         if ctxt.IsDir != nil {
173                 return ctxt.IsDir(path)
174         }
175         fi, err := os.Stat(path)
176         return err == nil && fi.IsDir()
177 }
178
179 // ReadDir behaves like ioutil.ReadDir,
180 // but uses the build context's file system interface, if any.
181 func ReadDir(ctxt *build.Context, path string) ([]os.FileInfo, error) {
182         if ctxt.ReadDir != nil {
183                 return ctxt.ReadDir(path)
184         }
185         return ioutil.ReadDir(path)
186 }
187
188 // SplitPathList behaves like filepath.SplitList,
189 // but uses the build context's file system interface, if any.
190 func SplitPathList(ctxt *build.Context, s string) []string {
191         if ctxt.SplitPathList != nil {
192                 return ctxt.SplitPathList(s)
193         }
194         return filepath.SplitList(s)
195 }
196
197 // sameFile returns true if x and y have the same basename and denote
198 // the same file.
199 //
200 func sameFile(x, y string) bool {
201         if path.Clean(x) == path.Clean(y) {
202                 return true
203         }
204         if filepath.Base(x) == filepath.Base(y) { // (optimisation)
205                 if xi, err := os.Stat(x); err == nil {
206                         if yi, err := os.Stat(y); err == nil {
207                                 return os.SameFile(xi, yi)
208                         }
209                 }
210         }
211         return false
212 }