mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 20:02:27 -06:00 
			
		
		
		
	
		
			
	
	
		
			762 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			762 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								// Copyright 2022 The Gc Authors. All rights reserved.
							 | 
						||
| 
								 | 
							
								// Use of this source code is governed by a BSD-style
							 | 
						||
| 
								 | 
							
								// license that can be found in the LICENSE file.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								//go:generate stringer -output stringer.go -linecomment -type=Kind,ScopeKind,ChanDir,TypeCheck
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package gc // modernc.org/gc/v3
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"go/build"
							 | 
						||
| 
								 | 
							
									"go/build/constraint"
							 | 
						||
| 
								 | 
							
									"go/token"
							 | 
						||
| 
								 | 
							
									"io"
							 | 
						||
| 
								 | 
							
									"io/fs"
							 | 
						||
| 
								 | 
							
									"os"
							 | 
						||
| 
								 | 
							
									"path/filepath"
							 | 
						||
| 
								 | 
							
									"runtime"
							 | 
						||
| 
								 | 
							
									"sort"
							 | 
						||
| 
								 | 
							
									"strconv"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"sync"
							 | 
						||
| 
								 | 
							
									"unicode"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									"github.com/hashicorp/golang-lru/v2"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var (
							 | 
						||
| 
								 | 
							
									trcErrors bool
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type FileFilter func(cfg *Config, importPath string, matchedFSPaths []string, withTestFiles bool) (pkgFiles []string, err error)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type TypeCheck int
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const (
							 | 
						||
| 
								 | 
							
									TypeCheckNone TypeCheck = iota
							 | 
						||
| 
								 | 
							
									TypeCheckAll
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type cacheKey struct {
							 | 
						||
| 
								 | 
							
									buildTagsKey string
							 | 
						||
| 
								 | 
							
									cfg          *Config
							 | 
						||
| 
								 | 
							
									fsPath       string
							 | 
						||
| 
								 | 
							
									goarch       string
							 | 
						||
| 
								 | 
							
									goos         string
							 | 
						||
| 
								 | 
							
									gopathKey    string
							 | 
						||
| 
								 | 
							
									goroot       string
							 | 
						||
| 
								 | 
							
									importPath   string
							 | 
						||
| 
								 | 
							
									typeCheck    TypeCheck
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									withTestFiles bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type cacheItem struct {
							 | 
						||
| 
								 | 
							
									pkg *Package
							 | 
						||
| 
								 | 
							
									ch  chan struct{}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func newCacheItem() *cacheItem { return &cacheItem{ch: make(chan struct{})} }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *cacheItem) set(pkg *Package) {
							 | 
						||
| 
								 | 
							
									c.pkg = pkg
							 | 
						||
| 
								 | 
							
									close(c.ch)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *cacheItem) wait() *Package {
							 | 
						||
| 
								 | 
							
									<-c.ch
							 | 
						||
| 
								 | 
							
									return c.pkg
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type Cache struct {
							 | 
						||
| 
								 | 
							
									sync.Mutex
							 | 
						||
| 
								 | 
							
									lru *lru.TwoQueueCache[cacheKey, *cacheItem]
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func NewCache(size int) (*Cache, error) {
							 | 
						||
| 
								 | 
							
									c, err := lru.New2Q[cacheKey, *cacheItem](size)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return &Cache{lru: c}, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func MustNewCache(size int) *Cache {
							 | 
						||
| 
								 | 
							
									c, err := NewCache(size)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										panic(todo("", err))
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return c
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type ConfigOption func(*Config) error
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Config configures NewPackage
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// Config instances can be shared, they are not mutated once created and
							 | 
						||
| 
								 | 
							
								// configured.
							 | 
						||
| 
								 | 
							
								type Config struct {
							 | 
						||
| 
								 | 
							
									abi           *ABI
							 | 
						||
| 
								 | 
							
									buildTagMap   map[string]bool
							 | 
						||
| 
								 | 
							
									buildTags     []string
							 | 
						||
| 
								 | 
							
									buildTagsKey  string // Zero byte separated
							 | 
						||
| 
								 | 
							
									builtin       *Package
							 | 
						||
| 
								 | 
							
									cache         *Cache
							 | 
						||
| 
								 | 
							
									cmp           *Package // Go 1.21
							 | 
						||
| 
								 | 
							
									env           map[string]string
							 | 
						||
| 
								 | 
							
									fs            fs.FS
							 | 
						||
| 
								 | 
							
									goarch        string
							 | 
						||
| 
								 | 
							
									gocompiler    string // "gc", "gccgo"
							 | 
						||
| 
								 | 
							
									goos          string
							 | 
						||
| 
								 | 
							
									gopath        string
							 | 
						||
| 
								 | 
							
									gopathKey     string // Zero byte separated
							 | 
						||
| 
								 | 
							
									goroot        string
							 | 
						||
| 
								 | 
							
									goversion     string
							 | 
						||
| 
								 | 
							
									lookup        func(rel, importPath, version string) (fsPath string, err error)
							 | 
						||
| 
								 | 
							
									parallel      *parallel
							 | 
						||
| 
								 | 
							
									searchGoPaths []string
							 | 
						||
| 
								 | 
							
									searchGoroot  []string
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									int  Type // Set by NewConfig
							 | 
						||
| 
								 | 
							
									uint Type // Set by NewConfig
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									arch32bit  bool
							 | 
						||
| 
								 | 
							
									configured bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewConfig returns a newly created config or an error, if any.
							 | 
						||
| 
								 | 
							
								func NewConfig(opts ...ConfigOption) (r *Config, err error) {
							 | 
						||
| 
								 | 
							
									r = &Config{
							 | 
						||
| 
								 | 
							
										buildTagMap: map[string]bool{},
							 | 
						||
| 
								 | 
							
										env:         map[string]string{},
							 | 
						||
| 
								 | 
							
										parallel:    newParallel(),
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										if r != nil {
							 | 
						||
| 
								 | 
							
											r.configured = true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									r.lookup = r.DefaultLookup
							 | 
						||
| 
								 | 
							
									ctx := build.Default
							 | 
						||
| 
								 | 
							
									r.goos = r.getenv("GOOS", ctx.GOOS)
							 | 
						||
| 
								 | 
							
									r.goarch = r.getenv("GOARCH", ctx.GOARCH)
							 | 
						||
| 
								 | 
							
									r.goroot = r.getenv("GOROOT", ctx.GOROOT)
							 | 
						||
| 
								 | 
							
									r.gopath = r.getenv("GOPATH", ctx.GOPATH)
							 | 
						||
| 
								 | 
							
									r.buildTags = append(r.buildTags, r.goos, r.goarch)
							 | 
						||
| 
								 | 
							
									r.gocompiler = runtime.Compiler
							 | 
						||
| 
								 | 
							
									for _, opt := range opts {
							 | 
						||
| 
								 | 
							
										if err := opt(r); err != nil {
							 | 
						||
| 
								 | 
							
											return nil, err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if r.abi, err = NewABI(r.goos, r.goarch); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									switch r.goarch {
							 | 
						||
| 
								 | 
							
									case "386", "arm":
							 | 
						||
| 
								 | 
							
										r.arch32bit = true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									//  During a particular build, the following build tags are satisfied:
							 | 
						||
| 
								 | 
							
									//
							 | 
						||
| 
								 | 
							
									//  the target operating system, as spelled by runtime.GOOS, set with the GOOS environment variable.
							 | 
						||
| 
								 | 
							
									//  the target architecture, as spelled by runtime.GOARCH, set with the GOARCH environment variable.
							 | 
						||
| 
								 | 
							
									//  "unix", if GOOS is a Unix or Unix-like system.
							 | 
						||
| 
								 | 
							
									//  the compiler being used, either "gc" or "gccgo"
							 | 
						||
| 
								 | 
							
									//  "cgo", if the cgo command is supported (see CGO_ENABLED in 'go help environment').
							 | 
						||
| 
								 | 
							
									//  a term for each Go major release, through the current version: "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
							 | 
						||
| 
								 | 
							
									//  any additional tags given by the -tags flag (see 'go help build').
							 | 
						||
| 
								 | 
							
									//  There are no separate build tags for beta or minor releases.
							 | 
						||
| 
								 | 
							
									if r.goversion == "" {
							 | 
						||
| 
								 | 
							
										r.goversion = runtime.Version()
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if !strings.HasPrefix(r.goversion, "go") || !strings.Contains(r.goversion, ".") {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("cannot parse Go version: %s", r.goversion)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									ver := strings.SplitN(r.goversion[len("go"):], ".", 2)
							 | 
						||
| 
								 | 
							
									verMajor, err := strconv.Atoi(ver[0])
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("cannot parse Go version %s: %v", r.goversion, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if verMajor != 1 {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("unsupported Go version: %s", r.goversion)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch x, x2 := strings.IndexByte(ver[1], '.'), strings.Index(ver[1], "rc"); {
							 | 
						||
| 
								 | 
							
									case x >= 0:
							 | 
						||
| 
								 | 
							
										ver[1] = ver[1][:x]
							 | 
						||
| 
								 | 
							
									case x2 >= 0:
							 | 
						||
| 
								 | 
							
										ver[1] = ver[1][:x2]
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									verMinor, err := strconv.Atoi(ver[1])
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("cannot parse Go version %s: %v", r.goversion, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for i := 1; i <= verMinor; i++ {
							 | 
						||
| 
								 | 
							
										r.buildTags = append(r.buildTags, fmt.Sprintf("go%d.%d", verMajor, i))
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									r.buildTags = append(r.buildTags, r.gocompiler)
							 | 
						||
| 
								 | 
							
									r.buildTags = append(r.buildTags, extraTags(verMajor, verMinor, r.goos, r.goarch)...)
							 | 
						||
| 
								 | 
							
									if r.getenv("CGO_ENABLED", "1") == "1" {
							 | 
						||
| 
								 | 
							
										r.buildTags = append(r.buildTags, "cgo")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									for i, v := range r.buildTags {
							 | 
						||
| 
								 | 
							
										tag := strings.TrimSpace(v)
							 | 
						||
| 
								 | 
							
										r.buildTags[i] = tag
							 | 
						||
| 
								 | 
							
										r.buildTagMap[tag] = true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									sort.Strings(r.buildTags)
							 | 
						||
| 
								 | 
							
									r.buildTagsKey = strings.Join(r.buildTags, "\x00")
							 | 
						||
| 
								 | 
							
									r.searchGoroot = []string{filepath.Join(r.goroot, "src")}
							 | 
						||
| 
								 | 
							
									r.searchGoPaths = filepath.SplitList(r.gopath)
							 | 
						||
| 
								 | 
							
									r.gopathKey = strings.Join(r.searchGoPaths, "\x00")
							 | 
						||
| 
								 | 
							
									for i, v := range r.searchGoPaths {
							 | 
						||
| 
								 | 
							
										r.searchGoPaths[i] = filepath.Join(v, "src")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									switch r.cmp, err = r.NewPackage("", "cmp", "", nil, false, TypeCheckNone); {
							 | 
						||
| 
								 | 
							
									case err != nil:
							 | 
						||
| 
								 | 
							
										r.cmp = nil
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										//TODO r.cmp.Scope.kind = UniverseScope
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if r.builtin, err = r.NewPackage("", "builtin", "", nil, false, TypeCheckNone); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									r.builtin.Scope.kind = UniverseScope
							 | 
						||
| 
								 | 
							
									if err := r.builtin.check(newCtx(r)); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return r, nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) universe() *Scope {
							 | 
						||
| 
								 | 
							
									if c.builtin != nil {
							 | 
						||
| 
								 | 
							
										return c.builtin.Scope
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) stat(name string) (fs.FileInfo, error) {
							 | 
						||
| 
								 | 
							
									if c.fs == nil {
							 | 
						||
| 
								 | 
							
										return os.Stat(name)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									name = filepath.ToSlash(name)
							 | 
						||
| 
								 | 
							
									if x, ok := c.fs.(fs.StatFS); ok {
							 | 
						||
| 
								 | 
							
										return x.Stat(name)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									f, err := c.fs.Open(name)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, err
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer f.Close()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return f.Stat()
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) open(name string) (fs.File, error) {
							 | 
						||
| 
								 | 
							
									if c.fs == nil {
							 | 
						||
| 
								 | 
							
										return os.Open(name)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									name = filepath.ToSlash(name)
							 | 
						||
| 
								 | 
							
									return c.fs.Open(name)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) glob(pattern string) (matches []string, err error) {
							 | 
						||
| 
								 | 
							
									if c.fs == nil {
							 | 
						||
| 
								 | 
							
										return filepath.Glob(pattern)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pattern = filepath.ToSlash(pattern)
							 | 
						||
| 
								 | 
							
									return fs.Glob(c.fs, pattern)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) checkConstraints(pos token.Position, sep string) (r bool) {
							 | 
						||
| 
								 | 
							
									if !strings.Contains(sep, "//go:build") && !strings.Contains(sep, "+build") {
							 | 
						||
| 
								 | 
							
										return true
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// defer func() { trc("", r) }()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									lines := strings.Split(sep, "\n")
							 | 
						||
| 
								 | 
							
									var build, plusBuild []string
							 | 
						||
| 
								 | 
							
									for i, line := range lines {
							 | 
						||
| 
								 | 
							
										if constraint.IsGoBuild(line) && i < len(lines)-1 && lines[i+1] == "" {
							 | 
						||
| 
								 | 
							
											build = append(build, line)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if constraint.IsPlusBuild(line) {
							 | 
						||
| 
								 | 
							
											plusBuild = append(plusBuild, line)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									switch len(build) {
							 | 
						||
| 
								 | 
							
									case 0:
							 | 
						||
| 
								 | 
							
										// ok
							 | 
						||
| 
								 | 
							
									case 1:
							 | 
						||
| 
								 | 
							
										expr, err := constraint.Parse(build[0])
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return expr.Eval(func(tag string) (r bool) {
							 | 
						||
| 
								 | 
							
											// defer func() { trc("%q: %v", tag, r) }()
							 | 
						||
| 
								 | 
							
											switch tag {
							 | 
						||
| 
								 | 
							
											case "unix":
							 | 
						||
| 
								 | 
							
												return unixOS[c.goos]
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												return c.buildTagMap[tag]
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										})
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										panic(todo("%v: %q", pos, build))
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for _, line := range plusBuild {
							 | 
						||
| 
								 | 
							
										expr, err := constraint.Parse(line)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return true
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if !expr.Eval(func(tag string) (r bool) {
							 | 
						||
| 
								 | 
							
											// defer func() { trc("%q: %v", tag, r) }()
							 | 
						||
| 
								 | 
							
											switch tag {
							 | 
						||
| 
								 | 
							
											case "unix":
							 | 
						||
| 
								 | 
							
												return unixOS[c.goos]
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												return c.buildTagMap[tag]
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}) {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Default lookup translates import paths, possibly relative to rel, to file system paths.
							 | 
						||
| 
								 | 
							
								func (c *Config) DefaultLookup(rel, importPath, version string) (fsPath string, err error) {
							 | 
						||
| 
								 | 
							
									if importPath == "" {
							 | 
						||
| 
								 | 
							
										return "", fmt.Errorf("import path cannot be emtpy")
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Implementation restriction: A compiler may restrict ImportPaths to non-empty
							 | 
						||
| 
								 | 
							
									// strings using only characters belonging to Unicode's L, M, N, P, and S
							 | 
						||
| 
								 | 
							
									// general categories (the Graphic characters without spaces) and may also
							 | 
						||
| 
								 | 
							
									// exclude the characters !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement
							 | 
						||
| 
								 | 
							
									// character U+FFFD.
							 | 
						||
| 
								 | 
							
									if strings.ContainsAny(importPath, "!\"#$%&'()*,:;<=>?[\\]^`{|}\ufffd") {
							 | 
						||
| 
								 | 
							
										return "", fmt.Errorf("invalid import path: %s", importPath)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for _, r := range importPath {
							 | 
						||
| 
								 | 
							
										if !unicode.Is(unicode.L, r) &&
							 | 
						||
| 
								 | 
							
											!unicode.Is(unicode.M, r) &&
							 | 
						||
| 
								 | 
							
											!unicode.Is(unicode.N, r) &&
							 | 
						||
| 
								 | 
							
											!unicode.Is(unicode.P, r) &&
							 | 
						||
| 
								 | 
							
											!unicode.Is(unicode.S, r) {
							 | 
						||
| 
								 | 
							
											return "", fmt.Errorf("invalid import path: %s", importPath)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									var search []string
							 | 
						||
| 
								 | 
							
									ip0 := importPath
							 | 
						||
| 
								 | 
							
									switch slash := strings.IndexByte(importPath, '/'); {
							 | 
						||
| 
								 | 
							
									case strings.HasPrefix(importPath, "./"):
							 | 
						||
| 
								 | 
							
										if rel != "" {
							 | 
						||
| 
								 | 
							
											panic(todo(""))
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return "", fmt.Errorf("invalid import path: %s", importPath)
							 | 
						||
| 
								 | 
							
									case strings.HasPrefix(importPath, "/"):
							 | 
						||
| 
								 | 
							
										return importPath, nil
							 | 
						||
| 
								 | 
							
									case slash > 0:
							 | 
						||
| 
								 | 
							
										ip0 = importPath[:slash]
							 | 
						||
| 
								 | 
							
									default:
							 | 
						||
| 
								 | 
							
										ip0 = importPath
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if ip0 != "" {
							 | 
						||
| 
								 | 
							
										switch {
							 | 
						||
| 
								 | 
							
										case strings.Contains(ip0, "."):
							 | 
						||
| 
								 | 
							
											search = c.searchGoPaths
							 | 
						||
| 
								 | 
							
										default:
							 | 
						||
| 
								 | 
							
											search = c.searchGoroot
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									for _, v := range search {
							 | 
						||
| 
								 | 
							
										fsPath = filepath.Join(v, importPath)
							 | 
						||
| 
								 | 
							
										dir, err := c.open(fsPath)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										fi, err := dir.Stat()
							 | 
						||
| 
								 | 
							
										dir.Close()
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if fi.IsDir() {
							 | 
						||
| 
								 | 
							
											return fsPath, nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return "", fmt.Errorf("cannot find package %s, searched %v", importPath, search)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) getenv(nm, deflt string) (r string) {
							 | 
						||
| 
								 | 
							
									if r = c.env[nm]; r != "" {
							 | 
						||
| 
								 | 
							
										return r
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if r = os.Getenv(nm); r != "" {
							 | 
						||
| 
								 | 
							
										return r
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return deflt
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func DefaultFileFilter(cfg *Config, importPath string, matchedFSPaths []string, withTestFiles bool) (pkgFiles []string, err error) {
							 | 
						||
| 
								 | 
							
									w := 0
							 | 
						||
| 
								 | 
							
									for _, v := range matchedFSPaths {
							 | 
						||
| 
								 | 
							
										base := filepath.Base(v)
							 | 
						||
| 
								 | 
							
										base = base[:len(base)-len(filepath.Ext(base))]
							 | 
						||
| 
								 | 
							
										const testSuffix = "_test"
							 | 
						||
| 
								 | 
							
										if strings.HasSuffix(base, testSuffix) {
							 | 
						||
| 
								 | 
							
											if !withTestFiles {
							 | 
						||
| 
								 | 
							
												continue
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											base = base[:len(base)-len(testSuffix)]
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if x := strings.LastIndexByte(base, '_'); x > 0 {
							 | 
						||
| 
								 | 
							
											last := base[x+1:]
							 | 
						||
| 
								 | 
							
											base = base[:x]
							 | 
						||
| 
								 | 
							
											var prevLast string
							 | 
						||
| 
								 | 
							
											if x := strings.LastIndexByte(base, '_'); x > 0 {
							 | 
						||
| 
								 | 
							
												prevLast = base[x+1:]
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											if last != "" && prevLast != "" {
							 | 
						||
| 
								 | 
							
												//  *_GOOS_GOARCH
							 | 
						||
| 
								 | 
							
												if knownOS[prevLast] && prevLast != cfg.goos {
							 | 
						||
| 
								 | 
							
													continue
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if knownArch[last] && last != cfg.goarch {
							 | 
						||
| 
								 | 
							
													continue
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if last != "" {
							 | 
						||
| 
								 | 
							
												// *_GOOS or *_GOARCH
							 | 
						||
| 
								 | 
							
												if knownOS[last] && last != cfg.goos {
							 | 
						||
| 
								 | 
							
													continue
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if knownArch[last] && last != cfg.goarch {
							 | 
						||
| 
								 | 
							
													continue
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										matchedFSPaths[w] = v
							 | 
						||
| 
								 | 
							
										w++
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return matchedFSPaths[:w], nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ConfigBuildTags configures build tags.
							 | 
						||
| 
								 | 
							
								func ConfigBuildTags(tags []string) ConfigOption {
							 | 
						||
| 
								 | 
							
									return func(cfg *Config) error {
							 | 
						||
| 
								 | 
							
										if cfg.configured {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("ConfigBuildTags: Config instance already configured")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cfg.buildTags = append(cfg.buildTags, tags...)
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ConfigEnviron configures environment variables.
							 | 
						||
| 
								 | 
							
								func ConfigEnviron(env []string) ConfigOption {
							 | 
						||
| 
								 | 
							
									return func(cfg *Config) error {
							 | 
						||
| 
								 | 
							
										if cfg.configured {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("ConfigEnviron: Config instance already configured")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for _, v := range env {
							 | 
						||
| 
								 | 
							
											switch x := strings.IndexByte(v, '='); {
							 | 
						||
| 
								 | 
							
											case x < 0:
							 | 
						||
| 
								 | 
							
												cfg.env[v] = ""
							 | 
						||
| 
								 | 
							
											default:
							 | 
						||
| 
								 | 
							
												cfg.env[v[:x]] = v[x+1:]
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ConfigFS configures a file system used for opening Go source files. If not
							 | 
						||
| 
								 | 
							
								// explicitly configured, a default os.DirFS("/") is used on Unix-like
							 | 
						||
| 
								 | 
							
								// operating systems. On Windows it will be rooted on the volume where
							 | 
						||
| 
								 | 
							
								// runtime.GOROOT() is.
							 | 
						||
| 
								 | 
							
								func ConfigFS(fs fs.FS) ConfigOption {
							 | 
						||
| 
								 | 
							
									return func(cfg *Config) error {
							 | 
						||
| 
								 | 
							
										if cfg.configured {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("ConfigFS: Config instance already configured")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cfg.fs = fs
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ConfigLookup configures a lookup function.
							 | 
						||
| 
								 | 
							
								func ConfigLookup(f func(dir, importPath, version string) (fsPath string, err error)) ConfigOption {
							 | 
						||
| 
								 | 
							
									return func(cfg *Config) error {
							 | 
						||
| 
								 | 
							
										if cfg.configured {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("ConfigLookup: Config instance already configured")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cfg.lookup = f
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ConfigCache configures a cache.
							 | 
						||
| 
								 | 
							
								func ConfigCache(c *Cache) ConfigOption {
							 | 
						||
| 
								 | 
							
									return func(cfg *Config) error {
							 | 
						||
| 
								 | 
							
										if cfg.configured {
							 | 
						||
| 
								 | 
							
											return fmt.Errorf("ConfigCache: Config instance already configured")
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cfg.cache = c
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								type importGuard struct {
							 | 
						||
| 
								 | 
							
									m     map[string]struct{}
							 | 
						||
| 
								 | 
							
									stack []string
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func newImportGuard() *importGuard { return &importGuard{m: map[string]struct{}{}} }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Package represents a Go package. The instance must not be mutated.
							 | 
						||
| 
								 | 
							
								type Package struct {
							 | 
						||
| 
								 | 
							
									AST            map[string]*AST // AST maps fsPaths of individual files to their respective ASTs
							 | 
						||
| 
								 | 
							
									FSPath         string
							 | 
						||
| 
								 | 
							
									GoFiles        []fs.FileInfo
							 | 
						||
| 
								 | 
							
									ImportPath     string
							 | 
						||
| 
								 | 
							
									InvalidGoFiles map[string]error // errors for particular files, if any
							 | 
						||
| 
								 | 
							
									Name           Token
							 | 
						||
| 
								 | 
							
									Scope          *Scope // Package scope.
							 | 
						||
| 
								 | 
							
									Version        string
							 | 
						||
| 
								 | 
							
									cfg            *Config
							 | 
						||
| 
								 | 
							
									guard          *importGuard
							 | 
						||
| 
								 | 
							
									mu             sync.Mutex
							 | 
						||
| 
								 | 
							
									typeCheck      TypeCheck
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									isUnsafe bool // ImportPath == "usnafe"
							 | 
						||
| 
								 | 
							
									// isChecked bool
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// NewPackage returns a Package, possibly cached, for importPath@version or an
							 | 
						||
| 
								 | 
							
								// error, if any. The fileFilter argument can be nil, in such case
							 | 
						||
| 
								 | 
							
								// DefaultFileFilter is used, which ignores Files with suffix _test.go unless
							 | 
						||
| 
								 | 
							
								// withTestFiles is true.
							 | 
						||
| 
								 | 
							
								//
							 | 
						||
| 
								 | 
							
								// NewPackage is safe for concurrent use by multiple goroutines.
							 | 
						||
| 
								 | 
							
								func (c *Config) NewPackage(dir, importPath, version string, fileFilter FileFilter, withTestFiles bool, typeCheck TypeCheck) (pkg *Package, err error) {
							 | 
						||
| 
								 | 
							
									return c.newPackage(dir, importPath, version, fileFilter, withTestFiles, typeCheck, newImportGuard())
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) newPackage(dir, importPath, version string, fileFilter FileFilter, withTestFiles bool, typeCheck TypeCheck, guard *importGuard) (pkg *Package, err error) {
							 | 
						||
| 
								 | 
							
									if _, ok := guard.m[importPath]; ok {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("import cycle %v", guard.stack)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									guard.stack = append(guard.stack, importPath)
							 | 
						||
| 
								 | 
							
									fsPath, err := c.lookup(dir, importPath, version)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("lookup %s: %v", importPath, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pat := filepath.Join(fsPath, "*.go")
							 | 
						||
| 
								 | 
							
									matches, err := c.glob(pat)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("glob %s: %v", pat, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if len(matches) == 0 {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("no Go files in %s", fsPath)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if fileFilter == nil {
							 | 
						||
| 
								 | 
							
										fileFilter = DefaultFileFilter
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if matches, err = fileFilter(c, importPath, matches, withTestFiles); err != nil {
							 | 
						||
| 
								 | 
							
										return nil, fmt.Errorf("matching Go files in %s: %v", fsPath, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var k cacheKey
							 | 
						||
| 
								 | 
							
									if c.cache != nil {
							 | 
						||
| 
								 | 
							
										k = cacheKey{
							 | 
						||
| 
								 | 
							
											buildTagsKey:  c.buildTagsKey,
							 | 
						||
| 
								 | 
							
											cfg:           c,
							 | 
						||
| 
								 | 
							
											fsPath:        fsPath,
							 | 
						||
| 
								 | 
							
											goarch:        c.goarch,
							 | 
						||
| 
								 | 
							
											goos:          c.goos,
							 | 
						||
| 
								 | 
							
											gopathKey:     c.gopathKey,
							 | 
						||
| 
								 | 
							
											goroot:        c.goroot,
							 | 
						||
| 
								 | 
							
											importPath:    importPath,
							 | 
						||
| 
								 | 
							
											typeCheck:     typeCheck,
							 | 
						||
| 
								 | 
							
											withTestFiles: withTestFiles,
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										c.cache.Lock() // ---------------------------------------- lock
							 | 
						||
| 
								 | 
							
										item, ok := c.cache.lru.Get(k)
							 | 
						||
| 
								 | 
							
										if ok {
							 | 
						||
| 
								 | 
							
											c.cache.Unlock() // ---------------------------- unlock
							 | 
						||
| 
								 | 
							
											if pkg = item.wait(); pkg != nil && pkg.matches(&k, matches) {
							 | 
						||
| 
								 | 
							
												return pkg, nil
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										item = newCacheItem()
							 | 
						||
| 
								 | 
							
										c.cache.lru.Add(k, item)
							 | 
						||
| 
								 | 
							
										c.cache.Unlock() // ------------------------------------ unlock
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										defer func() {
							 | 
						||
| 
								 | 
							
											if pkg != nil && err == nil {
							 | 
						||
| 
								 | 
							
												item.set(pkg)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}()
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									r := &Package{
							 | 
						||
| 
								 | 
							
										AST:        map[string]*AST{},
							 | 
						||
| 
								 | 
							
										FSPath:     fsPath,
							 | 
						||
| 
								 | 
							
										ImportPath: importPath,
							 | 
						||
| 
								 | 
							
										Scope:      newScope(c.universe(), PackageScope),
							 | 
						||
| 
								 | 
							
										Version:    version,
							 | 
						||
| 
								 | 
							
										cfg:        c,
							 | 
						||
| 
								 | 
							
										guard:      guard,
							 | 
						||
| 
								 | 
							
										isUnsafe:   importPath == "unsafe",
							 | 
						||
| 
								 | 
							
										typeCheck:  typeCheck,
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() { r.guard = nil }()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									sort.Strings(matches)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										sort.Slice(r.GoFiles, func(i, j int) bool { return r.GoFiles[i].Name() < r.GoFiles[j].Name() })
							 | 
						||
| 
								 | 
							
										if err != nil || len(r.InvalidGoFiles) != 0 || typeCheck == TypeCheckNone {
							 | 
						||
| 
								 | 
							
											return
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//TODO err = r.check(newCtx(c))
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									c.parallel.throttle(func() {
							 | 
						||
| 
								 | 
							
										for _, path := range matches {
							 | 
						||
| 
								 | 
							
											if err = c.newPackageFile(r, path); err != nil {
							 | 
						||
| 
								 | 
							
												return
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									})
							 | 
						||
| 
								 | 
							
									return r, err
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (c *Config) newPackageFile(pkg *Package, path string) (err error) {
							 | 
						||
| 
								 | 
							
									f, err := c.open(path)
							 | 
						||
| 
								 | 
							
									if err != nil {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("opening file %q: %v", path, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										f.Close()
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											if pkg.InvalidGoFiles == nil {
							 | 
						||
| 
								 | 
							
												pkg.InvalidGoFiles = map[string]error{}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
											pkg.InvalidGoFiles[path] = err
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var fi fs.FileInfo
							 | 
						||
| 
								 | 
							
									if fi, err = f.Stat(); err != nil {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("stat %s: %v", path, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if !fi.Mode().IsRegular() {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var b []byte
							 | 
						||
| 
								 | 
							
									if b, err = io.ReadAll(f); err != nil {
							 | 
						||
| 
								 | 
							
										return fmt.Errorf("reading %s: %v", path, err)
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									p := newParser(pkg.Scope, path, b, false)
							 | 
						||
| 
								 | 
							
									if p.peek(0) == PACKAGE {
							 | 
						||
| 
								 | 
							
										tok := Token{p.s.source, p.s.toks[p.ix].ch, int32(p.ix)}
							 | 
						||
| 
								 | 
							
										if !c.checkConstraints(tok.Position(), tok.Sep()) {
							 | 
						||
| 
								 | 
							
											return nil
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pkg.GoFiles = append(pkg.GoFiles, fi)
							 | 
						||
| 
								 | 
							
									var ast *AST
							 | 
						||
| 
								 | 
							
									if ast, err = p.parse(); err != nil {
							 | 
						||
| 
								 | 
							
										return nil
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									pkg.AST[path] = ast
							 | 
						||
| 
								 | 
							
									return nil
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func (p *Package) matches(k *cacheKey, matches []string) bool {
							 | 
						||
| 
								 | 
							
									matched := map[string]struct{}{}
							 | 
						||
| 
								 | 
							
									for _, match := range matches {
							 | 
						||
| 
								 | 
							
										matched[match] = struct{}{}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									for _, cachedInfo := range p.GoFiles {
							 | 
						||
| 
								 | 
							
										name := cachedInfo.Name()
							 | 
						||
| 
								 | 
							
										path := filepath.Join(p.FSPath, name)
							 | 
						||
| 
								 | 
							
										if _, ok := matched[path]; !ok {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										info, err := k.cfg.stat(path)
							 | 
						||
| 
								 | 
							
										if err != nil {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if info.IsDir() ||
							 | 
						||
| 
								 | 
							
											info.Size() != cachedInfo.Size() ||
							 | 
						||
| 
								 | 
							
											info.ModTime().After(cachedInfo.ModTime()) ||
							 | 
						||
| 
								 | 
							
											info.Mode() != cachedInfo.Mode() {
							 | 
						||
| 
								 | 
							
											return false
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// ParseFile parses 'b', assuming it comes from 'path' and returns an AST or error, if any.
							 | 
						||
| 
								 | 
							
								func ParseFile(path string, b []byte) (*AST, error) {
							 | 
						||
| 
								 | 
							
									return newParser(newScope(nil, PackageScope), path, b, false).parse()
							 | 
						||
| 
								 | 
							
								}
							 |