// Copyright 2018 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 packages

import (
	
	
	
	
	
	
	
	
	
	

	
)

// processGolistOverlay provides rudimentary support for adding
// files that don't exist on disk to an overlay. The results can be
// sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path
func ( *golistState) ( *responseDeduper) (,  []string,  error) {
	 := make(map[string]string) // importPath -> non-test package ID
	 := make(map[string]bool)
	 := make(map[string]bool)

	 := make(map[string][]*Package)
	for ,  := range .dr.Packages {
		// This is an approximation of import path to id. This can be
		// wrong for tests, vendored packages, and a number of other cases.
		[.PkgPath] = .ID
		,  := commonDir(.GoFiles)
		if  != nil {
			return nil, nil, 
		}
		if  != "" {
			[] = append([], )
		}
	}

	// If no new imports are added, it is safe to avoid loading any needPkgs.
	// Otherwise, it's hard to tell which package is actually being loaded
	// (due to vendoring) and whether any modified package will show up
	// in the transitive set of dependencies (because new imports are added,
	// potentially modifying the transitive set of dependencies).
	var  bool

	// If both a package and its test package are created by the overlay, we
	// need the real package first. Process all non-test files before test
	// files, and make the whole process deterministic while we're at it.
	var  []string
	for  := range .cfg.Overlay {
		 = append(, )
	}
	sort.Slice(, func(,  int) bool {
		 := strings.HasSuffix([], "_test.go")
		 := strings.HasSuffix([], "_test.go")
		if  !=  {
			return ! // non-tests are before tests.
		}
		return [] < []
	})
	for ,  := range  {
		 := .cfg.Overlay[]
		 := filepath.Base()
		 := filepath.Dir()
		var  *Package           // if opath belongs to both a package and its test variant, this will be the test variant
		var  *Package // if opath is a test file, this is the package it is testing
		var  bool
		 := strings.HasSuffix(, "_test.go")
		,  := extractPackageName(, )
		if ! {
			// Don't bother adding a file that doesn't even have a parsable package statement
			// to the overlay.
			continue
		}
		// If all the overlay files belong to a different package, change the
		// package name to that package.
		maybeFixPackageName(, , [])
	:
		for ,  := range .dr.Packages {
			if  != .Name && .ID != "command-line-arguments" {
				continue
			}
			for ,  := range .GoFiles {
				if !sameFile(filepath.Dir(), ) {
					continue
				}
				// Make sure to capture information on the package's test variant, if needed.
				if  && !hasTestFiles() {
					// TODO(matloob): Are there packages other than the 'production' variant
					// of a package that this can match? This shouldn't match the test main package
					// because the file is generated in another directory.
					 = 
					continue 
				} else if ! && hasTestFiles() {
					// We're examining a test variant, but the overlaid file is
					// a non-test file. Because the overlay implementation
					// (currently) only adds a file to one package, skip this
					// package, so that we can add the file to the production
					// variant of the package. (https://golang.org/issue/36857
					// tracks handling overlays on both the production and test
					// variant of a package).
					continue 
				}
				if  != nil &&  !=  && .PkgPath == .PkgPath {
					// We have already seen the production version of the
					// for which p is a test variant.
					if hasTestFiles() {
						 = 
					}
				}
				 = 
				if filepath.Base() ==  {
					 = true
				}
			}
		}
		// The overlay could have included an entirely new package or an
		// ad-hoc package. An ad-hoc package is one that we have manually
		// constructed from inadequate `go list` results for a file= query.
		// It will have the ID command-line-arguments.
		if  == nil || .ID == "command-line-arguments" {
			// Try to find the module or gopath dir the file is contained in.
			// Then for modules, add the module opath to the beginning.
			, ,  := .getPkgPath()
			if  != nil {
				return nil, nil, 
			}
			if ! {
				break
			}
			var  string // only set for x tests
			 := strings.HasSuffix(, "_test")
			if  {
				 = 
				 += "_test"
			}
			 := 
			if  {
				if  {
					 = fmt.Sprintf("%s [%s.test]", , )
				} else {
					 = fmt.Sprintf("%s [%s.test]", , )
				}
			}
			if  != nil {
				// TODO(rstambler): We should change the package's path and ID
				// here. The only issue is that this messes with the roots.
			} else {
				// Try to reclaim a package with the same ID, if it exists in the response.
				for ,  := range .dr.Packages {
					if reclaimPackage(, , , ) {
						 = 
						break
					}
				}
				// Otherwise, create a new package.
				if  == nil {
					 = &Package{
						PkgPath: ,
						ID:      ,
						Name:    ,
						Imports: make(map[string]*Package),
					}
					.addPackage()
					[.PkgPath] = 
					// Add the production package's sources for a test variant.
					if  && ! &&  != nil {
						.GoFiles = append(.GoFiles, .GoFiles...)
						.CompiledGoFiles = append(.CompiledGoFiles, .CompiledGoFiles...)
						// Add the package under test and its imports to the test variant.
						.forTest = .PkgPath
						for ,  := range .Imports {
							.Imports[] = &Package{ID: .ID}
						}
					}
					if  {
						.forTest = 
					}
				}
			}
		}
		if ! {
			.GoFiles = append(.GoFiles, )
			// TODO(matloob): Adding the file to CompiledGoFiles can exhibit the wrong behavior
			// if the file will be ignored due to its build tags.
			.CompiledGoFiles = append(.CompiledGoFiles, )
			[.ID] = true
		}
		,  := extractImports(, )
		if  != nil {
			// Let the parser or type checker report errors later.
			continue
		}
		for ,  := range  {
			// TODO(rstambler): If the package is an x test and the import has
			// a test variant, make sure to replace it.
			if ,  := .Imports[];  {
				continue
			}
			 = true
			,  := []
			if ! {
				var  error
				,  = .resolveImport(, )
				if  != nil {
					return nil, nil, 
				}
			}
			.Imports[] = &Package{ID: }
			// Add dependencies to the non-test variant version of this package as well.
			if  != nil {
				.Imports[] = &Package{ID: }
			}
		}
	}

	// toPkgPath guesses the package path given the id.
	 := func(,  string) (string, error) {
		if  := strings.IndexByte(, ' ');  >= 0 {
			return .resolveImport(, [:])
		}
		return .resolveImport(, )
	}

	// Now that new packages have been created, do another pass to determine
	// the new set of missing packages.
	for ,  := range .dr.Packages {
		for ,  := range .Imports {
			if len(.GoFiles) == 0 {
				return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", .PkgPath)
			}
			,  := (filepath.Dir(.GoFiles[0]), .ID)
			if  != nil {
				return nil, nil, 
			}
			if ,  := []; ! {
				[] = true
			}
		}
	}

	if  {
		 = make([]string, 0, len())
		for  := range  {
			 = append(, )
		}
	}
	 = make([]string, 0, len())
	for  := range  {
		 = append(, )
	}
	return , , 
}

// resolveImport finds the ID of a package given its import path.
// In particular, it will find the right vendored copy when in GOPATH mode.
func ( *golistState) (,  string) (string, error) {
	,  := .getEnv()
	if  != nil {
		return "", 
	}
	if ["GOMOD"] != "" {
		return , nil
	}

	 := 
	for {
		 := filepath.Join(, "vendor")
		,  := .vendorDirs[]
		if ! {
			,  := os.Stat()
			 =  == nil && .IsDir()
			.vendorDirs[] = 
		}

		if  {
			 := filepath.Join(, )
			if ,  := os.Stat();  == nil && .IsDir() {
				// We should probably check for .go files here, but shame on anyone who fools us.
				, ,  := .getPkgPath()
				if  != nil {
					return "", 
				}
				if  {
					return , nil
				}
			}
		}

		// We know we've hit the top of the filesystem when we Dir / and get /,
		// or C:\ and get C:\, etc.
		 := filepath.Dir()
		if  ==  {
			break
		}
		 = 
	}
	return , nil
}

func ( *Package) bool {
	for ,  := range .GoFiles {
		if strings.HasSuffix(, "_test.go") {
			return true
		}
	}
	return false
}

// determineRootDirs returns a mapping from absolute directories that could
// contain code to their corresponding import path prefixes.
func ( *golistState) () (map[string]string, error) {
	,  := .getEnv()
	if  != nil {
		return nil, 
	}
	if ["GOMOD"] != "" {
		.rootsOnce.Do(func() {
			.rootDirs, .rootDirsError = .determineRootDirsModules()
		})
	} else {
		.rootsOnce.Do(func() {
			.rootDirs, .rootDirsError = .determineRootDirsGOPATH()
		})
	}
	return .rootDirs, .rootDirsError
}

func ( *golistState) () (map[string]string, error) {
	// List all of the modules--the first will be the directory for the main
	// module. Any replaced modules will also need to be treated as roots.
	// Editing files in the module cache isn't a great idea, so we don't
	// plan to ever support that.
	,  := .invokeGo("list", "-m", "-json", "all")
	if  != nil {
		// 'go list all' will fail if we're outside of a module and
		// GO111MODULE=on. Try falling back without 'all'.
		var  error
		,  = .invokeGo("list", "-m", "-json")
		if  != nil {
			return nil, 
		}
	}
	 := map[string]string{}
	 := map[string]string{}
	var  int
	for  := json.NewDecoder(); .More(); {
		 := new(gocommand.ModuleJSON)
		if  := .Decode();  != nil {
			return nil, 
		}
		if .Dir != "" && .Path != "" {
			// This is a valid module; add it to the map.
			,  := filepath.Abs(.Dir)
			if  != nil {
				return nil, 
			}
			[] = .Path
			// The first result is the main module.
			if  == 0 || .Replace != nil && .Replace.Path != "" {
				[] = .Path
			}
		}
		++
	}
	return , nil
}

func ( *golistState) () (map[string]string, error) {
	 := map[string]string{}
	for ,  := range filepath.SplitList(.mustGetEnv()["GOPATH"]) {
		,  := filepath.Abs()
		if  != nil {
			return nil, 
		}
		[filepath.Join(, "src")] = ""
	}
	return , nil
}

func ( string,  []byte) ([]string, error) {
	,  := parser.ParseFile(token.NewFileSet(), , , parser.ImportsOnly) // TODO(matloob): reuse fileset?
	if  != nil {
		return nil, 
	}
	var  []string
	for ,  := range .Imports {
		 := .Path.Value
		,  := strconv.Unquote()
		if  != nil {
			return nil, 
		}
		 = append(, )
	}
	return , nil
}

// reclaimPackage attempts to reuse a package that failed to load in an overlay.
//
// If the package has errors and has no Name, GoFiles, or Imports,
// then it's possible that it doesn't yet exist on disk.
func ( *Package,  string,  string,  []byte) bool {
	// TODO(rstambler): Check the message of the actual error?
	// It differs between $GOPATH and module mode.
	if .ID !=  {
		return false
	}
	if len(.Errors) != 1 {
		return false
	}
	if .Name != "" || .ExportFile != "" {
		return false
	}
	if len(.GoFiles) > 0 || len(.CompiledGoFiles) > 0 || len(.OtherFiles) > 0 {
		return false
	}
	if len(.Imports) > 0 {
		return false
	}
	,  := extractPackageName(, )
	if ! {
		return false
	}
	.Name = 
	.Errors = nil
	return true
}

func ( string,  []byte) (string, bool) {
	// TODO(rstambler): Check the message of the actual error?
	// It differs between $GOPATH and module mode.
	,  := parser.ParseFile(token.NewFileSet(), , , parser.PackageClauseOnly) // TODO(matloob): reuse fileset?
	if  != nil {
		return "", false
	}
	return .Name.Name, true
}

// commonDir returns the directory that all files are in, "" if files is empty,
// or an error if they aren't in the same directory.
func ( []string) (string, error) {
	 := make(map[string]bool)
	for ,  := range  {
		[filepath.Dir()] = true
	}
	if len() > 1 {
		return "", fmt.Errorf("files (%v) are in more than one directory: %v", , )
	}
	for  := range  {
		// seen has only one element; return it.
		return , nil
	}
	return "", nil // no files
}

// It is possible that the files in the disk directory dir have a different package
// name from newName, which is deduced from the overlays. If they all have a different
// package name, and they all have the same package name, then that name becomes
// the package name.
// It returns true if it changes the package name, false otherwise.
func ( string,  bool,  []*Package) {
	 := make(map[string]int)
	for ,  := range  {
		[.Name]++
	}
	if len() != 1 {
		// some files are in different packages
		return
	}
	var  string
	for  := range  {
		 = 
	}
	if  ==  {
		return
	}
	// We might have a case where all of the package names in the directory are
	// the same, but the overlay file is for an x test, which belongs to its
	// own package. If the x test does not yet exist on disk, we may not yet
	// have its package name on disk, but we should not rename the packages.
	//
	// We use a heuristic to determine if this file belongs to an x test:
	// The test file should have a package name whose package name has a _test
	// suffix or looks like "newName_test".
	 := strings.HasPrefix(+"_test", ) || strings.HasSuffix(, "_test")
	if  &&  {
		return
	}
	for ,  := range  {
		.Name = 
	}
}

// This function is copy-pasted from
// https://github.com/golang/go/blob/9706f510a5e2754595d716bd64be8375997311fb/src/cmd/go/internal/search/search.go#L360.
// It should be deleted when we remove support for overlays from go/packages.
//
// NOTE: This does not handle any ./... or ./ style queries, as this function
// doesn't know the working directory.
//
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Unfortunately, there are two special cases. Quoting "go help packages":
//
// First, /... at the end of the pattern can match an empty string,
// so that net/... matches both net and packages in its subdirectories, like net/http.
// Second, any slash-separated pattern element containing a wildcard never
// participates in a match of the "vendor" element in the path of a vendored
// package, so that ./... does not match packages in subdirectories of
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
// Note, however, that a directory named vendor that itself contains code
// is not a vendored package: cmd/vendor would be a command named vendor,
// and the pattern cmd/... matches it.
func ( string) func( string) bool {
	// Convert pattern to regular expression.
	// The strategy for the trailing /... is to nest it in an explicit ? expression.
	// The strategy for the vendor exclusion is to change the unmatchable
	// vendor strings to a disallowed code point (vendorChar) and to use
	// "(anything but that codepoint)*" as the implementation of the ... wildcard.
	// This is a bit complicated but the obvious alternative,
	// namely a hand-written search like in most shell glob matchers,
	// is too easy to make accidentally exponential.
	// Using package regexp guarantees linear-time matching.

	const  = "\x00"

	if strings.Contains(, ) {
		return func( string) bool { return false }
	}

	 := regexp.QuoteMeta()
	 = replaceVendor(, )
	switch {
	case strings.HasSuffix(, `/`++`/\.\.\.`):
		 = strings.TrimSuffix(, `/`++`/\.\.\.`) + `(/vendor|/` +  + `/\.\.\.)`
	case  == +`/\.\.\.`:
		 = `(/vendor|/` +  + `/\.\.\.)`
	case strings.HasSuffix(, `/\.\.\.`):
		 = strings.TrimSuffix(, `/\.\.\.`) + `(/\.\.\.)?`
	}
	 = strings.ReplaceAll(, `\.\.\.`, `[^`++`]*`)

	 := regexp.MustCompile(`^` +  + `$`)

	return func( string) bool {
		if strings.Contains(, ) {
			return false
		}
		return .MatchString(replaceVendor(, ))
	}
}

// replaceVendor returns the result of replacing
// non-trailing vendor path elements in x with repl.
func (,  string) string {
	if !strings.Contains(, "vendor") {
		return 
	}
	 := strings.Split(, "/")
	for  := 0;  < len()-1; ++ {
		if [] == "vendor" {
			[] = 
		}
	}
	return strings.Join(, "/")
}