Copyright 2021 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. This file caches information about which standard library types, methods, and functions appeared in what version of Go Copied from https://go.googlesource.com/tools/+/5ab06b02d60653d5a5220fb2d99064055da3bdbd/godoc/versions.go with these modifications.

package symbol

import (
	
	
	
	
	
	
	
	

	
	
)
ParseAPIInfo parses apiVersions using contents of the specified directory.
Process files in reverse semver order (vx.y.z, vz.y.z-1, ...). The signature of an identifier may change (for example, a function that accepts a type replaced with an alias), and so an existing symbol may show up again in a later api/vX.Y.Z.txt file. Parsing in reverse version order means we end up with the earliest version of Go when the symbol was added. See golang.org/issue/44081.
	 := func( string) string {
		 := filepath.Base()
		 := strings.TrimSuffix(, ".txt")
stdlib files have the structure goN.txt. Get the semantic version.
			 = stdlib.VersionForTag()
		}
		return 
	}
	sort.Slice(, func(,  int) bool {
		return semver.Compare(([]), ([])) > 0
	})

	 := new(versionParser)
	for ,  := range  {
		if  := .parseFile();  != nil {
			return nil, 
		}
	}
	if len(.res) == 0 {
		return nil, fmt.Errorf("apiVersions should not be empty")
	}
	return .res, nil
}
LoadAPIFiles loads data about the API for the given package from dir.
func (,  string) ([]string, error) {
	var  string
	if stdlib.Contains() {
		 = filepath.Join(filepath.Clean(runtime.GOROOT()), "api", "go*.txt")
	} else {
		 = filepath.Join(, , "v*.txt")
	}

	,  := filepath.Glob()
	if  != nil {
		return nil, 
	}
	if len() == 0 {
		return nil, fmt.Errorf("no files matching %q", )
	}
	return , nil
}
apiVersions is a map of packages to information about those packages' symbols and when they were added to Go. Only things added after Go1 are tracked. Version strings are of the form "1.1", "1.2", etc.
type apiVersions map[string]pkgAPIVersions // keyed by Go package ("net/http")
pkgAPIVersions contains information about which version of Go added certain package symbols. Only things added after Go1 are tracked. Version strings are of the form "1.1", "1.2", etc.
type pkgAPIVersions struct {
	constSince  map[string]string
	varSince    map[string]string
	typeSince   map[string]string            // "Server" -> "1.7"
	methodSince map[string]map[string]string // "*Server" ->"Shutdown"->1.8
	funcSince   map[string]string            // "NewServer" -> "1.7"
	fieldSince  map[string]map[string]string // "ClientTrace" -> "Got1xxResponse" -> "1.11"
}
versionedRow represents an API feature, a parsed line of a $GOROOT/api/go.*txt file.
type versionedRow struct {
	pkg        string // "net/http"
	kind       string // "type", "func", "method", "field" TODO: "const", "var"
	recv       string // for methods, the receiver type ("Server", "*Server")
	name       string // name of type, (struct) field, func, method
	structName string // for struct fields, the outer struct name
}
versionParser parses $GOROOT/api/go*.txt files and stores them in in its rows field.
type versionParser struct {
	res apiVersions // initialized lazily
}
parseFile parses the named <apidata>/VERSION.txt file. For each row, it updates the corresponding entry in vp.res to VERSION, overwriting any previous value.
func ( *versionParser) ( string) error {
	,  := os.Open()
	if  != nil {
		return 
	}
	defer .Close()
	 := filepath.Base()
	 := strings.TrimSuffix(, ".txt")
	 := bufio.NewScanner()
	for .Scan() {
		,  := parseRow(.Text())
		if ! {
			continue
		}
		if .res == nil {
			.res = make(apiVersions)
		}
		,  := .res[.pkg]
		if ! {
			 = pkgAPIVersions{
				constSince:  make(map[string]string),
				varSince:    make(map[string]string),
				typeSince:   make(map[string]string),
				methodSince: make(map[string]map[string]string),
				funcSince:   make(map[string]string),
				fieldSince:  make(map[string]map[string]string),
			}
			.res[.pkg] = 
		}
		switch .kind {
		case "const":
			.constSince[.name] = 
		case "var":
			.varSince[.name] = 
		case "func":
			.funcSince[.name] = 
		case "type":
			.typeSince[.name] = 
		case "method":
			if ,  := .methodSince[.recv]; ! {
				.methodSince[.recv] = make(map[string]string)
			}
			.methodSince[.recv][.name] = 
		case "field":
			if ,  := .fieldSince[.structName]; ! {
				.fieldSince[.structName] = make(map[string]string)
			}
			.fieldSince[.structName][.name] = 
		}
	}
	return .Err()
}
func ( string) ( versionedRow,  bool) {
Skip comments, blank lines, etc.
		return
	}
	 := [len("pkg "):]
	 := strings.IndexFunc(, func( rune) bool { return !(unicode.IsLetter() ||  == '.' ||  == '/' || unicode.IsDigit()) })
	if  == -1 {
		return
	}
	.pkg,  = [:], [:]
If the part after the pkg name isn't ", ", then it's a OS/ARCH-dependent line of the form: pkg syscall (darwin-amd64), const ImplementsGetwd = false We skip those for now.
		return
	}
	 = [len(", "):]
	switch {
	case strings.HasPrefix(, "type "):
		 = [len("type "):]
		 := strings.IndexByte(, ' ')
		if  == -1 {
			return
		}
		.name,  = [:], [+1:]
		switch {
		case strings.HasPrefix(, "struct, "):
			 = [len("struct, "):]
			if  := strings.IndexByte(, ' ');  != -1 {
				.kind = "field"
				.structName = .name
				.name = [:]
				return , true
			}
		case strings.HasPrefix(, "interface, "):
			 = [len("interface, "):]
			if  := strings.IndexByte(, '(');  != -1 {
				.kind = "method"
				.recv = .name
				.name = [:]
				return , true
			}
		default:
			.kind = "type"
			return , true
		}
	case strings.HasPrefix(, "const "):
		.kind = "const"
		 = [len("const "):]
		if  := strings.IndexByte(, ' ');  != -1 {
			.name = [:]
			return , true
		}
	case strings.HasPrefix(, "var "):
		.kind = "var"
		 = [len("var "):]
		if  := strings.IndexByte(, ' ');  != -1 {
			.name = [:]
			return , true
		}
	case strings.HasPrefix(, "func "):
		.kind = "func"
		 = [len("func "):]
		if  := strings.IndexByte(, '(');  != -1 {
			.name = [:]
			return , true
		}
	case strings.HasPrefix(, "method "): // "method (*File) SetModTime(time.Time)"
		.kind = "method"
		 = [len("method "):] // "(*File) SetModTime(time.Time)"
		 := strings.IndexByte(, ' ')
		if  == -1 {
			return
		}
		.recv = strings.Trim([:], "()")    // "*File"
		.recv = strings.TrimPrefix(.recv, "*") // "File"
		 = [+1:]                         // SetMode(os.FileMode)
		 := strings.IndexByte(, '(')
		if  == -1 {
			return
		}
		.name = [:]
		return , true
	}
	return // TODO: handle more cases