build/analysis: copy cockroachdb code to third_party

We are currently fetching the full cockroach repository just for a hand
full of nogo passes. As the version we use is licensed under Apache 2.0,
we can copy them to third_party, allowing us to modify them and keep them
stable.

Change-Id: Ia28b181296138eef922485b6517d1e0066766715
Reviewed-on: https://review.monogon.dev/c/monogon/+/4486
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/third_party/com_github_cockroachdb_cockroach/unconvert/BUILD.bazel b/third_party/com_github_cockroachdb_cockroach/unconvert/BUILD.bazel
new file mode 100644
index 0000000..9e42ebf
--- /dev/null
+++ b/third_party/com_github_cockroachdb_cockroach/unconvert/BUILD.bazel
@@ -0,0 +1,20 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+filegroup(
+    name = "testdata",
+    srcs = glob(["testdata/**"]),
+    visibility = ["//pkg/testutils/lint/passes:__subpackages__"],
+)
+
+go_library(
+    name = "unconvert",
+    srcs = ["unconvert.go"],
+    importpath = "source.monogon.dev/third_party/com_github_cockroachdb_cockroach/unconvert",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//third_party/com_github_cockroachdb_cockroach/passesutil",
+        "@org_golang_x_tools//go/analysis",
+        "@org_golang_x_tools//go/analysis/passes/inspect",
+        "@org_golang_x_tools//go/ast/inspector",
+    ],
+)
diff --git a/third_party/com_github_cockroachdb_cockroach/unconvert/unconvert.go b/third_party/com_github_cockroachdb_cockroach/unconvert/unconvert.go
new file mode 100644
index 0000000..4a6655b
--- /dev/null
+++ b/third_party/com_github_cockroachdb_cockroach/unconvert/unconvert.go
@@ -0,0 +1,165 @@
+// Copyright 2016 The Cockroach Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package unconvert defines an Analyzer that detects unnecessary type
+// conversions.
+package unconvert
+
+import (
+	"go/ast"
+	"go/token"
+	"go/types"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/inspect"
+	"golang.org/x/tools/go/ast/inspector"
+
+	"source.monogon.dev/third_party/com_github_cockroachdb_cockroach/passesutil"
+)
+
+// Doc documents this pass.
+const Doc = `check for unnecessary type conversions`
+
+const name = "unconvert"
+
+// Analyzer defines this pass.
+var Analyzer = &analysis.Analyzer{
+	Name:     name,
+	Doc:      Doc,
+	Requires: []*analysis.Analyzer{inspect.Analyzer},
+	Run:      run,
+}
+
+// Adapted from https://github.com/mdempsky/unconvert/blob/beb68d938016d2dec1d1b078054f4d3db25f97be/unconvert.go#L371-L414.
+func run(pass *analysis.Pass) (interface{}, error) {
+	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+	nodeFilter := []ast.Node{
+		(*ast.CallExpr)(nil),
+	}
+	inspect.Preorder(nodeFilter, func(n ast.Node) {
+		call, ok := n.(*ast.CallExpr)
+		if !ok {
+			return
+		}
+		if len(call.Args) != 1 || call.Ellipsis != token.NoPos {
+			return
+		}
+		ft, ok := pass.TypesInfo.Types[call.Fun]
+		if !ok {
+			pass.Reportf(call.Pos(), "missing type")
+			return
+		}
+		if !ft.IsType() {
+			// Function call; not a conversion.
+			return
+		}
+		at, ok := pass.TypesInfo.Types[call.Args[0]]
+		if !ok {
+			pass.Reportf(call.Pos(), "missing type")
+			return
+		}
+		if !types.Identical(ft.Type, at.Type) {
+			// A real conversion.
+			return
+		}
+		if isUntypedValue(call.Args[0], pass.TypesInfo) {
+			// Workaround golang.org/issue/13061.
+			return
+		}
+		// Adapted from https://github.com/mdempsky/unconvert/blob/beb68d938016d2dec1d1b078054f4d3db25f97be/unconvert.go#L416-L430.
+		//
+		// cmd/cgo generates explicit type conversions that
+		// are often redundant when introducing
+		// _cgoCheckPointer calls (issue #16).  Users can't do
+		// anything about these, so skip over them.
+		if ident, ok := call.Fun.(*ast.Ident); ok {
+			if ident.Name == "_cgoCheckPointer" {
+				return
+			}
+		}
+		if passesutil.HasNolintComment(pass, call, name) {
+			return
+		}
+		pass.Reportf(call.Pos(), "unnecessary conversion")
+	})
+
+	return nil, nil
+}
+
+// Cribbed from https://github.com/mdempsky/unconvert/blob/beb68d938016d2dec1d1b078054f4d3db25f97be/unconvert.go#L557-L607.
+func isUntypedValue(n ast.Expr, info *types.Info) bool {
+	switch n := n.(type) {
+	case *ast.BinaryExpr:
+		switch n.Op {
+		case token.SHL, token.SHR:
+			// Shifts yield an untyped value if their LHS is untyped.
+			return isUntypedValue(n.X, info)
+		case token.EQL, token.NEQ, token.LSS, token.GTR, token.LEQ, token.GEQ:
+			// Comparisons yield an untyped boolean value.
+			return true
+		case token.ADD, token.SUB, token.MUL, token.QUO, token.REM,
+			token.AND, token.OR, token.XOR, token.AND_NOT,
+			token.LAND, token.LOR:
+			return isUntypedValue(n.X, info) && isUntypedValue(n.Y, info)
+		}
+	case *ast.UnaryExpr:
+		switch n.Op {
+		case token.ADD, token.SUB, token.NOT, token.XOR:
+			return isUntypedValue(n.X, info)
+		}
+	case *ast.BasicLit:
+		// Basic literals are always untyped.
+		return true
+	case *ast.ParenExpr:
+		return isUntypedValue(n.X, info)
+	case *ast.SelectorExpr:
+		return isUntypedValue(n.Sel, info)
+	case *ast.Ident:
+		if obj, ok := info.Uses[n]; ok {
+			if obj.Pkg() == nil && obj.Name() == "nil" {
+				// The universal untyped zero value.
+				return true
+			}
+			if b, ok := obj.Type().(*types.Basic); ok && b.Info()&types.IsUntyped != 0 {
+				// Reference to an untyped constant.
+				return true
+			}
+		}
+	case *ast.CallExpr:
+		if b, ok := asBuiltin(n.Fun, info); ok {
+			switch b.Name() {
+			case "real", "imag":
+				return isUntypedValue(n.Args[0], info)
+			case "complex":
+				return isUntypedValue(n.Args[0], info) && isUntypedValue(n.Args[1], info)
+			}
+		}
+	}
+
+	return false
+}
+
+// Cribbed from https://github.com/mdempsky/unconvert/blob/beb68d938016d2dec1d1b078054f4d3db25f97be/unconvert.go#L609-L630.
+func asBuiltin(n ast.Expr, info *types.Info) (*types.Builtin, bool) {
+	for {
+		paren, ok := n.(*ast.ParenExpr)
+		if !ok {
+			break
+		}
+		n = paren.X
+	}
+
+	ident, ok := n.(*ast.Ident)
+	if !ok {
+		return nil, false
+	}
+
+	obj, ok := info.Uses[ident]
+	if !ok {
+		return nil, false
+	}
+
+	b, ok := obj.(*types.Builtin)
+	return b, ok
+}