Explicitly define Rust default lints

Add documentation on how lints are defined and used in Android. Merge
the deny_warnings attribute with a new attribute (no_lint) which can be
used to disable the default linting parameters.
Explicitly allow all lints for external/ and prebuilts/, which remove
any warning when building sysroot for the devices.

Test: cd external/rust/crates; mma
Test: add dummy internal Rust module; mma
Change-Id: I62be1c41aeda4068fb9e288038727c1de5ffe547
diff --git a/rust/config/lints.go b/rust/config/lints.go
new file mode 100644
index 0000000..529d094
--- /dev/null
+++ b/rust/config/lints.go
@@ -0,0 +1,150 @@
+// Copyright 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+// Overarching principles for Rust lints on Android:
+// The Android build system tries to avoid reporting warnings during the build.
+// Therefore, by default, we upgrade warnings to denials. For some of these
+// lints, an allow exception is setup, using the variables below.
+// There are different default lints depending on the repository location (see
+// DefaultLocalClippyChecks).
+// The lints are split into two categories. The first one contains the built-in
+// lints (https://doc.rust-lang.org/rustc/lints/index.html). The second is
+// specific to Clippy lints (https://rust-lang.github.io/rust-clippy/master/).
+//
+// When developing a module, it is possible to use the `no_lint` property to
+// disable the built-in lints configuration. It is also possible to set
+// `clippy` to false to disable the clippy verification. Expect some
+// questioning during review if you enable one of these options. For external/
+// code, if you need to use them, it is likely a bug. Otherwise, it may be
+// useful to add an exception (that is, move a lint from deny to allow).
+var (
+	// Default Rust lints that applies to Google-authored modules.
+	defaultRustcLints = []string{
+		"-A deprecated",
+		"-D missing-docs",
+		"-D warnings",
+	}
+	// Default Clippy lints. These are applied on top of defaultRustcLints.
+	// It should be assumed that any warning lint will be promoted to a
+	// deny.
+	defaultClippyLints = []string{
+		"-A clippy::type-complexity",
+	}
+
+	// Rust lints for vendor code.
+	defaultRustcVendorLints = []string{
+		"-A deprecated",
+		"-D warnings",
+	}
+	// Clippy lints for vendor source. These are applied on top of
+	// defaultRustcVendorLints.  It should be assumed that any warning lint
+	// will be promoted to a deny.
+	defaultClippyVendorLints = []string{
+		"-A clippy::complexity",
+		"-A clippy::perf",
+		"-A clippy::style",
+	}
+
+	// For prebuilts/ and external/, no linting is expected. If a warning
+	// or a deny is reported, it should be fixed upstream.
+	allowAllLints = []string{
+		"--cap-lints allow",
+	}
+)
+
+func init() {
+	// Default Rust lints. These apply to all Google-authored modules.
+	pctx.VariableFunc("RustDefaultLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_DEFAULT_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultRustcLints, " ")
+	})
+	pctx.VariableFunc("ClippyDefaultLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("CLIPPY_DEFAULT_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultClippyLints, " ")
+	})
+
+	// Rust lints that only applies to external code.
+	pctx.VariableFunc("RustVendorLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("RUST_VENDOR_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultRustcVendorLints, " ")
+	})
+	pctx.VariableFunc("ClippyVendorLints", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("CLIPPY_VENDOR_LINTS"); override != "" {
+			return override
+		}
+		return strings.Join(defaultClippyVendorLints, " ")
+	})
+	pctx.StaticVariable("RustAllowAllLints", strings.Join(allowAllLints, " "))
+}
+
+type PathBasedClippyConfig struct {
+	PathPrefix    string
+	RustcConfig   string
+	ClippyEnabled bool
+	ClippyConfig  string
+}
+
+const noLint = ""
+const rustcDefault = "${config.RustDefaultLints}"
+const rustcVendor = "${config.RustVendorLints}"
+const rustcAllowAll = "${config.RustAllowAllLints}"
+const clippyDefault = "${config.ClippyDefaultLints}"
+const clippyVendor = "${config.ClippyVendorLints}"
+
+// This is a map of local path prefixes to a set of parameters for the linting:
+// - a string for the lints to apply to rustc.
+// - a boolean to indicate if clippy should be executed.
+// - a string for the lints to apply to clippy.
+// The first entry matching will be used.
+var DefaultLocalClippyChecks = []PathBasedClippyConfig{
+	{"external/", rustcAllowAll, false, noLint},
+	{"hardware/", rustcVendor, true, clippyVendor},
+	{"prebuilts/", rustcAllowAll, false, noLint},
+	{"vendor/google", rustcDefault, true, clippyDefault},
+	{"vendor/", rustcVendor, true, clippyVendor},
+}
+
+// ClippyLintsForDir returns a boolean if Clippy should be executed and if so, the lints to be used.
+func ClippyLintsForDir(dir string) (bool, string) {
+	for _, pathCheck := range DefaultLocalClippyChecks {
+		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
+			return pathCheck.ClippyEnabled, pathCheck.ClippyConfig
+		}
+	}
+	return true, clippyDefault
+}
+
+// RustcLintsForDir returns the standard lints to be used for a repository.
+func RustcLintsForDir(dir string) string {
+	for _, pathCheck := range DefaultLocalClippyChecks {
+		if strings.HasPrefix(dir, pathCheck.PathPrefix) {
+			return pathCheck.RustcConfig
+		}
+	}
+	return rustcDefault
+}