Update gitea-vet to v0.2.1 (#12282)
* change to new code location * vendor * tagged version v0.2.0 * gitea-vet v0.2.1 Co-authored-by: techknowlogick <techknowlogick@gitea.io>tokarchuk/v1.17
parent
eb60a5d054
commit
ee97e6a66a
@ -0,0 +1,30 @@ |
|||||||
|
# The full repository name |
||||||
|
repo: gitea/gitea-vet |
||||||
|
|
||||||
|
# Service type (gitea or github) |
||||||
|
service: gitea |
||||||
|
|
||||||
|
# Base URL for Gitea instance if using gitea service type (optional) |
||||||
|
base-url: https://gitea.com |
||||||
|
|
||||||
|
# Changelog groups and which labeled PRs to add to each group |
||||||
|
groups: |
||||||
|
- |
||||||
|
name: BREAKING |
||||||
|
labels: |
||||||
|
- breaking |
||||||
|
- |
||||||
|
name: FEATURES |
||||||
|
labels: |
||||||
|
- feature |
||||||
|
- |
||||||
|
name: BUGFIXES |
||||||
|
labels: |
||||||
|
- bug |
||||||
|
- |
||||||
|
name: ENHANCEMENTS |
||||||
|
labels: |
||||||
|
- enhancement |
||||||
|
|
||||||
|
# regex indicating which labels to skip for the changelog |
||||||
|
skip-labels: skip-changelog|backport\/.+ |
@ -0,0 +1,45 @@ |
|||||||
|
--- |
||||||
|
kind: pipeline |
||||||
|
name: compliance |
||||||
|
|
||||||
|
platform: |
||||||
|
os: linux |
||||||
|
arch: arm64 |
||||||
|
|
||||||
|
trigger: |
||||||
|
event: |
||||||
|
- pull_request |
||||||
|
|
||||||
|
steps: |
||||||
|
- name: check |
||||||
|
pull: always |
||||||
|
image: golang:1.14 |
||||||
|
environment: |
||||||
|
GOPROXY: https://goproxy.cn |
||||||
|
commands: |
||||||
|
- make build |
||||||
|
- make lint |
||||||
|
- make vet |
||||||
|
|
||||||
|
--- |
||||||
|
kind: pipeline |
||||||
|
name: build-master |
||||||
|
|
||||||
|
platform: |
||||||
|
os: linux |
||||||
|
arch: amd64 |
||||||
|
|
||||||
|
trigger: |
||||||
|
branch: |
||||||
|
- master |
||||||
|
event: |
||||||
|
- push |
||||||
|
|
||||||
|
steps: |
||||||
|
- name: build |
||||||
|
pull: always |
||||||
|
image: techknowlogick/xgo:latest |
||||||
|
environment: |
||||||
|
GOPROXY: https://goproxy.cn |
||||||
|
commands: |
||||||
|
- make build |
@ -0,0 +1,23 @@ |
|||||||
|
linters: |
||||||
|
enable: |
||||||
|
- deadcode |
||||||
|
- dogsled |
||||||
|
- dupl |
||||||
|
- errcheck |
||||||
|
- gocognit |
||||||
|
- goconst |
||||||
|
- gocritic |
||||||
|
- gocyclo |
||||||
|
- gofmt |
||||||
|
- golint |
||||||
|
- gosimple |
||||||
|
- govet |
||||||
|
- maligned |
||||||
|
- misspell |
||||||
|
- prealloc |
||||||
|
- staticcheck |
||||||
|
- structcheck |
||||||
|
- typecheck |
||||||
|
- unparam |
||||||
|
- unused |
||||||
|
- varcheck |
@ -0,0 +1,11 @@ |
|||||||
|
## [v0.2.1](https://gitea.com/gitea/gitea-vet/releases/tag/v0.2.1) - 2020-08-15 |
||||||
|
|
||||||
|
* BUGFIXES |
||||||
|
* Split migration check to Deps and Imports (#9) |
||||||
|
|
||||||
|
## [0.2.0](https://gitea.com/gitea/gitea-vet/pulls?q=&type=all&state=closed&milestone=1272) - 2020-07-20 |
||||||
|
|
||||||
|
* FEATURES |
||||||
|
* Add migrations check (#5) |
||||||
|
* BUGFIXES |
||||||
|
* Correct Import Paths (#6) |
@ -0,0 +1,22 @@ |
|||||||
|
GO ?= go
|
||||||
|
|
||||||
|
.PHONY: build |
||||||
|
build: |
||||||
|
$(GO) build
|
||||||
|
|
||||||
|
.PHONY: fmt |
||||||
|
fmt: |
||||||
|
$(GO) fmt ./...
|
||||||
|
|
||||||
|
.PHONY: vet |
||||||
|
vet: build |
||||||
|
$(GO) vet ./...
|
||||||
|
$(GO) vet -vettool=gitea-vet ./...
|
||||||
|
|
||||||
|
.PHONY: lint |
||||||
|
lint: |
||||||
|
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||||
|
export BINARY="golangci-lint"; \
|
||||||
|
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(shell $(GO) env GOPATH)/bin v1.24.0; \
|
||||||
|
fi
|
||||||
|
golangci-lint run --timeout 5m
|
@ -0,0 +1,11 @@ |
|||||||
|
# gitea-vet |
||||||
|
|
||||||
|
[![Build Status](https://drone.gitea.com/api/badges/gitea/gitea-vet/status.svg)](https://drone.gitea.com/gitea/gitea-vet) |
||||||
|
|
||||||
|
`go vet` tool for Gitea |
||||||
|
|
||||||
|
| Analyzer | Description | |
||||||
|
|------------|-----------------------------------------------------------------------------| |
||||||
|
| Imports | Checks for import sorting. stdlib->code.gitea.io->other | |
||||||
|
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs` | |
||||||
|
| Migrations | Checks for black-listed packages in `code.gitea.io/gitea/models/migrations` | |
@ -0,0 +1,77 @@ |
|||||||
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package checks |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"os/exec" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"golang.org/x/tools/go/analysis" |
||||||
|
) |
||||||
|
|
||||||
|
var Migrations = &analysis.Analyzer{ |
||||||
|
Name: "migrations", |
||||||
|
Doc: "check migrations for black-listed packages.", |
||||||
|
Run: checkMigrations, |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
migrationDepBlockList = []string{ |
||||||
|
"code.gitea.io/gitea/models", |
||||||
|
} |
||||||
|
migrationImpBlockList = []string{ |
||||||
|
"code.gitea.io/gitea/modules/structs", |
||||||
|
} |
||||||
|
) |
||||||
|
|
||||||
|
func checkMigrations(pass *analysis.Pass) (interface{}, error) { |
||||||
|
if !strings.EqualFold(pass.Pkg.Path(), "code.gitea.io/gitea/models/migrations") { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
if _, err := exec.LookPath("go"); err != nil { |
||||||
|
return nil, errors.New("go was not found in the PATH") |
||||||
|
} |
||||||
|
|
||||||
|
depsCmd := exec.Command("go", "list", "-f", `{{join .Deps "\n"}}`, "code.gitea.io/gitea/models/migrations") |
||||||
|
depsOut, err := depsCmd.Output() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
deps := strings.Split(string(depsOut), "\n") |
||||||
|
for _, dep := range deps { |
||||||
|
if stringInSlice(dep, migrationDepBlockList) { |
||||||
|
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot depend on the following packages: %s", migrationDepBlockList) |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impsCmd := exec.Command("go", "list", "-f", `{{join .Imports "\n"}}`, "code.gitea.io/gitea/models/migrations") |
||||||
|
impsOut, err := impsCmd.Output() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
imps := strings.Split(string(impsOut), "\n") |
||||||
|
for _, imp := range imps { |
||||||
|
if stringInSlice(imp, migrationImpBlockList) { |
||||||
|
pass.Reportf(0, "code.gitea.io/gitea/models/migrations cannot import the following packages: %s", migrationImpBlockList) |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
|
||||||
|
func stringInSlice(needle string, haystack []string) bool { |
||||||
|
for _, h := range haystack { |
||||||
|
if strings.EqualFold(needle, h) { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
2
vendor/gitea.com/jolheiser/gitea-vet/go.mod → vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
2
vendor/gitea.com/jolheiser/gitea-vet/go.mod → vendor/code.gitea.io/gitea-vet/go.mod
generated
vendored
@ -1,4 +1,4 @@ |
|||||||
module gitea.com/jolheiser/gitea-vet |
module code.gitea.io/gitea-vet |
||||||
|
|
||||||
go 1.14 |
go 1.14 |
||||||
|
|
0
vendor/gitea.com/jolheiser/gitea-vet/go.sum → vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
0
vendor/gitea.com/jolheiser/gitea-vet/go.sum → vendor/code.gitea.io/gitea-vet/go.sum
generated
vendored
@ -1,7 +0,0 @@ |
|||||||
.PHONY: build |
|
||||||
build: |
|
||||||
go build
|
|
||||||
|
|
||||||
.PHONY: fmt |
|
||||||
fmt: |
|
||||||
go fmt ./...
|
|
@ -1,7 +0,0 @@ |
|||||||
# gitea-vet |
|
||||||
`go vet` tool for Gitea |
|
||||||
|
|
||||||
| Analyzer | Description | |
|
||||||
|----------|---------------------------------------------------------------------| |
|
||||||
| Imports | Checks for import sorting. stdlib->code.gitea.io->other | |
|
||||||
| License | Checks file headers for some form of `Copyright...YYYY...Gitea/Gogs`| |
|
@ -0,0 +1,421 @@ |
|||||||
|
// Copyright 2020 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 analysisinternal exposes internal-only fields from go/analysis.
|
||||||
|
package analysisinternal |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"go/ast" |
||||||
|
"go/token" |
||||||
|
"go/types" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"golang.org/x/tools/go/ast/astutil" |
||||||
|
"golang.org/x/tools/internal/lsp/fuzzy" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
GetTypeErrors func(p interface{}) []types.Error |
||||||
|
SetTypeErrors func(p interface{}, errors []types.Error) |
||||||
|
) |
||||||
|
|
||||||
|
func TypeErrorEndPos(fset *token.FileSet, src []byte, start token.Pos) token.Pos { |
||||||
|
// Get the end position for the type error.
|
||||||
|
offset, end := fset.PositionFor(start, false).Offset, start |
||||||
|
if offset >= len(src) { |
||||||
|
return end |
||||||
|
} |
||||||
|
if width := bytes.IndexAny(src[offset:], " \n,():;[]+-*"); width > 0 { |
||||||
|
end = start + token.Pos(width) |
||||||
|
} |
||||||
|
return end |
||||||
|
} |
||||||
|
|
||||||
|
func ZeroValue(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { |
||||||
|
under := typ |
||||||
|
if n, ok := typ.(*types.Named); ok { |
||||||
|
under = n.Underlying() |
||||||
|
} |
||||||
|
switch u := under.(type) { |
||||||
|
case *types.Basic: |
||||||
|
switch { |
||||||
|
case u.Info()&types.IsNumeric != 0: |
||||||
|
return &ast.BasicLit{Kind: token.INT, Value: "0"} |
||||||
|
case u.Info()&types.IsBoolean != 0: |
||||||
|
return &ast.Ident{Name: "false"} |
||||||
|
case u.Info()&types.IsString != 0: |
||||||
|
return &ast.BasicLit{Kind: token.STRING, Value: `""`} |
||||||
|
default: |
||||||
|
panic("unknown basic type") |
||||||
|
} |
||||||
|
case *types.Chan, *types.Interface, *types.Map, *types.Pointer, *types.Signature, *types.Slice, *types.Array: |
||||||
|
return ast.NewIdent("nil") |
||||||
|
case *types.Struct: |
||||||
|
texpr := TypeExpr(fset, f, pkg, typ) // typ because we want the name here.
|
||||||
|
if texpr == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.CompositeLit{ |
||||||
|
Type: texpr, |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// IsZeroValue checks whether the given expression is a 'zero value' (as determined by output of
|
||||||
|
// analysisinternal.ZeroValue)
|
||||||
|
func IsZeroValue(expr ast.Expr) bool { |
||||||
|
switch e := expr.(type) { |
||||||
|
case *ast.BasicLit: |
||||||
|
return e.Value == "0" || e.Value == `""` |
||||||
|
case *ast.Ident: |
||||||
|
return e.Name == "nil" || e.Name == "false" |
||||||
|
default: |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TypeExpr(fset *token.FileSet, f *ast.File, pkg *types.Package, typ types.Type) ast.Expr { |
||||||
|
switch t := typ.(type) { |
||||||
|
case *types.Basic: |
||||||
|
switch t.Kind() { |
||||||
|
case types.UnsafePointer: |
||||||
|
return &ast.SelectorExpr{X: ast.NewIdent("unsafe"), Sel: ast.NewIdent("Pointer")} |
||||||
|
default: |
||||||
|
return ast.NewIdent(t.Name()) |
||||||
|
} |
||||||
|
case *types.Pointer: |
||||||
|
x := TypeExpr(fset, f, pkg, t.Elem()) |
||||||
|
if x == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.UnaryExpr{ |
||||||
|
Op: token.MUL, |
||||||
|
X: x, |
||||||
|
} |
||||||
|
case *types.Array: |
||||||
|
elt := TypeExpr(fset, f, pkg, t.Elem()) |
||||||
|
if elt == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.ArrayType{ |
||||||
|
Len: &ast.BasicLit{ |
||||||
|
Kind: token.INT, |
||||||
|
Value: fmt.Sprintf("%d", t.Len()), |
||||||
|
}, |
||||||
|
Elt: elt, |
||||||
|
} |
||||||
|
case *types.Slice: |
||||||
|
elt := TypeExpr(fset, f, pkg, t.Elem()) |
||||||
|
if elt == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.ArrayType{ |
||||||
|
Elt: elt, |
||||||
|
} |
||||||
|
case *types.Map: |
||||||
|
key := TypeExpr(fset, f, pkg, t.Key()) |
||||||
|
value := TypeExpr(fset, f, pkg, t.Elem()) |
||||||
|
if key == nil || value == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.MapType{ |
||||||
|
Key: key, |
||||||
|
Value: value, |
||||||
|
} |
||||||
|
case *types.Chan: |
||||||
|
dir := ast.ChanDir(t.Dir()) |
||||||
|
if t.Dir() == types.SendRecv { |
||||||
|
dir = ast.SEND | ast.RECV |
||||||
|
} |
||||||
|
value := TypeExpr(fset, f, pkg, t.Elem()) |
||||||
|
if value == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return &ast.ChanType{ |
||||||
|
Dir: dir, |
||||||
|
Value: value, |
||||||
|
} |
||||||
|
case *types.Signature: |
||||||
|
var params []*ast.Field |
||||||
|
for i := 0; i < t.Params().Len(); i++ { |
||||||
|
p := TypeExpr(fset, f, pkg, t.Params().At(i).Type()) |
||||||
|
if p == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
params = append(params, &ast.Field{ |
||||||
|
Type: p, |
||||||
|
Names: []*ast.Ident{ |
||||||
|
{ |
||||||
|
Name: t.Params().At(i).Name(), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}) |
||||||
|
} |
||||||
|
var returns []*ast.Field |
||||||
|
for i := 0; i < t.Results().Len(); i++ { |
||||||
|
r := TypeExpr(fset, f, pkg, t.Results().At(i).Type()) |
||||||
|
if r == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
returns = append(returns, &ast.Field{ |
||||||
|
Type: r, |
||||||
|
}) |
||||||
|
} |
||||||
|
return &ast.FuncType{ |
||||||
|
Params: &ast.FieldList{ |
||||||
|
List: params, |
||||||
|
}, |
||||||
|
Results: &ast.FieldList{ |
||||||
|
List: returns, |
||||||
|
}, |
||||||
|
} |
||||||
|
case *types.Named: |
||||||
|
if t.Obj().Pkg() == nil { |
||||||
|
return ast.NewIdent(t.Obj().Name()) |
||||||
|
} |
||||||
|
if t.Obj().Pkg() == pkg { |
||||||
|
return ast.NewIdent(t.Obj().Name()) |
||||||
|
} |
||||||
|
pkgName := t.Obj().Pkg().Name() |
||||||
|
// If the file already imports the package under another name, use that.
|
||||||
|
for _, group := range astutil.Imports(fset, f) { |
||||||
|
for _, cand := range group { |
||||||
|
if strings.Trim(cand.Path.Value, `"`) == t.Obj().Pkg().Path() { |
||||||
|
if cand.Name != nil && cand.Name.Name != "" { |
||||||
|
pkgName = cand.Name.Name |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if pkgName == "." { |
||||||
|
return ast.NewIdent(t.Obj().Name()) |
||||||
|
} |
||||||
|
return &ast.SelectorExpr{ |
||||||
|
X: ast.NewIdent(pkgName), |
||||||
|
Sel: ast.NewIdent(t.Obj().Name()), |
||||||
|
} |
||||||
|
default: |
||||||
|
return nil // TODO: anonymous structs, but who does that
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type TypeErrorPass string |
||||||
|
|
||||||
|
const ( |
||||||
|
NoNewVars TypeErrorPass = "nonewvars" |
||||||
|
NoResultValues TypeErrorPass = "noresultvalues" |
||||||
|
UndeclaredName TypeErrorPass = "undeclaredname" |
||||||
|
) |
||||||
|
|
||||||
|
// StmtToInsertVarBefore returns the ast.Stmt before which we can safely insert a new variable.
|
||||||
|
// Some examples:
|
||||||
|
//
|
||||||
|
// Basic Example:
|
||||||
|
// z := 1
|
||||||
|
// y := z + x
|
||||||
|
// If x is undeclared, then this function would return `y := z + x`, so that we
|
||||||
|
// can insert `x := ` on the line before `y := z + x`.
|
||||||
|
//
|
||||||
|
// If stmt example:
|
||||||
|
// if z == 1 {
|
||||||
|
// } else if z == y {}
|
||||||
|
// If y is undeclared, then this function would return `if z == 1 {`, because we cannot
|
||||||
|
// insert a statement between an if and an else if statement. As a result, we need to find
|
||||||
|
// the top of the if chain to insert `y := ` before.
|
||||||
|
func StmtToInsertVarBefore(path []ast.Node) ast.Stmt { |
||||||
|
enclosingIndex := -1 |
||||||
|
for i, p := range path { |
||||||
|
if _, ok := p.(ast.Stmt); ok { |
||||||
|
enclosingIndex = i |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
if enclosingIndex == -1 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
enclosingStmt := path[enclosingIndex] |
||||||
|
switch enclosingStmt.(type) { |
||||||
|
case *ast.IfStmt: |
||||||
|
// The enclosingStmt is inside of the if declaration,
|
||||||
|
// We need to check if we are in an else-if stmt and
|
||||||
|
// get the base if statement.
|
||||||
|
return baseIfStmt(path, enclosingIndex) |
||||||
|
case *ast.CaseClause: |
||||||
|
// Get the enclosing switch stmt if the enclosingStmt is
|
||||||
|
// inside of the case statement.
|
||||||
|
for i := enclosingIndex + 1; i < len(path); i++ { |
||||||
|
if node, ok := path[i].(*ast.SwitchStmt); ok { |
||||||
|
return node |
||||||
|
} else if node, ok := path[i].(*ast.TypeSwitchStmt); ok { |
||||||
|
return node |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if len(path) <= enclosingIndex+1 { |
||||||
|
return enclosingStmt.(ast.Stmt) |
||||||
|
} |
||||||
|
// Check if the enclosing statement is inside another node.
|
||||||
|
switch expr := path[enclosingIndex+1].(type) { |
||||||
|
case *ast.IfStmt: |
||||||
|
// Get the base if statement.
|
||||||
|
return baseIfStmt(path, enclosingIndex+1) |
||||||
|
case *ast.ForStmt: |
||||||
|
if expr.Init == enclosingStmt || expr.Post == enclosingStmt { |
||||||
|
return expr |
||||||
|
} |
||||||
|
} |
||||||
|
return enclosingStmt.(ast.Stmt) |
||||||
|
} |
||||||
|
|
||||||
|
// baseIfStmt walks up the if/else-if chain until we get to
|
||||||
|
// the top of the current if chain.
|
||||||
|
func baseIfStmt(path []ast.Node, index int) ast.Stmt { |
||||||
|
stmt := path[index] |
||||||
|
for i := index + 1; i < len(path); i++ { |
||||||
|
if node, ok := path[i].(*ast.IfStmt); ok && node.Else == stmt { |
||||||
|
stmt = node |
||||||
|
continue |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
return stmt.(ast.Stmt) |
||||||
|
} |
||||||
|
|
||||||
|
// WalkASTWithParent walks the AST rooted at n. The semantics are
|
||||||
|
// similar to ast.Inspect except it does not call f(nil).
|
||||||
|
func WalkASTWithParent(n ast.Node, f func(n ast.Node, parent ast.Node) bool) { |
||||||
|
var ancestors []ast.Node |
||||||
|
ast.Inspect(n, func(n ast.Node) (recurse bool) { |
||||||
|
if n == nil { |
||||||
|
ancestors = ancestors[:len(ancestors)-1] |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
var parent ast.Node |
||||||
|
if len(ancestors) > 0 { |
||||||
|
parent = ancestors[len(ancestors)-1] |
||||||
|
} |
||||||
|
ancestors = append(ancestors, n) |
||||||
|
return f(n, parent) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// FindMatchingIdents finds all identifiers in 'node' that match any of the given types.
|
||||||
|
// 'pos' represents the position at which the identifiers may be inserted. 'pos' must be within
|
||||||
|
// the scope of each of identifier we select. Otherwise, we will insert a variable at 'pos' that
|
||||||
|
// is unrecognized.
|
||||||
|
func FindMatchingIdents(typs []types.Type, node ast.Node, pos token.Pos, info *types.Info, pkg *types.Package) map[types.Type][]*ast.Ident { |
||||||
|
matches := map[types.Type][]*ast.Ident{} |
||||||
|
// Initialize matches to contain the variable types we are searching for.
|
||||||
|
for _, typ := range typs { |
||||||
|
if typ == nil { |
||||||
|
continue |
||||||
|
} |
||||||
|
matches[typ] = []*ast.Ident{} |
||||||
|
} |
||||||
|
seen := map[types.Object]struct{}{} |
||||||
|
ast.Inspect(node, func(n ast.Node) bool { |
||||||
|
if n == nil { |
||||||
|
return false |
||||||
|
} |
||||||
|
// Prevent circular definitions. If 'pos' is within an assignment statement, do not
|
||||||
|
// allow any identifiers in that assignment statement to be selected. Otherwise,
|
||||||
|
// we could do the following, where 'x' satisfies the type of 'f0':
|
||||||
|
//
|
||||||
|
// x := fakeStruct{f0: x}
|
||||||
|
//
|
||||||
|
assignment, ok := n.(*ast.AssignStmt) |
||||||
|
if ok && pos > assignment.Pos() && pos <= assignment.End() { |
||||||
|
return false |
||||||
|
} |
||||||
|
if n.End() > pos { |
||||||
|
return n.Pos() <= pos |
||||||
|
} |
||||||
|
ident, ok := n.(*ast.Ident) |
||||||
|
if !ok || ident.Name == "_" { |
||||||
|
return true |
||||||
|
} |
||||||
|
obj := info.Defs[ident] |
||||||
|
if obj == nil || obj.Type() == nil { |
||||||
|
return true |
||||||
|
} |
||||||
|
if _, ok := obj.(*types.TypeName); ok { |
||||||
|
return true |
||||||
|
} |
||||||
|
// Prevent duplicates in matches' values.
|
||||||
|
if _, ok = seen[obj]; ok { |
||||||
|
return true |
||||||
|
} |
||||||
|
seen[obj] = struct{}{} |
||||||
|
// Find the scope for the given position. Then, check whether the object
|
||||||
|
// exists within the scope.
|
||||||
|
innerScope := pkg.Scope().Innermost(pos) |
||||||
|
if innerScope == nil { |
||||||
|
return true |
||||||
|
} |
||||||
|
_, foundObj := innerScope.LookupParent(ident.Name, pos) |
||||||
|
if foundObj != obj { |
||||||
|
return true |
||||||
|
} |
||||||
|
// The object must match one of the types that we are searching for.
|
||||||
|
if idents, ok := matches[obj.Type()]; ok { |
||||||
|
matches[obj.Type()] = append(idents, ast.NewIdent(ident.Name)) |
||||||
|
} |
||||||
|
// If the object type does not exactly match any of the target types, greedily
|
||||||
|
// find the first target type that the object type can satisfy.
|
||||||
|
for typ := range matches { |
||||||
|
if obj.Type() == typ { |
||||||
|
continue |
||||||
|
} |
||||||
|
if equivalentTypes(obj.Type(), typ) { |
||||||
|
matches[typ] = append(matches[typ], ast.NewIdent(ident.Name)) |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
}) |
||||||
|
return matches |
||||||
|
} |
||||||
|
|
||||||
|
func equivalentTypes(want, got types.Type) bool { |
||||||
|
if want == got || types.Identical(want, got) { |
||||||
|
return true |
||||||
|
} |
||||||
|
// Code segment to help check for untyped equality from (golang/go#32146).
|
||||||
|
if rhs, ok := want.(*types.Basic); ok && rhs.Info()&types.IsUntyped > 0 { |
||||||
|
if lhs, ok := got.Underlying().(*types.Basic); ok { |
||||||
|
return rhs.Info()&types.IsConstType == lhs.Info()&types.IsConstType |
||||||
|
} |
||||||
|
} |
||||||
|
return types.AssignableTo(want, got) |
||||||
|
} |
||||||
|
|
||||||
|
// FindBestMatch employs fuzzy matching to evaluate the similarity of each given identifier to the
|
||||||
|
// given pattern. We return the identifier whose name is most similar to the pattern.
|
||||||
|
func FindBestMatch(pattern string, idents []*ast.Ident) ast.Expr { |
||||||
|
fuzz := fuzzy.NewMatcher(pattern) |
||||||
|
var bestFuzz ast.Expr |
||||||
|
highScore := float32(-1) // minimum score is -1 (no match)
|
||||||
|
for _, ident := range idents { |
||||||
|
// TODO: Improve scoring algorithm.
|
||||||
|
score := fuzz.Score(ident.Name) |
||||||
|
if score > highScore { |
||||||
|
highScore = score |
||||||
|
bestFuzz = ident |
||||||
|
} else if score == -1 { |
||||||
|
// Order matters in the fuzzy matching algorithm. If we find no match
|
||||||
|
// when matching the target to the identifier, try matching the identifier
|
||||||
|
// to the target.
|
||||||
|
revFuzz := fuzzy.NewMatcher(ident.Name) |
||||||
|
revScore := revFuzz.Score(pattern) |
||||||
|
if revScore > highScore { |
||||||
|
highScore = revScore |
||||||
|
bestFuzz = ident |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return bestFuzz |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
// Copyright 2019 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 core provides support for event based telemetry.
|
||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"time" |
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label" |
||||||
|
) |
||||||
|
|
||||||
|
// Event holds the information about an event of note that ocurred.
|
||||||
|
type Event struct { |
||||||
|
at time.Time |
||||||
|
|
||||||
|
// As events are often on the stack, storing the first few labels directly
|
||||||
|
// in the event can avoid an allocation at all for the very common cases of
|
||||||
|
// simple events.
|
||||||
|
// The length needs to be large enough to cope with the majority of events
|
||||||
|
// but no so large as to cause undue stack pressure.
|
||||||
|
// A log message with two values will use 3 labels (one for each value and
|
||||||
|
// one for the message itself).
|
||||||
|
|
||||||
|
static [3]label.Label // inline storage for the first few labels
|
||||||
|
dynamic []label.Label // dynamically sized storage for remaining labels
|
||||||
|
} |
||||||
|
|
||||||
|
// eventLabelMap implements label.Map for a the labels of an Event.
|
||||||
|
type eventLabelMap struct { |
||||||
|
event Event |
||||||
|
} |
||||||
|
|
||||||
|
func (ev Event) At() time.Time { return ev.at } |
||||||
|
|
||||||
|
func (ev Event) Format(f fmt.State, r rune) { |
||||||
|
if !ev.at.IsZero() { |
||||||
|
fmt.Fprint(f, ev.at.Format("2006/01/02 15:04:05 ")) |
||||||
|
} |
||||||
|
for index := 0; ev.Valid(index); index++ { |
||||||
|
if l := ev.Label(index); l.Valid() { |
||||||
|
fmt.Fprintf(f, "\n\t%v", l) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (ev Event) Valid(index int) bool { |
||||||
|
return index >= 0 && index < len(ev.static)+len(ev.dynamic) |
||||||
|
} |
||||||
|
|
||||||
|
func (ev Event) Label(index int) label.Label { |
||||||
|
if index < len(ev.static) { |
||||||
|
return ev.static[index] |
||||||
|
} |
||||||
|
return ev.dynamic[index-len(ev.static)] |
||||||
|
} |
||||||
|
|
||||||
|
func (ev Event) Find(key label.Key) label.Label { |
||||||
|
for _, l := range ev.static { |
||||||
|
if l.Key() == key { |
||||||
|
return l |
||||||
|
} |
||||||
|
} |
||||||
|
for _, l := range ev.dynamic { |
||||||
|
if l.Key() == key { |
||||||
|
return l |
||||||
|
} |
||||||
|
} |
||||||
|
return label.Label{} |
||||||
|
} |
||||||
|
|
||||||
|
func MakeEvent(static [3]label.Label, labels []label.Label) Event { |
||||||
|
return Event{ |
||||||
|
static: static, |
||||||
|
dynamic: labels, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// CloneEvent event returns a copy of the event with the time adjusted to at.
|
||||||
|
func CloneEvent(ev Event, at time.Time) Event { |
||||||
|
ev.at = at |
||||||
|
return ev |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright 2019 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
"sync/atomic" |
||||||
|
"time" |
||||||
|
"unsafe" |
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label" |
||||||
|
) |
||||||
|
|
||||||
|
// Exporter is a function that handles events.
|
||||||
|
// It may return a modified context and event.
|
||||||
|
type Exporter func(context.Context, Event, label.Map) context.Context |
||||||
|
|
||||||
|
var ( |
||||||
|
exporter unsafe.Pointer |
||||||
|
) |
||||||
|
|
||||||
|
// SetExporter sets the global exporter function that handles all events.
|
||||||
|
// The exporter is called synchronously from the event call site, so it should
|
||||||
|
// return quickly so as not to hold up user code.
|
||||||
|
func SetExporter(e Exporter) { |
||||||
|
p := unsafe.Pointer(&e) |
||||||
|
if e == nil { |
||||||
|
// &e is always valid, and so p is always valid, but for the early abort
|
||||||
|
// of ProcessEvent to be efficient it needs to make the nil check on the
|
||||||
|
// pointer without having to dereference it, so we make the nil function
|
||||||
|
// also a nil pointer
|
||||||
|
p = nil |
||||||
|
} |
||||||
|
atomic.StorePointer(&exporter, p) |
||||||
|
} |
||||||
|
|
||||||
|
// deliver is called to deliver an event to the supplied exporter.
|
||||||
|
// it will fill in the time.
|
||||||
|
func deliver(ctx context.Context, exporter Exporter, ev Event) context.Context { |
||||||
|
// add the current time to the event
|
||||||
|
ev.at = time.Now() |
||||||
|
// hand the event off to the current exporter
|
||||||
|
return exporter(ctx, ev, ev) |
||||||
|
} |
||||||
|
|
||||||
|
// Export is called to deliver an event to the global exporter if set.
|
||||||
|
func Export(ctx context.Context, ev Event) context.Context { |
||||||
|
// get the global exporter and abort early if there is not one
|
||||||
|
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) |
||||||
|
if exporterPtr == nil { |
||||||
|
return ctx |
||||||
|
} |
||||||
|
return deliver(ctx, *exporterPtr, ev) |
||||||
|
} |
||||||
|
|
||||||
|
// ExportPair is called to deliver a start event to the supplied exporter.
|
||||||
|
// It also returns a function that will deliver the end event to the same
|
||||||
|
// exporter.
|
||||||
|
// It will fill in the time.
|
||||||
|
func ExportPair(ctx context.Context, begin, end Event) (context.Context, func()) { |
||||||
|
// get the global exporter and abort early if there is not one
|
||||||
|
exporterPtr := (*Exporter)(atomic.LoadPointer(&exporter)) |
||||||
|
if exporterPtr == nil { |
||||||
|
return ctx, func() {} |
||||||
|
} |
||||||
|
ctx = deliver(ctx, *exporterPtr, begin) |
||||||
|
return ctx, func() { deliver(ctx, *exporterPtr, end) } |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
// Copyright 2019 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/keys" |
||||||
|
"golang.org/x/tools/internal/event/label" |
||||||
|
) |
||||||
|
|
||||||
|
// Log1 takes a message and one label delivers a log event to the exporter.
|
||||||
|
// It is a customized version of Print that is faster and does no allocation.
|
||||||
|
func Log1(ctx context.Context, message string, t1 label.Label) { |
||||||
|
Export(ctx, MakeEvent([3]label.Label{ |
||||||
|
keys.Msg.Of(message), |
||||||
|
t1, |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// Log2 takes a message and two labels and delivers a log event to the exporter.
|
||||||
|
// It is a customized version of Print that is faster and does no allocation.
|
||||||
|
func Log2(ctx context.Context, message string, t1 label.Label, t2 label.Label) { |
||||||
|
Export(ctx, MakeEvent([3]label.Label{ |
||||||
|
keys.Msg.Of(message), |
||||||
|
t1, |
||||||
|
t2, |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// Metric1 sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric1(ctx context.Context, t1 label.Label) context.Context { |
||||||
|
return Export(ctx, MakeEvent([3]label.Label{ |
||||||
|
keys.Metric.New(), |
||||||
|
t1, |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// Metric2 sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric2(ctx context.Context, t1, t2 label.Label) context.Context { |
||||||
|
return Export(ctx, MakeEvent([3]label.Label{ |
||||||
|
keys.Metric.New(), |
||||||
|
t1, |
||||||
|
t2, |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// Start1 sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start1(ctx context.Context, name string, t1 label.Label) (context.Context, func()) { |
||||||
|
return ExportPair(ctx, |
||||||
|
MakeEvent([3]label.Label{ |
||||||
|
keys.Start.Of(name), |
||||||
|
t1, |
||||||
|
}, nil), |
||||||
|
MakeEvent([3]label.Label{ |
||||||
|
keys.End.New(), |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// Start2 sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start2(ctx context.Context, name string, t1, t2 label.Label) (context.Context, func()) { |
||||||
|
return ExportPair(ctx, |
||||||
|
MakeEvent([3]label.Label{ |
||||||
|
keys.Start.Of(name), |
||||||
|
t1, |
||||||
|
t2, |
||||||
|
}, nil), |
||||||
|
MakeEvent([3]label.Label{ |
||||||
|
keys.End.New(), |
||||||
|
}, nil)) |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
// Copyright 2019 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 event provides a set of packages that cover the main
|
||||||
|
// concepts of telemetry in an implementation agnostic way.
|
||||||
|
package event |
@ -0,0 +1,127 @@ |
|||||||
|
// Copyright 2019 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 event |
||||||
|
|
||||||
|
import ( |
||||||
|
"context" |
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/core" |
||||||
|
"golang.org/x/tools/internal/event/keys" |
||||||
|
"golang.org/x/tools/internal/event/label" |
||||||
|
) |
||||||
|
|
||||||
|
// Exporter is a function that handles events.
|
||||||
|
// It may return a modified context and event.
|
||||||
|
type Exporter func(context.Context, core.Event, label.Map) context.Context |
||||||
|
|
||||||
|
// SetExporter sets the global exporter function that handles all events.
|
||||||
|
// The exporter is called synchronously from the event call site, so it should
|
||||||
|
// return quickly so as not to hold up user code.
|
||||||
|
func SetExporter(e Exporter) { |
||||||
|
core.SetExporter(core.Exporter(e)) |
||||||
|
} |
||||||
|
|
||||||
|
// Log takes a message and a label list and combines them into a single event
|
||||||
|
// before delivering them to the exporter.
|
||||||
|
func Log(ctx context.Context, message string, labels ...label.Label) { |
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{ |
||||||
|
keys.Msg.Of(message), |
||||||
|
}, labels)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsLog returns true if the event was built by the Log function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsLog(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Msg |
||||||
|
} |
||||||
|
|
||||||
|
// Error takes a message and a label list and combines them into a single event
|
||||||
|
// before delivering them to the exporter. It captures the error in the
|
||||||
|
// delivered event.
|
||||||
|
func Error(ctx context.Context, message string, err error, labels ...label.Label) { |
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{ |
||||||
|
keys.Msg.Of(message), |
||||||
|
keys.Err.Of(err), |
||||||
|
}, labels)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsError returns true if the event was built by the Error function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsError(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Msg && |
||||||
|
ev.Label(1).Key() == keys.Err |
||||||
|
} |
||||||
|
|
||||||
|
// Metric sends a label event to the exporter with the supplied labels.
|
||||||
|
func Metric(ctx context.Context, labels ...label.Label) { |
||||||
|
core.Export(ctx, core.MakeEvent([3]label.Label{ |
||||||
|
keys.Metric.New(), |
||||||
|
}, labels)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsMetric returns true if the event was built by the Metric function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsMetric(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Metric |
||||||
|
} |
||||||
|
|
||||||
|
// Label sends a label event to the exporter with the supplied labels.
|
||||||
|
func Label(ctx context.Context, labels ...label.Label) context.Context { |
||||||
|
return core.Export(ctx, core.MakeEvent([3]label.Label{ |
||||||
|
keys.Label.New(), |
||||||
|
}, labels)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsLabel returns true if the event was built by the Label function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsLabel(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Label |
||||||
|
} |
||||||
|
|
||||||
|
// Start sends a span start event with the supplied label list to the exporter.
|
||||||
|
// It also returns a function that will end the span, which should normally be
|
||||||
|
// deferred.
|
||||||
|
func Start(ctx context.Context, name string, labels ...label.Label) (context.Context, func()) { |
||||||
|
return core.ExportPair(ctx, |
||||||
|
core.MakeEvent([3]label.Label{ |
||||||
|
keys.Start.Of(name), |
||||||
|
}, labels), |
||||||
|
core.MakeEvent([3]label.Label{ |
||||||
|
keys.End.New(), |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsStart returns true if the event was built by the Start function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsStart(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Start |
||||||
|
} |
||||||
|
|
||||||
|
// IsEnd returns true if the event was built by the End function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsEnd(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.End |
||||||
|
} |
||||||
|
|
||||||
|
// Detach returns a context without an associated span.
|
||||||
|
// This allows the creation of spans that are not children of the current span.
|
||||||
|
func Detach(ctx context.Context) context.Context { |
||||||
|
return core.Export(ctx, core.MakeEvent([3]label.Label{ |
||||||
|
keys.Detach.New(), |
||||||
|
}, nil)) |
||||||
|
} |
||||||
|
|
||||||
|
// IsDetach returns true if the event was built by the Detach function.
|
||||||
|
// It is intended to be used in exporters to identify the semantics of the
|
||||||
|
// event when deciding what to do with it.
|
||||||
|
func IsDetach(ev core.Event) bool { |
||||||
|
return ev.Label(0).Key() == keys.Detach |
||||||
|
} |
@ -0,0 +1,564 @@ |
|||||||
|
// Copyright 2019 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 keys |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"math" |
||||||
|
"strconv" |
||||||
|
|
||||||
|
"golang.org/x/tools/internal/event/label" |
||||||
|
) |
||||||
|
|
||||||
|
// Value represents a key for untyped values.
|
||||||
|
type Value struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// New creates a new Key for untyped values.
|
||||||
|
func New(name, description string) *Value { |
||||||
|
return &Value{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Value) Name() string { return k.name } |
||||||
|
func (k *Value) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Value) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
fmt.Fprint(w, k.From(l)) |
||||||
|
} |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Value) Get(lm label.Map) interface{} { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Value) From(t label.Label) interface{} { return t.UnpackValue() } |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Value) Of(value interface{}) label.Label { return label.OfValue(k, value) } |
||||||
|
|
||||||
|
// Tag represents a key for tagging labels that have no value.
|
||||||
|
// These are used when the existence of the label is the entire information it
|
||||||
|
// carries, such as marking events to be of a specific kind, or from a specific
|
||||||
|
// package.
|
||||||
|
type Tag struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewTag creates a new Key for tagging labels.
|
||||||
|
func NewTag(name, description string) *Tag { |
||||||
|
return &Tag{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Tag) Name() string { return k.name } |
||||||
|
func (k *Tag) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Tag) Format(w io.Writer, buf []byte, l label.Label) {} |
||||||
|
|
||||||
|
// New creates a new Label with this key.
|
||||||
|
func (k *Tag) New() label.Label { return label.OfValue(k, nil) } |
||||||
|
|
||||||
|
// Int represents a key
|
||||||
|
type Int struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewInt creates a new Key for int values.
|
||||||
|
func NewInt(name, description string) *Int { |
||||||
|
return &Int{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Int) Name() string { return k.name } |
||||||
|
func (k *Int) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Int) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int) Of(v int) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int) Get(lm label.Map) int { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int) From(t label.Label) int { return int(t.Unpack64()) } |
||||||
|
|
||||||
|
// Int8 represents a key
|
||||||
|
type Int8 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewInt8 creates a new Key for int8 values.
|
||||||
|
func NewInt8(name, description string) *Int8 { |
||||||
|
return &Int8{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Int8) Name() string { return k.name } |
||||||
|
func (k *Int8) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Int8) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int8) Of(v int8) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int8) Get(lm label.Map) int8 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int8) From(t label.Label) int8 { return int8(t.Unpack64()) } |
||||||
|
|
||||||
|
// Int16 represents a key
|
||||||
|
type Int16 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewInt16 creates a new Key for int16 values.
|
||||||
|
func NewInt16(name, description string) *Int16 { |
||||||
|
return &Int16{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Int16) Name() string { return k.name } |
||||||
|
func (k *Int16) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Int16) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int16) Of(v int16) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int16) Get(lm label.Map) int16 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int16) From(t label.Label) int16 { return int16(t.Unpack64()) } |
||||||
|
|
||||||
|
// Int32 represents a key
|
||||||
|
type Int32 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewInt32 creates a new Key for int32 values.
|
||||||
|
func NewInt32(name, description string) *Int32 { |
||||||
|
return &Int32{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Int32) Name() string { return k.name } |
||||||
|
func (k *Int32) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Int32) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendInt(buf, int64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int32) Of(v int32) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int32) Get(lm label.Map) int32 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int32) From(t label.Label) int32 { return int32(t.Unpack64()) } |
||||||
|
|
||||||
|
// Int64 represents a key
|
||||||
|
type Int64 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewInt64 creates a new Key for int64 values.
|
||||||
|
func NewInt64(name, description string) *Int64 { |
||||||
|
return &Int64{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Int64) Name() string { return k.name } |
||||||
|
func (k *Int64) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Int64) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendInt(buf, k.From(l), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Int64) Of(v int64) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Int64) Get(lm label.Map) int64 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Int64) From(t label.Label) int64 { return int64(t.Unpack64()) } |
||||||
|
|
||||||
|
// UInt represents a key
|
||||||
|
type UInt struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewUInt creates a new Key for uint values.
|
||||||
|
func NewUInt(name, description string) *UInt { |
||||||
|
return &UInt{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *UInt) Name() string { return k.name } |
||||||
|
func (k *UInt) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *UInt) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt) Of(v uint) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt) Get(lm label.Map) uint { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt) From(t label.Label) uint { return uint(t.Unpack64()) } |
||||||
|
|
||||||
|
// UInt8 represents a key
|
||||||
|
type UInt8 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewUInt8 creates a new Key for uint8 values.
|
||||||
|
func NewUInt8(name, description string) *UInt8 { |
||||||
|
return &UInt8{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *UInt8) Name() string { return k.name } |
||||||
|
func (k *UInt8) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *UInt8) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt8) Of(v uint8) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt8) Get(lm label.Map) uint8 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt8) From(t label.Label) uint8 { return uint8(t.Unpack64()) } |
||||||
|
|
||||||
|
// UInt16 represents a key
|
||||||
|
type UInt16 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewUInt16 creates a new Key for uint16 values.
|
||||||
|
func NewUInt16(name, description string) *UInt16 { |
||||||
|
return &UInt16{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *UInt16) Name() string { return k.name } |
||||||
|
func (k *UInt16) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *UInt16) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt16) Of(v uint16) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt16) Get(lm label.Map) uint16 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt16) From(t label.Label) uint16 { return uint16(t.Unpack64()) } |
||||||
|
|
||||||
|
// UInt32 represents a key
|
||||||
|
type UInt32 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewUInt32 creates a new Key for uint32 values.
|
||||||
|
func NewUInt32(name, description string) *UInt32 { |
||||||
|
return &UInt32{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *UInt32) Name() string { return k.name } |
||||||
|
func (k *UInt32) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *UInt32) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendUint(buf, uint64(k.From(l)), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt32) Of(v uint32) label.Label { return label.Of64(k, uint64(v)) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt32) Get(lm label.Map) uint32 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt32) From(t label.Label) uint32 { return uint32(t.Unpack64()) } |
||||||
|
|
||||||
|
// UInt64 represents a key
|
||||||
|
type UInt64 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewUInt64 creates a new Key for uint64 values.
|
||||||
|
func NewUInt64(name, description string) *UInt64 { |
||||||
|
return &UInt64{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *UInt64) Name() string { return k.name } |
||||||
|
func (k *UInt64) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *UInt64) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendUint(buf, k.From(l), 10)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *UInt64) Of(v uint64) label.Label { return label.Of64(k, v) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *UInt64) Get(lm label.Map) uint64 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *UInt64) From(t label.Label) uint64 { return t.Unpack64() } |
||||||
|
|
||||||
|
// Float32 represents a key
|
||||||
|
type Float32 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewFloat32 creates a new Key for float32 values.
|
||||||
|
func NewFloat32(name, description string) *Float32 { |
||||||
|
return &Float32{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Float32) Name() string { return k.name } |
||||||
|
func (k *Float32) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Float32) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendFloat(buf, float64(k.From(l)), 'E', -1, 32)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Float32) Of(v float32) label.Label { |
||||||
|
return label.Of64(k, uint64(math.Float32bits(v))) |
||||||
|
} |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Float32) Get(lm label.Map) float32 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Float32) From(t label.Label) float32 { |
||||||
|
return math.Float32frombits(uint32(t.Unpack64())) |
||||||
|
} |
||||||
|
|
||||||
|
// Float64 represents a key
|
||||||
|
type Float64 struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewFloat64 creates a new Key for int64 values.
|
||||||
|
func NewFloat64(name, description string) *Float64 { |
||||||
|
return &Float64{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Float64) Name() string { return k.name } |
||||||
|
func (k *Float64) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Float64) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendFloat(buf, k.From(l), 'E', -1, 64)) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Float64) Of(v float64) label.Label { |
||||||
|
return label.Of64(k, math.Float64bits(v)) |
||||||
|
} |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Float64) Get(lm label.Map) float64 { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Float64) From(t label.Label) float64 { |
||||||
|
return math.Float64frombits(t.Unpack64()) |
||||||
|
} |
||||||
|
|
||||||
|
// String represents a key
|
||||||
|
type String struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewString creates a new Key for int64 values.
|
||||||
|
func NewString(name, description string) *String { |
||||||
|
return &String{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *String) Name() string { return k.name } |
||||||
|
func (k *String) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *String) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendQuote(buf, k.From(l))) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *String) Of(v string) label.Label { return label.OfString(k, v) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *String) Get(lm label.Map) string { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *String) From(t label.Label) string { return t.UnpackString() } |
||||||
|
|
||||||
|
// Boolean represents a key
|
||||||
|
type Boolean struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewBoolean creates a new Key for bool values.
|
||||||
|
func NewBoolean(name, description string) *Boolean { |
||||||
|
return &Boolean{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Boolean) Name() string { return k.name } |
||||||
|
func (k *Boolean) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Boolean) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
w.Write(strconv.AppendBool(buf, k.From(l))) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Boolean) Of(v bool) label.Label { |
||||||
|
if v { |
||||||
|
return label.Of64(k, 1) |
||||||
|
} |
||||||
|
return label.Of64(k, 0) |
||||||
|
} |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Boolean) Get(lm label.Map) bool { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Boolean) From(t label.Label) bool { return t.Unpack64() > 0 } |
||||||
|
|
||||||
|
// Error represents a key
|
||||||
|
type Error struct { |
||||||
|
name string |
||||||
|
description string |
||||||
|
} |
||||||
|
|
||||||
|
// NewError creates a new Key for int64 values.
|
||||||
|
func NewError(name, description string) *Error { |
||||||
|
return &Error{name: name, description: description} |
||||||
|
} |
||||||
|
|
||||||
|
func (k *Error) Name() string { return k.name } |
||||||
|
func (k *Error) Description() string { return k.description } |
||||||
|
|
||||||
|
func (k *Error) Format(w io.Writer, buf []byte, l label.Label) { |
||||||
|
io.WriteString(w, k.From(l).Error()) |
||||||
|
} |
||||||
|
|
||||||
|
// Of creates a new Label with this key and the supplied value.
|
||||||
|
func (k *Error) Of(v error) label.Label { return label.OfValue(k, v) } |
||||||
|
|
||||||
|
// Get can be used to get a label for the key from a label.Map.
|
||||||
|
func (k *Error) Get(lm label.Map) error { |
||||||
|
if t := lm.Find(k); t.Valid() { |
||||||
|
return k.From(t) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// From can be used to get a value from a Label.
|
||||||
|
func (k *Error) From(t label.Label) error { |
||||||
|
err, _ := t.UnpackValue().(error) |
||||||
|
return err |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
// Copyright 2020 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 keys |
||||||
|
|
||||||
|
var ( |
||||||
|
// Msg is a key used to add message strings to label lists.
|
||||||
|
Msg = NewString("message", "a readable message") |
||||||
|
// Label is a key used to indicate an event adds labels to the context.
|
||||||
|
Label = NewTag("label", "a label context marker") |
||||||
|
// Start is used for things like traces that have a name.
|
||||||
|
Start = NewString("start", "span start") |
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
End = NewTag("end", "a span end marker") |
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
Detach = NewTag("detach", "a span detach marker") |
||||||
|
// Err is a key used to add error values to label lists.
|
||||||
|
Err = NewError("error", "an error that occurred") |
||||||
|
// Metric is a key used to indicate an event records metrics.
|
||||||
|
Metric = NewTag("metric", "a metric event marker") |
||||||
|
) |
@ -0,0 +1,213 @@ |
|||||||
|
// Copyright 2019 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 label |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"reflect" |
||||||
|
"unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
// Key is used as the identity of a Label.
|
||||||
|
// Keys are intended to be compared by pointer only, the name should be unique
|
||||||
|
// for communicating with external systems, but it is not required or enforced.
|
||||||
|
type Key interface { |
||||||
|
// Name returns the key name.
|
||||||
|
Name() string |
||||||
|
// Description returns a string that can be used to describe the value.
|
||||||
|
Description() string |
||||||
|
|
||||||
|
// Format is used in formatting to append the value of the label to the
|
||||||
|
// supplied buffer.
|
||||||
|
// The formatter may use the supplied buf as a scratch area to avoid
|
||||||
|
// allocations.
|
||||||
|
Format(w io.Writer, buf []byte, l Label) |
||||||
|
} |
||||||
|
|
||||||
|
// Label holds a key and value pair.
|
||||||
|
// It is normally used when passing around lists of labels.
|
||||||
|
type Label struct { |
||||||
|
key Key |
||||||
|
packed uint64 |
||||||
|
untyped interface{} |
||||||
|
} |
||||||
|
|
||||||
|
// Map is the interface to a collection of Labels indexed by key.
|
||||||
|
type Map interface { |
||||||
|
// Find returns the label that matches the supplied key.
|
||||||
|
Find(key Key) Label |
||||||
|
} |
||||||
|
|
||||||
|
// List is the interface to something that provides an iterable
|
||||||
|
// list of labels.
|
||||||
|
// Iteration should start from 0 and continue until Valid returns false.
|
||||||
|
type List interface { |
||||||
|
// Valid returns true if the index is within range for the list.
|
||||||
|
// It does not imply the label at that index will itself be valid.
|
||||||
|
Valid(index int) bool |
||||||
|
// Label returns the label at the given index.
|
||||||
|
Label(index int) Label |
||||||
|
} |
||||||
|
|
||||||
|
// list implements LabelList for a list of Labels.
|
||||||
|
type list struct { |
||||||
|
labels []Label |
||||||
|
} |
||||||
|
|
||||||
|
// filter wraps a LabelList filtering out specific labels.
|
||||||
|
type filter struct { |
||||||
|
keys []Key |
||||||
|
underlying List |
||||||
|
} |
||||||
|
|
||||||
|
// listMap implements LabelMap for a simple list of labels.
|
||||||
|
type listMap struct { |
||||||
|
labels []Label |
||||||
|
} |
||||||
|
|
||||||
|
// mapChain implements LabelMap for a list of underlying LabelMap.
|
||||||
|
type mapChain struct { |
||||||
|
maps []Map |
||||||
|
} |
||||||
|
|
||||||
|
// OfValue creates a new label from the key and value.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func OfValue(k Key, value interface{}) Label { return Label{key: k, untyped: value} } |
||||||
|
|
||||||
|
// UnpackValue assumes the label was built using LabelOfValue and returns the value
|
||||||
|
// that was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) UnpackValue() interface{} { return t.untyped } |
||||||
|
|
||||||
|
// Of64 creates a new label from a key and a uint64. This is often
|
||||||
|
// used for non uint64 values that can be packed into a uint64.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func Of64(k Key, v uint64) Label { return Label{key: k, packed: v} } |
||||||
|
|
||||||
|
// Unpack64 assumes the label was built using LabelOf64 and returns the value that
|
||||||
|
// was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) Unpack64() uint64 { return t.packed } |
||||||
|
|
||||||
|
// OfString creates a new label from a key and a string.
|
||||||
|
// This method is for implementing new key types, label creation should
|
||||||
|
// normally be done with the Of method of the key.
|
||||||
|
func OfString(k Key, v string) Label { |
||||||
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) |
||||||
|
return Label{ |
||||||
|
key: k, |
||||||
|
packed: uint64(hdr.Len), |
||||||
|
untyped: unsafe.Pointer(hdr.Data), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// UnpackString assumes the label was built using LabelOfString and returns the
|
||||||
|
// value that was passed to that constructor.
|
||||||
|
// This method is for implementing new key types, for type safety normal
|
||||||
|
// access should be done with the From method of the key.
|
||||||
|
func (t Label) UnpackString() string { |
||||||
|
var v string |
||||||
|
hdr := (*reflect.StringHeader)(unsafe.Pointer(&v)) |
||||||
|
hdr.Data = uintptr(t.untyped.(unsafe.Pointer)) |
||||||
|
hdr.Len = int(t.packed) |
||||||
|
return *(*string)(unsafe.Pointer(hdr)) |
||||||
|
} |
||||||
|
|
||||||
|
// Valid returns true if the Label is a valid one (it has a key).
|
||||||
|
func (t Label) Valid() bool { return t.key != nil } |
||||||
|
|
||||||
|
// Key returns the key of this Label.
|
||||||
|
func (t Label) Key() Key { return t.key } |
||||||
|
|
||||||
|
// Format is used for debug printing of labels.
|
||||||
|
func (t Label) Format(f fmt.State, r rune) { |
||||||
|
if !t.Valid() { |
||||||
|
io.WriteString(f, `nil`) |
||||||
|
return |
||||||
|
} |
||||||
|
io.WriteString(f, t.Key().Name()) |
||||||
|
io.WriteString(f, "=") |
||||||
|
var buf [128]byte |
||||||
|
t.Key().Format(f, buf[:0], t) |
||||||
|
} |
||||||
|
|
||||||
|
func (l *list) Valid(index int) bool { |
||||||
|
return index >= 0 && index < len(l.labels) |
||||||
|
} |
||||||
|
|
||||||
|
func (l *list) Label(index int) Label { |
||||||
|
return l.labels[index] |
||||||
|
} |
||||||
|
|
||||||
|
func (f *filter) Valid(index int) bool { |
||||||
|
return f.underlying.Valid(index) |
||||||
|
} |
||||||
|
|
||||||
|
func (f *filter) Label(index int) Label { |
||||||
|
l := f.underlying.Label(index) |
||||||
|
for _, f := range f.keys { |
||||||
|
if l.Key() == f { |
||||||
|
return Label{} |
||||||
|
} |
||||||
|
} |
||||||
|
return l |
||||||
|
} |
||||||
|
|
||||||
|
func (lm listMap) Find(key Key) Label { |
||||||
|
for _, l := range lm.labels { |
||||||
|
if l.Key() == key { |
||||||
|
return l |
||||||
|
} |
||||||
|
} |
||||||
|
return Label{} |
||||||
|
} |
||||||
|
|
||||||
|
func (c mapChain) Find(key Key) Label { |
||||||
|
for _, src := range c.maps { |
||||||
|
l := src.Find(key) |
||||||
|
if l.Valid() { |
||||||
|
return l |
||||||
|
} |
||||||
|
} |
||||||
|
return Label{} |
||||||
|
} |
||||||
|
|
||||||
|
var emptyList = &list{} |
||||||
|
|
||||||
|
func NewList(labels ...Label) List { |
||||||
|
if len(labels) == 0 { |
||||||
|
return emptyList |
||||||
|
} |
||||||
|
return &list{labels: labels} |
||||||
|
} |
||||||
|
|
||||||
|
func Filter(l List, keys ...Key) List { |
||||||
|
if len(keys) == 0 { |
||||||
|
return l |
||||||
|
} |
||||||
|
return &filter{keys: keys, underlying: l} |
||||||
|
} |
||||||
|
|
||||||
|
func NewMap(labels ...Label) Map { |
||||||
|
return listMap{labels: labels} |
||||||
|
} |
||||||
|
|
||||||
|
func MergeMaps(srcs ...Map) Map { |
||||||
|
var nonNil []Map |
||||||
|
for _, src := range srcs { |
||||||
|
if src != nil { |
||||||
|
nonNil = append(nonNil, src) |
||||||
|
} |
||||||
|
} |
||||||
|
if len(nonNil) == 1 { |
||||||
|
return nonNil[0] |
||||||
|
} |
||||||
|
return mapChain{maps: nonNil} |
||||||
|
} |
@ -0,0 +1,102 @@ |
|||||||
|
// Copyright 2020 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 gocommand |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"context" |
||||||
|
"fmt" |
||||||
|
"os" |
||||||
|
"path/filepath" |
||||||
|
"regexp" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"golang.org/x/mod/semver" |
||||||
|
) |
||||||
|
|
||||||
|
// ModuleJSON holds information about a module.
|
||||||
|
type ModuleJSON struct { |
||||||
|
Path string // module path
|
||||||
|
Replace *ModuleJSON // replaced by this module
|
||||||
|
Main bool // is this the main module?
|
||||||
|
Indirect bool // is this module only an indirect dependency of main module?
|
||||||
|
Dir string // directory holding files for this module, if any
|
||||||
|
GoMod string // path to go.mod file for this module, if any
|
||||||
|
GoVersion string // go version used in module
|
||||||
|
} |
||||||
|
|
||||||
|
var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`) |
||||||
|
|
||||||
|
// VendorEnabled reports whether vendoring is enabled. It takes a *Runner to execute Go commands
|
||||||
|
// with the supplied context.Context and Invocation. The Invocation can contain pre-defined fields,
|
||||||
|
// of which only Verb and Args are modified to run the appropriate Go command.
|
||||||
|
// Inspired by setDefaultBuildMod in modload/init.go
|
||||||
|
func VendorEnabled(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { |
||||||
|
mainMod, go114, err := getMainModuleAnd114(ctx, inv, r) |
||||||
|
if err != nil { |
||||||
|
return nil, false, err |
||||||
|
} |
||||||
|
|
||||||
|
// We check the GOFLAGS to see if there is anything overridden or not.
|
||||||
|
inv.Verb = "env" |
||||||
|
inv.Args = []string{"GOFLAGS"} |
||||||
|
stdout, err := r.Run(ctx, inv) |
||||||
|
if err != nil { |
||||||
|
return nil, false, err |
||||||
|
} |
||||||
|
goflags := string(bytes.TrimSpace(stdout.Bytes())) |
||||||
|
matches := modFlagRegexp.FindStringSubmatch(goflags) |
||||||
|
var modFlag string |
||||||
|
if len(matches) != 0 { |
||||||
|
modFlag = matches[1] |
||||||
|
} |
||||||
|
if modFlag != "" { |
||||||
|
// Don't override an explicit '-mod=' argument.
|
||||||
|
return mainMod, modFlag == "vendor", nil |
||||||
|
} |
||||||
|
if mainMod == nil || !go114 { |
||||||
|
return mainMod, false, nil |
||||||
|
} |
||||||
|
// Check 1.14's automatic vendor mode.
|
||||||
|
if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() { |
||||||
|
if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 { |
||||||
|
// The Go version is at least 1.14, and a vendor directory exists.
|
||||||
|
// Set -mod=vendor by default.
|
||||||
|
return mainMod, true, nil |
||||||
|
} |
||||||
|
} |
||||||
|
return mainMod, false, nil |
||||||
|
} |
||||||
|
|
||||||
|
// getMainModuleAnd114 gets the main module's information and whether the
|
||||||
|
// go command in use is 1.14+. This is the information needed to figure out
|
||||||
|
// if vendoring should be enabled.
|
||||||
|
func getMainModuleAnd114(ctx context.Context, inv Invocation, r *Runner) (*ModuleJSON, bool, error) { |
||||||
|
const format = `{{.Path}} |
||||||
|
{{.Dir}} |
||||||
|
{{.GoMod}} |
||||||
|
{{.GoVersion}} |
||||||
|
{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}} |
||||||
|
` |
||||||
|
inv.Verb = "list" |
||||||
|
inv.Args = []string{"-m", "-f", format} |
||||||
|
stdout, err := r.Run(ctx, inv) |
||||||
|
if err != nil { |
||||||
|
return nil, false, err |
||||||
|
} |
||||||
|
|
||||||
|
lines := strings.Split(stdout.String(), "\n") |
||||||
|
if len(lines) < 5 { |
||||||
|
return nil, false, fmt.Errorf("unexpected stdout: %q", stdout.String()) |
||||||
|
} |
||||||
|
mod := &ModuleJSON{ |
||||||
|
Path: lines[0], |
||||||
|
Dir: lines[1], |
||||||
|
GoMod: lines[2], |
||||||
|
GoVersion: lines[3], |
||||||
|
Main: true, |
||||||
|
} |
||||||
|
return mod, lines[4] == "go1.14", nil |
||||||
|
} |
@ -0,0 +1,168 @@ |
|||||||
|
// Copyright 2019 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 fuzzy |
||||||
|
|
||||||
|
import ( |
||||||
|
"unicode" |
||||||
|
) |
||||||
|
|
||||||
|
// RuneRole specifies the role of a rune in the context of an input.
|
||||||
|
type RuneRole byte |
||||||
|
|
||||||
|
const ( |
||||||
|
// RNone specifies a rune without any role in the input (i.e., whitespace/non-ASCII).
|
||||||
|
RNone RuneRole = iota |
||||||
|
// RSep specifies a rune with the role of segment separator.
|
||||||
|
RSep |
||||||
|
// RTail specifies a rune which is a lower-case tail in a word in the input.
|
||||||
|
RTail |
||||||
|
// RUCTail specifies a rune which is an upper-case tail in a word in the input.
|
||||||
|
RUCTail |
||||||
|
// RHead specifies a rune which is the first character in a word in the input.
|
||||||
|
RHead |
||||||
|
) |
||||||
|
|
||||||
|
// RuneRoles detects the roles of each byte rune in an input string and stores it in the output
|
||||||
|
// slice. The rune role depends on the input type. Stops when it parsed all the runes in the string
|
||||||
|
// or when it filled the output. If output is nil, then it gets created.
|
||||||
|
func RuneRoles(str string, reuse []RuneRole) []RuneRole { |
||||||
|
var output []RuneRole |
||||||
|
if cap(reuse) < len(str) { |
||||||
|
output = make([]RuneRole, 0, len(str)) |
||||||
|
} else { |
||||||
|
output = reuse[:0] |
||||||
|
} |
||||||
|
|
||||||
|
prev, prev2 := rtNone, rtNone |
||||||
|
for i := 0; i < len(str); i++ { |
||||||
|
r := rune(str[i]) |
||||||
|
|
||||||
|
role := RNone |
||||||
|
|
||||||
|
curr := rtLower |
||||||
|
if str[i] <= unicode.MaxASCII { |
||||||
|
curr = runeType(rt[str[i]] - '0') |
||||||
|
} |
||||||
|
|
||||||
|
if curr == rtLower { |
||||||
|
if prev == rtNone || prev == rtPunct { |
||||||
|
role = RHead |
||||||
|
} else { |
||||||
|
role = RTail |
||||||
|
} |
||||||
|
} else if curr == rtUpper { |
||||||
|
role = RHead |
||||||
|
|
||||||
|
if prev == rtUpper { |
||||||
|
// This and previous characters are both upper case.
|
||||||
|
|
||||||
|
if i+1 == len(str) { |
||||||
|
// This is last character, previous was also uppercase -> this is UCTail
|
||||||
|
// i.e., (current char is C): aBC / BC / ABC
|
||||||
|
role = RUCTail |
||||||
|
} |
||||||
|
} |
||||||
|
} else if curr == rtPunct { |
||||||
|
switch r { |
||||||
|
case '.', ':': |
||||||
|
role = RSep |
||||||
|
} |
||||||
|
} |
||||||
|
if curr != rtLower { |
||||||
|
if i > 1 && output[i-1] == RHead && prev2 == rtUpper && (output[i-2] == RHead || output[i-2] == RUCTail) { |
||||||
|
// The previous two characters were uppercase. The current one is not a lower case, so the
|
||||||
|
// previous one can't be a HEAD. Make it a UCTail.
|
||||||
|
// i.e., (last char is current char - B must be a UCTail): ABC / ZABC / AB.
|
||||||
|
output[i-1] = RUCTail |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
output = append(output, role) |
||||||
|
prev2 = prev |
||||||
|
prev = curr |
||||||
|
} |
||||||
|
return output |
||||||
|
} |
||||||
|
|
||||||
|
type runeType byte |
||||||
|
|
||||||
|
const ( |
||||||
|
rtNone runeType = iota |
||||||
|
rtPunct |
||||||
|
rtLower |
||||||
|
rtUpper |
||||||
|
) |
||||||
|
|
||||||
|
const rt = "00000000000000000000000000000000000000000000001122222222221000000333333333333333333333333330000002222222222222222222222222200000" |
||||||
|
|
||||||
|
// LastSegment returns the substring representing the last segment from the input, where each
|
||||||
|
// byte has an associated RuneRole in the roles slice. This makes sense only for inputs of Symbol
|
||||||
|
// or Filename type.
|
||||||
|
func LastSegment(input string, roles []RuneRole) string { |
||||||
|
// Exclude ending separators.
|
||||||
|
end := len(input) - 1 |
||||||
|
for end >= 0 && roles[end] == RSep { |
||||||
|
end-- |
||||||
|
} |
||||||
|
if end < 0 { |
||||||
|
return "" |
||||||
|
} |
||||||
|
|
||||||
|
start := end - 1 |
||||||
|
for start >= 0 && roles[start] != RSep { |
||||||
|
start-- |
||||||
|
} |
||||||
|
|
||||||
|
return input[start+1 : end+1] |
||||||
|
} |
||||||
|
|
||||||
|
// ToLower transforms the input string to lower case, which is stored in the output byte slice.
|
||||||
|
// The lower casing considers only ASCII values - non ASCII values are left unmodified.
|
||||||
|
// Stops when parsed all input or when it filled the output slice. If output is nil, then it gets
|
||||||
|
// created.
|
||||||
|
func ToLower(input string, reuse []byte) []byte { |
||||||
|
output := reuse |
||||||
|
if cap(reuse) < len(input) { |
||||||
|
output = make([]byte, len(input)) |
||||||
|
} |
||||||
|
|
||||||
|
for i := 0; i < len(input); i++ { |
||||||
|
r := rune(input[i]) |
||||||
|
if r <= unicode.MaxASCII { |
||||||
|
if 'A' <= r && r <= 'Z' { |
||||||
|
r += 'a' - 'A' |
||||||
|
} |
||||||
|
} |
||||||
|
output[i] = byte(r) |
||||||
|
} |
||||||
|
return output[:len(input)] |
||||||
|
} |
||||||
|
|
||||||
|
// WordConsumer defines a consumer for a word delimited by the [start,end) byte offsets in an input
|
||||||
|
// (start is inclusive, end is exclusive).
|
||||||
|
type WordConsumer func(start, end int) |
||||||
|
|
||||||
|
// Words find word delimiters in an input based on its bytes' mappings to rune roles. The offset
|
||||||
|
// delimiters for each word are fed to the provided consumer function.
|
||||||
|
func Words(roles []RuneRole, consume WordConsumer) { |
||||||
|
var wordStart int |
||||||
|
for i, r := range roles { |
||||||
|
switch r { |
||||||
|
case RUCTail, RTail: |
||||||
|
case RHead, RNone, RSep: |
||||||
|
if i != wordStart { |
||||||
|
consume(wordStart, i) |
||||||
|
} |
||||||
|
wordStart = i |
||||||
|
if r != RHead { |
||||||
|
// Skip this character.
|
||||||
|
wordStart = i + 1 |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if wordStart != len(roles) { |
||||||
|
consume(wordStart, len(roles)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,398 @@ |
|||||||
|
// Copyright 2019 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 fuzzy implements a fuzzy matching algorithm.
|
||||||
|
package fuzzy |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// MaxInputSize is the maximum size of the input scored against the fuzzy matcher. Longer inputs
|
||||||
|
// will be truncated to this size.
|
||||||
|
MaxInputSize = 127 |
||||||
|
// MaxPatternSize is the maximum size of the pattern used to construct the fuzzy matcher. Longer
|
||||||
|
// inputs are truncated to this size.
|
||||||
|
MaxPatternSize = 63 |
||||||
|
) |
||||||
|
|
||||||
|
type scoreVal int |
||||||
|
|
||||||
|
func (s scoreVal) val() int { |
||||||
|
return int(s) >> 1 |
||||||
|
} |
||||||
|
|
||||||
|
func (s scoreVal) prevK() int { |
||||||
|
return int(s) & 1 |
||||||
|
} |
||||||
|
|
||||||
|
func score(val int, prevK int /*0 or 1*/) scoreVal { |
||||||
|
return scoreVal(val<<1 + prevK) |
||||||
|
} |
||||||
|
|
||||||
|
// Matcher implements a fuzzy matching algorithm for scoring candidates against a pattern.
|
||||||
|
// The matcher does not support parallel usage.
|
||||||
|
type Matcher struct { |
||||||
|
pattern string |
||||||
|
patternLower []byte // lower-case version of the pattern
|
||||||
|
patternShort []byte // first characters of the pattern
|
||||||
|
caseSensitive bool // set if the pattern is mix-cased
|
||||||
|
|
||||||
|
patternRoles []RuneRole // the role of each character in the pattern
|
||||||
|
roles []RuneRole // the role of each character in the tested string
|
||||||
|
|
||||||
|
scores [MaxInputSize + 1][MaxPatternSize + 1][2]scoreVal |
||||||
|
|
||||||
|
scoreScale float32 |
||||||
|
|
||||||
|
lastCandidateLen int // in bytes
|
||||||
|
lastCandidateMatched bool |
||||||
|
|
||||||
|
// Here we save the last candidate in lower-case. This is basically a byte slice we reuse for
|
||||||
|
// performance reasons, so the slice is not reallocated for every candidate.
|
||||||
|
lowerBuf [MaxInputSize]byte |
||||||
|
rolesBuf [MaxInputSize]RuneRole |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Matcher) bestK(i, j int) int { |
||||||
|
if m.scores[i][j][0].val() < m.scores[i][j][1].val() { |
||||||
|
return 1 |
||||||
|
} |
||||||
|
return 0 |
||||||
|
} |
||||||
|
|
||||||
|
// NewMatcher returns a new fuzzy matcher for scoring candidates against the provided pattern.
|
||||||
|
func NewMatcher(pattern string) *Matcher { |
||||||
|
if len(pattern) > MaxPatternSize { |
||||||
|
pattern = pattern[:MaxPatternSize] |
||||||
|
} |
||||||
|
|
||||||
|
m := &Matcher{ |
||||||
|
pattern: pattern, |
||||||
|
patternLower: ToLower(pattern, nil), |
||||||
|
} |
||||||
|
|
||||||
|
for i, c := range m.patternLower { |
||||||
|
if pattern[i] != c { |
||||||
|
m.caseSensitive = true |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if len(pattern) > 3 { |
||||||
|
m.patternShort = m.patternLower[:3] |
||||||
|
} else { |
||||||
|
m.patternShort = m.patternLower |
||||||
|
} |
||||||
|
|
||||||
|
m.patternRoles = RuneRoles(pattern, nil) |
||||||
|
|
||||||
|
if len(pattern) > 0 { |
||||||
|
maxCharScore := 4 |
||||||
|
m.scoreScale = 1 / float32(maxCharScore*len(pattern)) |
||||||
|
} |
||||||
|
|
||||||
|
return m |
||||||
|
} |
||||||
|
|
||||||
|
// Score returns the score returned by matching the candidate to the pattern.
|
||||||
|
// This is not designed for parallel use. Multiple candidates must be scored sequentially.
|
||||||
|
// Returns a score between 0 and 1 (0 - no match, 1 - perfect match).
|
||||||
|
func (m *Matcher) Score(candidate string) float32 { |
||||||
|
if len(candidate) > MaxInputSize { |
||||||
|
candidate = candidate[:MaxInputSize] |
||||||
|
} |
||||||
|
lower := ToLower(candidate, m.lowerBuf[:]) |
||||||
|
m.lastCandidateLen = len(candidate) |
||||||
|
|
||||||
|
if len(m.pattern) == 0 { |
||||||
|
// Empty patterns perfectly match candidates.
|
||||||
|
return 1 |
||||||
|
} |
||||||
|
|
||||||
|
if m.match(candidate, lower) { |
||||||
|
sc := m.computeScore(candidate, lower) |
||||||
|
if sc > minScore/2 && !m.poorMatch() { |
||||||
|
m.lastCandidateMatched = true |
||||||
|
if len(m.pattern) == len(candidate) { |
||||||
|
// Perfect match.
|
||||||
|
return 1 |
||||||
|
} |
||||||
|
|
||||||
|
if sc < 0 { |
||||||
|
sc = 0 |
||||||
|
} |
||||||
|
normalizedScore := float32(sc) * m.scoreScale |
||||||
|
if normalizedScore > 1 { |
||||||
|
normalizedScore = 1 |
||||||
|
} |
||||||
|
|
||||||
|
return normalizedScore |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
m.lastCandidateMatched = false |
||||||
|
return -1 |
||||||
|
} |
||||||
|
|
||||||
|
const minScore = -10000 |
||||||
|
|
||||||
|
// MatchedRanges returns matches ranges for the last scored string as a flattened array of
|
||||||
|
// [begin, end) byte offset pairs.
|
||||||
|
func (m *Matcher) MatchedRanges() []int { |
||||||
|
if len(m.pattern) == 0 || !m.lastCandidateMatched { |
||||||
|
return nil |
||||||
|
} |
||||||
|
i, j := m.lastCandidateLen, len(m.pattern) |
||||||
|
if m.scores[i][j][0].val() < minScore/2 && m.scores[i][j][1].val() < minScore/2 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
var ret []int |
||||||
|
k := m.bestK(i, j) |
||||||
|
for i > 0 { |
||||||
|
take := (k == 1) |
||||||
|
k = m.scores[i][j][k].prevK() |
||||||
|
if take { |
||||||
|
if len(ret) == 0 || ret[len(ret)-1] != i { |
||||||
|
ret = append(ret, i) |
||||||
|
ret = append(ret, i-1) |
||||||
|
} else { |
||||||
|
ret[len(ret)-1] = i - 1 |
||||||
|
} |
||||||
|
j-- |
||||||
|
} |
||||||
|
i-- |
||||||
|
} |
||||||
|
// Reverse slice.
|
||||||
|
for i := 0; i < len(ret)/2; i++ { |
||||||
|
ret[i], ret[len(ret)-1-i] = ret[len(ret)-1-i], ret[i] |
||||||
|
} |
||||||
|
return ret |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Matcher) match(candidate string, candidateLower []byte) bool { |
||||||
|
i, j := 0, 0 |
||||||
|
for ; i < len(candidateLower) && j < len(m.patternLower); i++ { |
||||||
|
if candidateLower[i] == m.patternLower[j] { |
||||||
|
j++ |
||||||
|
} |
||||||
|
} |
||||||
|
if j != len(m.patternLower) { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// The input passes the simple test against pattern, so it is time to classify its characters.
|
||||||
|
// Character roles are used below to find the last segment.
|
||||||
|
m.roles = RuneRoles(candidate, m.rolesBuf[:]) |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Matcher) computeScore(candidate string, candidateLower []byte) int { |
||||||
|
pattLen, candLen := len(m.pattern), len(candidate) |
||||||
|
|
||||||
|
for j := 0; j <= len(m.pattern); j++ { |
||||||
|
m.scores[0][j][0] = minScore << 1 |
||||||
|
m.scores[0][j][1] = minScore << 1 |
||||||
|
} |
||||||
|
m.scores[0][0][0] = score(0, 0) // Start with 0.
|
||||||
|
|
||||||
|
segmentsLeft, lastSegStart := 1, 0 |
||||||
|
for i := 0; i < candLen; i++ { |
||||||
|
if m.roles[i] == RSep { |
||||||
|
segmentsLeft++ |
||||||
|
lastSegStart = i + 1 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// A per-character bonus for a consecutive match.
|
||||||
|
consecutiveBonus := 2 |
||||||
|
wordIdx := 0 // Word count within segment.
|
||||||
|
for i := 1; i <= candLen; i++ { |
||||||
|
|
||||||
|
role := m.roles[i-1] |
||||||
|
isHead := role == RHead |
||||||
|
|
||||||
|
if isHead { |
||||||
|
wordIdx++ |
||||||
|
} else if role == RSep && segmentsLeft > 1 { |
||||||
|
wordIdx = 0 |
||||||
|
segmentsLeft-- |
||||||
|
} |
||||||
|
|
||||||
|
var skipPenalty int |
||||||
|
if i == 1 || (i-1) == lastSegStart { |
||||||
|
// Skipping the start of first or last segment.
|
||||||
|
skipPenalty++ |
||||||
|
} |
||||||
|
|
||||||
|
for j := 0; j <= pattLen; j++ { |
||||||
|
// By default, we don't have a match. Fill in the skip data.
|
||||||
|
m.scores[i][j][1] = minScore << 1 |
||||||
|
|
||||||
|
// Compute the skip score.
|
||||||
|
k := 0 |
||||||
|
if m.scores[i-1][j][0].val() < m.scores[i-1][j][1].val() { |
||||||
|
k = 1 |
||||||
|
} |
||||||
|
|
||||||
|
skipScore := m.scores[i-1][j][k].val() |
||||||
|
// Do not penalize missing characters after the last matched segment.
|
||||||
|
if j != pattLen { |
||||||
|
skipScore -= skipPenalty |
||||||
|
} |
||||||
|
m.scores[i][j][0] = score(skipScore, k) |
||||||
|
|
||||||
|
if j == 0 || candidateLower[i-1] != m.patternLower[j-1] { |
||||||
|
// Not a match.
|
||||||
|
continue |
||||||
|
} |
||||||
|
pRole := m.patternRoles[j-1] |
||||||
|
|
||||||
|
if role == RTail && pRole == RHead { |
||||||
|
if j > 1 { |
||||||
|
// Not a match: a head in the pattern matches a tail character in the candidate.
|
||||||
|
continue |
||||||
|
} |
||||||
|
// Special treatment for the first character of the pattern. We allow
|
||||||
|
// matches in the middle of a word if they are long enough, at least
|
||||||
|
// min(3, pattern.length) characters.
|
||||||
|
if !bytes.HasPrefix(candidateLower[i-1:], m.patternShort) { |
||||||
|
continue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Compute the char score.
|
||||||
|
var charScore int |
||||||
|
// Bonus 1: the char is in the candidate's last segment.
|
||||||
|
if segmentsLeft <= 1 { |
||||||
|
charScore++ |
||||||
|
} |
||||||
|
// Bonus 2: Case match or a Head in the pattern aligns with one in the word.
|
||||||
|
// Single-case patterns lack segmentation signals and we assume any character
|
||||||
|
// can be a head of a segment.
|
||||||
|
if candidate[i-1] == m.pattern[j-1] || role == RHead && (!m.caseSensitive || pRole == RHead) { |
||||||
|
charScore++ |
||||||
|
} |
||||||
|
|
||||||
|
// Penalty 1: pattern char is Head, candidate char is Tail.
|
||||||
|
if role == RTail && pRole == RHead { |
||||||
|
charScore-- |
||||||
|
} |
||||||
|
// Penalty 2: first pattern character matched in the middle of a word.
|
||||||
|
if j == 1 && role == RTail { |
||||||
|
charScore -= 4 |
||||||
|
} |
||||||
|
|
||||||
|
// Third dimension encodes whether there is a gap between the previous match and the current
|
||||||
|
// one.
|
||||||
|
for k := 0; k < 2; k++ { |
||||||
|
sc := m.scores[i-1][j-1][k].val() + charScore |
||||||
|
|
||||||
|
isConsecutive := k == 1 || i-1 == 0 || i-1 == lastSegStart |
||||||
|
if isConsecutive { |
||||||
|
// Bonus 3: a consecutive match. First character match also gets a bonus to
|
||||||
|
// ensure prefix final match score normalizes to 1.0.
|
||||||
|
// Logically, this is a part of charScore, but we have to compute it here because it
|
||||||
|
// only applies for consecutive matches (k == 1).
|
||||||
|
sc += consecutiveBonus |
||||||
|
} |
||||||
|
if k == 0 { |
||||||
|
// Penalty 3: Matching inside a segment (and previous char wasn't matched). Penalize for the lack
|
||||||
|
// of alignment.
|
||||||
|
if role == RTail || role == RUCTail { |
||||||
|
sc -= 3 |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if sc > m.scores[i][j][1].val() { |
||||||
|
m.scores[i][j][1] = score(sc, k) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
result := m.scores[len(candidate)][len(m.pattern)][m.bestK(len(candidate), len(m.pattern))].val() |
||||||
|
|
||||||
|
return result |
||||||
|
} |
||||||
|
|
||||||
|
// ScoreTable returns the score table computed for the provided candidate. Used only for debugging.
|
||||||
|
func (m *Matcher) ScoreTable(candidate string) string { |
||||||
|
var buf bytes.Buffer |
||||||
|
|
||||||
|
var line1, line2, separator bytes.Buffer |
||||||
|
line1.WriteString("\t") |
||||||
|
line2.WriteString("\t") |
||||||
|
for j := 0; j < len(m.pattern); j++ { |
||||||
|
line1.WriteString(fmt.Sprintf("%c\t\t", m.pattern[j])) |
||||||
|
separator.WriteString("----------------") |
||||||
|
} |
||||||
|
|
||||||
|
buf.WriteString(line1.String()) |
||||||
|
buf.WriteString("\n") |
||||||
|
buf.WriteString(separator.String()) |
||||||
|
buf.WriteString("\n") |
||||||
|
|
||||||
|
for i := 1; i <= len(candidate); i++ { |
||||||
|
line1.Reset() |
||||||
|
line2.Reset() |
||||||
|
|
||||||
|
line1.WriteString(fmt.Sprintf("%c\t", candidate[i-1])) |
||||||
|
line2.WriteString("\t") |
||||||
|
|
||||||
|
for j := 1; j <= len(m.pattern); j++ { |
||||||
|
line1.WriteString(fmt.Sprintf("M%6d(%c)\t", m.scores[i][j][0].val(), dir(m.scores[i][j][0].prevK()))) |
||||||
|
line2.WriteString(fmt.Sprintf("H%6d(%c)\t", m.scores[i][j][1].val(), dir(m.scores[i][j][1].prevK()))) |
||||||
|
} |
||||||
|
buf.WriteString(line1.String()) |
||||||
|
buf.WriteString("\n") |
||||||
|
buf.WriteString(line2.String()) |
||||||
|
buf.WriteString("\n") |
||||||
|
buf.WriteString(separator.String()) |
||||||
|
buf.WriteString("\n") |
||||||
|
} |
||||||
|
|
||||||
|
return buf.String() |
||||||
|
} |
||||||
|
|
||||||
|
func dir(prevK int) rune { |
||||||
|
if prevK == 0 { |
||||||
|
return 'M' |
||||||
|
} |
||||||
|
return 'H' |
||||||
|
} |
||||||
|
|
||||||
|
func (m *Matcher) poorMatch() bool { |
||||||
|
if len(m.pattern) < 2 { |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
i, j := m.lastCandidateLen, len(m.pattern) |
||||||
|
k := m.bestK(i, j) |
||||||
|
|
||||||
|
var counter, len int |
||||||
|
for i > 0 { |
||||||
|
take := (k == 1) |
||||||
|
k = m.scores[i][j][k].prevK() |
||||||
|
if take { |
||||||
|
len++ |
||||||
|
if k == 0 && len < 3 && m.roles[i-1] == RTail { |
||||||
|
// Short match in the middle of a word
|
||||||
|
counter++ |
||||||
|
if counter > 1 { |
||||||
|
return true |
||||||
|
} |
||||||
|
} |
||||||
|
j-- |
||||||
|
} else { |
||||||
|
len = 0 |
||||||
|
} |
||||||
|
i-- |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
@ -1,27 +1,14 @@ |
|||||||
// Package packagesinternal exposes internal-only fields from go/packages.
|
// Package packagesinternal exposes internal-only fields from go/packages.
|
||||||
package packagesinternal |
package packagesinternal |
||||||
|
|
||||||
import "time" |
import ( |
||||||
|
"golang.org/x/tools/internal/gocommand" |
||||||
// Fields must match go list;
|
) |
||||||
type Module struct { |
|
||||||
Path string // module path
|
|
||||||
Version string // module version
|
|
||||||
Versions []string // available module versions (with -versions)
|
|
||||||
Replace *Module // replaced by this module
|
|
||||||
Time *time.Time // time version was created
|
|
||||||
Update *Module // available update, if any (with -u)
|
|
||||||
Main bool // is this the main module?
|
|
||||||
Indirect bool // is this module only an indirect dependency of main module?
|
|
||||||
Dir string // directory holding files for this module, if any
|
|
||||||
GoMod string // path to go.mod file used when loading this module, if any
|
|
||||||
GoVersion string // go version used in module
|
|
||||||
Error *ModuleError // error loading module
|
|
||||||
} |
|
||||||
type ModuleError struct { |
|
||||||
Err string // the error itself
|
|
||||||
} |
|
||||||
|
|
||||||
var GetForTest = func(p interface{}) string { return "" } |
var GetForTest = func(p interface{}) string { return "" } |
||||||
|
|
||||||
var GetModule = func(p interface{}) *Module { return nil } |
var GetGoCmdRunner = func(config interface{}) *gocommand.Runner { return nil } |
||||||
|
|
||||||
|
var SetGoCmdRunner = func(config interface{}, runner *gocommand.Runner) {} |
||||||
|
|
||||||
|
var TypecheckCgo int |
||||||
|
@ -0,0 +1,28 @@ |
|||||||
|
// Copyright 2020 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 typesinternal |
||||||
|
|
||||||
|
import ( |
||||||
|
"go/types" |
||||||
|
"reflect" |
||||||
|
"unsafe" |
||||||
|
) |
||||||
|
|
||||||
|
func SetUsesCgo(conf *types.Config) bool { |
||||||
|
v := reflect.ValueOf(conf).Elem() |
||||||
|
|
||||||
|
f := v.FieldByName("go115UsesCgo") |
||||||
|
if !f.IsValid() { |
||||||
|
f = v.FieldByName("UsesCgo") |
||||||
|
if !f.IsValid() { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
addr := unsafe.Pointer(f.UnsafeAddr()) |
||||||
|
*(*bool)(addr) = true |
||||||
|
|
||||||
|
return true |
||||||
|
} |
Loading…
Reference in new issue