build/analysis/importsort: init

This adds an analyzer which enforces import sorting as per
CODING_STANDARDS.md. It is not yet enabled in nogo.

This effectively duplicates some logic that is present in goimports.
However, that logic is mostly within an 'internal' package in x/tools,
and would be somewhat tricky to make work within the framework of an
analysis.Analyser (as it expects to also mutate a file). Thus, we
end up writing the analysis logic ourselves. Tests are provided to make
sure this logic doesn't rot away.

We also move some common logic from 'noioutil' to a new 'lib' package,
and implement the last piece of the puzzle there: a code generator to
provide information about the toolchain's stdlib as a map/set.

Change-Id: Ia0f32d6f9122e13117d18ae781d8255c6e3a887d
Reviewed-on: https://review.monogon.dev/c/monogon/+/494
Reviewed-by: Leopold Schabel <leo@nexantic.com>
diff --git a/build/analysis/importsort/classify.go b/build/analysis/importsort/classify.go
new file mode 100644
index 0000000..dc5c45d
--- /dev/null
+++ b/build/analysis/importsort/classify.go
@@ -0,0 +1,54 @@
+package importsort
+
+import (
+	"strings"
+
+	alib "source.monogon.dev/build/analysis/lib"
+)
+
+// groupClass is the 'class' of a given import path or import group.
+type groupClass string
+
+const (
+	// groupClassMixed are import group that contain multiple different classes of
+	// import paths.
+	groupClassMixed = "mixed"
+	// groupClassStdlib is an import path or group that contains only Go standard
+	// library imports.
+	groupClassStdlib = "stdlib"
+	// groupClassGlobal is an import path or group that contains only third-party
+	// packages, ie. all packages that aren't part of stdlib and aren't local to the
+	// Monogon codebase.
+	groupClassGlobal = "global"
+	// groupClassLocal is an import path or group that contains only package that
+	// are local to the Monogon codebase.
+	groupClassLocal = "local"
+)
+
+// classifyImport returns a groupClass for a given import path.
+func classifyImport(path string) groupClass {
+	if alib.StdlibPackages[path] {
+		return groupClassStdlib
+	}
+	if strings.HasPrefix(path, "source.monogon.dev/") {
+		return groupClassLocal
+	}
+	return groupClassGlobal
+}
+
+// classifyImportGroup returns a groupClass for a list of import paths that are
+// part of a single import group.
+func classifyImportGroup(paths []string) groupClass {
+	res := groupClass("")
+	for _, p := range paths {
+		if res == "" {
+			res = classifyImport(p)
+			continue
+		}
+		class := classifyImport(p)
+		if res != class {
+			return groupClassMixed
+		}
+	}
+	return res
+}