test.bash: add staticcheck to our integration tests

This adds the use of the staticcheck tool to check our code.
The use of staticcheck would have caught a bug in CL/211737.
Also, we adjust one minor code patterns to avoid triggering
one of the staticcheck checks.

This CL also modifies the integration script to check SHA256
checksums for archives downloaded from a third-party source.

Change-Id: Ieb69ae79464de62970448195c77cdd88e21fc48f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/212220
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/integration_test.go b/integration_test.go
index 6118088..721aec9 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -10,6 +10,7 @@
 	"archive/tar"
 	"bytes"
 	"compress/gzip"
+	"crypto/sha256"
 	"flag"
 	"fmt"
 	"io"
@@ -33,8 +34,18 @@
 	buildRelease = flag.Bool("buildRelease", false, "build release binaries")
 
 	protobufVersion = "ef7cc811" // v3.12.0-rc1
-	golangVersions  = []string{"1.9.7", "1.10.8", "1.11.13", "1.12.17", "1.13.11", "1.14.3"}
-	golangLatest    = golangVersions[len(golangVersions)-1]
+	protobufSHA256  = ""         // ignored if protobufVersion is a git hash
+
+	golangVersions = []string{"1.9.7", "1.10.8", "1.11.13", "1.12.17", "1.13.11", "1.14.3"}
+	golangLatest   = golangVersions[len(golangVersions)-1]
+
+	staticcheckVersion = "2020.1.4"
+	staticcheckSHA256s = map[string]string{
+		"darwin/386":   "05ccb332a0c5ba812af165b0e69ffe317cb3e8bb10b0f4b4c4eaaf956ba9a50b",
+		"darwin/amd64": "5706d101426c025e8f165309e0cb2932e54809eb035ff23ebe19df0f810699d8",
+		"linux/386":    "e4dbf94e940678ae7108f0d22c7c2992339bc10a8fb384e7e734b1531a429a1c",
+		"linux/amd64":  "09d2c2002236296de2c757df111fe3ae858b89f9e183f645ad01f8135c83c519",
+	}
 
 	// purgeTimeout determines the maximum age of unused sub-directories.
 	purgeTimeout = 30 * 24 * time.Hour // 1 month
@@ -115,6 +126,29 @@
 	}
 	wg.Wait()
 
+	t.Run("GoStaticCheck", func(t *testing.T) {
+		checks := []string{
+			"all",     // start with all checks enabled
+			"-SA1019", // disable deprecated usage check
+			"-S*",     // disable code simplication checks
+			"-ST*",    // disable coding style checks
+			"-U*",     // disable unused declaration checks
+		}
+		out := mustRunCommand(t, "staticcheck", "-checks="+strings.Join(checks, ","), "-fail=none", "./...")
+
+		// Filter out findings from certain paths.
+		var findings []string
+		for _, finding := range strings.Split(strings.TrimSpace(out), "\n") {
+			switch {
+			case strings.HasPrefix(finding, "internal/testprotos/legacy/"):
+			default:
+				findings = append(findings, finding)
+			}
+		}
+		if len(findings) > 0 {
+			t.Fatalf("staticcheck findings:\n%v", strings.Join(findings, "\n"))
+		}
+	})
 	t.Run("CommittedGitChanges", func(t *testing.T) {
 		if strings.TrimSpace(gitDiff) != "" {
 			t.Fatalf("uncommitted changes")
@@ -214,7 +248,7 @@
 			command{Dir: protobufPath}.mustRun(t, "git", "checkout", protobufVersion)
 		} else {
 			url := fmt.Sprintf("https://github.com/google/protobuf/releases/download/v%v/protobuf-all-%v.tar.gz", protobufVersion, protobufVersion)
-			downloadArchive(check, protobufPath, url, "protobuf-"+protobufVersion)
+			downloadArchive(check, protobufPath, url, "protobuf-"+protobufVersion, protobufSHA256)
 		}
 
 		fmt.Printf("build %v\n", filepath.Base(protobufPath))
@@ -234,7 +268,7 @@
 		if _, err := os.Stat(workingDir); err != nil {
 			fmt.Printf("download %v\n", filepath.Base(workingDir))
 			url := fmt.Sprintf("https://dl.google.com/go/go%v.%v-%v.tar.gz", v, runtime.GOOS, runtime.GOARCH)
-			downloadArchive(check, workingDir, url, "go")
+			downloadArchive(check, workingDir, url, "go", "") // skip SHA256 check as we fetch over https from a trusted domain
 		}
 		registerBinary("go"+v, filepath.Join(workingDir, "bin", "go"))
 	}
@@ -242,6 +276,16 @@
 	registerBinary("gofmt", filepath.Join(testDir, "go"+golangLatest, "bin", "gofmt"))
 	workingDir = ""
 
+	// Download the staticcheck tool.
+	workingDir = filepath.Join(testDir, "staticcheck-"+staticcheckVersion)
+	if _, err := os.Stat(workingDir); err != nil {
+		fmt.Printf("download %v\n", filepath.Base(workingDir))
+		url := fmt.Sprintf("https://github.com/dominikh/go-tools/releases/download/%v/staticcheck_%v_%v.tar.gz", staticcheckVersion, runtime.GOOS, runtime.GOARCH)
+		downloadArchive(check, workingDir, url, "staticcheck", staticcheckSHA256s[runtime.GOOS+"/"+runtime.GOARCH])
+	}
+	registerBinary("staticcheck", filepath.Join(workingDir, "staticcheck"))
+	workingDir = ""
+
 	// Travis-CI sets GOROOT, which confuses invocations of the Go toolchain.
 	// Explicitly clear GOROOT, so each toolchain uses their default GOROOT.
 	check(os.Unsetenv("GOROOT"))
@@ -273,14 +317,25 @@
 	check(err)
 }
 
-func downloadArchive(check func(error), dstPath, srcURL, skipPrefix string) {
+func downloadArchive(check func(error), dstPath, srcURL, skipPrefix, wantSHA256 string) {
 	check(os.RemoveAll(dstPath))
 
 	resp, err := http.Get(srcURL)
 	check(err)
 	defer resp.Body.Close()
 
-	zr, err := gzip.NewReader(resp.Body)
+	var r io.Reader = resp.Body
+	if wantSHA256 != "" {
+		b, err := ioutil.ReadAll(resp.Body)
+		check(err)
+		r = bytes.NewReader(b)
+
+		if gotSHA256 := fmt.Sprintf("%x", sha256.Sum256(b)); gotSHA256 != wantSHA256 {
+			check(fmt.Errorf("checksum validation error:\ngot  %v\nwant %v", gotSHA256, wantSHA256))
+		}
+	}
+
+	zr, err := gzip.NewReader(r)
 	check(err)
 
 	tr := tar.NewReader(zr)