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/hash/BUILD.bazel b/third_party/com_github_cockroachdb_cockroach/hash/BUILD.bazel
new file mode 100644
index 0000000..3583690
--- /dev/null
+++ b/third_party/com_github_cockroachdb_cockroach/hash/BUILD.bazel
@@ -0,0 +1,12 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "hash",
+    srcs = ["hash.go"],
+    importpath = "source.monogon.dev/third_party/com_github_cockroachdb_cockroach/hash",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@org_golang_x_tools//go/analysis",
+        "@org_golang_x_tools//go/analysis/passes/inspect",
+    ],
+)
diff --git a/third_party/com_github_cockroachdb_cockroach/hash/hash.go b/third_party/com_github_cockroachdb_cockroach/hash/hash.go
new file mode 100644
index 0000000..aac9ab7
--- /dev/null
+++ b/third_party/com_github_cockroachdb_cockroach/hash/hash.go
@@ -0,0 +1,139 @@
+// Copyright 2016 The Cockroach Authors.
+// SPDX-License-Identifier: Apache-2.0
+
+// Package hash defines an Analyzer that detects correct use of hash.Hash.
+package hash
+
+import (
+	"go/ast"
+	"go/types"
+
+	"golang.org/x/tools/go/analysis"
+	"golang.org/x/tools/go/analysis/passes/inspect"
+)
+
+// Doc documents this pass.
+const Doc = `check for correct use of hash.Hash`
+
+// Analyzer defines this pass.
+var Analyzer = &analysis.Analyzer{
+	Name:     "hash",
+	Doc:      Doc,
+	Requires: []*analysis.Analyzer{inspect.Analyzer},
+	Run:      run,
+}
+
+// hashChecker assures that the hash.Hash interface is not misused. A common
+// mistake is to assume that the Sum function returns the hash of its input,
+// like so:
+//
+//	hashedBytes := sha256.New().Sum(inputBytes)
+//
+// In fact, the parameter to Sum is not the bytes to be hashed, but a slice that
+// will be used as output in case the caller wants to avoid an allocation. In
+// the example above, hashedBytes is not the SHA-256 hash of inputBytes, but
+// the concatenation of inputBytes with the hash of the empty string.
+//
+// Correct uses of the hash.Hash interface are as follows:
+//
+//	h := sha256.New()
+//	h.Write(inputBytes)
+//	hashedBytes := h.Sum(nil)
+//
+//	h := sha256.New()
+//	h.Write(inputBytes)
+//	var hashedBytes [sha256.Size]byte
+//	h.Sum(hashedBytes[:0])
+//
+// To differentiate between correct and incorrect usages, hashChecker applies a
+// simple heuristic: it flags calls to Sum where a) the parameter is non-nil and
+// b) the return value is used.
+//
+// The hash.Hash interface may be remedied in Go 2. See golang/go#21070.
+func run(pass *analysis.Pass) (interface{}, error) {
+	selectorIsHash := func(s *ast.SelectorExpr) bool {
+		tv, ok := pass.TypesInfo.Types[s.X]
+		if !ok {
+			return false
+		}
+		named, ok := tv.Type.(*types.Named)
+		if !ok {
+			return false
+		}
+		if named.Obj().Type().String() != "hash.Hash" {
+			return false
+		}
+		return true
+	}
+
+	stack := make([]ast.Node, 0, 32)
+	forAllFiles(pass.Files, func(n ast.Node) bool {
+		if n == nil {
+			stack = stack[:len(stack)-1] // pop
+			return true
+		}
+		stack = append(stack, n) // push
+
+		// Find a call to hash.Hash.Sum.
+		selExpr, ok := n.(*ast.SelectorExpr)
+		if !ok {
+			return true
+		}
+		if selExpr.Sel.Name != "Sum" {
+			return true
+		}
+		if !selectorIsHash(selExpr) {
+			return true
+		}
+		callExpr, ok := stack[len(stack)-2].(*ast.CallExpr)
+		if !ok {
+			return true
+		}
+		if len(callExpr.Args) != 1 {
+			return true
+		}
+		// We have a valid call to hash.Hash.Sum.
+
+		// Is the argument nil?
+		var nilArg bool
+		if id, ok := callExpr.Args[0].(*ast.Ident); ok && id.Name == "nil" {
+			nilArg = true
+		}
+
+		// Is the return value unused?
+		var retUnused bool
+	Switch:
+		switch t := stack[len(stack)-3].(type) {
+		case *ast.AssignStmt:
+			for i := range t.Rhs {
+				if t.Rhs[i] == stack[len(stack)-2] {
+					if id, ok := t.Lhs[i].(*ast.Ident); ok && id.Name == "_" {
+						// Assigning to the blank identifier does not count as using the
+						// return value.
+						retUnused = true
+					}
+					break Switch
+				}
+			}
+			panic("unreachable")
+		case *ast.ExprStmt:
+			// An expression statement means the return value is unused.
+			retUnused = true
+		default:
+		}
+
+		if !nilArg && !retUnused {
+			pass.Reportf(callExpr.Pos(), "probable misuse of hash.Hash.Sum: "+
+				"provide parameter or use return value, but not both")
+		}
+		return true
+	})
+
+	return nil, nil
+}
+
+func forAllFiles(files []*ast.File, fn func(node ast.Node) bool) {
+	for _, f := range files {
+		ast.Inspect(f, fn)
+	}
+}