gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/kisielk/gotool/match18.go (about) 1 // Copyright (c) 2009 The Go Authors. All rights reserved. 2 // 3 // Redistribution and use in source and binary forms, with or without 4 // modification, are permitted provided that the following conditions are 5 // met: 6 // 7 // * Redistributions of source code must retain the above copyright 8 // notice, this list of conditions and the following disclaimer. 9 // * Redistributions in binary form must reproduce the above 10 // copyright notice, this list of conditions and the following disclaimer 11 // in the documentation and/or other materials provided with the 12 // distribution. 13 // * Neither the name of Google Inc. nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 // +build !go1.9 30 31 package gotool 32 33 import ( 34 "fmt" 35 "go/build" 36 "log" 37 "os" 38 "path" 39 "path/filepath" 40 "regexp" 41 "strings" 42 ) 43 44 // This file contains code from the Go distribution. 45 46 // matchPattern(pattern)(name) reports whether 47 // name matches pattern. Pattern is a limited glob 48 // pattern in which '...' means 'any string' and there 49 // is no other special syntax. 50 func matchPattern(pattern string) func(name string) bool { 51 re := regexp.QuoteMeta(pattern) 52 re = strings.Replace(re, `\.\.\.`, `.*`, -1) 53 // Special case: foo/... matches foo too. 54 if strings.HasSuffix(re, `/.*`) { 55 re = re[:len(re)-len(`/.*`)] + `(/.*)?` 56 } 57 reg := regexp.MustCompile(`^` + re + `$`) 58 return reg.MatchString 59 } 60 61 // matchPackages returns a list of package paths matching pattern 62 // (see go help packages for pattern syntax). 63 func (c *Context) matchPackages(pattern string) []string { 64 match := func(string) bool { return true } 65 treeCanMatch := func(string) bool { return true } 66 if !isMetaPackage(pattern) { 67 match = matchPattern(pattern) 68 treeCanMatch = treeCanMatchPattern(pattern) 69 } 70 71 have := map[string]bool{ 72 "builtin": true, // ignore pseudo-package that exists only for documentation 73 } 74 if !c.BuildContext.CgoEnabled { 75 have["runtime/cgo"] = true // ignore during walk 76 } 77 var pkgs []string 78 79 for _, src := range c.BuildContext.SrcDirs() { 80 if (pattern == "std" || pattern == "cmd") && src != gorootSrc { 81 continue 82 } 83 src = filepath.Clean(src) + string(filepath.Separator) 84 root := src 85 if pattern == "cmd" { 86 root += "cmd" + string(filepath.Separator) 87 } 88 filepath.Walk(root, func(path string, fi os.FileInfo, err error) error { 89 if err != nil || !fi.IsDir() || path == src { 90 return nil 91 } 92 93 // Avoid .foo, _foo, and testdata directory trees. 94 _, elem := filepath.Split(path) 95 if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" { 96 return filepath.SkipDir 97 } 98 99 name := filepath.ToSlash(path[len(src):]) 100 if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") { 101 // The name "std" is only the standard library. 102 // If the name is cmd, it's the root of the command tree. 103 return filepath.SkipDir 104 } 105 if !treeCanMatch(name) { 106 return filepath.SkipDir 107 } 108 if have[name] { 109 return nil 110 } 111 have[name] = true 112 if !match(name) { 113 return nil 114 } 115 _, err = c.BuildContext.ImportDir(path, 0) 116 if err != nil { 117 if _, noGo := err.(*build.NoGoError); noGo { 118 return nil 119 } 120 } 121 pkgs = append(pkgs, name) 122 return nil 123 }) 124 } 125 return pkgs 126 } 127 128 // importPathsNoDotExpansion returns the import paths to use for the given 129 // command line, but it does no ... expansion. 130 func (c *Context) importPathsNoDotExpansion(args []string) []string { 131 if len(args) == 0 { 132 return []string{"."} 133 } 134 var out []string 135 for _, a := range args { 136 // Arguments are supposed to be import paths, but 137 // as a courtesy to Windows developers, rewrite \ to / 138 // in command-line arguments. Handles .\... and so on. 139 if filepath.Separator == '\\' { 140 a = strings.Replace(a, `\`, `/`, -1) 141 } 142 143 // Put argument in canonical form, but preserve leading ./. 144 if strings.HasPrefix(a, "./") { 145 a = "./" + path.Clean(a) 146 if a == "./." { 147 a = "." 148 } 149 } else { 150 a = path.Clean(a) 151 } 152 if isMetaPackage(a) { 153 out = append(out, c.allPackages(a)...) 154 continue 155 } 156 out = append(out, a) 157 } 158 return out 159 } 160 161 // importPaths returns the import paths to use for the given command line. 162 func (c *Context) importPaths(args []string) []string { 163 args = c.importPathsNoDotExpansion(args) 164 var out []string 165 for _, a := range args { 166 if strings.Contains(a, "...") { 167 if build.IsLocalImport(a) { 168 out = append(out, c.allPackagesInFS(a)...) 169 } else { 170 out = append(out, c.allPackages(a)...) 171 } 172 continue 173 } 174 out = append(out, a) 175 } 176 return out 177 } 178 179 // allPackages returns all the packages that can be found 180 // under the $GOPATH directories and $GOROOT matching pattern. 181 // The pattern is either "all" (all packages), "std" (standard packages), 182 // "cmd" (standard commands), or a path including "...". 183 func (c *Context) allPackages(pattern string) []string { 184 pkgs := c.matchPackages(pattern) 185 if len(pkgs) == 0 { 186 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 187 } 188 return pkgs 189 } 190 191 // allPackagesInFS is like allPackages but is passed a pattern 192 // beginning ./ or ../, meaning it should scan the tree rooted 193 // at the given directory. There are ... in the pattern too. 194 func (c *Context) allPackagesInFS(pattern string) []string { 195 pkgs := c.matchPackagesInFS(pattern) 196 if len(pkgs) == 0 { 197 fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern) 198 } 199 return pkgs 200 } 201 202 // matchPackagesInFS returns a list of package paths matching pattern, 203 // which must begin with ./ or ../ 204 // (see go help packages for pattern syntax). 205 func (c *Context) matchPackagesInFS(pattern string) []string { 206 // Find directory to begin the scan. 207 // Could be smarter but this one optimization 208 // is enough for now, since ... is usually at the 209 // end of a path. 210 i := strings.Index(pattern, "...") 211 dir, _ := path.Split(pattern[:i]) 212 213 // pattern begins with ./ or ../. 214 // path.Clean will discard the ./ but not the ../. 215 // We need to preserve the ./ for pattern matching 216 // and in the returned import paths. 217 prefix := "" 218 if strings.HasPrefix(pattern, "./") { 219 prefix = "./" 220 } 221 match := matchPattern(pattern) 222 223 var pkgs []string 224 filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error { 225 if err != nil || !fi.IsDir() { 226 return nil 227 } 228 if path == dir { 229 // filepath.Walk starts at dir and recurses. For the recursive case, 230 // the path is the result of filepath.Join, which calls filepath.Clean. 231 // The initial case is not Cleaned, though, so we do this explicitly. 232 // 233 // This converts a path like "./io/" to "io". Without this step, running 234 // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io 235 // package, because prepending the prefix "./" to the unclean path would 236 // result in "././io", and match("././io") returns false. 237 path = filepath.Clean(path) 238 } 239 240 // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..". 241 _, elem := filepath.Split(path) 242 dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".." 243 if dot || strings.HasPrefix(elem, "_") || elem == "testdata" { 244 return filepath.SkipDir 245 } 246 247 name := prefix + filepath.ToSlash(path) 248 if !match(name) { 249 return nil 250 } 251 252 // We keep the directory if we can import it, or if we can't import it 253 // due to invalid Go source files. This means that directories containing 254 // parse errors will be built (and fail) instead of being silently skipped 255 // as not matching the pattern. Go 1.5 and earlier skipped, but that 256 // behavior means people miss serious mistakes. 257 // See golang.org/issue/11407. 258 if p, err := c.BuildContext.ImportDir(path, 0); err != nil && shouldIgnoreImport(p) { 259 if _, noGo := err.(*build.NoGoError); !noGo { 260 log.Print(err) 261 } 262 return nil 263 } 264 pkgs = append(pkgs, name) 265 return nil 266 }) 267 return pkgs 268 } 269 270 // isMetaPackage checks if name is a reserved package name that expands to multiple packages. 271 func isMetaPackage(name string) bool { 272 return name == "std" || name == "cmd" || name == "all" 273 } 274 275 // isStandardImportPath reports whether $GOROOT/src/path should be considered 276 // part of the standard distribution. For historical reasons we allow people to add 277 // their own code to $GOROOT instead of using $GOPATH, but we assume that 278 // code will start with a domain name (dot in the first element). 279 func isStandardImportPath(path string) bool { 280 i := strings.Index(path, "/") 281 if i < 0 { 282 i = len(path) 283 } 284 elem := path[:i] 285 return !strings.Contains(elem, ".") 286 } 287 288 // hasPathPrefix reports whether the path s begins with the 289 // elements in prefix. 290 func hasPathPrefix(s, prefix string) bool { 291 switch { 292 default: 293 return false 294 case len(s) == len(prefix): 295 return s == prefix 296 case len(s) > len(prefix): 297 if prefix != "" && prefix[len(prefix)-1] == '/' { 298 return strings.HasPrefix(s, prefix) 299 } 300 return s[len(prefix)] == '/' && s[:len(prefix)] == prefix 301 } 302 } 303 304 // treeCanMatchPattern(pattern)(name) reports whether 305 // name or children of name can possibly match pattern. 306 // Pattern is the same limited glob accepted by matchPattern. 307 func treeCanMatchPattern(pattern string) func(name string) bool { 308 wildCard := false 309 if i := strings.Index(pattern, "..."); i >= 0 { 310 wildCard = true 311 pattern = pattern[:i] 312 } 313 return func(name string) bool { 314 return len(name) <= len(pattern) && hasPathPrefix(pattern, name) || 315 wildCard && strings.HasPrefix(name, pattern) 316 } 317 }